Udostępnij za pośrednictwem


Omówienie konwencji ARM64EC ABI

ARM64EC to interfejs binarny aplikacji (ABI), który umożliwia natywne uruchamianie plików binarnych arm64 i współdziałanie z kodem x64. W szczególności ARM64EC ABI jest zgodna z konwencjami oprogramowania x64, w tym konwencjami wywoływania, użyciem stosu i dopasowaniem danych, dzięki czemu ARM64EC i x64 kod jest współdziałający. System operacyjny emuluje część x64 pliku binarnego. (EC in ARM64EC oznacza emulację zgodną).

Aby uzyskać więcej informacji na temat interfejsów API x64 i ARM64, zobacz Omówienie konwencji X64 ABI i Omówienie konwencji ABI arm64.

ARM64EC nie rozwiązuje różnic między architekturami opartymi na architekturze x64 i arm. Aby uzyskać więcej informacji, zobacz Typowe problemy z migracją arm w języku Visual C++.

Definicje

  • ARM64 — strumień kodu dla procesów ARM64, który zawiera tradycyjny kod ARM64.
  • ARM64EC — strumień kodu korzystający z podzbioru zestawu rejestrów ARM64 w celu zapewnienia współdziałania z kodem x64.

Rejestrowanie mapowania

Procesy x64 mogą mieć wątki z uruchomionym kodem ARM64EC. Dlatego zawsze można pobrać kontekst rejestru x64, ARM64EC używa podzestawu rejestrów rdzeni ARM64, które mapuje 1:1 na emulowane rejestry x64. Co ważne, ARM64EC nigdy nie używa rejestrów spoza tego podzestawu, z wyjątkiem odczytu adresu bloku środowiska wątkowego (TEB) z x18.

Natywne procesy ARM64 nie powinny regresji wydajności, gdy niektóre lub wiele funkcji zostaną ponownie skompilowane jako ARM64EC. Aby utrzymać wydajność, usługa ABI jest zgodna z następującymi zasadami:

  • Podzestaw rejestru ARM64EC zawiera wszystkie rejestry będące częścią konwencji wywoływania funkcji ARM64.

  • ARM64EC konwencji wywoływania bezpośrednio mapuje się na konwencję wywoływania arm64.

Specjalne procedury pomocnicze, takie jak __chkstk_arm64ec używanie niestandardowych konwencji wywoływania i rejestrów. Rejestry te są również zawarte w podzestawie rejestrów ARM64EC.

Rejestrowanie mapowania dla rejestrów liczb całkowitych

rejestrowanie ARM64EC Rejestr x64 ARM64EC konwencji wywoływania Konwencja wywoływania arm64 Konwencja wywoływania x64
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 (niskie 64 bity rejestru x87 R1 ) volatile volatile volatile
x7 mm2 (niskie 64 bity rejestru x87 R2 ) volatile volatile volatile
x8 rax volatile volatile volatile
x9 mm3 (niskie 64 bity rejestru x87 R3 ) volatile volatile volatile
x10 mm4 (niskie 64 bity rejestru x87 R4 ) volatile volatile volatile
x11 mm5 (niskie 64 bity rejestru x87 R5 ) volatile volatile volatile
x12 mm6 (niskie 64 bity rejestru x87 R6 ) volatile volatile volatile
x13 Nie dotyczy Niedozwolone volatile Brak
x14 Brak Niedozwolone volatile Nie dotyczy
x15 mm7 (niskie 64 bity rejestru x87 R7 ) volatile volatile volatile
x16 Wysokie 16 bitów każdego z rejestrów x87 R0-R3 volatile(xip0) volatile(xip0) volatile
x17 Wysokie 16 bitów każdego z rejestrów x87 R4-R7 volatile(xip1) volatile(xip1) volatile
x18 GS.base fixed(TEB) fixed(TEB) fixed(TEB)
x19 r12 nietrwałe nietrwałe nietrwałe
x20 r13 nietrwałe nietrwałe nietrwałe
x21 r14 nietrwałe nietrwałe nietrwałe
x22 r15 nietrwałe nietrwałe nietrwałe
x23 Nie dotyczy Niedozwolone nietrwałe Brak
x24 Brak Niedozwolone nietrwałe Nie dotyczy
x25 rsi nietrwałe nietrwałe nietrwałe
x26 rdi nietrwałe nietrwałe nietrwałe
x27 rbx nietrwałe nietrwałe nietrwałe
x28 Nie dotyczy Niedozwolone Niedozwolone Nie dotyczy
fp rbp nietrwałe nietrwałe nietrwałe
lr mm0 (niskie 64 bity rejestru x87 R0 ) oba oba oba
sp rsp nietrwałe nietrwałe nietrwałe
pc rip wskaźnik instrukcji wskaźnik instrukcji wskaźnik instrukcji
PSTATEpodzestaw: NZSS/C//V/1, 2 RFLAGS podzbiór: SF/ZF/CF/OF/TF volatile volatile volatile
Nie dotyczy RFLAGS podzbiór: PF/AF Brak Brak volatile
Nie dotyczy RFLAGS podzbiór: DF Brak Brak nietrwałe

1 Unikaj bezpośredniego odczytywania, zapisywania lub przetwarzania mapowań między PSTATE i RFLAGS. Te bity mogą być używane w przyszłości i mogą ulec zmianie.

2 Flaga przenoszenia C ARM64EC jest odwrotnością flagi CF przenoszenia x64 dla operacji odejmowania. Nie ma specjalnej obsługi, ponieważ flaga jest nietrwała i dlatego jest zaśmiecona podczas przechodzenia między funkcjami (ARM64EC i x64).

Rejestrowanie mapowania dla rejestrów wektorów

rejestrowanie ARM64EC Rejestr x64 ARM64EC konwencji wywoływania Konwencja wywoływania arm64 Konwencja wywoływania x64
v0-v5 xmm0-xmm5 volatile volatile volatile
v6-v7 xmm6-xmm7 volatile volatile nietrwałe
v8-v15 xmm8-xmm15 volatile 1 volatile 1 nietrwałe
v16-v31 xmm16-xmm31 Niedozwolone volatile niedozwolone (emulator x64 nie obsługuje avX-512)
FPCR 2 MXCSR[15:6] nietrwałe nietrwałe nietrwałe
FPSR 2 MXCSR[5:0] volatile volatile volatile

1 Te rejestry ARM64 są specjalne w tym, że dolne 64 bity są nietrwałe, ale górne 64 bity są niestabilne. Z punktu widzenia obiektu wywołującego x64 są one skutecznie nietrwałe, ponieważ obiekt wywoływany będzie kosza na dane.

2 Unikaj bezpośredniego odczytywania, zapisywania lub mapowań obliczeniowych elementów FPCR i FPSR. Te bity mogą być używane w przyszłości i mogą ulec zmianie.

Pakowanie struktury

ARM64EC są zgodne z tymi samymi regułami pakowania struktury używanymi dla x64 w celu zapewnienia współdziałania kodu ARM64EC i kodu x64. Aby uzyskać więcej informacji i przykłady pakowania struktury x64, zobacz Omówienie konwencji X64 ABI.

Procedury ABI pomocnika emulacji

ARM64EC kodu i thunks używają procedur pomocnika emulacji do przejścia między x64 i ARM64EC funkcji.

W poniższej tabeli opisano każdą specjalną procedurę ABI i rejestry używane przez usługę ABI. Procedury nie modyfikują wymienionych zachowanych rejestrów w kolumnie ABI. Nie należy wprowadzać żadnych założeń dotyczących rejestrów nieznajdowanych na liście. Na dysku wskaźniki rutynowe ABI mają wartość null. W czasie ładowania moduł ładujący aktualizuje wskaźniki, aby wskazywały procedury emulatora x64.

Nazwa/nazwisko opis ABI
__os_arm64x_dispatch_call_no_redirect Wywoływana przez wyjście thunk w celu wywołania elementu docelowego x64 (funkcji x64 lub sekwencji szybkiego przekazywania x64). Rutyna wypycha adres zwrotny ARM64EC (w LR rejestrze), a następnie adres instrukcji, która kończy się powodzeniem blr x16 instrukcji, która wywołuje emulator x64. Następnie uruchamia instrukcję blr x16 zwracana wartość w (x8rax)
__os_arm64x_dispatch_ret Wywoływana przez wpis thunk, aby wrócić do swojego rozmówcę x64. Polecenie wyskakuje adres zwrotny x64 ze stosu i wywołuje emulator x64, aby przejść do niego Nie dotyczy
__os_arm64x_check_call Wywoływana przez kod ARM64EC ze wskaźnikiem do zakończenia thunk i pośrednim adresem docelowym ARM64EC do wykonania. Obiekt docelowy ARM64EC jest uznawany za możliwy do stosowania poprawek, a wykonanie zawsze wraca do obiektu wywołującego z tymi samymi danymi, z których został wywołany, lub ze zmodyfikowanymi danymi Argumenty:
x9: adres docelowy
x10: Adres wyjścia thunk
x11: adres sekwencji szybkiej sekwencji

Na zewnątrz:
x9: Jeśli funkcja docelowa została objazdowana, zawiera adres szybkiej sekwencji przesyłania dalej
x10: Adres wyjścia thunk
x11: Jeśli funkcja została objazdowa, zawiera adres exit thunk. W przeciwnym razie adres docelowy został przeskoczył do

Zachowane rejestry: x0-x8, x15 ().chkstk i q0-q7
__os_arm64x_check_icall Wywoływana przez kod ARM64EC ze wskaźnikiem do wyjścia thunk w celu obsługi skoku do adresu docelowego, który jest x64 lub ARM64EC. Jeśli element docelowy to x64, a kod x64 nie został poprawiony, procedury ustawiają rejestr adresów docelowych. Wskazuje ARM64EC wersję funkcji, jeśli istnieje. W przeciwnym razie ustawia rejestr, aby wskazywał wyjście thunk, który przechodzi do obiektu docelowego x64. Następnie powraca do kodu wywołującego ARM64EC, który następnie przechodzi do adresu w rejestrze. Ta rutyna jest nieoptymalizowana wersją __os_arm64x_check_callprogramu , gdzie adres docelowy nie jest znany w czasie kompilacji

Używane w lokacji wywołania pośredniego
Argumenty:
x9: adres docelowy
x10: Adres wyjścia thunk
x11: adres sekwencji szybkiej sekwencji

Na zewnątrz:
x9: Jeśli funkcja docelowa została objazdowana, zawiera adres szybkiej sekwencji przesyłania dalej
x10: Adres wyjścia thunk
x11: Jeśli funkcja została objazdowa, zawiera adres exit thunk. W przeciwnym razie adres docelowy został przeskoczył do

Zachowane rejestry: x0-x8, x15 (chkstk) i q0-q7
__os_arm64x_check_icall_cfg Tak samo jak w przypadku __os_arm64x_check_icall sprawdzania, czy określony adres jest prawidłowym elementem docelowym wywołania pośredniego grafu przepływu sterowania Argumenty:
x10: adres wyjścia thunk
x11: adres funkcji docelowej

Na zewnątrz:
x9: Jeśli element docelowy to x64, adres funkcji. W przeciwnym razie niezdefiniowane
x10: adres wyjścia thunk
x11: Jeśli element docelowy to x64, zawiera adres wyjścia thunk. W przeciwnym razie adres funkcji

Zachowane rejestry: x0-x8, x15 (chkstk) i q0-q7
__os_arm64x_get_x64_information Pobiera żądaną część kontekstu rejestracji na żywo x64 _Function_class_(ARM64X_GET_X64_INFORMATION) NTSTATUS LdrpGetX64Information(_In_ ULONG Type, _Out_ PVOID Output, _In_ PVOID ExtraInfo)
__os_arm64x_set_x64_information Ustawia żądaną część kontekstu rejestracji na żywo x64 _Function_class_(ARM64X_SET_X64_INFORMATION) NTSTATUS LdrpSetX64Information(_In_ ULONG Type,_In_ PVOID Input, _In_ PVOID ExtraInfo)
__os_arm64x_x64_jump Używany w korektorze bez podpisu i innych thunks, które bezpośrednio do przodu (jmp) wywołanie innej funkcji, która może mieć dowolny podpis, odroczenie potencjalnego zastosowania prawego thunk do rzeczywistego celu Argumenty:
x9: element docelowy, aby przejść do

Wszystkie rejestry parametrów zachowane (przekazywane)

Łącznikami

Thunks to mechanizmy niskiego poziomu do obsługi funkcji ARM64EC i x64 wywołujących się nawzajem. Istnieją dwa typy: thunks wejścia do wprowadzania funkcji ARM64EC i wyjścia thunks do wywoływania funkcji x64.

Entry thunk i wewnętrzny wpis thunks: x64 do ARM64EC wywołanie funkcji

Aby obsługiwać wywołania x64 podczas kompilowania funkcji C/C++ jako ARM64EC, łańcuch narzędzi generuje pojedynczy wpis thunk składający się z ARM64EC kodu maszynowego. Funkcje wewnętrzne mają własny wpis thunk własnych. Wszystkie inne funkcje współużytkują wpis thunk ze wszystkimi funkcjami, które mają zgodną konwencję wywoływania, parametry i typ zwracany. Zawartość thunk zależy od konwencji wywoływania funkcji C/C++.

Oprócz obsługi parametrów i adresu zwrotnego, thunk mosty różnice w zmienności między ARM64EC i x64 rejestry wektorów spowodowane przez ARM64EC mapowanie rejestru wektorów:

rejestrowanie ARM64EC Rejestr x64 ARM64EC konwencji wywoływania Konwencja wywoływania arm64 Konwencja wywoływania x64
v6-v15 xmm6-xmm15 volatile, ale zapisane/przywrócone we wpisie thunk (x64 do ARM64EC) volatile lub częściowo lotne górne 64 bity nietrwałe

Wpis thunk wykonuje następujące akcje:

Numer parametru Użycie stosu
0-4 Przechowuje ARM64EC v6 i v7 do przydzielonej przez obiekt wywołujący przestrzeni domowej

Ponieważ obiekt wywoływany jest ARM64EC, który nie ma pojęcia przestrzeni domowej, przechowywane wartości nie są clobbered.

Przydziela dodatkowe 128 bajtów na stosie i przechowuje ARM64EC v8 przez v15.
5-8 x4 = 5. parametr ze stosu
x5 = 6. parametr ze stosu
x6 = 7. parametr ze stosu
x7 = 8. parametr ze stosu

Jeśli parametr ma wartość SIMD, v4-v7 rejestry są używane zamiast tego
9+ Przydziela bajty AlignUp(NumParams - 8 , 2) * 8 na stosie. *

Kopiuje 9 i pozostałe parametry do tego obszaru

* Wyrównanie wartości do liczby parzystej gwarantuje, że stos pozostaje wyrównany do 16 bajtów

Jeśli funkcja akceptuje 32-bitową liczbę całkowitą, thunk może wypchnąć tylko 32 bity zamiast pełnych 64 bitów rejestru nadrzędnego.

Następnie thunk używa instrukcji ARM64 bl do wywoływania funkcji ARM64EC. Po powrocie funkcji thunk:

  1. Cofa wszystkie alokacje stosu
  2. Wywołuje pomocnika emulatora __os_arm64x_dispatch_ret , aby wyskakować adres zwrotny x64 i wznowić emulację x64.

Zamknij thunk: ARM64EC do wywołania funkcji x64

Dla każdego wywołania, które funkcja ARM64EC C/C++ wykonuje do potencjalnego kodu x64, łańcuch narzędzi MSVC generuje thunk zakończenia. Zawartość thunk zależy od parametrów wywołania x64 i czy wywoływanie używa standardowej konwencji wywoływania lub __vectorcall. Kompilator uzyskuje te informacje z deklaracji funkcji wywoływanej.

Po pierwsze, thunk wypycha adres zwrotny, który znajduje się w rejestrze ARM64EC lr i fikcyjną wartość 8 bajtów, aby zagwarantować, że stos jest wyrównany do 16 bajtów. Po drugie, thunk obsługuje parametry:

Numer parametru Użycie stosu
0-4 Przydziela 32 bajty miejsca macierzystego na stosie
5-8 AlignUp(NumParams - 4, 2) * 8 Przydziela więcej bajtów w górę na stosie. *

Kopiuje 5 i wszystkie kolejne parametry z ARM64EC x4-x7 do tego dodatkowego miejsca
9+ Kopiuje 9 i pozostałe parametry do dodatkowego miejsca

* Wyrównanie wartości do liczby parzystej gwarantuje, że stos pozostaje wyrównany do 16 bajtów.

Po trzecie, thunk wywołuje __os_arm64x_dispatch_call_no_redirect pomocnika emulatora, aby wywołać emulator x64 w celu uruchomienia funkcji x64. Wywołanie musi być instrukcją blr x16 (wygodnie, x16 jest rejestrem lotnym). Wymagana blr x16 jest instrukcja, ponieważ emulator x64 analizuje tę instrukcję jako wskazówkę.

Funkcja x64 zwykle próbuje wrócić do pomocnika emulatora przy użyciu instrukcji x64 ret . W tym momencie emulator x64 wykrywa, że znajduje się w ARM64EC kodzie. Następnie odczytuje poprzednią wskazówkę 4-bajtową, która jest instrukcją ARM64 blr x16 . Ponieważ ta wskazówka wskazuje, że adres zwrotny znajduje się w tym pomocniku, emulator przechodzi bezpośrednio do tego adresu.

Funkcja x64 może wrócić do pomocnika emulatora przy użyciu dowolnej instrukcji gałęzi, w tym x64 jmp i call. Emulator obsługuje również te scenariusze.

Kiedy pomocnik następnie wraca do thunk, thunk:

  1. Cofa wszystkie alokacje stosu
  2. Wyskakuje rejestr ARM64EC lr
  3. Wykonuje instrukcję ARM64 ret lr .

ARM64EC dekoracja nazwy funkcji

Nazwa funkcji ARM64EC ma dekorację pomocniczą zastosowaną po każdej dekoracji specyficznej dla języka. W przypadku funkcji z połączeniem języka C (czy skompilowanym jako C, czy przy użyciu ), extern "C"# element jest poprzedzony nazwą. W przypadku funkcji $$h ozdobionych w języku C++ tag jest wstawiany do nazwy.

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

__vectorcall

Łańcuch narzędzi ARM64EC obecnie nie obsługuje __vectorcallelementu . Kompilator emituje błąd podczas wykrywania __vectorcall użycia za pomocą ARM64EC.

Zobacz też

Opis ARM64EC ABI i kodu zestawu
Typowe problemy z migracją usługi ARM w języku Visual C++
Nazwy ozdobione