Я бы [все еще!] Как полный разборка корпуса 2. Но, я угадаю.
(1) Компилятор заполняет rdi
со значением [правильный]. Это адрес src
[возможно, от new
и/или malloc
].
В MS ABI rdi
считается «энергонезависимым». Она должна быть сохранена по вызываемым
(2) Случай 2 затем вызывает init_method
. Но, init_method
делает не сохранение rdi
[как оно должно]. Он использует его для своих целей (например, edi
). Итак, по возвращении, rdi
был trashed!
(3) Когда программа возвращается из init_method
, компилятор ожидает, чтоrdi
будет иметь такое же значение, которое было после того, как на стадии (1). (т. е. компилятор не знает, что init_method
поврежден rdi
, поэтому он использует свое значение для установки rcx
[первый аргумент ASM_Method
]. Этот должен быть значением src
, но на самом деле это независимо от значение init_method
установить его (т.е.значение нежелательной, условно говоря)
UPDATE:
ЛПИ отличается для различных платформ [обычно, только компилятор]. gcc
и clang
имеют другое соглашение о вызове, чем MS (то есть MS - нечетная утка или обычный подозреваемый). Например, с gcc/clang
, rdi
имеет первый аргумент и является летучий
Вот ссылка вики, которые должны выделить большинство АБИС: https://en.wikipedia.org/wiki/X86_calling_conventions
UPDATE # 2:
Но почему обращается к стеку (т.е. float src [64]), а другой относится к регистрам (новый поплавок [64]) перед вызовом?
Из-за компилятора оптимизация. Чтобы объяснить, мы немного «отключим» оптимизацию.
Все функции имеют ограниченные функции в кадре стека функции. Все эти «слоты» имеют фиксированное смещение в кадре стека, которое, как известно, [вычисляется] компилятором. Если функция имеет, то кадр стека вообще [некоторые функции листа могут его высвободить], тогда все переменные имеют свои слоты, независимо от того, используется ли оптимизация или нет. Держите эту мысль ...
Когда у вас есть массив фиксированного размера, как в случае 1, все пространство (то есть данные) для этого массива находится внутри кадра. Таким образом, адрес данного массива является указателем кадра + смещением массива. Следовательно, lea rcx,[rbp + offset_of_src]
Скалярные переменные также имеют слоты. Это включает в себя такие вещи, как «указатели на массивы», что есть у нас в случае 2.
[Помните, что оптимизация выключена] Часть недостающего кода в случае 2 была чем-то вроде [упрощенного]:
// allocate src
call malloc
mov [ebp + offset_of_src],rax
// allocate dest
call malloc
mov [ebp + offset_of_dest],rax
// push arguments for init_method and call it
call init_method
// call ASM_Method
mov r8d,64
mov edx,[ebp + offset_of_dest]
mov ecx,[ebp + offset_of_src]
call ASM_Method
Обратите внимание, здесь, мы не хотим, чтобы «толкать» адреса переменного указателя, мы хотим, чтобы «толкать» содержимого переменного указателя.
Теперь давайте вернем оптимизатор. Только потому, что функциональная переменная имеет, слот на стеке стека не означает, что сгенерированный код обязан использовать его. Для простой функции, как в случае 2, оптимизатор понимает, что он может использовать энергонезависимые регистры для хранения значений src
и dest
и может исключить доступ к ним для хранения/хранения стека.
Таким образом, с оптимизацией, случай 2 выглядит следующим образом:
// allocate src
call malloc
mov rdi,rax
// allocate dest
call malloc
mov rsi,rax
// push arguments for init_method and call it
call init_method
// call ASM_Method
mov r8d,64
mov edx,rsi
mov ecx,rdi
call ASM_Method
Конкретные нелетучих веществ, выбранных компилятором произвольны. В этом случае они просто оказались rsi
и rdi
, но есть и другие на выбор.
Компилятор/оптимизатор достаточно умен, чтобы выбрать эти регистры и другие для хранения значений данных. Он может видеть, когда заданная функция больше не нуждается в значение в регистре и может переназначить ее, чтобы сохранить другое [несвязанное] значение, если оно выбрало.
Хорошо, помните «держите эту мысль»? Время выдыхать. Обычно, когда переменной присваивается задание регистра, компилятор пытается оставить ее в покое, пока она больше не понадобится. Но, иногда, нет достаточно регистров для хранения всех активных переменных за один раз.
Например, если функция имеет [сказать] четыре вложенные петли for
и использует 20 разных переменных, для регистрации недостаточно регистров. Таким образом, компилятору, возможно, придется генерировать код, который «сбрасывает» значение в регистре обратно в слот кадра стека для соответствующей переменной. Это «разлив регистра».
Именно поэтому всегда слот в кадре стека для скаляра, даже если он никогда не используется [из-за оптимизации значения в регистре]. Он упрощает процесс компиляции, а смещения - то же самое.
Также мы говорили о callee сохранены регистры. Но, как насчет звонящий сохраненные регистры. В то время как большинство функций вытесняют нелетучие элементы при входе и выталкивают их при выходе (т. Е. Сохраняют нелетучие для их вызывающего абонента).
данной функции (например, A
) может использовать летучий регистр провести что-то (например, r10
) для переменной (например) sludge
. Если он вызывает другую функцию (например, B
), B
может удалить значение A
.
Таким образом, если A
хочет сохранить значение в r10
через вызов B
, A
необходимо сохранить его, вызовите B
, а затем восстановить его:
mov [rbp + offset_of_sludge],r10
call B
mov r10,[rbp + offset_of_sludge]
Таким образом, это удобно, чтобы иметь слот фрейма стека.
Иногда функция имеет так много переменных, что код, сгенерированный для некоторых из них выглядит как неоптимизированная версия:
mov rax,[rbp + offset_of_foo]
add rax,rdx
sub rax,rdi
mov [rbp + offset_of_foo],rax
потому foo
доступа/использование слишком редко, чтобы заслужить энергонезависимый регистр присваивание
Я вижу только 3 параметра в 'ASM_Method (void *, void *, int)' Вы должны показать свой код ассемблера для 'ASM_Method' –
Также помогли бы узнать целевые платформы. Окна? Linux? OSX? –
В окнах первые 3 параметра: _RCX_, _RDX_, _R8_.Первые три параметра Linux находятся в _RDI_, _RSI_, _RDX_. Надеюсь, вы нацелились на 64-битную Windows. –