C Language
Inline assemblage
Zoeken…
Opmerkingen
Inline assemblage is de praktijk van het toevoegen van montage-instructies in het midden van de C-broncode. Geen ISO C-standaard vereist ondersteuning van inline-assemblage. Aangezien dit niet vereist is, varieert de syntaxis voor inline-assemblage van compiler tot compiler. Hoewel het meestal wordt ondersteund, zijn er zeer weinig redenen om inline-assemblage te gebruiken en veel redenen om dit niet te doen.
Pros
- Prestaties Door de specifieke montage-instructies voor een bewerking te schrijven, kunt u betere prestaties behalen dan de montagecode die door de compiler wordt gegenereerd. Merk op dat deze prestatiewinst zeldzaam is. In de meeste gevallen kunt u betere prestaties behalen door uw C-code te herschikken zodat de optimizer zijn werk kan doen.
- Hardware-interface Apparaatstuurprogramma of opstartcode van processor kan enige assemblagecode nodig hebben om toegang te krijgen tot de juiste registers en om te garanderen dat bepaalde bewerkingen in een specifieke volgorde met een specifieke vertraging tussen bewerkingen plaatsvinden.
Cons
- Compiler-portabiliteit Syntaxis voor inline-assemblage is niet gegarandeerd hetzelfde van de ene compiler naar de andere. Als u code schrijft met inline assembly die door verschillende compilers moet worden ondersteund, gebruik dan preprocessormacro's (
#ifdef
) om te controleren welke compiler wordt gebruikt. Schrijf vervolgens een afzonderlijk inline-assemblagegedeelte voor elke ondersteunde compiler. - Draagbaarheid van processor U kunt geen inline assembly schrijven voor een x86-processor en verwachten dat deze op een ARM-processor werkt. Inline assembly is bedoeld om te worden geschreven voor een specifieke processor of processorfamilie. Als u inline assemblage hebt die u op verschillende processors wilt ondersteunen, gebruik dan preprocessormacro's om te controleren voor welke processor de code wordt gecompileerd en om de juiste assemblagecodesectie te selecteren.
- Toekomstige prestatiewijzigingen Inline assemblage kan worden geschreven met vertragingen op basis van een bepaalde kloksnelheid van de processor. Als het programma is samengesteld voor een processor met een snellere klok, werkt de montagecode mogelijk niet zoals verwacht.
gcc Basic asm-ondersteuning
Basisassemblage-ondersteuning met gcc heeft de volgende syntaxis:
asm [ volatile ] ( AssemblerInstructions )
waarbij AssemblerInstructions
de directe montagecode is voor de gegeven processor. Het vluchtige sleutelwoord is optioneel en heeft geen effect, aangezien gcc code niet optimaliseert binnen een standaard asm-statement. AssemblerInstructions
instructies kunnen meerdere montage-instructies bevatten. Een eenvoudige asm-instructie wordt gebruikt als u een asm-routine hebt die buiten een C-functie moet bestaan. Het volgende voorbeeld komt uit de GCC-handleiding:
/* Note that this code will not compile with -masm=intel */
#define DebugBreak() asm("int $3")
In dit voorbeeld kunt u DebugBreak()
op andere plaatsen in uw code gebruiken en wordt de montage-instructie in int $3
. Merk op dat hoewel gcc geen code in een standaard asm-instructie zal wijzigen, de optimizer toch opeenvolgende asm-instructies kan verplaatsen. Als u meerdere montage-instructies hebt die in een specifieke volgorde moeten voorkomen, neemt u deze op in één ASM-instructie.
gcc Uitgebreide asm-ondersteuning
Uitgebreide asm-ondersteuning in gcc heeft de volgende syntaxis:
asm [volatile] ( AssemblerTemplate
: OutputOperands
[ : InputOperands
[ : Clobbers ] ])
asm [volatile] goto ( AssemblerTemplate
:
: InputOperands
: Clobbers
: GotoLabels)
waar AssemblerTemplate
de sjabloon is voor de assemblerinstructie, OutputOperands
zijn alle C-variabelen die kunnen worden gewijzigd door de assemblagecode, InputOperands
zijn alle C-variabelen die worden gebruikt als invoerparameters, Clobbers
zijn een lijst of registers die worden gewijzigd door de assemblagecode en GotoLabels
zijn alle ga-instructie-labels die in de assemblagecode kunnen worden gebruikt.
Het uitgebreide formaat wordt gebruikt binnen C-functies en is het meer gebruikelijke gebruik van inline-assemblage. Hieronder is een voorbeeld uit de Linux-kernel voor byte-uitwisseling van 16-bits en 32-bits nummers voor een ARM-processor:
/* From arch/arm/include/asm/swab.h in Linux kernel version 4.6.4 */
#if __LINUX_ARM_ARCH__ >= 6
static inline __attribute_const__ __u32 __arch_swahb32(__u32 x)
{
__asm__ ("rev16 %0, %1" : "=r" (x) : "r" (x));
return x;
}
#define __arch_swahb32 __arch_swahb32
#define __arch_swab16(x) ((__u16)__arch_swahb32(x))
static inline __attribute_const__ __u32 __arch_swab32(__u32 x)
{
__asm__ ("rev %0, %1" : "=r" (x) : "r" (x));
return x;
}
#define __arch_swab32 __arch_swab32
#endif
Elke asm-sectie gebruikt de variabele x
als invoer- en uitvoerparameter. De functie C retourneert vervolgens het gemanipuleerde resultaat.
Met het uitgebreide asm-formaat kan gcc de montage-instructies in een asm-blok optimaliseren volgens dezelfde regels die het gebruikt voor het optimaliseren van de C-code. Als u wilt dat uw asm-sectie onaangeroerd blijft, gebruikt u het volatile
trefwoord voor de asm-sectie.
gcc Inline-assemblage in macro's
We kunnen montage-instructies in een macro plaatsen en de macro gebruiken zoals je een functie zou noemen.
#define mov(x,y) \
{ \
__asm__ ("l.cmov %0,%1,%2" : "=r" (x) : "r" (y), "r" (0x0000000F)); \
}
/// some definition and assignment
unsigned char sbox[size][size];
unsigned char sbox[size][size];
///Using
mov(state[0][1], sbox[si][sj]);
Het gebruik van inline montage-instructies ingebed in C-code kan de looptijd van een programma verbeteren. Dit is zeer nuttig in tijdkritieke situaties zoals cryptografische algoritmen zoals AES. Voor een eenvoudige shift-operatie die nodig is in het AES-algoritme, kunnen we bijvoorbeeld een directe instructie voor Rotate Right
vervangen door C shift-operator >>
.
In een implementatie van 'AES256', in de functie 'AddRoundKey ()', hebben we enkele verklaringen zoals deze:
unsigned int w; // 32-bit
unsigned char subkey[4]; // 8-bit, 4*8 = 32
subkey[0] = w >> 24; // hold 8 bit, MSB, leftmost group of 8-bits
subkey[1] = w >> 16; // hold 8 bit, second group of 8-bit from left
subkey[2] = w >> 8; // hold 8 bit, second group of 8-bit from right
subkey[3] = w; // hold 8 bit, LSB, rightmost group of 8-bits
/// subkey <- w
Ze wijzen eenvoudigweg de bitwaarde van w
aan een subkey
.
We kunnen de drie shift + -toewijzingen en een C-expressie toewijzen met slechts één bewerking Rotate Right
.
__asm__ ("l.ror %0,%1,%2" : "=r" (* (unsigned int *) subkey) : "r" (w), "r" (0x10));
Het eindresultaat is precies hetzelfde.