В общем случае, test
должен работать, когда вы передаете ему произвольные указатели, в том числе extern int global_var
или как угодно . Затем main
должен вызвать его в соответствии с соглашением ABI / вызова.
Таким образом, определение asm test
не может предполагать, где int *a
указывает, например, что он указывает на фрейм стека вызывающего.
(Или вы могли бы посмотреть на это как на оптимизацию адресации при вызове по ссылке на локальных объектах, поэтому вызывающий объект должен поместить объекты, на которые указывает указатель, в слоты, передаваемые через arg, и вернуть эти 2 слова стека память содержит потенциально обновленные значения *a
и *b
.)
Вы скомпилированы с отключенной оптимизацией. Специально для особого случая, когда вызывающая сторона передает указатели местным жителям, решение этой проблемы состоит в том, чтобы встроить всю функцию , что будут делать компиляторы, когда включена оптимизация.
Компиляторам разрешено создавать частный клон test
, который принимает свои аргументы по значению, или в регистрах, или с любым пользовательским соглашением о вызовах, которое компилятор хочет использовать. Однако большинство компиляторов на самом деле этого не делают и полагаются на встраивание вместо пользовательских соглашений о вызовах для частных функций, чтобы избавиться от накладных расходов на передачу аргументов.
Или, если бы он был объявлен static test
, то компилятор уже знал бы, что он является частным и теоретически мог использовать любое соглашение о вызовах, которое ему нужно, без создания клона с именем, подобным test.clone1234
. GCC иногда делает это для постоянного распространения, например, если вызывающая сторона передает константу времени компиляции, но gcc выбирает не inline. (Или не может, потому что вы использовали __attribute__((noinline)) static test() {}
)
И, кстати, с хорошим соглашением о вызовах регистров-аргументов, таким как x86-64 System V , вызывающий абонент будет делать lea 12(%rsp), %rdi
/ lea 8(%rsp), %rsi
/ call test
или что-то в этом роде. Соглашение о вызовах i386 System V является старым и неэффективным, поскольку все данные в стеке передаются, что приводит к необходимости сохранения / перезагрузки.
Вы в основном определили одну из причин, по которой соглашения о вызовах стековых аргументов имеют более высокие издержки и обычно отстой.