Dlaczego Windows64 używa innej konwencji wywoływania niż wszystkie inne systemy operacyjne na x86-64?

110

AMD ma specyfikację ABI, która opisuje konwencję wywoływania używaną na x86-64. Podążają za nią wszystkie systemy operacyjne, z wyjątkiem systemu Windows, który ma własną konwencję wywoływania x86-64. Czemu?

Czy ktoś zna techniczne, historyczne lub polityczne powody tej różnicy, czy też jest to wyłącznie kwestia zespołu NIH?

Rozumiem, że różne systemy operacyjne mogą mieć różne potrzeby w zakresie rzeczy wyższego poziomu, ale to nie wyjaśnia, dlaczego na przykład kolejność przekazywania parametrów rejestru w systemie Windows jest, rcx - rdx - r8 - r9 - rest on stackpodczas gdy wszyscy inni używają rdi - rsi - rdx - rcx - r8 - r9 - rest on stack.

PS Zdaję sobie sprawę, jak ogólnie te konwencje wywoływania różnią się i wiem, gdzie znaleźć szczegóły, jeśli zajdzie taka potrzeba. Chcę wiedzieć, dlaczego .

Edytuj: aby dowiedzieć się, jak to zrobić, zobacz np. Wpis na Wikipedii i stamtąd linki.

JanKanis
źródło
3
Cóż, tylko dla pierwszego rejestru: rcx: ecx był parametrem „this” dla konwencji msvc __thiscall x86. Więc prawdopodobnie tylko po to, aby ułatwić przenoszenie ich kompilatora na x64, zaczęli od rcx jako pierwsi. To, że wszystko inne też będzie inaczej, było tylko konsekwencją tej początkowej decyzji.
Chris Becke,
@Chris: Dodałem poniżej odniesienie do dokumentu uzupełniającego AMD64 ABI (i kilka wyjaśnień, czym właściwie jest).
FrankH.
1
Nie znalazłem uzasadnienia ze stwardnienia rozsianego, ale znalazłem tutaj
phuclv

Odpowiedzi:

81

Wybór czterech rejestrów argumentów na x64 - wspólny dla UN * X / Win64

Jedną z rzeczy, o których należy pamiętać w przypadku x86, jest to, że nazwa rejestru do kodowania „numeru rejestru” nie jest oczywista; pod względem kodowania instrukcji ( bajt MOD R / M , patrz http://www.c-jump.com/CIS77/CPU/x86/X77_0060_mod_reg_r_m_byte.htm ), numery rejestrów 0 ... 7 są - w tej kolejności - ?AX, ?CX, ?DX, ?BX, ?SP, ?BP, ?SI, ?DI.

Dlatego wybranie A / C / D (regs 0..2) jako wartości zwracanej i pierwszych dwóch argumentów (co jest "klasyczną" __fastcallkonwencją 32-bitową ) jest logicznym wyborem. Jeśli chodzi o wersję 64-bitową, zamawiane są „wyższe” regy, a Microsoft i UN * X / Linux wybrali R8/ R9jako pierwsi.

Mając to na uwadze, wybór przez Microsoft RAX(wartości zwracanej) oraz RCX, RDX, R8, R9(arg [0..3]) są zrozumiałe wybór jeśli zdecydujesz cztery rejestry argumentów.

Nie wiem, dlaczego AMD64 UN * X ABI wybrało RDXwcześniej RCX.

Wybór sześciu rejestrów argumentowych na x64 - specyficzny dla UN * X

UN * X, na architekturach RISC, tradycyjnie przekazywał argumenty w rejestrach - konkretnie dla pierwszych sześciu argumentów (tak jest przynajmniej w przypadku PPC, SPARC, MIPS). Co może być jednym z głównych powodów, dla których projektanci AMD64 (UN * X) ABI zdecydowali się na użycie sześciu rejestrów również w tej architekturze.

Więc jeśli chcesz sześć rejestrów przekazać argumenty, i to jest logiczne, aby wybrać RCX, RDX, R8a R9dla czterech z nich, co dwa pozostałe należy wybrać?

„Wyższe” rejestry wymagają dodatkowego bajtu przedrostka instrukcji, aby je wybrać i dlatego mają większy rozmiar instrukcji, więc nie chciałbyś wybierać żadnej z nich, jeśli masz opcje. Z klasycznych rejestrów, ze względu na ukryte znaczenie RBPi RSPnie są one dostępne, a RBXtradycyjnie ma specjalne zastosowanie w UN * X (globalna tabela offsetów), z którymi najwyraźniej projektanci AMD64 ABI nie chcieli niepotrzebnie stać się niekompatybilnymi.
Ergo, jedynym wyborem były RSI/ RDI.

Więc jeśli musisz wziąć RSI/ RDIjako rejestry argumentów, jakie powinny to być argumenty?

Wykonanie ich arg[0]i arg[1]ma pewne zalety. Zobacz komentarz cHao.
?SIi ?DIsą operandami źródłowymi / docelowymi instrukcji łańcuchowych, a jak wspomniało cHao, ich użycie jako rejestrów argumentów oznacza, że ​​w przypadku konwencji wywoływania AMD64 UN * X najprostsza możliwa strcpy()funkcja składa się na przykład tylko z dwóch instrukcji procesora, repz movsb; retponieważ źródło / cel adresy zostały wprowadzone przez dzwoniącego do odpowiednich rejestrów. Występuje, szczególnie w niskopoziomowym i generowanym przez kompilator kodzie „klejowym” (pomyśl, na przykład, niektóre alokatory sterty w C ++ wypełniające zerami obiekty w konstrukcji lub strony jądra wypełniające stertysbrk()lub błędy stronicowania przy kopiowaniu przy zapisie) ogromną ilość kopiowania / wypełniania bloków, dlatego będzie to przydatne w przypadku kodu tak często używanego do zapisywania dwóch lub trzech instrukcji procesora, które w przeciwnym razie ładowałyby takie argumenty adresu źródłowego / docelowego do „prawidłowe” rejestry.

Więc w pewnym sensie, UN * X i Win64 różnią się tylko tym, że UN * X „wstawia” dwa dodatkowe argumenty, w celowo wybranych RSI/ RDIrejestrów, z naturalnym wyborem cztery argumenty RCX, RDX, R8i R9.

Ponadto ...

Istnieje więcej różnic między interfejsami ABI UN * X i Windows x64 niż tylko mapowanie argumentów do określonych rejestrów. Aby zapoznać się z przeglądem systemu Win64, sprawdź:

http://msdn.microsoft.com/en-us/library/7kcdt6fy.aspx

Win64 i AMD64 UN * X również uderzająco różnią się sposobem wykorzystania przestrzeni stosowej; na przykład w Win64 wywołujący musi przydzielić przestrzeń stosu dla argumentów funkcji, mimo że argumenty 0 ... 3 są przekazywane w rejestrach. Z drugiej strony w UN * X funkcja liścia (tj. Taka, która nie wywołuje innych funkcji) nie jest nawet wymagana do przydzielania miejsca na stosie, jeśli nie potrzebuje więcej niż 128 bajtów (tak, posiadasz i możesz używać pewna ilość stosu bez przydzielania go ... no chyba, że ​​jesteś kodem jądra, źródłem sprytnych błędów). Wszystko to są określone wybory optymalizacyjne, większość ich uzasadnienia jest wyjaśniona w pełnych odniesieniach ABI, na które wskazuje wikipedia pierwotnego postu.

FrankH.
źródło
1
O nazwach rejestrów: Bajt przedrostka może być czynnikiem. Ale wtedy byłoby bardziej logiczne dla MS, aby wybrać rcx - rdx - rdi - rsi jako rejestry argumentów. Ale wartość liczbowa pierwszych ośmiu może cię poprowadzić, jeśli projektujesz ABI od zera, ale nie ma powodu, aby je zmieniać, jeśli doskonale dobry ABI już istnieje, co prowadzi tylko do większego zamieszania.
JanKanis
2
Na RSI / RDI: Te instrukcje są zwykle wbudowane, w takim przypadku konwencja wywoływania nie ma znaczenia. W przeciwnym razie istnieje tylko jedna kopia (lub może kilka) tej funkcji w całym systemie, więc w sumie oszczędza tylko garść bajtów . Nie warte tego. O innych różnicach / stosie wywołań: Użyteczność określonych wyborów jest wyjaśniona w odniesieniach ABI, ale nie dokonują porównania. Nie mówią, dlaczego nie wybrano innych optymalizacji - np. Dlaczego Windows nie ma 128-bajtowej czerwonej strefy i dlaczego AMD ABI nie ma dodatkowych miejsc na stosy dla argumentów?
JanKanis
1
@cHao: nie. Ale i tak to zmienili. Win64 ABI różni się od Win32 (i nie jest kompatybilny), a także różni się od AMD ABI.
JanKanis
8
@Somejan: Win64 i Win32 __fastcallsą w 100% identyczne w przypadku, gdy mają nie więcej niż dwa argumenty nie większe niż 32-bitowe i zwracają wartość nie większą niż 32-bitowe. To nie jest mała klasa funkcji. Żadna taka wsteczna kompatybilność nie jest w ogóle możliwa między interfejsami UN * X ABI dla i386 / amd64.
FrankH.
2
@szx: Właśnie znalazłem odpowiedni wątek na liście mailingowej z listopada 2000 roku i zamieściłem odpowiedź podsumowującą rozumowanie. Zauważ, memcpyże można to zaimplementować w ten sposób, a nie strcpy.
Peter Cordes,
42

IDK, dlaczego Windows zrobił to, co zrobił. Aby zgadnąć, zobacz koniec tej odpowiedzi. Byłem ciekawy, jak zdecydowano o konwencji wywoływania SysV, więc poszperałem w archiwum list dyskusyjnych i znalazłem kilka fajnych rzeczy.

Warto przeczytać niektóre z tych starych wątków na liście mailingowej AMD64, ponieważ architekci AMD byli na niej aktywni. Np. wybór nazw rejestrów był jedną z najtrudniejszychUAX części: AMD rozważało zmianę nazwy oryginalnych 8 rejestrów r0-r7 lub wywoływanie nowych rejestrów w stylu .

Ponadto, informacje zwrotne od jądra DEVS zidentyfikowanych rzeczy, które sprawiły, że oryginalny projekt syscalli swapgsbezużyteczny . W ten sposób AMD zaktualizowało instrukcję, aby rozwiązać ten problem przed wypuszczeniem jakichkolwiek rzeczywistych układów. Ciekawe jest również to, że pod koniec 2000 roku założono, że Intel prawdopodobnie nie przyjmie AMD64.


Konwencja wywołań SysV (Linux) i decyzja o tym, ile rejestrów powinno być zachowanych w porównaniu z zapisywaniem wywołań, została podjęta początkowo w listopadzie 2000 r. Przez Jana Hubickiego (programistę gcc). On skompilowany SPEC2000 i spojrzał na rozmiar kodu i liczby instrukcji. Ten wątek dyskusji odbija się wokół niektórych z tych samych pomysłów, co odpowiedzi i komentarze na to pytanie SO. W drugim wątku zaproponował obecną sekwencję jako optymalną i miejmy nadzieję ostateczną, generującą mniejszy kod niż niektóre alternatywy .

Używa terminu „globalny” na oznaczenie rejestrów z zachowaniem wywołań, które muszą być wypychane / popychane, jeśli są używane.

Wybór rdi, rsi, rdxjak pierwsze trzy args było motywowane przez:

  • niewielkie zapisywanie rozmiaru kodu w funkcjach, które wywołują memsetlub inną funkcję ciągu C w swoich argumentach (gdzie gcc wstawia operację ciągu rep?)
  • rbxjest zachowywany, ponieważ posiadanie dwóch rejestrów zachowanych w wywołaniach dostępnych bez prefiksów REX (rbx i rbp) jest wygraną. Prawdopodobnie wybrany, ponieważ jest to jedyny inny reg, który nie jest domyślnie używany przez żadną instrukcję. (ciąg powtórzeń, liczba przesunięć i wyjścia / wejścia mul / div dotykają wszystkiego innego).
  • Żaden z rejestrów o specjalnym przeznaczeniu nie jest zachowywany wywołaniami (patrz punkt prev), więc funkcja, która chce użyć instrukcji rep string lub shift-count, może musieć przenieść argumenty funkcji w inne miejsce, ale nie musi zapisywać / przywrócić wartość dzwoniącego.
  • Staramy się unikać RCX na początku sekwencji, ponieważ jest to rejestr powszechnie używany do specjalnych celów, takich jak EAX, więc ma ten sam cel, aby go brakować w sekwencji. Nie można go również używać do wywołań systemowych i chcielibyśmy, aby sekwencja wywołań systemowych pasowała do sekwencji wywołań funkcji w jak największym stopniu.

    (tło: syscall/ sysretnieuchronnie niszczy rcx(z rip) i r11(z RFLAGS), więc jądro nie może zobaczyć, co było pierwotnie w rcxczasie działania syscall.)

Wywołanie systemowe jądra ABI zostało wybrane tak, aby pasowało do wywołania funkcji ABI, z wyjątkiem r10zamiast zamiast rcx, więc opakowanie libc działa jak mmap(2)can po prostu mov %rcx, %r10/ mov $0x9, %eax/ syscall.


Zauważ, że konwencja wywoływania SysV używana przez i386 Linux jest do bani w porównaniu do 32-bitowego __vectorcall systemu Windows. Przekazuje wszystko ze stosu i wraca tylko edx:eaxdo int64, a nie do małych struktur . Nic dziwnego, że podjęto niewielki wysiłek, aby zachować zgodność z nim. Kiedy nie ma powodu, rbxby tego nie robić , robili takie rzeczy, jak zachowywanie połączeń, ponieważ zdecydowali, że posiadanie innego w oryginalnym 8 (który nie potrzebuje przedrostka REX) było dobre.

Ustalenie optymalnego wskaźnika ABI jest o wiele ważniejsze w perspektywie długoterminowej niż jakiekolwiek inne rozważanie. Myślę, że wykonali całkiem dobrą robotę. Nie jestem do końca pewien, czy zwracać struktury spakowane do rejestrów, zamiast różnych pól w różnych rejestrach. Wydaje mi się, że kod, który przekazuje je według wartości bez faktycznego działania na polach, wygrywa w ten sposób, ale dodatkowa praca związana z rozpakowaniem wydaje się głupia. Mogli mieć więcej rejestrów zwracających liczby całkowite, więcej niż tylko rdx:rax, więc zwrócenie struktury z 4 członami może zwrócić je w postaci rdi, rsi, rdx, rax lub coś w tym rodzaju.

Rozważali przekazywanie liczb całkowitych w regach wektora, ponieważ SSE2 może operować na liczbach całkowitych. Na szczęście tego nie zrobili. Liczby całkowite są bardzo często używane jako przesunięcia wskaźnika, a podróż w obie strony do pamięci stosowej jest dość tania . Również instrukcje SSE2 zajmują więcej bajtów kodu niż instrukcje w postaci liczb całkowitych.


Podejrzewam, że projektanci Windows ABI mogli dążyć do zminimalizowania różnic między 32 a 64 bitami z korzyścią dla ludzi, którzy muszą przenosić asm z jednego na drugi lub mogą używać kilku #ifdefs w niektórych ASM, aby to samo źródło mogło łatwiej budować 32- lub 64-bitowa wersja funkcji.

Minimalizowanie zmian w łańcuchu narzędzi wydaje się mało prawdopodobne. Kompilator x86-64 wymaga oddzielnej tabeli, w której rejestr jest używany do czego i jaka jest konwencja wywoływania. Niewielkie nakładanie się na wersję 32-bitową prawdopodobnie nie przyniesie znaczących oszczędności w rozmiarze / złożoności kodu łańcucha narzędzi.

Peter Cordes
źródło
1
Myślę, że czytałem gdzieś na blogu Raymonda Chena o uzasadnieniu wyboru tych rejestrów po benchmarkingu ze strony MS, ale nie mogę go już znaleźć. Jednak niektóre powody dotyczące strefy domowej
phuclv
@phuclv: Zobacz także Czy można pisać poniżej ESP? . Komentarze Raymonda dotyczące mojej odpowiedzi wskazywały na pewne szczegóły SEH, których nie znałem, które wyjaśniają, dlaczego Windows x86 32/64 nie ma obecnie de facto czerwonej strefy. Jego post na blogu zawiera kilka prawdopodobnych przypadków tej samej możliwości obsługi strony kodowej, o której wspomniałem w tej odpowiedzi :) Więc tak, Raymond wykonał lepszą robotę wyjaśniając to niż ja (co nie jest zaskakujące, ponieważ zacząłem niewiele wiedzieć o systemie Windows), a tabela rozmiarów stref czerwonych dla systemów innych niż x86 jest naprawdę zgrabna.
Peter Cordes
13

Pamiętaj, że Microsoft był początkowo „oficjalnie niezobowiązujący w stosunku do wczesnych wysiłków AMD64” (z „A History of Modern 64-bit Computing” autorstwa Matthew Kernera i Neila Padgetta), ponieważ byli silnymi partnerami Intela w zakresie architektury IA64. Myślę, że oznaczało to, że nawet gdyby w innym przypadku byliby otwarci na współpracę z inżynierami GCC nad ABI do użytku zarówno w systemie Unix, jak i Windows, nie zrobiliby tego, ponieważ oznaczałoby to publiczne wspieranie wysiłków AMD64, kiedy tego nie zrobili. t jeszcze oficjalnie to zrobił (i prawdopodobnie zdenerwowałby Intela).

Co więcej, w tamtych czasach Microsoft nie miał absolutnie żadnych skłonności do przyjaźni z projektami open source. Na pewno nie Linux czy GCC.

Dlaczego więc mieliby współpracować przy ABI? Domyślam się, że ABI są różne po prostu dlatego, że zostały zaprojektowane mniej więcej w tym samym czasie i w izolacji.

Inny cytat z „A History of Modern 64-bit Computing”:

Równolegle ze współpracą z Microsoftem AMD zaangażowało społeczność open source do przygotowania chipa. AMD podpisało kontrakt zarówno z Code Sorcery, jak i SuSE na pracę w łańcuchu narzędzi (Red Hat był już zaangażowany przez Intel w zakresie portu łańcucha narzędzi IA64). Russell wyjaśnił, że SuSE wyprodukowało kompilatory C i FORTRAN, a Code Sorcery - kompilator Pascal. Weber wyjaśnił, że firma współpracowała również ze społecznością Linuksa w celu przygotowania portu Linux. Wysiłek ten był bardzo ważny: stanowił zachętę dla Microsoft do dalszego inwestowania w wysiłki AMD64 Windows, a także zapewnił, że Linux, który w tamtym czasie stawał się ważnym systemem operacyjnym, będzie dostępny po wydaniu chipów.

Weber posuwa się nawet do stwierdzenia, że ​​praca w Linuksie była absolutnie kluczowa dla sukcesu AMD64, ponieważ umożliwiła AMD wyprodukowanie kompleksowego systemu bez pomocy innych firm, jeśli to konieczne. Ta możliwość zapewniała, że ​​AMD miało najgorszą strategię przetrwania, nawet jeśli inni partnerzy się wycofali, co z kolei utrzymywało innych partnerów zaangażowanych w obawie przed pozostawieniem w tyle.

To wskazuje, że nawet AMD nie uważało, że współpraca między MS i Unixem była najważniejsza, ale posiadanie obsługi Unix / Linux było bardzo ważne. Może nawet próba przekonania jednej lub obu stron do kompromisu lub współpracy nie była warta wysiłku ani ryzyka (?) Irytowania którejkolwiek z nich? Być może AMD pomyślało, że nawet sugerowanie wspólnego ABI może opóźnić lub zakłócić ważniejszy cel, jakim jest po prostu przygotowanie obsługi oprogramowania, gdy chip będzie gotowy.

Spekulacje z mojej strony, ale myślę, że głównym powodem, dla którego ABI są różne, był polityczny powód, dla którego MS i strony Unix / Linux po prostu nie współpracowały nad tym, a AMD nie postrzegało tego jako problemu.

Michael Burr
źródło
Niezłe spojrzenie na politykę. Zgadzam się, że to nie wina ani odpowiedzialność AMD. Winię Microsoft za wybór gorszej konwencji telefonicznej. Gdyby ich konwencja callowania okazała się lepsza, miałbym trochę współczucia, ale musieli zmienić początkowe ABI na, __vectorcallponieważ przekazywanie __m128stosu było do niczego. Mając semantykę połączeń zachowane za niską 128b niektórych regs wektor jest również dziwny (częściowo wina Intela na nie projektuje rozszerzalną zapisu / przywrócenia mechanizmu z SSE pierwotnie, i nadal nie z AVX.)
Peter Cordes
1
Nie mam żadnej wiedzy ani wiedzy na temat tego, jak dobre są ABI. Po prostu czasami muszę wiedzieć, jakie one są, aby móc zrozumieć / debugować na poziomie zestawu.
Michael Burr
1
Dobry ABI minimalizuje rozmiar kodu i liczbę instrukcji oraz utrzymuje małe opóźnienia łańcuchów zależności, unikając dodatkowych obiegów przez pamięć. (dla argumentów lub dla lokalnych, które muszą zostać rozlane / przeładowane). Istnieją kompromisy. Czerwona strefa SysV pobiera kilka dodatkowych instrukcji w jednym miejscu (dyspozytorze obsługi sygnału jądra), co daje stosunkowo dużą korzyść dla funkcji liści, ponieważ nie trzeba dostosowywać wskaźnika stosu, aby uzyskać trochę miejsca na zarysowania. To oczywiste zwycięstwo z niemal zerową wadą. Został przyjęty praktycznie bez dyskusji po tym, jak został zaproponowany dla SysV.
Peter Cordes
1
@dgnuff: Tak, to jest odpowiedź na pytanie: Dlaczego kod jądra nie może używać czerwonej strefy . Przerwania używają stosu jądra, a nie stosu przestrzeni użytkownika, nawet jeśli pojawiają się, gdy procesor wykonuje kod przestrzeni użytkownika. Jądro nie ufa stosom przestrzeni użytkownika, ponieważ inny wątek w tym samym procesie przestrzeni użytkownika mógłby go zmodyfikować, przejmując w ten sposób kontrolę nad jądrem!
Peter Cordes
1
@ DavidA.Gray: tak, ABI nie mówi, że musisz używać RBP jako wskaźnika ramki, więc zoptymalizowany kod zwykle tego nie robi (z wyjątkiem funkcji, które używają allocalub kilku innych przypadków). Jest to normalne, jeśli jesteś przyzwyczajony do gcc -fomit-frame-pointerbycia domyślnym w systemie Linux. ABI definiuje metadane, które pozwalają na obsługę wyjątków. (Zakładam, że działa to coś w rodzaju pliku CFI systemu V w systemie GNU / Linux x86-64 .eh_frame). gcc -fomit-frame-pointerjest ustawieniem domyślnym (z włączoną optymalizacją) od zawsze na x86-64, a inne kompilatory (takie jak MSVC) robią to samo.
Peter Cordes
12

Win32 ma swoje własne zastosowania dla ESI i EDI i wymaga, aby nie były modyfikowane (lub przynajmniej aby zostały przywrócone przed wywołaniem do API). Wyobrażam sobie, że 64-bitowy kod robi to samo z RSI i RDI, co wyjaśniałoby, dlaczego nie są one używane do przekazywania argumentów funkcji.

Nie potrafię jednak powiedzieć, dlaczego zamieniono RCX i RDX.

cHao
źródło
1
Wszystkie konwencje wywoływania mają niektóre rejestry oznaczone jako scratch, a niektóre jako zachowane, jak ESI / EDI i RSI / RDI na Win64. Ale to są rejestry ogólnego przeznaczenia, Microsoft mógł bez problemu wybrać ich użycie w inny sposób.
JanKanis
1
@Somejan: Jasne, gdyby chcieli przepisać całe API i mieć dwa różne systemy operacyjne. Nie nazwałbym tego jednak „bez problemu”. Przez dziesiątki lat MS składało pewne obietnice dotyczące tego, co zrobi, a czego nie zrobi z rejestrami x86, i przez cały ten czas były one mniej więcej spójne i kompatybilne. Nie wyrzucą tego wszystkiego przez okno tylko z powodu jakiegoś edyktu AMD, szczególnie tak arbitralnego i wykraczającego poza domenę „budowania procesora”.
cHao
5
@Somejan: AMD64 UN * X ABI zawsze było dokładnie tym - elementem specyficznym dla systemu UNIX . Nie bez powodu dokument x86-64.org/documentation/abi.pdf zatytułowany jest Interfejs binarny aplikacji System V, Dodatek do procesora architektury AMD64 . (Powszechne) UNIX- owe ABI (zbiór wielotomowy, sco.com/developers/devspecs ) pozostawiają sekcję poświęconą rozdziałowi 3 specyficznemu dla procesora - Dodatku - które są konwencjami wywoływania funkcji i regułami układu danych dla konkretnego procesora.
FrankH.
7
@Somejan: Microsoft Windows nigdy nie próbował być szczególnie blisko UN * X, a jeśli chodzi o przeniesienie systemu Windows na x64 / AMD64, po prostu zdecydował się rozszerzyć własną __fastcall konwencję wywoływania. Twierdzisz, że Win32 / Win64 nie są kompatybilne, ale przyjrzyj się uważnie: w przypadku funkcji, która pobiera dwa 32-bitowe argumenty i zwraca 32-bitowe, Win64 i Win32 są w__fastcall rzeczywistości w 100% kompatybilne (te same reguły dotyczące przekazywania dwóch 32-bitowych argumentów, ta sama wartość zwracana). Nawet kod binarny (!) Może działać w obu trybach pracy. Strona UNIX całkowicie zerwała ze „starymi sposobami”. Nie bez powodu, ale przerwa to przerwa.
FrankH.
2
@Olof: To coś więcej niż tylko kompilator. Miałem problemy z ESI i EDI, kiedy robiłem samodzielne rzeczy w NASM. Windows zdecydowanie dba o te rejestry. Ale tak, możesz ich użyć, jeśli zapiszesz je przed wykonaniem i przywrócisz, zanim system Windows będzie ich potrzebował.
cHao