Все, что имеет значение, это то, что находится в стеке, когда выполнение достигает вершины функции, которую вы хотите вызвать. Неважно, как или в каком порядке он туда попал, просто ESP
указывает на адрес возврата, ESP+4
указывает на первый аргумент стека и т. Д.
Функция также не 'не знает или не заботится о том, достигли ли вы этого с call
, или jmp
хвостовым вызовом, или даже с jae
условным хвостовым вызовом.
Вы даже не иметь для использования push
вообще, вы можете sub esp, 24
в верхней части функции и просто использовать mov
для хранения своих аргументов. (Как gcc -m32 -maccumulate-outgoing-args
делает, что было хорошо на старых процессорах без стекового механизма, где push
не был столь эффективен.) Почему gcc использует movl вместо push для передачи аргументов функции?
(Конечно, более эффективные соглашения о вызовах передают аргументы в регистрах, используя стек только в том случае, если имеется более 2 или 3 целочисленных / указательных аргументов. Но та же разница, соглашение о вызовах определяет требуемое состояние при входе вфункция, , а не , как вы это делаете. )
Поскольку вам даже приходилось задавать этот вопрос, помните, что ЦП по сути является конечным автоматом. Каждая инструкция имеет свое документированное влияние на архитектурное состояние (содержимое регистра и памяти, включая специальные регистры, такие как EFLAGS и указатель инструкции). Кроме этого, нет контекста. Неважно, как вы достигли состояния, только то, что вы находитесь в нем.
Контекст имеет значение для производительности для таких вещей, как задержки с частичной регистрацией, остановки пересылки в хранилище, прогноз ветвлений иобщий для дублирующего выполнения нескольких инструкций. Но не для корректности.
(я игнорирую эксплойты Spectre / Meltdown, которые создают известное микроархитектурное состояние, а затем считывают это в архитектурное состояние.)