Wygląda na to, że kompilatory state-of-art traktują argumenty przekazywane przez stos jako tylko do odczytu. Zauważ, że w konwencji wywoływania x86, wywołujący wypycha argumenty na stos, a wywołujący używa argumentów w stosie. Na przykład, następujące kod C:Konwencja wywoływania x86: czy argumenty przekazywane przez stos powinny być tylko do odczytu?
extern int goo(int *x);
int foo(int x, int y) {
goo(&x);
return x;
}
zbierane przez clang -O3 -c g.c -S -m32
Os X 10.10 na:
.section __TEXT,__text,regular,pure_instructions
.macosx_version_min 10, 10
.globl _foo
.align 4, 0x90
_foo: ## @foo
## BB#0:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
movl 8(%ebp), %eax
movl %eax, -4(%ebp)
leal -4(%ebp), %eax
movl %eax, (%esp)
calll _goo
movl -4(%ebp), %eax
addl $8, %esp
popl %ebp
retl
.subsections_via_symbols
tu parametr x
(8(%ebp)
) najpierw ładowane do %eax
; a następnie przechowywany w -4(%ebp)
; a adres -4(%ebp)
jest przechowywany w %eax
; i %eax
jest przekazywana do funkcji goo
.
Zastanawiam się dlaczego Clang generuje kod, który skopiować wartość przechowywaną w 8(%ebp)
do -4(%ebp)
, zamiast po prostu przechodząc adres 8(%ebp)
do funkcji goo
. Pozwoli to zaoszczędzić operacje związane z pamięcią i zapewnić lepszą wydajność. Podobnie zaobserwowałem zachowanie w GCC (pod OS X). Aby być bardziej konkretny, zastanawiam się, dlaczego kompilatory nie generują:
.section __TEXT,__text,regular,pure_instructions
.macosx_version_min 10, 10
.globl _foo
.align 4, 0x90
_foo: ## @foo
## BB#0:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
leal 8(%ebp), %eax
movl %eax, (%esp)
calll _goo
movl 8(%ebp), %eax
addl $8, %esp
popl %ebp
retl
.subsections_via_symbols
szukałem dokumentów czy konwencja x86 wywołanie wymaga przekazany argumenty mają być tylko do odczytu, ale nie mogłem znaleźć nic na ten temat. Czy ktoś ma jakąś myśl na ten temat?
Masz tu dobry punkt! '8 (% ebp)' znajduje się w ramce stosu wywołującego, ale jest to przestrzeń, która została przydzielona specjalnie do przekazywania argumentów do 'foo'. Czy wywołujący użyje tej przestrzeni dla własnych celów * po * 'foo' zwróci, zamiast po prostu go zniszczyć, dopasowując wskaźnik stosu? Jeśli tak, konieczne jest skopiowanie wartości do ramki stosu 'foo'. Jeśli nie, może to być bezpieczne dla 'foo', aby" pożyczyć "przestrzeń w ramce stosu dzwoniącego zamiast kopiować. Aby wiedzieć, czy twój pomysł jest dobry, czy nie, musisz zobaczyć, jak wygląda kod dla 'foo's * caller *. –
@AlexD Dziękujemy za komentarz! Ponieważ 'foo' może być wywołany przez dowolną funkcję, myślę, że jest to pytanie dotyczące konwencji wywoływania, a nie konkretnego kontekstu, w którym' foo' jest wywoływany. –
To interesujące pytanie. Znalazłem [to inne pytanie] (http://stackoverflow.com/questions/1684682/c-calling-conventions-and-passed-arguments?rq=1), które twierdzi, że gcc -O2 faktycznie zmodyfikował argument stosu wywoływanego przez wywołującego . – JS1