Кстати говоря, ни адрес возврата, ни аргументы функции вовсе не обязаны быть константой, известной на стадии компиляции и они могут свободно модифицироваться в любой момент командами MOV/STOS. Аналогичным образом, если аргументы хранятся в локальных переменных, то засылать их в стек необязательно! Достаточно лишь скорректировать регистр ESP таким образом, чтобы переменные-аргументы оказались на вершине (естественно, порядок размещения аргументов в памяти должен совпадать с порядком передачи аргументов, но на ассемблере, в отличии от языков высокого уровня мы можем самостоятельно выбирать нужную схему размещения переменных, так что это не проблема).
Еще одна тонкость — "оптимизированный" вариант обладает всеми формальными атрибутами "передачи по значению", но де-факто, аргументы передаются по ссылке. То есть нет! Совсем наоборот! Аргументы передаются по _значению_ но это значение после выхода из функции сохраняет свое состояние, то есть ведет себя так, как будто бы ото было передано по ссылке. Иногда это экономит такты процессора и сокращает потребности в памяти, но иногда ведет к трудноуловимым ошибкам, лишний раз подтверждая тезис, что совершенства в мире не бывает.
И последнее: при всех этих играх со стеком следует помнить, что целый ряд API-функций требует, чтобы указатель стека был выровнен на границу 4х байт. Нарушение этого правила ведет к непредсказуемым последствиям.