Översikt över ARM64EC ABI-konventioner

ARM64EC är ett program binärt gränssnitt (ABI) som gör att ARM64-binärfiler kan köras internt och driftskompatibelt med x64-kod. Mer specifikt följer ARM64EC ABI x64-programvarukonventioner, inklusive anropskonventioner, stackanvändning och datajustering, vilket gör ARM64EC och x64-kod kompatibla. Operativsystemet emulerar x64-delen av binärfilen. (EG i ARM64EC står för emuleringskompatibel.)

Mer information om x64- och ARM64-API:er finns i Översikt över x64 ABI-konventioner och Översikt över ARM64 ABI-konventioner.

ARM64EC löser inte skillnader i minnesmodell mellan x64- och ARM-baserade arkitekturer. Mer information finns i Vanliga problem med Migrering av Microsoft C++ ARM.

Definitioner

  • ARM64 – kodströmmen för ARM64-processer som innehåller traditionell ARM64-kod.
  • ARM64EC – Kodströmmen som använder en delmängd av ARM64-registret för att tillhandahålla samverkan med x64-kod.

Registrera mappning

x64-processer kan ha trådar som kör ARM64EC kod. Så det är alltid möjligt att hämta en x64-registerkontext, ARM64EC använder en delmängd av ARM64-kärnregister som mappar 1:1 till emulerade x64-register. Viktigt är att ARM64EC aldrig använder register utanför den här delmängden, förutom att läsa TEB-adressen (Thread Environment Block) från x18.

Inbyggda ARM64-processer bör inte gå tillbaka i prestanda när vissa eller många funktioner omkompileras som ARM64EC. För att upprätthålla prestanda följer ABI dessa principer:

  • ARM64EC-registerunderuppsättningen innehåller alla register som ingår i ARM64-funktionsanropskonventionen.

  • ARM64EC-anropskonventionen mappar direkt till ARM64-anropskonventionen.

Särskilda hjälpmetoder som __chkstk_arm64ec använder anpassade samtalskonventioner och register. Dessa register ingår också i ARM64EC delmängd av register.

Kartläggning av heltalsregister

ARM64EC-register x64-register ARM64EC samtalskonvention ARM64-samtalskonvention x64-samtalskonvention
x0 rcx flyktig flyktig flyktig
x1 rdx flyktig flyktig flyktig
x2 r8 flyktig flyktig flyktig
x3 r9 flyktig flyktig flyktig
x4 r10 flyktig flyktig flyktig
x5 r11 flyktig flyktig flyktig
x6 mm1 (låga 64 bitar av x87-register R1) flyktig flyktig flyktig
x7 mm2 (låga 64 bitar av x87-register R2) flyktig flyktig flyktig
x8 rax flyktig flyktig flyktig
x9 mm3 (låga 64 bitar av x87-register R3) flyktig flyktig flyktig
x10 mm4 (låga 64 bitar av x87-register R4) flyktig flyktig flyktig
x11 mm5 (låga 64 bitar av x87-register R5) flyktig flyktig flyktig
x12 mm6 (låga 64 bitar av x87-register R6) flyktig flyktig flyktig
x13 Inte tillgänglig Otillåtet flyktig Inte tillgänglig
x14 Inte tillgänglig Otillåten flyktig Inte tillgänglig
x15 mm7 (låga 64 bitar av x87-register R7) flyktig flyktig flyktig
x16 De övre 16 bitarna av varje x87-register R0-R3 flyktig(xip0) flyktig(xip0) flyktig
x17 De övre 16 bitarna av varje x87-register R4-R7 flyktig(xip1) flyktig(xip1) flyktig
x18 GS.base fixed(TEB) fixed(TEB) fixed(TEB)
x19 r12 icke-flyktig icke-flyktig icke-flyktig
x20 r13 icke-flyktig icke-flyktig icke-flyktig
x21 r14 icke-flyktig icke-flyktig icke-flyktig
x22 r15 icke-flyktig icke-flyktig icke-flyktig
x23 Inte tillgänglig Otillåten icke-flyktig Inte tillgänglig
x24 Inte tillgänglig Otillåten icke-flyktig Inte tillgänglig
x25 rsi icke-flyktig icke-flyktig icke-flyktig
x26 rdi icke-flyktig icke-flyktig icke-flyktig
x27 rbx icke-flyktig icke-flyktig icke-flyktig
x28 Inte tillgänglig Förbjuden Otillåten Inte tillgänglig
fp rbp icke-flyktig icke-flyktig icke-flyktig
lr mm0 (låga 64 bitar av x87-register R0) båda båda båda
sp rsp icke-flyktig icke-flyktig icke-flyktig
pc rip instruktionspekare instruktionspekare instruktionspekare
PSTATEdelmängd: N/Z/C/V/SS1, 2 RFLAGS delmängd: SF/ZF/CF/OF/TF flyktig flyktig flyktig
Inte tillgänglig RFLAGS delmängd: PF/AF Inte tillgänglig Inte tillgänglig flyktig
Inte tillgänglig RFLAGS delmängd: DF Inte tillgänglig Inte tillgänglig icke-flyktig

1 Undvik att läsa, skriva eller beräkna mappningar direkt mellan PSTATE och RFLAGS. Dessa bitar kan användas i framtiden och kan komma att ändras.

2 Den ARM64EC-känneteckenflaggan C är den omvända av x64-känneteckenflaggan CF för subtraktionsoperationer. Det finns ingen särskild hantering eftersom flaggan är flyktig och därför förstörs när du övergår mellan (ARM64EC och x64) funktioner.

Registrera mappning för vektorregister

ARM64EC-register x64-register ARM64EC samtalskonvention ARM64-samtalskonvention x64-samtalskonvention
v0-v5 xmm0-xmm5 flyktig flyktig flyktig
v6-v7 xmm6-xmm7 flyktig flyktig icke-flyktig
v8-v15 xmm8-xmm15 instabil 1 instabil 1 icke-flyktig
v16-v31 xmm16-xmm31 Otillåtet flyktig otillåten (x64-emulatorn stöder inte AVX-512)
FPCR 2 MXCSR[15:6] icke-flyktig icke-flyktig icke-flyktig
FPSR 2 MXCSR[5:0] flyktig flyktig flyktig

1 Dessa ARM64-register är speciella eftersom de lägre 64 bitarna är icke-flyktiga men de övre 64 bitarna är flyktiga. När det gäller en x64-uppringare är de i praktiken instabila eftersom den anropade personen skulle förstöra data.

2 Undvik direkt läsning, skrivning eller databehandling av FPCR och FPSR. Dessa bitar kan användas i framtiden och kan komma att ändras.

Strukturpackning

ARM64EC följer samma struct-förpackningsregler som används för x64 för att säkerställa samverkan mellan ARM64EC kod och x64-kod. Mer information och exempel på x64-struct-packning finns i Översikt över x64 ABI-konventioner.

Flyttalsundantag

För att avgöra om en ARM-processor stöder undantag skriver du ett värde som aktiverar undantag till FPCR-registret och läser sedan tillbaka det. Om processorn stöder flyttalsundantag förblir de bitar som motsvarar undantag som stöds inställda, medan processorn återställer bitarna för undantag som inte stöds.

På ARM64EC hanterar Windows processorns flyttalsundantag och inaktiverar dem i FPCR-registret. Detta säkerställer konsekvent beteende för olika processorvarianter.

ABI-rutiner för emuleringshjälp

ARM64EC kod och thunks använder emuleringshjälprutiner för att övergå mellan x64- och ARM64EC-funktioner.

I följande tabell beskrivs varje särskild ABI-rutin och de register som ABI använder. Rutinerna ändrar inte de angivna bevarade registeren under ABI-kolumnen. Inga antaganden bör göras om olistade register. På disken är ABI-rutinpekarna null. Vid inläsningen uppdaterar inläsaren pekarna så att de pekar på x64-emulatorns rutiner.

Namn Beskrivning ABI
__os_arm64x_dispatch_call_no_redirect Anropas av en exit-thunk för att anropa ett x64-mål (antingen en x64-funktion eller en x64-snabbframåtsekvens). Rutinen skickar ARM64EC returadress (i LR registret) följt av adressen till instruktionen som lyckas med en blr x16 instruktion som anropar x64-emulatorn. Sedan körs instruktionen blr x16 returvärde i x8 (rax)
__os_arm64x_dispatch_ret Anropas av en inträdesfunktion för att återgå till sin x64-uppringare. Den visar x64-returadressen från stacken och anropar x64-emulatorn för att hoppa till den Inte tillgänglig
__os_arm64x_check_call Anropas av ARM64EC kod med en pekare till en slut-thunk och den indirekta ARM64EC måladress som ska köras. ARM64EC-målet anses vara korrigeringsbart och programkörningen återgår alltid till anroparen, antingen med samma data som det kallades med, eller med modifierad data. Argumenten:
x9: Måladressen
x10: Adressen för utgångs-thunken
x11: Sekvensadressen för snabbsnabb framåt

Ut:
x9: Om målfunktionen har omdirigerats innehåller den adressen till snabbspolning framåt-sekvensen
x10: Adressen för utgångs-thunken
x11: Om funktionen har omletts innehåller den adressen för exit-thunk. Annars hoppade måladressen över till

Bevarade register: x0-x8, x15 (chkstk). och q0-q7
__os_arm64x_check_icall Anropas av ARM64EC kod, med en pekare till en exit thunk, för att hantera ett hopp till en måladress som är antingen x64 eller ARM64EC. Om målet är x64 och x64-koden inte har korrigerats anger rutinen måladressregistret. Den pekar på den ARM64EC versionen av funktionen om det finns någon. I annat fall ställer den in registret så att det pekar på den avslutande tröskeln som övergår till x64-målplattformen. Sedan återgår den till den anropande ARM64EC-koden, som sedan hoppar till adressen i registret. Den här rutinen är en icke-optimerad version av __os_arm64x_check_call, där måladressen inte är känd vid kompileringstillfället

Används på en anropsplats för ett indirekt anrop
Argumenten:
x9: Måladressen
x10: Adressen för utgångs-thunken
x11: Sekvensadressen för snabbsnabb framåt

Ut:
x9: Om målfunktionen har omdirigerats innehåller den adressen till snabbspolning framåt-sekvensen
x10: Adressen för utgångs-thunken
x11: Om funktionen har omletts innehåller den adressen för exit-thunk. Annars hoppade måladressen över till

Bevarade register: x0-x8, x15 (chkstk) och q0-q7
__os_arm64x_check_icall_cfg Samma som __os_arm64x_check_icall men kontrollerar också att den angivna adressen är ett giltigt kontrollflödesdiagrams indirekta anropsmål Argumenten:
x10: Adressen till exit-thunk
x11: Målfunktionens adress

Ut:
x9: Om målet är x64, adressen till funktionen. I annat fall, odefinierad
x10: Adressen till exit-thunk
x11: Om målet är x64 innehåller det adressen till exit-funktionen. Annars är det funktionens adress...

Bevarade register: x0-x8, x15 (chkstk) och q0-q7
__os_arm64x_get_x64_information Hämtar den begärda delen av live x64-registerkontexten _Function_class_(ARM64X_GET_X64_INFORMATION) NTSTATUS LdrpGetX64Information(_In_ ULONG Type, _Out_ PVOID Output, _In_ PVOID ExtraInfo)
__os_arm64x_set_x64_information Anger den begärda delen av live x64-registerkontexten _Function_class_(ARM64X_SET_X64_INFORMATION) NTSTATUS LdrpSetX64Information(_In_ ULONG Type,_In_ PVOID Input, _In_ PVOID ExtraInfo)
__os_arm64x_x64_jump Används i anpassare utan signatur och andra thunks som direkt vidarebefordrar (jmp) ett anrop till en annan funktion som kan ha vilken signatur som helst, och därmed skjuter upp den potentiella tillämpningen av rätt thunk till det verkliga målet. Argumenten:
x9: mål att hoppa till

Alla parameterregister bevarade (vidarebefordrade)

Thunk

Thunks är de lågnivåmekanismer som stöder ARM64EC- och x64-funktioner som anropar varandra. Det finns två typer: entry-thunks för att komma in i ARM64EC-funktioner och exit-thunks för att anropa x64-funktioner.

Inmatningstrick och inbyggda inmatnings thunks: x64 till ARM64EC funktionsanrop

För att stödja x64-anropare när en C/C++-funktion kompileras som ARM64EC, genererar verktygskedjan en ingångspunkt som består av ARM64EC maskinkod. Intrinsics har en egen inträdes-thunk. Alla andra funktioner delar en inmatningsfunktion med alla funktioner med samma anropskonvention, parametrar och returtyp. Innehållet i thunk beror på anropskonventionen för C/C++-funktionen.

Förutom att hantera parametrar och returadressen överbryggar thunk skillnaderna i volatilitet mellan ARM64EC- och x64-vektorregister som orsakas av ARM64EC mappning av vektorregister:

ARM64EC-register x64-register ARM64EC samtalskonvention ARM64-samtalskonvention x64-samtalskonvention
v6-v15 xmm6-xmm15 volatil, men sparad/återställd i inmatningsstubben (x64 till ARM64EC) flyktiga eller delvis flyktiga övre 64 bitar icke-flyktig

Inmatningstunk utför följande åtgärder:

Parameternummer Stackanvändning
0-4 Lagrar ARM64EC v6 och v7 i uppringarens allokerade hemutrymme

Eftersom anroparen är ARM64EC, som inte har begreppet hemutrymme, klonas inte de lagrade värdena.

Allokerar ytterligare 128 byte på stacken och lagrar ARM64EC v8 via v15.
5-8 x4 = 5:e parametern från stacken
x5 = 6:e parametern från stacken
x6 = 7:e parametern från stacken
x7 = 8:e parametern från stacken

Om parametern är SIMD används registren v4-v7 i stället
9+ Allokerar AlignUp(NumParams - 8 , 2) * 8 byte på stacken. *

Kopierar de 9:e och återstående parametrarna till det här området

* Om värdet justeras till ett jämnt tal garanteras att stacken förblir justerad till 16 byte

Om funktionen accepterar en 32-bitars heltalsparameter är det tillåtet för thunken att endast pusha 32 bitar i stället för hela 64 bitar av registret.

Därefter använder thunk en ARM64-instruktion bl för att anropa funktionen ARM64EC. När funktionen har returnerat, kommer thunken att aktiveras:

  1. Ångrar eventuella stackallokeringar
  2. __os_arm64x_dispatch_ret Anropar emulatorhjälpare för att poppa x64-returadressen och återuppta x64-emulering.

Avsluta thunk: ARM64EC till x64-funktionsanrop

För varje anrop som en ARM64EC C/C++-funktion gör till potentiell x64-kod genererar MSVC-verktygskedjan en avslutningsknapp. Innehållet i thunk beror på parametrarna för x64-funktionen och om funktionen använder standardanropskonventionen eller __vectorcall. Kompilatorn hämtar den här informationen från en funktionsdeklaration för anroparen.

Först pushar Thunk returadressen som finns i ARM64EC-registret lr och ett fiktivt värde på 8 byte för att säkerställa att stacken är justerad till 16 byte. För det andra hanterar "thunk" parametrarna:

Parameternummer Stackanvändning
0-4 Allokerar 32 bytes av hemutrymme på stacken
5-8 Allokerar AlignUp(NumParams - 4, 2) * 8 fler byte högre upp på stacken. *

Kopierar den 5:e och eventuella efterföljande parametrar från ARM64EC:s x4-x7 till det här extra utrymmet
9+ Kopierar de 9:e och återstående parametrarna till det extra utrymmet

* Om du justerar värdet till ett jämnt tal garanteras att stacken förblir justerad till 16 byte.

För det tredje anropar __os_arm64x_dispatch_call_no_redirect thunk emulatorhjälpen för att anropa x64-emulatorn för att köra x64-funktionen. Anropet måste vara en blr x16 instruktion (praktiskt nog är x16 ett flyktigt register). En blr x16 instruktion krävs eftersom x64-emulatorn parsar den här instruktionen som ett tips.

Funktionen x64 försöker vanligtvis återgå till emulatorhjälpen med hjälp av en x64-instruktion ret . Nu identifierar x64-emulatorn att den finns i ARM64EC kod. Den läser sedan föregående 4-byte-ledtråd som råkar vara ARM64-instruktionen blr x16. Eftersom detta tips anger att returadressen finns i den här hjälparen, hoppar emulatorn direkt till den adressen.

Funktionen x64 tillåts återgå till emulatorhjälpfunktionen med hjälp av vilken greninstruktion som helst, inklusive x64 jmp och call. Emulatorn hanterar även dessa scenarier.

När hjälpen sedan återgår till thunken, tonk:

  1. Ångrar allokering av staplar
  2. Poppar ARM64EC lr-registret
  3. Verkställer en ARM64-instruktion ret lr .

ARM64EC funktionsnamnsdekoration

Ett ARM64EC funktionsnamn har en sekundär dekoration tillämpad efter valfri språkspecifik dekoration. För funktioner med C-länkning (oavsett om de kompileras som C eller med hjälp av extern "C"), läggs ett # till i namnet. För C++-dekorerade funktioner infogas en $$h tagg i namnet.

foo         => #foo
?foo@@YAHXZ => ?foo@@$$hYAHXZ

__vectorcall

Verktygskedjan ARM64EC stöder __vectorcallför närvarande inte . Kompilatorn genererar ett fel när den identifierar __vectorcall användning med ARM64EC.

Se även

Förstå ARM64EC ABI och sammansättningskod
Vanliga problem med Migrering av Microsoft C++ ARM
Dekorerade namn