Konvence volání - Calling convention

Ve vědě o počítačích , je konvence volání je implementační úrovni (low-level) režim, jak podprogramy přijímat parametry z jejich volajícím a jak se vrátit výsledek. Rozdíly v různých implementacích zahrnují, kde jsou umístěny parametry, návratové hodnoty , zpáteční adresy a odkazy na rozsah ( registry , zásobník nebo paměť atd.) A jak jsou úkoly přípravy na volání funkce a následné obnovení prostředí rozděleny mezi volajícího a volaný.

Konvence volání mohou souviset s evaluační strategií konkrétního programovacího jazyka , ale nejčastěji se nepovažují za její součást (nebo naopak), protože evaluační strategie je obvykle definována na vyšší úrovni abstrakce a je vnímána spíše jako součást jazyka než jako nízkoúrovňový implementační detail překladače konkrétního jazyka .

Variace

Konvence volání se mohou lišit v:

  • Kde jsou umístěny parametry, návratové hodnoty a zpáteční adresy (v registrech , v zásobníku volání , v kombinaci obou nebo v jiných strukturách paměti)
  • U parametrů předávaných v paměti pořadí, ve kterém jsou předávány skutečné argumenty pro formální parametry (nebo části velkého nebo složitého argumentu)
  • Jak se (případně dlouhá nebo složitá) návratová hodnota doručuje z volaného zpět volajícímu (na zásobníku, v registru nebo v haldě)
  • Jak je úkol nastavení a vyčištění po volání funkce rozdělen mezi volajícího a volaného
  • Zda a jak se předávají metadata popisující argumenty
  • Kde je uložena předchozí hodnota ukazatele rámce, která se používá k obnovení ukazatele rámce po ukončení rutiny (v rámci zásobníku nebo v nějakém registru)
  • Kde jsou umístěny statické odkazy rozsahu pro nelokální přístup k datům rutiny (obvykle na jedné nebo více pozicích v rámci zásobníku, ale někdy v obecném registru nebo u některých architektur ve speciálních registrech)
  • Způsob přidělování místních proměnných může být někdy také součástí konvence volání (když volající přiděluje volanému)

V některých případech rozdíly zahrnují také následující:

  • Konvence, na kterých mohou být volané přímo používány registry, aniž by byly zachovány
  • Které registry jsou považovány za těkavé a pokud jsou těkavé, nemusí je volaný obnovit

Mnoho architektur má pouze jednu široce používanou konvenci volání, kterou často navrhuje architekt. U RISC včetně SPARC, MIPS a RISC-V se často používají názvy registrů založené na této konvenci volání. Například MIPS registruje $4přes $7mají „jména ABI“ $a0přes $a3odrážející jejich využití při předávání parametrů standardní konvence volání. (CPU RISC mají mnoho ekvivalentních registrů pro všeobecné účely, takže pro jejich pojmenování kromě čísel obvykle neexistuje žádný hardwarový důvod.)

Ačkoli některé programovací jazyky mohou částečně specifikovat volací sekvenci ve specifikaci jazyka nebo v klíčové implementaci, různé implementace těchto jazyků (tj. Různé překladače ) mohou stále používat různé konvence volání a implementace může nabídnout výběr více než jednoho volání konvence. Důvodem je výkon, časté přizpůsobování konvencím jiných populárních jazyků, ať už s technickými důvody nebo bez nich, a omezení nebo konvence uložené různými „ výpočetními platformami “.

Architektury

x86 (32 bitů)

Architektura x86 se používá s mnoha různými konvencemi volání. Kvůli malému počtu architektonických registrů a historickému zaměření na jednoduchost a malou velikost kódu předává mnoho konvencí volání x86 argumenty na zásobníku. Návratová hodnota (nebo ukazatel na ni) je vrácena v registru. Některé konvence používají registry pro prvních pár parametrů, které mohou zlepšit výkon, zejména u velmi často vyvolávaných krátkých a jednoduchých listových rutin (tj. Rutin, které neříkají jiné rutiny).

Příklad volání:

 push EAX            ; pass some register result
 push dword [EBP+20] ; pass some memory variable (FASM/TASM syntax)
 push 3              ; pass some constant
 call calc           ; the returned result is now in EAX

Typická volaná struktura: ( některé nebo všechny (kromě ret) níže uvedených pokynů mohou být optimalizovány pomocí jednoduchých postupů ). Některé konvence ponechávají vyhrazený prostor parametrů, retmísto toho používá prostý ret imm16. V takovém případě by volající mohl add esp,12v tomto příkladu nebo jinak řešit změnu ESP.

calc:
  push EBP            ; save old frame pointer
  mov EBP,ESP         ; get new frame pointer
  sub ESP,localsize   ; reserve stack space for locals
  .
  .                   ; perform calculations, leave result in EAX
  .
  mov ESP,EBP         ; free space for locals
  pop EBP             ; restore old frame pointer
  ret paramsize       ; free parameter space and return.

RAMENO (A32)

Standardní 32bitová konvence volání ARM přiděluje 15 univerzálních registrů jako:

  • r15: Počítadlo programu (podle specifikace sady instrukcí).
  • r14: registr odkazů. Instrukce BL použitá při volání podprogramu uloží zpáteční adresu do tohoto registru.
  • r13: Ukazatel zásobníku. Pokyny Push / Pop v provozním režimu "Palec" používají pouze tento registr.
  • r12: Registr poškrábání volání uvnitř procedury.
  • r4 až r11: Místní proměnné.
  • r0 až r3: Hodnoty argumentů předané podprogramu a výsledky vrácené z podprogramu.

Pokud je typ vrácené hodnoty příliš velký na to, aby se vešel od r0 do r3, nebo jehož velikost nelze staticky určit v době kompilace, musí volající v době běhu přidělit této hodnotě prostor a předat ukazatel na tento prostor v r0.

Subrutiny musí zachovat obsah r4 až r11 a ukazatel zásobníku (možná tím, že je uložíte do zásobníku v prologu funkcí , poté je použijete jako odkládací prostor a poté je obnovíte ze zásobníku v epilogu funkce ). Zejména podprogramy, které volají jiné podprogramy, musí před vyvoláním těchto dalších podprogramů uložit zpáteční adresu v registru odkazů r14 do zásobníku. Takové podprogramy však nemusí tuto hodnotu vrátit na r14 - pouze ji potřebují načíst do r15, čítače programu, aby se vrátily.

Konvence volání ARM nařizuje použití plně sestupného zásobníku.

Tato konvence volání způsobí „typický“ podprogram ARM:

  • V prologu posuňte r4 na r11 do zásobníku a posuňte zpáteční adresu v r14 do zásobníku (lze to provést pomocí jedné instrukce STM);
  • Zkopírujte všechny předané argumenty (v r0 až r3) do místních registrů škrábanců (r4 až r11);
  • Přidělte další místní proměnné zbývajícím místním registrům škrábanců (r4 až r11);
  • Proveďte výpočty a podle potřeby volejte další podprogramy pomocí BL, za předpokladu, že r0 až r3, r12 a r14 nebudou zachovány;
  • Výsledek vložte do r0;
  • V epilogu vytáhněte ze zásobníku r4 na r11 a vytáhněte zpáteční adresu na čítač programu r15. To lze provést pomocí jedné instrukce LDM.

RAMENO (A64)

64bitová konvence volání ARM ( AArch64 ) přiděluje 31 univerzálních registrů jako:

  • x31 (SP): Ukazatel zásobníku nebo nulový registr, v závislosti na kontextu.
  • x30 (LR): Register link register, slouží k návratu z podprogramů.
  • x29 (FP): Ukazatel rámce.
  • x19 až x29: Callee uloženo.
  • x18 (PR): Registr platformy. Používá se pro speciální účel specifický pro operační systém nebo pro další registr uložený volajícím.
  • x16 (IP0) a x17 (IP1): Stírací registry pro volání uvnitř procedury.
  • x9 až x15: Místní proměnné, volající uložen.
  • x8 (XR): Adresa nepřímé návratové hodnoty.
  • x0 až x7: Hodnoty argumentů předané a výsledky vrácené z podprogramu.

Všechny registry začínající na x mají odpovídající 32bitový registr s předponou w . 32bitové x0 se tedy nazývá w0.

Podobně je 32 registrů s plovoucí desetinnou čárkou přiděleno jako:

  • v0 až v7: Hodnoty argumentů předané a výsledky vrácené z podprogramu.
  • v8 až v15: volané uloženo, ale musí být zachováno pouze dolních 64 bitů.
  • v16 až v31: Místní proměnné, volající uložen.

PowerPC

Architektura PowerPC má velký počet registrů, takže většina funkcí může předat všechny argumenty v registrech pro volání na jedné úrovni . Na zásobníku se předávají další argumenty a místo pro argumenty založené na registrech se také vždy přiděluje na zásobníku jako pohodlí volané funkci v případě, že se používají víceúrovňová volání (rekurzivní nebo jinak) a registry se musí uložit. Toto se také používá ve variadických funkcích , jako například printf(), kde k argumentům funkce je třeba přistupovat jako k poli. Pro všechny procedurální jazyky se používá jednotná konvence volání.

MIPS

O32 ABI je nejčastěji používá ABI, kvůli jeho postavení jako původní System V ABI pro MIPS. Je striktně založen na zásobníku a pro předávání argumentů jsou k dispozici pouze čtyři registry . Tato vnímaná pomalost, spolu se starožitným modelem s plovoucí desetinnou čárkou pouze se 16 registry, podpořila šíření mnoha dalších konvence volání. ABI se formoval v roce 1990 a od roku 1994 nebyl nikdy aktualizován. Je definován pouze pro 32bitový MIPS, ale GCC vytvořil 64bitovou variantu nazvanou O64. $a0-$a3

Pro 64-bit se nejčastěji používá N64 ABI (nesouvisí s Nintendo 64 ) od Silicon Graphics. Nejdůležitějším vylepšením je, že pro předávání argumentů je nyní k dispozici osm registrů; Také zvyšuje počet registrů s plovoucí desetinnou čárkou na 32. K dispozici je také verze ILP32 s názvem N32, která používá 32bitové ukazatele pro menší kód, analogicky k x32 ABI . Oba běží v 64bitovém režimu CPU.

Bylo učiněno několik pokusů nahradit O32 32bitovým ABI, který více připomíná N32. Konference z roku 1995 přišla s MIPS EABI, pro kterou byla 32bitová verze docela podobná. EABI inspirovalo MIPS Technologies, aby navrhlo radikálnější „NUBI“ ABI, které navíc znovu používá registry argumentů pro návratovou hodnotu. MIPS EABI podporuje GCC, ale ne LLVM; ani nepodporuje NUBI.

U všech O32 a N32 / N64 je zpáteční adresa uložena v $raregistru. To se automaticky nastavuje pomocí pokynů JAL(skok a odkaz) nebo JALR(registr skoků a odkazů). Zásobník roste směrem dolů.

SPARC

Architektura SPARC je na rozdíl od většiny architektur RISC postavena na oknech registrů . V každém okně registru je 24 přístupných registrů: 8 je „in“ registrů (% i0-% i7), 8 je „lokálních“ registrů (% l0-% l7) a 8 je „out“ registrů (% o0-% o7). Registry "in" se používají k předání argumentů volané funkci a jakékoli další argumenty je třeba vložit do zásobníku . Volaná funkce však vždy přiděluje prostor ke zpracování možného přetečení okna registru, lokálních proměnných a (na 32bitovém SPARC) vrácení struktury podle hodnoty. Chcete-li volat funkci, umístíte argumenty pro funkci, která má být volána, do registrů "out"; když je funkce volána, "out" registry se stávají "in" registry a volaná funkce přistupuje k argumentům ve svých "in" registrech. Po dokončení volané funkce umístí návratovou hodnotu do prvního registru „in“, který se po návratu volané funkce stane prvním registrem „out“.

System V ABI , který většina moderních Unix systémy like následovat, prochází prvních šest argumenty „v“ registrech% i0 až% i5, vyhrazení% I6 k rámu ukazatele a% i7 pro zpáteční adresu.

IBM System / 360 a jeho nástupci

IBM System / 360 je dalším architektura bez hardwarového stohu. Níže uvedené příklady ilustrují konvenci volání používanou OS / 360 a následníky před zavedením 64bitové architektury z / Architecture ; jiné operační systémy pro System / 360 mohou mít různé konvence volání.

Volací program:

     LA  1,ARGS      Load argument list address
     L   15,=A(SUB)  Load subroutine address
     BALR 14,15      Branch to called routine1
     ...
ARGS DC A(FIRST)     Address of 1st argument
     DC A(SECOND)
     ...
     DC A(THIRD)+X'80000000' Last argument2

Volaný program:

SUB  EQU *            This is the entry point of the subprogram

Standardní vstupní sekvence:

     USING *,153
     STM 14,12,12(13) Save registers4
     ST  13,SAVE+4    Save caller's savearea addr
     LA  12,SAVE      Chain saveareas
     ST  12,8(13)
     LR  13,12
     ...

Standardní návratová sekvence:

     L   13,SAVE+45
     LM  14,12,12(13)
     L   15,RETVAL6
     BR  14          Return to caller
SAVE DS  18F         Savearea7

Poznámky:

  1. Tyto BALRinstrukce ukládá adresu příští instrukce (zpáteční adresou) v rejstříku určeném prvním argumentem zaregistrovat 14 a větví do druhého argumentu adresu uvedenou v registru 15.
  2. Volající předá adresu seznamu adres argumentů v registru 1. Poslední adresa má nastavený bit vyššího řádu, který označuje konec seznamu. To omezuje programy využívající tuto konvenci na 31bitové adresování.
  3. Adresa volané rutiny je v registru 15. Normálně se načte do jiného registru a registr 15 se nepoužívá jako základní registr.
  4. STMVýuka šetří registry 14, 15, a 0 až 12 v 72 bajtů prostoru poskytnutého volajícím byl nazván save plochy , na kterou ukazuje rejstříku 13. nazývá rutina poskytuje vlastní úspory prostoru pro použití podprogramů to volá; adresa této oblasti je obvykle udržována v registru 13 po celou dobu rutiny. Následující pokyny STMaktualizují řetězy vpřed a vzad, které spojují tuto oblast uložení s oblastí uložení volajícího.
  5. Návratová sekvence obnoví registry volajícího.
  6. Registr 15 se obvykle používá k předání návratové hodnoty.
  7. Deklarování savearea staticky ve volané rutině způsobí, že není reentrantní a nerekurzivní ; reentrantní program používá dynamickou oblast, získanou buď z operačního systému a uvolněnou po návratu, nebo v úložišti předaném volajícím programem.

V System / 390 ABI a z / Architecture ABI, používané v Linuxu:

  • Registry 0 a 1 jsou volatilní
  • Registry 2 a 3 se používají pro předávání a návrat hodnot
  • Registry 4 a 5 se také používají pro předávání parametrů
  • Registr 6 se používá pro předávání parametrů a musí být uložen a obnoven volaným
  • Registry 7 až 13 jsou používány volaným a musí je ukládat a obnovovat
  • Registr 14 se používá pro zpáteční adresu
  • Registr 15 se používá jako ukazatel zásobníku
  • Registry s plovoucí desetinnou čárkou 0 a 2 se používají pro předávání a návrat hodnot
  • Registry s plovoucí desetinnou čárkou 4 a 6 jsou používány volaným a musí je ukládat a obnovovat
  • V architektuře z / Architecture jsou registry s plovoucí desetinnou čárkou 1, 3, 5 a 7 až 15 používány volaným
  • Registr přístupu 0 je vyhrazen pro použití systému
  • Přístupové registry 1 až 15 jsou používány volaným

SuperH

Registrovat Windows CE 5.0 gcc Renesas
R0 Návratové hodnoty. Dočasně pro rozšíření montážních pseudonávodů. Implicitní zdroj / cíl pro 8 / 16bitové operace. Nezachováno. Návratová hodnota, volající uloží Proměnné / dočasné. Není zaručeno
R1..R3 Slouží jako dočasné registry. Nezachováno. Volající zachránil škrábnutí. Adresa struktury (ve výchozím nastavení uložení volajícího) Proměnné / dočasné. Není zaručeno
R4..R7 První čtyři slova celočíselných argumentů. Oblast sestavení argumentů poskytuje prostor, do kterého se mohou přelévat argumenty obsahující R4 až R7. Nezachováno. Předávání parametrů, volající ukládá Argumenty. Není zaručeno.
R8..R13 Slouží jako trvalé registry. Zachovalé. Callee šetří Proměnné / dočasné. Zaručeno.
R14 Výchozí ukazatel rámečku. (R8-R13 mohou také sloužit jako ukazatel rámce a listové rutiny mohou používat R1 – R3 jako ukazatel rámce.) Zachováno. Frame Pointer, FP, callee save Proměnné / dočasné. Zaručeno.
R15 Slouží jako ukazatel zásobníku nebo jako trvalý registr. Zachovalé. Stack Pointer, SP, volané šetří Ukazatel zásobníku. Zaručeno.

Poznámka: „konzervované“ rezervy pro volání volaného; totéž platí pro „zaručené“.

68 tis

Nejběžnější konvence volání pro řadu Motorola 68000 je:

  • d0, d1, a0 a a1 jsou stírací registry
  • Všechny ostatní registry jsou uloženy pomocí volání
  • a6 je ukazatel rámce, který lze deaktivovat volbou kompilátoru
  • Parametry jsou vloženy do zásobníku, zprava doleva
  • Návratová hodnota je uložena v d0

IBM 1130

IBM 1130 byl malý 16bitové slovo adresovatelné stroj. Mělo jen šest registrů plus indikátory stavu a žádný zásobník. Registry jsou Instruction Address Register (IAR) , Accumulator (ACC) , Accumulator Extension (EXT) a tři rejstříkové registry X1 – X3. Volající program je zodpovědný za ukládání ACC, EXT, X1 a X2. Existují dvě pseudo-operace pro volání podprogramů, CALLpro kódování nepřemístitelných podprogramů přímo spojených s hlavním programem a LIBFpro volání přemístitelných podprogramů knihovny prostřednictvím vektoru přenosu . Obě pseudoopy se vyřeší na strojovou instrukci Branch and Store IAR ( BSI), která ukládá adresu další instrukce na její efektivní adresu (EA) a větve do EA + 1.

Argumenty se řídí BSI‍ —‌ obvykle jde o jednoslovné adresy argumentů‍ — called volaná rutina musí vědět, kolik argumentů lze očekávat, aby je při návratu mohla přeskočit. Alternativně lze argumenty předávat v registrech. Funkční rutiny vrátily výsledek v ACC pro skutečné argumenty nebo v paměťovém umístění označovaném jako pseudoakumulátor reálného čísla (FAC). Argumenty a zpáteční adresa byly adresovány pomocí offsetu k hodnotě IAR uložené v prvním umístění podprogramu.

  *                  1130 subroutine example
     ENT  SUB        Declare "SUB" an external entry point
 SUB DC   0          Reserved word at entry point, conventionally coded "DC *-*"
 *                   Subroutine code begins here
 *                   If there were arguments the addresses can be loaded indirectly from the return addess
     LDX I 1 SUB     Load X1 with the address of the first argument (for example)
 ...
 *                   Return sequence
     LD      RES     Load integer result into ACC
 *                   If no arguments were provided, indirect branch to the stored return address
     B   I   SUB     If no arguments were provided
     END  SUB

Subrutiny v IBM 1130, CDC 6600 a PDP-8 (všechny tři počítače byly představeny v roce 1965) ukládají zpáteční adresu v prvním umístění podprogramu.

Aspekty implementace

Tuto variabilitu je třeba vzít v úvahu při kombinování modulů napsaných ve více jazycích nebo při volání rozhraní API operačního systému nebo knihovny z jiného jazyka, než ve kterém jsou napsány; v těchto případech je třeba věnovat zvláštní pozornost koordinaci volacích konvencí používaných volajícím a volaným. Dokonce i program využívající jeden programovací jazyk může používat více konvencí volání, buď zvolených kompilátorem, pro optimalizaci kódu, nebo specifikovaných programátorem.

Kód se závitem

Vláknový kód klade veškerou odpovědnost za nastavení a vyčištění po volání funkce u volaného kódu. Volací kód nedělá nic jiného než seznam podprogramů, které mají být volány. Tím se umístí veškeré nastavení funkce a čisticí kód na jedno místo - prolog a epilog funkce - spíše než na mnoho míst, která tuto funkci nazývají. Díky tomu je vláknový kód nejkompaktnější konvence volání.

Vláknový kód předá všechny argumenty v zásobníku. Všechny vrácené hodnoty jsou vráceny do zásobníku. Díky tomu jsou naivní implementace pomalejší než konvence volání, které udržují více hodnot v registrech. Implementace podprocesového kódu, které ukládají do mezipaměti několik hodnot nejvyššího zásobníku v registrech - zejména zpáteční adresu - jsou však obvykle rychlejší než konvence volání podprogramů, které vždy posílají a vyskakují zpáteční adresu do zásobníku.

PL / I.

Výchozí konvence volání pro programy napsané v jazyce PL / I předává všechny argumenty odkazem , i když lze volitelně zadat i jiné konvence. Argumenty se pro různé kompilátory a platformy zpracovávají odlišně, ale adresy argumentů se obvykle předávají prostřednictvím seznamu argumentů v paměti. Může být předána konečná skrytá adresa ukazující na oblast, která obsahuje návratovou hodnotu. Vzhledem k široké škále datových typů podporovaných PL / I může být předán i deskriptor dat, který definuje například délky znakových nebo bitových řetězců, dimenzi a hranice polí ( vektory drogy ) nebo rozložení a obsah z datové struktury . Fiktivní argumenty se vytvářejí pro argumenty, které jsou konstanty nebo které nesouhlasí s typem argumentu, který volaná procedura očekává.

Viz také

Reference

externí odkazy