Кажется, что современные компиляторы трактуют аргументы, переданные стеком, только для чтения. Обратите внимание, что в соглашении на вызов x86 вызывающий пользователь вставляет аргументы в стек, а вызываемый использует аргументы в стеке. Например, следующий код C:
extern int goo(int *x);
int foo(int x, int y) {
goo(&x);
return x;
}
скомпилирован clang -O3 -c g.c -S -m32
в OS X 10.10 в:
.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
Здесь параметр x
(8(%ebp)
) сначала загружается в %eax
; и затем сохраняется в -4(%ebp)
; и адрес -4(%ebp)
сохраняется в %eax
; и %eax
передается функции goo
.
Интересно, почему Clang генерирует код, который копирует значение, хранящееся в 8(%ebp)
, в -4(%ebp)
, а не просто передает адрес 8(%ebp)
функции goo
. Это позволит сэкономить память и повысить производительность. Я наблюдал подобное поведение в GCC тоже (под OS X). Чтобы быть более конкретным, мне интересно, почему компиляторы не генерируют:
.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
Я искал документы, если соглашение о вызове x86 требует, чтобы переданные аргументы были доступны только для чтения, но я не смог найти ничего в этом вопросе. Кто-нибудь думает по этому поводу?