Ассемблер Delphi: понимание регистра результата

Я возился с ASM в Delphi. Насколько я понимаю, EAX содержит Result. В дальнейшем я должен поставить RET в конце, иначе результат неверен (это правильно, если ввод равен 0). Что я делаю не так, или я должен сказать, что я не понимаю в этом?

function MSb(const Val: Integer): Integer;
label
  Go;
asm
  CMP       EAX, 0
  JNZ       Go
  MOV       EAX, -1
  RET
Go:
  BSR       EBX, EAX
  MOV       EAX, EBX
  RET
end;

Если я скажу следующее:

  MOV       Result, EBX

Затем я получаю следующую компиляцию:

  MOV [EPB-$04], EBX
  MOV EAX, [EPB-$04]

Однако мой код выше имеет следующий постскриптум:

  MOV       EAX, EDX
  RET

person IamIC    schedule 25.11.2014    source источник


Ответы (2)


По крайней мере, без включенной оптимизации у вашей функции есть преамбула и постамбула. Посмотри на это:

Project46.dpr.13: asm
0041A1F4 55               push ebp
0041A1F5 8BEC             mov ebp,esp
0041A1F7 83C4F8           add esp,-$08
0041A1FA 8945F8           mov [ebp-$08],eax
Project46.dpr.14: CMP       EAX, 0
0041A1FD 83F800           cmp eax,$00
Project46.dpr.15: JNZ       Go
0041A200 7506             jnz $0041a208
Project46.dpr.16: MOV       EAX, -1
0041A202 B8FFFFFFFF       mov eax,$ffffffff
Project46.dpr.17: RET
0041A207 C3               ret 
Project46.dpr.19: BSR       EBX, EAX
0041A208 0FBDD8           bsr ebx,eax
Project46.dpr.20: MOV       EAX, EBX
0041A20B 89D8             mov eax,ebx
Project46.dpr.21: RET
0041A20D C3               ret 
Project46.dpr.22: end;
0041A20E 8B45FC           mov eax,[ebp-$04]
0041A211 59               pop ecx
0041A212 59               pop ecx
0041A213 5D               pop ebp
0041A214 C3               ret 

Таким образом, преамбула устанавливает фрейм стека. Он сохраняет регистр ebp и изменяет регистры ebp и esp. Обратите внимание также на постамбулу. Вам нужно выполнить этот код, чтобы восстановить сохраненные регистры.

Обычный способ справиться с этим — перейти в конец функции вместо использования ret. Итак, напишите свой код следующим образом:

function MSb(const Val: Integer): Integer;
asm
  CMP       EAX, 0
  JNZ       @@go
  MOV       EAX, -1
  JMP       @@exit
@@go:
  BSR       EBX, EAX
  MOV       EAX, EBX
@@exit:
end;

Таким образом, вы гарантируете, что post-amble будет выполнен. Вы действительно должны привыкнуть писать код таким образом, чтобы гарантировать выполнение любой преамбулы.


Теперь, помимо этого, я подозреваю, что проблема, о которой вы упоминаете в вопросе, на самом деле связана с ошибкой компилятора, связанной с использованием вами метки Pascal, а не метки asm. Что ж, возможно, это ошибка компилятора, но, возможно, это просто ошибка использования метки Pascal. Рассмотрим следующую программу:

{$APPTYPE CONSOLE}

function MSb(const Val: Integer): Integer;
asm
  CMP       EAX, 0
  JNZ       @@Go
  MOV       EAX, -1
  JMP       @@exit
@@Go:
  BSR       EBX, EAX
  MOV       EAX, EBX
@@exit:
end;

function MSb2(const Val: Integer): Integer;
label
  Go;
asm
  CMP       EAX, 0
  JNZ       Go
  MOV       EAX, -1
  RET
Go:
  BSR       EBX, EAX
  MOV       EAX, EBX
end;

begin
  Writeln(Msb(0));
  Writeln(Msb(1));
  Writeln(Msb2(0));
  Writeln(Msb2(1));
  Readln;
end.

Вывод при компиляции с оптимизацией:

-1
0
-1
4

Итак, как насчет этого довольно странного 4. Что ж, давайте посмотрим на собранный код для Msb2, который по сути является вашим кодом:

004059E8 83F800           cmp eax,$00
004059EB 7506             jnz $004059f3
004059ED B8FFFFFFFF       mov eax,$ffffffff
004059F2 C3               ret 
004059F3 0FBDD8           bsr ebx,eax
004059F6 89D8             mov eax,ebx
004059F8 8BC2             mov eax,edx
004059FA C3               ret 

С какой стати значение edx, изменчивого регистра, значение которого не было присвоено, перемещается в eax непосредственно перед возвратом из функции. Это проблема, о которой вы сообщаете. Я предполагаю, что использование метки Pascal сбивает с толку ассемблер. Придерживайтесь ассемблерных этикеток.

Вот собранный код для Msb:

004059D4 83F800           cmp eax,$00
004059D7 7506             jnz $004059df
004059D9 B8FFFFFFFF       mov eax,$ffffffff
004059DE C3               ret 
004059DF 0FBDD8           bsr ebx,eax
004059E2 89D8             mov eax,ebx
004059E4 C3               ret 

Это больше походит на это! Обратите внимание, как компилятор знает, что здесь нет возможности публикации, и заменяет jmp @@exit прямым ret.

person David Heffernan    schedule 25.11.2014

Что я делаю не так, или я должен сказать, что я не понимаю в этом?

В основном, как ясно указано до и после имбов, но также будьте осторожны при использовании кода ассемблера. Параметры передаются по-разному в зависимости от соглашений о вызовах. Ваш код будет нормально работать с соглашением о вызовах Pascal (по иронии судьбы устаревшим), если вы удалите label, RET и используете ассемблерные метки (@@). Хорошей практикой является всегда указывать соглашение о вызовах для ассемблерного кода, поскольку Result действительно может ссылаться на EAX или на локальную переменную для его хранения. EAX сопоставляется непосредственно с Result при использовании соглашения о вызовах Register (по умолчанию).

person alvaroc    schedule 25.11.2014