Přehled konvencí ARM64EC ABI
ARM64EC je binární rozhraní aplikace (ABI), které umožňuje binárním souborům ARM64 spouštět nativně a interoperativně s kódem x64. Konkrétně ARM64EC ABI se řídí softwarovými konvencemi x64, včetně konvence volání, použití zásobníku a zarovnání dat, což ARM64EC a x64 kód interoperabilní. Operační systém emuluje část binárního souboru x64. (ES v ARM64EC je zkratka pro emulaci kompatibilní.)
Další informace o konvencích ABI pro platformu x64 a ARM64 najdete v tématu Přehled konvencí X64 ABI a přehledu konvencí ARM64 ABI.
ARM64EC nevyřeší rozdíly v modelu paměti mezi architekturami založenými na platformě x64 a ARM. Další informace najdete v tématu Běžné problémy s migrací ARM v jazyce Visual C++.
Definice
- ARM64 – Stream kódu pro procesy ARM64, které obsahují tradiční kód ARM64.
- ARM64EC – datový proud kódu, který využívá podmnožinu registru ARM64, která poskytuje interoperabilitu s kódem x64.
Mapování registru
Procesy x64 můžou obsahovat vlákna, na kterých běží ARM64EC kód. Proto je vždy možné načíst kontext registru x64, ARM64EC používá podmnožinu registrů jader ARM64, které mapují 1:1 na emulované registry x64. Důležité je, že ARM64EC nikdy nepoužívá registry mimo tuto podmnožinu, s výjimkou čtení adresy TEB (Thread Environment Block) z x18
.
Nativní procesy ARM64 by se neměly při rekompilování některých nebo mnoha funkcí rekompilovat jako ARM64EC. Aby se zachoval výkon, ABI se řídí těmito principy:
Podmnožina ARM64EC registru zahrnuje všechny registry, které jsou součástí konvence volání funkce ARM64.
Konvence volání ARM64EC se přímo mapuje na konvenci volání ARM64.
Speciální pomocné rutiny, jako je __chkstk_arm64ec
použití vlastních konvencí volání a registrů Tyto registry jsou také zahrnuty v ARM64EC podmnožině registrů.
Mapování registru pro celočíselné registry
registrace ARM64EC | Registrace x64 | ARM64EC konvence volání | Konvence volání ARM64 | x64 – konvence volání |
---|---|---|---|---|
x0 |
rcx |
volatile | volatile | volatile |
x1 |
rdx |
volatile | volatile | volatile |
x2 |
r8 |
volatile | volatile | volatile |
x3 |
r9 |
volatile | volatile | volatile |
x4 |
r10 |
volatile | volatile | volatile |
x5 |
r11 |
volatile | volatile | volatile |
x6 |
mm1 (nízkých 64 bitů registru x87 R1 ) |
volatile | volatile | volatile |
x7 |
mm2 (nízkých 64 bitů registru x87 R2 ) |
volatile | volatile | volatile |
x8 |
rax |
volatile | volatile | volatile |
x9 |
mm3 (nízkých 64 bitů registru x87 R3 ) |
volatile | volatile | volatile |
x10 |
mm4 (nízkých 64 bitů registru x87 R4 ) |
volatile | volatile | volatile |
x11 |
mm5 (nízkých 64 bitů registru x87 R5 ) |
volatile | volatile | volatile |
x12 |
mm6 (nízkých 64 bitů registru x87 R6 ) |
volatile | volatile | volatile |
x13 |
– | zamítnutý | volatile | – |
x14 |
N/A | zamítnutý | volatile | – |
x15 |
mm7 (nízkých 64 bitů registru x87 R7 ) |
volatile | volatile | volatile |
x16 |
Vysoká 16 bitů každého z registrů x87 R0 -R3 |
volatile(xip0 ) |
volatile(xip0 ) |
volatile |
x17 |
Vysoká 16 bitů každého z registrů x87 R4 -R7 |
volatile(xip1 ) |
volatile(xip1 ) |
volatile |
x18 |
GS.base | fixed(TEB) | fixed(TEB) | fixed(TEB) |
x19 |
r12 |
nestálé | nestálé | nestálé |
x20 |
r13 |
nestálé | nestálé | nestálé |
x21 |
r14 |
nestálé | nestálé | nestálé |
x22 |
r15 |
nestálé | nestálé | nestálé |
x23 |
– | zamítnutý | nestálé | – |
x24 |
N/A | zamítnutý | nestálé | – |
x25 |
rsi |
nestálé | nestálé | nestálé |
x26 |
rdi |
nestálé | nestálé | nestálé |
x27 |
rbx |
nestálé | nestálé | nestálé |
x28 |
– | zamítnutý | zamítnutý | – |
fp |
rbp |
nestálé | nestálé | nestálé |
lr |
mm0 (nízkých 64 bitů registru x87 R0 ) |
Oba | Oba | Oba |
sp |
rsp |
nestálé | nestálé | nestálé |
pc |
rip |
ukazatel instrukce | ukazatel instrukce | ukazatel instrukce |
PSTATE podmnožina: N Z SS /C //V /1, 2 |
RFLAGS podmnožina: SF /ZF /CF /OF /TF |
volatile | volatile | volatile |
– | RFLAGS podmnožina: PF /AF |
– | N/A | volatile |
– | RFLAGS podmnožina: DF |
– | N/A | nestálé |
1 Vyhněte se přímému čtení, zápisu nebo výpočtu mapování mezi PSTATE
a RFLAGS
. Tyto bity se můžou v budoucnu používat a můžou se změnit.
2 Příznak pro přenos ARM64EC C
je inverzní příznak k přenosu CF
x64 pro operace odčítání. Neexistuje žádná zvláštní manipulace, protože příznak je nestálý a proto je při přechodu mezi funkcemi (ARM64EC a x64) odpadlý.
Registrace mapování vektorových registrů
registrace ARM64EC | Registrace x64 | ARM64EC konvence volání | Konvence volání ARM64 | x64 – konvence volání |
---|---|---|---|---|
v0 -v5 |
xmm0 -xmm5 |
volatile | volatile | volatile |
v6 -v7 |
xmm6 -xmm7 |
volatile | volatile | nestálé |
v8 -v15 |
xmm8 -xmm15 |
volatile 1 | volatile 1 | nestálé |
v16 -v31 |
xmm16 -xmm31 |
zamítnutý | volatile | nepovolené (emulátor x64 nepodporuje AVX-512) |
FPCR 2 |
MXCSR[15:6] |
nestálé | nestálé | nestálé |
FPSR 2 |
MXCSR[5:0] |
volatile | volatile | volatile |
1 Tyto registry ARM64 jsou speciální v tom, že nižší 64 bity jsou nestálé, ale horní 64 bity jsou nestálé. Z pohledu volajícího x64 jsou efektivně nestálé, protože volaný by volaný vyhazoval data.
2 Vyhněte se přímému čtení, psaní nebo výpočtu mapování FPCR
a FPSR
. Tyto bity se můžou v budoucnu používat a můžou se změnit.
Balení struktur
ARM64EC se řídí stejnými pravidly balení struktur, která se používají pro x64 k zajištění interoperability mezi ARM64EC kódem a kódem x64. Další informace a příklady balení struktur x64 naleznete v tématu Přehled konvencí X64 ABI.
Pomocné rutiny ABI emulace
ARM64EC kódu a thunks používají pomocné rutiny emulace k přechodu mezi funkcemi x64 a ARM64EC.
Následující tabulka popisuje každou speciální rutinu ABI a registru používá ABI. Rutiny neupravují uvedené zachované registry ve sloupci ABI. O nezaregistrovaných registrech by se neměly provádět žádné předpoklady. Na disku mají rutinní ukazatele ABI hodnotu null. Při načítání zavaděč aktualizuje ukazatele tak, aby ukazovaly na rutiny emulátoru x64.
Název | Popis | ABI |
---|---|---|
__os_arm64x_dispatch_call_no_redirect |
Volal by exit thunk pro volání cíle x64 (funkce x64 nebo x64 fast-forward sekvence). Rutina odešle ARM64EC návratovou adresu (v LR registru) následovanou adresou instrukce, která uspěje blr x16 pokyn, který vyvolá emulátor x64. Potom spustí instrukce.blr x16 |
návratová hodnota v x8 (rax ) |
__os_arm64x_dispatch_ret |
Zavolá se vstupním záznamem, který se vrátí do volajícího x64. Zobrazí návratovou adresu x64 ze zásobníku a vyvolá emulátor x64, aby na ni přeskočil. | – |
__os_arm64x_check_call |
Volal by ARM64EC kód s ukazatelem na konec thunk a nepřímá ARM64EC cílová adresa ke spuštění. Cíl ARM64EC se považuje za opravitelný a provádění se vždy vrátí volajícímu se stejnými daty, se kterými byl volán, nebo s upravenými daty. | Argumenty:x9 : Cílová adresax10 : Adresa výstupního thunkux11 : Adresa posloupnosti rychlého předáváníVen: x9 : Pokud byla cílová funkce objížděna, obsahuje adresu rychlého předávání.x10 : Adresa výstupního thunkux11 : Pokud byla funkce objížděna, obsahuje adresu výstupního záznamu. Jinak cílová adresa přeskočí naZachované rejstříky: x0 -x8 , x15 ().chkstk a q0 -q7 |
__os_arm64x_check_icall |
Volal by ARM64EC kód s ukazatelem na konec thunk pro zpracování skoku na cílovou adresu, která je x64 nebo ARM64EC. Pokud je cíl x64 a kód x64 nebyl opraven, rutina nastaví registr cílových adres. Odkazuje na ARM64EC verzi funkce, pokud existuje. V opačném případě nastaví registr tak, aby ukazoval na výstupní blok, který přejde na cíl x64. Pak se vrátí do volajícího ARM64EC kódu, který pak přeskočí na adresu v registru. Tato rutina je neoptimalizovatelní verze __os_arm64x_check_call , kde cílová adresa není v době kompilace známá.Používá se na místě volání nepřímého volání. |
Argumenty:x9 : Cílová adresax10 : Adresa výstupního thunkux11 : Adresa posloupnosti rychlého předáváníVen: x9 : Pokud byla cílová funkce objížděna, obsahuje adresu rychlého předávání.x10 : Adresa výstupního thunkux11 : Pokud byla funkce objížděna, obsahuje adresu výstupního záznamu. Jinak cílová adresa přeskočí naZachované rejstříky: x0 -x8 , x15 (chkstk ) a q0 -q7 |
__os_arm64x_check_icall_cfg |
Stejné jako __os_arm64x_check_icall ale také kontroluje, že zadaná adresa je platným cílem nepřímého volání grafu toku řízení. |
Argumenty:x10 : Adresa výstupního útržkux11 : Adresa cílové funkceVen: x9 : Pokud je cíl x64, adresa funkce. Jinak není definovánox10 : Adresa výstupního útržkux11 : Pokud je cíl x64, obsahuje adresu výstupního záznamu. V opačném případě adresa funkceZachované rejstříky: x0 -x8 , x15 (chkstk ) a q0 -q7 |
__os_arm64x_get_x64_information |
Získá požadovanou část živého kontextu registru x64. | _Function_class_(ARM64X_GET_X64_INFORMATION) NTSTATUS LdrpGetX64Information(_In_ ULONG Type, _Out_ PVOID Output, _In_ PVOID ExtraInfo) |
__os_arm64x_set_x64_information |
Nastaví požadovanou část živého kontextu registru x64. | _Function_class_(ARM64X_SET_X64_INFORMATION) NTSTATUS LdrpSetX64Information(_In_ ULONG Type,_In_ PVOID Input, _In_ PVOID ExtraInfo) |
__os_arm64x_x64_jump |
Používá se v úpravce bez podpisu a dalších útržcích, které přímo přesměrují (jmp ) volání jiné funkce, která může mít libovolný podpis, odloží potenciální použití správného převodu na skutečný cíl. |
Argumenty:x9 : cíl přejít naVšechny registry parametrů se zachovají (přeposílané) |
Thunks
Thunks jsou mechanismy nízké úrovně pro podporu ARM64EC a funkcí x64, které navzájem volají. Existují dva typy: vstupní bloky pro zadávání ARM64EC funkcí a ukončení thunků pro volání funkcí x64.
Záznam thunk a vnitřní vstupní thunks: x64 to ARM64EC volání funkce
Pro podporu volajících x64, když je funkce C/C++ zkompilována jako ARM64EC, vygeneruje sada nástrojů jeden záznam skládající se z ARM64EC strojového kódu. Vnitřní objekty mají vlastní záznam. Všechny ostatní funkce sdílejí záznamový blok se všemi funkcemi, které mají odpovídající konvenci volání, parametry a návratový typ. Obsah thunk závisí na konvenci volání funkce C/C++.
Kromě zpracování parametrů a návratové adresy přemostí thunk rozdíly v nestálosti mezi ARM64EC a vektorovými registry x64 způsobenými mapováním ARM64EC vektorového registru:
registrace ARM64EC | Registrace x64 | ARM64EC konvence volání | Konvence volání ARM64 | x64 – konvence volání |
---|---|---|---|---|
v6 -v15 |
xmm6 -xmm15 |
volatile, ale uložen/obnoven v záznamu thunk (x64 to ARM64EC) | volatile or partially volatile upper 64 bits | nestálé |
Záznam thunk provede následující akce:
Číslo parametru | Využití zásobníku |
---|---|
0-4 | Ukládá ARM64EC v6 a v7 do domovského prostoru přiděleného volajícím.Vzhledem k tomu, že volaný je ARM64EC, který nemá představu o domovském prostoru, uložené hodnoty nejsou zahlcené. Přidělí dalších 128 bajtů v zásobníku a uloží ARM64EC v8 prostřednictvím v15 . |
5-8 | x4 = 5. parametr ze zásobníkux5 = 6. parametr ze zásobníkux6 = 7. parametr ze zásobníkux7 = 8. parametr ze zásobníkuPokud je parametr SIMD, v4 -v7 použijí se místo toho registry. |
9+ | AlignUp(NumParams - 8 , 2) * 8 Přidělí bajty v zásobníku. *Zkopíruje 9. a zbývající parametry do této oblasti. |
* Zarovnání hodnoty na sudé číslo zaručuje, že zásobník zůstane zarovnaný na 16 bajtů.
Pokud funkce přijímá 32bitový celočíselnou parametr, je tento blok povolený pouze nasdílení 32 bitů namísto plných 64 bitů nadřazeného registru.
V dalším kroku použije thunk instrukce ARM64 bl
k volání ARM64EC funkce. Po návratu funkce se zobrazí thunk:
- Vrátí zpět všechna přidělení zásobníku.
- Zavolá pomocnou rutinu emulátoru
__os_arm64x_dispatch_ret
, aby se vysuhla návratová adresa x64 a obnovila emulaci x64.
Konec thunk: ARM64EC volání funkce x64
Pro každé volání, které ARM64EC funkce C/C++ provádí pro potenciální kód x64, vygeneruje sada nástrojů MSVC ukončovací blok. Obsah thunk závisí na parametrech volaného x64 a na tom, zda volaný používá standardní konvenci volání nebo __vectorcall
. Kompilátor získá tyto informace z deklarace funkce pro volaný.
Za prvé, thunk nasdílí zpáteční adresu, která je v ARM64EC lr
registru a fiktivní 8 bajtová hodnota, aby se zajistilo, že zásobník je zarovnaný na 16 bajtů. Za druhé, thunk zpracovává parametry:
Číslo parametru | Využití zásobníku |
---|---|
0-4 | Přidělí 32 bajtů domovského prostoru v zásobníku. |
5-8 | Přiděluje AlignUp(NumParams - 4, 2) * 8 více bajtů vyšších v zásobníku. * Zkopíruje 5. a všechny další parametry z ARM64EC x4 -x7 do tohoto nadbytečného prostoru. |
9+ | Zkopíruje 9. a zbývající parametry do nadbytečného prostoru. |
* Zarovnání hodnoty na sudé číslo zaručuje, že zásobník zůstane zarovnaný na 16 bajtů.
Za třetí, thunk volá pomocník emulátoru __os_arm64x_dispatch_call_no_redirect
vyvolá emulátor emulátoru x64 ke spuštění funkce x64. Hovor musí být blr x16
instrukce (pohodlně, x16
je nestálý registr). Vyžaduje blr x16
se instrukce, protože emulátor x64 analyzuje tuto instrukci jako nápovědu.
Funkce x64 se obvykle pokouší vrátit do pomocné rutiny emulátoru pomocí instrukce x64 ret
. V tomto okamžiku emulátor x64 zjistí, že je v ARM64EC kódu. Pak přečte předchozí 4bajtů nápovědu, která se stane instrukce ARM64 blr x16
. Vzhledem k tomu, že tato nápověda indikuje, že návratová adresa je v této pomocné rutině, emulátor přeskočí přímo na tuto adresu.
Funkce x64 se může vrátit do pomocné rutiny emulátoru pomocí jakékoli instrukce větve, včetně x64 jmp
a call
. Emulátor také zpracovává tyto scénáře.
Když se pomocná rutina vrátí k útržku, je to:
- Vrácení všech přidělení zásobníku zpět
- Zobrazí ARM64EC
lr
registraci. - Spustí instrukce ARM64
ret lr
.
dekorace názvu funkce ARM64EC
Název ARM64EC funkce má sekundární dekoraci použitou po jakékoli dekoraci specifické pro jazyk. U funkcí s propojením jazyka C (ať už zkompilovaným jako C nebo pomocí extern "C"
) #
je před názvem. Pro funkce zdobené jazykem $$h
C++ se značka vloží do názvu.
foo => #foo
?foo@@YAHXZ => ?foo@@$$hYAHXZ
__vectorcall
Sada nástrojů ARM64EC v současné době nepodporuje __vectorcall
. Kompilátor vygeneruje chybu, když zjistí __vectorcall
použití s ARM64EC.
Viz také
Principy ARM64EC ABI a kódu sestavení
Běžné problémy s migrací ARM v jazyce Visual C++
Zdobené názvy