В большинстве случаев (зависит от типа результата), функция возвращает результат через регистры процессора. Напомним, что в отличие от передачи параметров в функцию, где в большинстве соглашений используют стек, для возврата результата, все соглашения используют регистры для допустимых типов!
Таблица 3 содержит обзор о вариантах возврата результатов. В большинстве случаев, результат возвращается через регистр EAX или FP(0). Особый случай, когда в результате возвращается длинная строка или другой тип, возвращаемый через указатель. В случае длинных строк, динамических массивов, больших множеств, вариантов и больших записей, переданных через параметр с директивой var, используется 32-битный указатель на результат. Так, где же хранится действительное содержимое результата (например, длинных строк)? Ответ на это в том, что вы должны выделить место в куче, заполнить его данными, и вернуть указатель на эту область памяти через переменную Result. Заметим, что, для множеств, записей и массивов, которые могут разместиться в регистре, переменная Result возвращает их через регистр. Только для длинных строк, вариантов и множеств, записей и массивов, которые занимают свыше 32 бит, переменная Result возвращает указатель на дополнительный указатель, размещенный функцией, аналогично директиве var параметра (мы рассмотрим директиву параметра var в главе 1.9).
Не беспокойтесь, если что-то сейчас не понятно, позже мы рассмотрим эти типы подробнее.
Теперь поясним это на примере. Функция PlusMinusLine возвращает длинную строку, состоящую из последовательности плюсов и минусов, для формирования строки. Например, когда вы напишите так S:=PlusMinusLine(9), то S должна получить значение: "-+-+-+-+-".
Декларация функции следующая:
function PlusMinusLine(L: Integer): AnsiString; register;
Функция принимает один параметр: длину строки символов (L). Поскольку мы используем соглашение по умолчанию, то параметр передается через регистр EAX. Функция должна вернуть длинную строку, Что в действительности означает указатель на область памяти, содержащей нашу строку. Вы можете использовать переменную Result для обращения к этой области, но поскольку ее поведение аналогично var, то в этом случае @Result
эквивалентно регистру EDX (второй параметр отдельной функции передается через регистр EDX, при использования соглашения register)! Подробности мы рассмотрим в главе 1.9. EDX не содержит самого указателя, но указатель на область памяти для этого указателя! Тем не менее, пока еще не распределена память для нашей длинной строки. Будем использовать функцию NewAnsiString из модуля system для размещения памяти в куче и установке длины строки. Функция NewAnsiString
function PlusMinusLine(L: Integer): AnsiString; register;
asm
push EDI
push ESI
push EBX
mov ESI,EDX {Указатель памяти на Result}
mov EBX,EAX {EBX хранит длину параметра}
call System.@NewAnsiString
mov EDI,EAX {EDI используется для заполнения строки}
mov [ESI],EDI
mov ECX,EBX
shr ECX,2 {обрабатываем по 4 байта за раз}
test ECX,ECX
jz @@remain
mov EAX,'+-+-'
@@loop:
mov [EDI],EAX
add EDI,4
dec ECX
jnz @@loop
@@remain: {заполняем оставшие байты, если length/4 не ноль}
mov ECX,EBX
and ECX,3
jz @@ending
mov EAX,'+-+-'
@@loop2:
mov BYTE PTR [EDI],al
shr EAX,8
inc EDI
dec ECX
jnz @@loop2
@@ending:
mov EAX,ESI {для совместимости: возврат указателя через EAX}
pop EBX
pop ESI
pop EDI
end;
Для облегчения понимания, данного примера пришлось пожертвовать некоторой эффективностью. Для ускорения за раз одновременно обрабатывается по 4 байта для заполнения строки. Тем не менее, можно сделать еще быстрее, если использовать указатель в EDI на конец строки и использовать отрицательный счетчик в ECX, постепенно увеличивая его до нуля и используя его как индекс ([EDI+ECX*4]), что также сделало бы не нужным увеличение регистра EDI после каждой итерации. Это прекрасный повод для читателя переписать эту функцию данным образом и сравнить результаты выполнения. Также, может быть, вы захотите уменьшить количество циклов для еще большей эффективности. Например, обрабатывать по 8 байт за каждую итерацию, уменьшив этим количество переходов.
Как видим, возврат информации через регистры не всегда самый простой путь, особенно для структурных переменных типа длинных строк.