Wpis z mikrobloga

Dlaczego się wszędzie mówi że argumenty funkcji sa przekazywane przez stos, skoro nie są?
Czytam sobie specyfikację SystemV ABI dla AMD64 i w części 3.2.3 jest napisane:

2. If the class is INTEGER, the next available register of the sequence %rdi,%rsi, %rdx, %rcx, %r8 and %r9 is used.
Hmm, napisałem więc prostą funkcję i sprawdzam w asm jej wywołanie:

mov edi, 23
call square(int)

a po stronie samej funkcji:

mov DWORD PTR [rbp-4], edi
więc parametr ewidentnie leci przez rdi tak jak mówi specyfikacja.
Dlaczego zatem powszechnie się mówi że parametry są przekazywane przez stos, zamiast mówić że są przekazywane przez stos albo rejestry?

#programowanie #informatyka #cpp
  • 17
@Saly: mało rejestrów to było na procesorach z serii x86, gdzie indziej było inaczej, w latach 90 kompilatory miały możliwość ustawienia optymalizacji, by argumenty funkcji przekazywać przez rejestry, ale fakt to były kompilatory na procesory 68k
@Passer93: no i w we wszystkich maszynach stosowych czyli np. wszelakie języki interpretowane takie jak Java przekazywanie argumentów jest tylko przez stos.
@Passer93: przenośne architektury będą miały raczej coś w stylu - staramy się przez rejestry póki są wolne rejestry, resztę argumentów przez stos. W ten sposób Linux na jednej platformie przekazuje x argumentów przez rejestry na drugiej y.
Jeżeli uważasz że nie ma funkcji która w normalnych zastosowaniach przyjmuje więcej argumentów niż cpu ma rejestrów to popatrz chociażby na printf.
@Passer93: W Visual Studio możesz sobie dodać do funkcji __fastcall, i wtedy pierwsze 2 parametry będą przekazywane za pomocą rejestrów ecx i edx (x86), a pozostałe już na stosie. Jednakże, na większości kompilatorów w domyślnych ustawieniach wszystkie lecą na stos.
@inflo: A fakt, dla windowsa znalazłem takiego linka do MSDNu.

EDIT: Ale domyślnie z tego co widzę to 4 pierwsze argumenty też idą przez rejestry:

By default, the x64 calling convention passes the first four arguments to a function in registers.
Tutaj LINK.
@Passer93: poniewaz przez kilkanascie lat wlasnie taka byla konwencja, wszystkie argumenty byly przekazywane przez stos - System V ABI i386. Jak nie wierzysz, to skompiluj sobie z flaga -m32 -O0, argumenty beda ukladane na stosie za pomoca pushl.
W System V ABI x86-64 konwencja sie zmienila i teraz masz gwarancje, ze niektore rejestry zachowaja swoja wartosc, stad tez 6 argumentow laduje w rejestrach, reszta w stacku.
@inflo: @hszan: @Kaczus2B: @keton22: @Saly: @Loperamid: Pozwolę sobię was wszystkich zawołać bo nasuwa mi się kolejne pytanie.
Jeśli calling convention może zależeć od flag kompilatora, (jak fastcall dla MSCV) to różne object file mogą mieć różną calling convention na tym samym systemie z tą sama archiktekturą.
Jak wtedy wyglądala linkowanie tych pilków jesli odwołują się do funkcji w innych object plikach? Jeśli każdy object file może
@Passer93: nie wiem jak to jest zrobione, ale linker chyba nic nie musi wiedzieć. Skompilowana funkcja np. z __fastcall zakłada, że wołający wie jak ją zawołać. A wołający wie jak ją zawołać, bo używa headera, w którym jest napisana konwencja
@Passer93: o ile pamiętam to fastcall jest akurat przypadkiem specjalnym i jest skodyfikowany w windowsowym abi. obiekty mają też zapewne flagę mówiącą że dany symbol jest fastcallem coś jak far pointery w niektórych architekturach embedded (tam ustawiany jest najstarszy bit adresu i to magicznie czyni wartość specjalną).
@Saly: Ale w czy wołąjący zna konwencje z headera? Przecież w headerze będzie tylko prototyp funkcji więc coś w stylu:

int square(int, int)
co daje informacje o typie parametrów, ale nie mówi nic o ABI ani o calling convention ergo nie mówi to nic o tym czy przekazać parametry przez stos czy przez rejestry.
Chyba że o innym headerze mówisz a ja nie podumałem.