2016-04-05 2 views
1

Итак, у меня есть процедура сборки с 3 параметрами ASM_Method(void*, void*, int) и init_method(float, int*). Интересующими являются указатели void на первом.Вызов Конвенции Неисправность в x86_64 Сборка

Когда я вызываю метод из файла C++ с параметрами, как:

float src[64]; 
float dest[64]; 
int radius[3]; 

init_method(1.5, radius); 
ASM_Method(src, dest, 64); 

Демонтажные этого вызывающего процесса:

mov   r8d,100h 
lea   rdx,[rbp+0A0h] 
lea   rcx,[rbp-60h] 
call  ASM_Method 

инициализирован или нет, программа работает нормально. ОДНАКО, когда я делаю:

float* src = new float[64]; 
float* dest = new float[64]; 
int radius[3]; 

init_method(1.5, radius); 
ASM_Method(src, dest, 64); 

При вызове RCX устанавливается на значение, которое не правильный адрес, но RDX является правильным. В результате программа выйдет из строя.

Демонтажные этот вызывающий процесс:

mov   r8d,100h 
mov   rdx,rbx 
mov   rcx,rdi 
call  ASM_Method 

Если я не инициализировать SRC для некоторых значений, RCX меняется на неправильный адрес (в данном случае 1) при вызове.

код Ассамблеи для ASM_Method:

mov rax, rdx 
add rax, r8 
shr r8, 4 
inc r8 
xor r9, r9 
movdqu xmm1, [rax] 

MainLoop: 
movdqu xmm0, [rcx + r9] 
movdqu [rdx + r9], xmm0 
add r9, 16 
dec r8 
jnz MainLoop 

movdqu [rax], xmm1 

ret 

код Ассамблеи для init_method:

mulss xmm0, xmm0 
mov ecx, 4 
cvtsi2ss xmm1, ecx 
mulss xmm0, xmm1 

shr ecx, 2 
cvtsi2ss xmm2, ecx 
addss xmm2, xmm0 
sqrtss xmm2, xmm2 

stmxcsr roundFlags 
or roundFlags, 2000h 
ldmxcsr roundFlags 

cvtss2si ecx, xmm2 

stmxcsr roundFlags 
and roundFlags, 0DFFFh 
ldmxcsr roundFlags 

mov eax, ecx 
dec eax 
bt ecx, 0 
cmovnc ecx, eax 

mov eax, 3 
cvtsi2ss xmm1, eax 
mulss xmm0, xmm1 

cvtsi2ss xmm3, ecx 
movss xmm2, xmm3 
movss xmm4, xmm3 

mulss xmm2, xmm2 
mulss xmm2, xmm1 

mov eax, 12 
cvtsi2ss xmm1, eax 
mulss xmm3, xmm1 

mov eax, -4 
cvtsi2ss xmm1, eax 
mulss xmm4, xmm1 
addss xmm4, xmm1 

mov eax, 9 
cvtsi2ss xmm1, eax 

subss xmm0, xmm2 
addss xmm3, xmm1 
subss xmm0, xmm3 
divss xmm0, xmm4 

cvtss2si eax, xmm0 

mov esi, ecx 
add esi, 2 

mov edi, ecx 
cmp eax, 0 
cmovle edi, esi 
shr edi, 1 
mov dword ptr [edx], edi 

mov edi, ecx 
cmp eax, 1 
cmovle edi, esi 
shr edi, 1 
mov dword ptr [edx + 4], edi 

mov edi, ecx 
cmp eax, 2 
cmovle edi, esi 
shr edi, 1 
mov dword ptr [edx + 8], edi 

ret 

Что происходит?

+1

Я вижу только 3 параметра в 'ASM_Method (void *, void *, int)' Вы должны показать свой код ассемблера для 'ASM_Method' –

+0

Также помогли бы узнать целевые платформы. Окна? Linux? OSX? –

+0

В окнах первые 3 параметра: _RCX_, _RDX_, _R8_.Первые три параметра Linux находятся в _RDI_, _RSI_, _RDX_. Надеюсь, вы нацелились на 64-битную Windows. –

ответ

5

Я бы [все еще!] Как полный разборка корпуса 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 доступа/использование слишком редко, чтобы заслужить энергонезависимый регистр присваивание

+0

Я собирался опубликовать это [link] (https://msdn.microsoft.com/en-us/library/6t169e9c.aspx? f = 255 и MSPPError = -2147217396) в качестве комментария раньше. Я считаю, что «RDI_» был разрушен, поэтому он видит это одно конкретное поведение. _RSI_ постигнет ту же участь. –

+0

@MichaelPetch Мне потребовалось некоторое время, потому что я так привык к не-MS ABI [что мне пришлось искать его, когда ничего больше не имело смысла]. Ну, проблема решена [наконец!]. И я думаю, что вам нужно в другом месте, поскольку я задал вопрос MikeOS :-) –

+0

Да, это была проблема. Но почему обращается к стеку (i.e float src [64]), а другой относится к регистрам (новый float [64]) перед вызовом? – user2741371

Смежные вопросы