我在Delphi 7中编写了一个asm函数,但它将我的代码转换为其他代码:
function f(x: Cardinal): Cardinal; register;
label err;
asm
not eax
mov edx,eax
shr edx, 1
and eax, edx
bsf ecx, eax
jz err
mov eax, 1
shl eax, cl
mov edx, eax
add edx, edx
or eax, edx
ret
err:
xor eax, eax
end;
// compiled version
f:
push ebx // !!!
not eax
mov edx,eax
shr edx, 1
and eax, edx
bsf ecx, eax
jz +$0e
mov eax, 1
shl eax, cl
mov edx, eax
add edx, edx
or eax, edx
ret
err:
xor eax, eax
mov eax, ebx // !!!
pop ebx // !!!
ret
// the almost equivalent without asm
function f(x: Cardinal): Cardinal;
var
c: Cardinal;
begin
x := not x;
x := x and x shr 1;
if x <> 0 then
begin
c := bsf(x); // bitscanforward
x := 1 shl c;
Result := x or (x shl 1)
end
else
Result := 0;
end;
为什么会生成push ebx
和pop ebx
?为什么会这样做mov eax, ebx
?
由于mov eax, ebx
。
这个简单的测试会生成mov eax, edx
,但不会生成该堆栈帧:
function asmtest(x: Cardinal): Cardinal; register;
label err;
asm
not eax
and eax, 1
jz err
ret
err:
xor eax, eax
end;
// compiled
asmtest:
not eax
and eax, $01
jz +$01
ret
xor eax, eax
mov eax, edx // !!!
ret
它似乎与label err
有关。如果我删除了,我没有得到mov eax, *
部分。
为什么会这样?
在Quality Central上发布错误报告。
答案 0 :(得分:7)
实用建议是:不要在asm代码中使用label关键字,使用@@ - 前缀标签:
function f(x: Cardinal): Cardinal; register;
asm
not eax
mov edx,eax
shr edx, 1
and eax, edx
bsf ecx, eax
jz @@err
mov eax, 1
shl eax, cl
mov edx, eax
add edx, edx
or eax, edx
ret
@@err:
xor eax, eax
end;
<强>更新强>:
我没有在Basm area中找到错误报告。它看起来像一个bug,但我已经使用BASM很多年了,从来没有想过使用label关键字这样的方式。实际上我从来没有在Delphi中使用label关键字。 :)
答案 1 :(得分:1)
那么......那时候,在Delphi-Manual中,它曾经说过一些关于编译器优化和类似疯狂的事情:
编译器仅为嵌套例程,具有局部变量的例程和具有堆栈参数的例程生成Stackframes
自动生成的例程初始化和终结代码包括:
PUSH EBP ; If Locals <> 0 or Params <> 0
MOV EBP,ESP ; If Locals <> 0 or Params <> 0
SUB ESP,Locals ; If Locals <> 0
...
MOV ESP,EBP ; If Locals <> 0
POP EBP ; If Locals <> 0 or Params <> 0
RET Params ; Always
如果局部变量包含变量,长字符串或接口,则使用Null初始化它们,但之后不会完成。
Locals是局部变量的大小,Params是参数的大小。如果Locals和Params都是Null,则不会生成Init-Code,Finalizationcode只包含RET-Intruction。
也许这与这一切有关......