понедельник, 12 декабря 2016 г.

#1323. Коротко. О соглашениях о вызовах под Win64

Ссылка на MSDN - https://msdn.microsoft.com/en-us/library/windows/hardware/ff561499(v=vs.85).aspx

По мотивам - http://programmingmindstream.blogspot.ru/2016/12/1321.html

Под Win64 модель передачи параметров только одна.

Т.е. stdcall, pascal, cdecl, register - эквивалентны.

Параметры передаются через регистры в порядке - rcx, rdx, r8, r9. Остальные через стек.

Если значение не помещается в регистр, то оно кладется на стек, а в регистре передается указатель на это значение.

Возвращаемое значение помещается в rax. Если оно целочисленное (Integer) или указатель (Pointer).

Вещественные числа возвращаются через регистр xmm0.

Регистры rax, rcx, rdx, r8-r11 - могут изменяться внутри вызываемой функции.

Регистры rbx, rbp, rdi, rsi, r12-r15 - должны сохранять своё значение при работе вызываемой функции.

Также спецификация вызова обязует вызывающую процедуру распределять место в собственном стековом фрейме для временного сохранения (spill) значений тех параметров, которые переданы через регистры.

(Оригинальная цитата - "The caller reserves space on the stack for arguments passed in registers. The called function can use this space to spill the contents of registers to the stack.")

Вызываемая процедура может использовать это место по своему усмотрению.

Пример ручного резервирования:

procedure TMethodHandlerInstance.Handler(Params: Pointer);
asm
        .NOFRAME
        SUB     RSP, 28H // - выделяем место в стеке
        CALL    InternalHandler // InternalHandler - может использовать это место адресуясь через [RSP+Offset]
        MOV     [RSP], RAX // - в этих двух строках ещё попользовали отведённое место
        MOVSD   XMM0, [RSP] // в качестве собственной переменной
        ADD     RSP, 28H // - возвращаем место в стеке
end;

Листьевые процедуры метятся директивой .NOFRAME, которая гарантирует, что компилятор не будет распределять стековый фрейм.

Локальные процедуры обрабатываются особо. В регистр rcx помещается значение регистра базы (rbp) той процедуры в которую вложена локальная процедура.

От этой базы адресуются локальные переменные охватывающей процедуры.

Таким образом вложенная локальная процедура получает доступ к локальным переменным охватывающей процедуры.

На основании этой информации можно строить заглушки для вызовов локальных функций вместо анонимных.

Ссылки:
http://18delphi.blogspot.ru/2013/03/blog-post_5929.html
http://18delphi.blogspot.ru/2013/07/embarcadero.html

Подсмотреть направление исследований можно в коде Embarcadero. В методе MakeObjectInstance.

Позже я выложу примеры кода.




Комментариев нет:

Отправить комментарий