Кажется, что современные компиляторы трактуют аргументы, переданные стеком как доступные только для чтения. Обратите внимание, что в соглашении на вызов x86 вызывающий пользователь вставляет аргументы в стек, а вызываемый использует аргументы в стеке. Например, следующий код С:Согласование вызовов x86: должны ли аргументы, передаваемые стеком, только для чтения?
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 соглашение о вызовах требует пройденные аргументы, которые будут доступны только для чтения, но я не мог найти что-либо по этому вопросу. Кто-нибудь думает по этому поводу?
У вас здесь хороший момент! '8 (% ebp)' находится в кадре стека вызывающего, но это пространство, которое было выделено специально для передачи аргументов в 'foo'. Будет ли вызывающий абонент использовать это пространство для своих целей * после того, как '' foo' вернется, а не просто уничтожит его, отрегулировав указатель стека? Если это так, необходимо скопировать значение в стек стека 'foo'. Если нет, может быть безопасно «foo» «заимствовать» пространство в кадре стека вызывающего, а не копировать. Итак, чтобы узнать, хороша ли ваша идея или нет, вам нужно посмотреть, как выглядит код для вызывающего пользователя 'foo' *. –
@AlexD Спасибо за ваш комментарий! Поскольку 'foo' может вызываться произвольной функцией, я думаю, что это вопрос о вызовах конвенций, а не в конкретном контексте, где вызывается' foo'. –
Это интересный вопрос. Я нашел [этот другой вопрос] (http://stackoverflow.com/questions/1684682/c-calling-conventions-and-passed-arguments?rq=1), который утверждает, что gcc -O2 действительно изменил аргумент стоп-кода вызывающего абонента , – JS1