至少在未启用优化的情况下,您的函数具有前导码和后同步码。看看吧:
项目 46.dpr.13:asm
0041A1F4 55推ebp
0041A1F5 8BEC 移动 ebp,esp
0041A1F7 83C4F8 添加 esp,-$08
0041A1FA 8945F8 mov [ebp-$08],eax
Project46.dpr.14:CMP EAX,0
0041A1FD 83F800 cmp 每个,00 美元
Project46.dpr.15:JNZ Go
0041A200 7506 jnz $0041a208
Project46.dpr.16:移动 EAX,-1
0041A202 B8FFFFFFFF 移动 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:结束;
0041A20E 8B45FC mov eax,[ebp-$04]
0041A211 59 流行音乐
0041A212 59 流行音乐
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;
这样可以确保执行后同步码。您确实应该养成以这种方式编写代码的习惯,以确保执行任何前导码。
现在,除此之外,我怀疑您在问题中提到的问题实际上与与您使用 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 移动 eax,ebx
004059F8 8BC2 移动 eax,edx
004059FA C3 RET
为什么edx 的值是一个尚未赋值的易失寄存器,在函数返回之前被移入eax。这是您报告的问题。我的猜测是使用 Pascal 标签会使汇编程序感到困惑。坚持使用 asm 标签。
这是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 移动 eax,ebx
004059E4 C3 RET
这更像是它!注意编译器是如何知道这里没有可发布的,并将jmp @@exit 替换为直接的ret。