GCC Inline Assembly – A GCC fordító belső összeszerelője , amely egy makróleíró nyelv a lefordított magas szintű kódok interfészéhez összeállítás- beillesztéssel .
A GCC Inline Assembly szintaxisa és szemantikája a következő jelentős különbségekkel rendelkezik:
A GCC Inline Assembly működésének megértéséhez jól kell ismernie a fordítási folyamat lépéseit.
Kezdetben a gcc meghívja a cpp előfeldolgozót, amely tartalmazza a fejlécfájlokat , kibontja az összes feltételes direktívát, és makróhelyettesítéseket hajt végre. Megnézheti, mi történt a makró paranccsal történő helyettesítése után gcc -E -o preprocessed.c some_file.c. Az -E kapcsolót ritkán használják, főleg makrók hibakeresésekor.
Ezután a gcc elemzi a kapott kódot, optimalizálja a kódot ugyanabban a fázisban, és végül összeszerelő kódot állít elő. A generált assembler kódot a paranccsal láthatja gcc -S -o some_file.S some_file.c.
Ezután a gcc meghívja az assembler gázt, hogy objektumkódot hozzon létre az assembler kódból . Általában a -c (csak fordítás) kapcsolót használják sok fájlból álló projektekben.
A gcc ezután meghívja az ld linkert , hogy a kapott objektumfájlokból létrehozza a végrehajtható fájlt .
A folyamat szemléltetésére hozzunk létre egy test.c fájlt a következő tartalommal:
int main () { asm ( "Bla-Bla-Bla" ); // ilyen utasítás beszúrása return 0 ; }Ha a fordítás során a -Wimplicit-function-declaration "Implicit asm függvénydeklaráció" figyelmeztetés keletkezik, használja:
__asm__ ( "Bla-Bla-Bla" );Ha azt mondjuk, hogy execute gcc -S -o test.S test.c, akkor egy fontos tényt fedezünk fel: a fordító feldolgozta a "rossz" utasítást, és az eredményül kapott assembler file test.S tartalmazza a "Bla-Bla-Bla" karakterláncunkat. Ha azonban objektumkódot vagy bináris fájlt próbálunk létrehozni, akkor a gcc a következőt adja ki:
test.c: Assembler üzenetei: test.c:3: Hiba: nincs ilyen utasítás: 'Bla-Bla-Bla'
Az üzenet az összeszerelőtől érkezik.
Ebből egy fontos következtetés következik: a GCC semmilyen módon nem értelmezi az assembler beillesztés tartalmát, azt fordítási idejű makróhelyettesítésként fogja fel.
Az összeszerelő betét általános felépítése a következő:
asm [volatile]("assembler parancsok és direktívák" : kimeneti paraméterek : bemeneti paraméterek : változtatható paraméterek);
Van azonban egy rövidebb forma is:
asm [illékony] ("összeszerelő utasítások");
A gas assembler és a gcc fordító jellemzője, hogy az AT&T szintaxist használják, ami szokatlan az x86-nál , ami jelentősen eltér az Intel szintaxisától . Főbb különbségek [1] :
Jó szolgálatot tehet az az általában figyelmen kívül hagyott tény, hogy az asm direktíván belül nem csak assembler parancsok lehetnek, hanem általában bármilyen, a gas által felismert direktíva. Például beillesztheti egy bináris fájl tartalmát a kapott objektumkódba:
asm ( "adatfájlunk: \n\t " ".incbin \" some_bin_file.txt \"\n\t " // használja a "our_data_file_len: \n\t " .incbin direktívát ".long .-our_data_file \n\t " // .long érték beszúrása számított fájlhosszal );Ezután címezze meg ezt a bináris fájlt:
extern char our_data_file []; extern long our_data_file_len ;Lássuk, hogyan történik a helyettesítés.
Tervezés:
asm ( "movl %0,%%eax" :: "i" ( 1 ));át fog alakulni
movl $1 , %eaxA volatile kulcsszó arra szolgál, hogy jelezze a fordítónak, hogy a beillesztett assembler kódnak lehetnek mellékhatásai, így az optimalizálási kísérletek logikai hibákhoz vezethetnek.
Azok az esetek, amikor a volatile kulcsszó kötelező:
Tegyük fel, hogy van egy assembler beszúrás a cikluson belül, amely ellenőrzi egy globális változó alkalmazását, és a spinlockban várja a kiadását. Amikor a fordító elkezdi optimalizálni a ciklust, mindent kidob a ciklusból, ami nincs kifejezetten megváltoztatva a ciklusban. Mivel ebben az esetben az optimalizáló fordító nem lát explicit kapcsolatot az assembler inszert paraméterei és a ciklusban változó változók között, az assembler beszúrást ki lehet dobni a ciklusból az ebből eredő összes következménnyel.
TIPP: Mindig adja meg az asm volatile -t azokban az esetekben, amikor az assembler betétnek „ott kell lennie, ahol van”. Ez különösen igaz, ha atomi primitívekkel dolgozik.
A következő "finom momentum" a "memória" kifejezett megjelölése a cölöplistában. Amellett, hogy egyszerűen közli a fordítóval, hogy az assembler beillesztés megváltoztatja a memória tartalmát, memóriakorlát direktívaként is szolgál a fordító számára. Ez azt jelenti, hogy azok a memóriaelérési műveletek, amelyek magasabbak a kódban, az eredményül kapott gépi kódban kerülnek végrehajtásra, mielőtt azok, amelyek alacsonyabbak, mint az assembler beillesztése. Többszálú környezet esetén, amikor a versenyhelyzet kockázata közvetlenül ettől függ, ez a körülmény elengedhetetlen.
TIPP #1:
Memóriakorlát készítésének gyors módja
#define mbarrier() asm volatile ("":::"memória")TIPP #2: A „memória” megadása a cölöplistában nem csak „jó gyakorlat”, hanem a versenyfeltételek megoldására tervezett atomműveletek esetében is kötelező.