Лекс Айрин писал(а):Кстати, место под возвращаемое значение может как формироваться заранее, так и после вызова. Все зависит от того, кто работает со стеком сама процедура (функция) или нет. Подозреваю, что проще все сделать заранее.
Я, пожалуй, ещё позанимаюсь ликбезом.
То, как функция принимает, и возвращает аргументы называется «конвенцией вызова» (calling convention). Конвенция определяет то, в каком порядке и каким способом будут переданы входные аргументы, как и куда в зависимости от типа будет положено возвращаемое значение, кто (функция или вызывающий функцию код) и когда меняет стек, какие регистры функция не изменит за время своей работы и прочие, порой зависящие от архитектуры процессора, нюансы.
В x86 архитектуре распространены две конвенции: stdcall, cdecl. В паскале по умолчанию используется своя конвенция (я так полагаю, называется register). Эта конвенция предписывает возвращать результат функции через регистры, если умеющаются, а если не умещаются, то класть в специально подготовленную область памяти.
Рассмотрим для примера как возвращают значения следующие три функции, откомпилированные в 32-битном fpc:
Код: Выделить всё
function func_register_int(A, B: LongInt): LongInt;
begin
Result := A + B;
end;
function func_register_single(A, B: LongInt): Single;
begin
Result := A + B;
end;
function func_register_int64(A, B: LongInt): Int64;
begin
Result := A + B;
end;
Первая функция самая простая — в ней результат возвращается через регистр EAX:
Код: Выделить всё
.section .text.n_p$program_$$_func_register_int$longint$longint$$longint,"x"
.balign 16,0x90
.globl P$PROGRAM_$$_FUNC_REGISTER_INT$LONGINT$LONGINT$$LONGINT
P$PROGRAM_$$_FUNC_REGISTER_INT$LONGINT$LONGINT$$LONGINT:
# [15] begin
pushl %ebp
movl %esp,%ebp
leal -12(%esp),%esp
# Var A located at ebp-4, size=OS_S32
# Var B located at ebp-8, size=OS_S32
# Var $result located at ebp-12, size=OS_S32
movl %eax,-4(%ebp)
movl %edx,-8(%ebp)
# [16] Result := A + B;
movl -4(%ebp),%edx
movl -8(%ebp),%eax
leal (%edx,%eax),%eax
movl %eax,-12(%ebp)
# [17] end;
movl -12(%ebp),%eax // <- Вот тут кладётся результат сложнения из памяти в регистр EAX
leave
ret
Вторая функция более интересна. Дело в том, что для арифметики чисел с плавающей точкой предусмотрен свой отдельный стек внутри процессора (или сопроцессора), и, как правило, результат с плавающей точкой возвращается через этот стек:
Код: Выделить всё
.section .text.n_p$program_$$_func_register_single$longint$longint$$single,"x"
.balign 16,0x90
.globl P$PROGRAM_$$_FUNC_REGISTER_SINGLE$LONGINT$LONGINT$$SINGLE
P$PROGRAM_$$_FUNC_REGISTER_SINGLE$LONGINT$LONGINT$$SINGLE:
# Temps allocated between ebp-16 and ebp-12
# [25] begin
pushl %ebp
movl %esp,%ebp
leal -16(%esp),%esp
# Var A located at ebp-4, size=OS_S32
# Var B located at ebp-8, size=OS_S32
# Var $result located at ebp-12, size=OS_F32
movl %eax,-4(%ebp)
movl %edx,-8(%ebp)
# [26] Result := A + B;
movl -4(%ebp),%edx
movl -8(%ebp),%eax
leal (%edx,%eax),%eax
movl %eax,-16(%ebp)
fildl -16(%ebp)
fstps -12(%ebp)
# [27] end;
flds -12(%ebp) // <- Загружаем результат в FPU стек
leave
ret
Наконец, третья функций. В ней возвращаемое значение имеет размер 64 бита. Это в два раза больше, чем может содержать в себе регистр в 32-битном x86 процессоре. Поэтому это число возвращается в двух регистрах — EAX и EDX:
Код: Выделить всё
.section .text.n_p$program_$$_func_register_int64$longint$longint$$int64,"x"
.balign 16,0x90
.globl P$PROGRAM_$$_FUNC_REGISTER_INT64$LONGINT$LONGINT$$INT64
P$PROGRAM_$$_FUNC_REGISTER_INT64$LONGINT$LONGINT$$INT64:
# [20] begin
pushl %ebp
movl %esp,%ebp
leal -16(%esp),%esp
# Var A located at ebp-4, size=OS_S32
# Var B located at ebp-8, size=OS_S32
# Var $result located at ebp-16, size=OS_S64
movl %eax,-4(%ebp)
movl %edx,-8(%ebp)
# [21] Result := A + B;
movl -4(%ebp),%edx
movl -8(%ebp),%eax
leal (%edx,%eax),%edx
movl %edx,%eax
sarl $31,%eax
movl %edx,-16(%ebp)
movl %eax,-12(%ebp)
# [22] end;
movl -12(%ebp),%edx // <- Одну половину числа кладём в EDX
movl -16(%ebp),%eax // <- Другую в EAX
leave
ret
Как видим, в трёх случаях ни разу значение не возвращалось через стек.
Это одна из причин, почему мне совершенно непонятно что имеется в виду в
viewtopic.php?f=2&t=10858&start=15#p92264 под словами «данная (псевдо)переменная находится (после окончания работы функции) на вершине стека» (вторая причина — не объяснено чётко что такое «после окончания работы функции»).