GCC Inline Assembly - Inline assembler af GCC compileren , som er et makrobeskrivelsessprog til grænsefladen af kompileret højniveaukode med assembly-indsættelse .
Syntaksen og semantikken for GCC Inline Assembly har følgende væsentlige forskelle:
For at forstå, hvordan GCC Inline Assembly fungerer, skal du have en god forståelse af de trin, der er involveret i kompileringsprocessen.
I begyndelsen kalder gcc cpp-forprocessoren, som inkluderer header-filerne , udvider alle betingede direktiver og udfører makroerstatninger. Du kan se, hvad der skete efter makrosubstitution med kommandoen gcc -E -o preprocessed.c some_file.c. -E switchen bruges sjældent, mest når du fejlfinder makroer.
Derefter analyserer gcc den resulterende kode, optimerer koden i samme fase og producerer til sidst assembler-kode. Du kan se den genererede assembler-kode med kommandoen gcc -S -o some_file.S some_file.c.
Derefter kalder gcc assembler-gassen for at oprette objektkode fra assembler-koden . Normalt bruges switchen -c (kun kompilering) i projekter, der består af mange filer.
gcc kalder derefter ld- linkeren for at bygge den eksekverbare fra de resulterende objektfiler .
For at illustrere denne proces, lad os oprette en test.c-fil med følgende indhold:
int main () { asm ( "Bla-Bla-Bla" ); // indsæt en sådan instruktion returnerer 0 ; }Hvis advarslen -Wimplicit-function-declaration "Implicit asm function declaration" genereres under kompilering, skal du bruge:
__asm__ ( "Bla-Bla-Bla" );Hvis vi siger execute gcc -S -o test.S test.c, så opdager vi en vigtig kendsgerning: compileren behandlede den "forkerte" instruktion, og den resulterende assembler-filtest.S indeholder vores streng "Bla-Bla-Bla". Men hvis vi forsøger at oprette objektkode eller bygge en binær fil, vil gcc udsende følgende:
test.c: Assembler-meddelelser: test.c:3: Fejl: ingen sådan instruktion: 'Bla-Bla-Bla'
Beskeden kommer fra samleren.
En vigtig konklusion følger af dette: GCC fortolker ikke indholdet af assembler-indsættet på nogen måde, idet det opfatter det som en makrosubstitution på kompileringstidspunktet.
Den generelle struktur af monteringsindsatsen er som følger:
asm [volatile]("assembler-kommandoer og -direktiver": outputparametre: inputparametre: mutable parametre);
Der er dog også en kortere form:
asm [flygtig] ("monteringsvejledning");
Et træk ved gassamleren og gcc-kompileren er, at de bruger AT&T-syntaksen , hvilket er usædvanligt for x86 , som adskiller sig væsentligt fra Intel-syntaksen . Hovedforskelle [1] :
Den normalt ignorerede kendsgerning, at inde i asm-direktivet ikke kun kan være assembler-kommandoer, men generelt alle direktiver, der genkendes af gas, kan tjene godt. For eksempel kan du indsætte indholdet af en binær fil i den resulterende objektkode:
asm ( "vores_data_fil: \n\t " ".incbin \" some_bin_file.txt \"\n\t " // brug .incbin-direktivet "our_data_file_len: \n\t " ".long .-our_data_file \n\t " // indsæt .long værdi med beregnet fillængde );Og adresser derefter denne binære fil:
ekstern char vores_data_fil []; ekstern lang our_data_file_len ;Lad os se, hvordan udskiftningen sker.
Design:
asm ( "movl %0,%%eax" :: "i" ( 1 ));vil blive til
movl $1 , %eaxDet flygtige nøgleord bruges til at indikere over for compileren, at den indsatte assemblerkode kan have bivirkninger, så optimeringsforsøg kan føre til logiske fejl.
Tilfælde, hvor det flygtige søgeord er obligatorisk:
Antag, at der er en assembler-indsats inde i løkken, der kontrollerer, om en global variabel anvendes, og som venter i spinlocken på dens frigivelse. Når compileren begynder at optimere løkken, smider den alt ud fra løkken, som ikke er eksplicit ændret i løkken. Da optimeringskompileren i dette tilfælde ikke ser et eksplicit forhold mellem parametrene for assembler-indsatsen og de variabler, der ændrer sig i løkken, kan assembler-indsættet blive smidt ud af løkken med alle de deraf følgende konsekvenser.
TIP: Angiv altid asm volatile i tilfælde, hvor din assembler-indsats skal "være, hvor den er". Dette gælder især, når du arbejder med atomare primitiver.
Det næste "subtile øjeblik" er den eksplicitte indikation af "hukommelse" i clobberlisten. Ud over blot at fortælle compileren, at en assembler-indsats ændrer indholdet af hukommelsen, fungerer den også som et Memory Barrier - direktiv for compileren. Dette betyder, at de hukommelsesadgangsoperationer, der er højere i koden, vil blive udført i den resulterende maskinkode før dem, der er lavere end assembler-indsatsen. I tilfælde af et multi-threaded miljø, når risikoen for en race tilstand direkte afhænger af dette , er denne omstændighed afgørende.
TIP #1:
En hurtig måde at lave en hukommelsesbarriere på
#define mbarrier() asm flygtig ("":::"hukommelse")TIP #2: Angivelse af "hukommelse" i clobber-listen er ikke kun "god praksis", men også i tilfælde af at arbejde med atomoperationer designet til at løse racetilstanden, er obligatorisk.