如果同时激活溢出检查和优化,则Delphi 2006中存在一个错误。该错误似乎只在特定情况下出现,即先将32位整数加上自身,然后再将字节添加到此前的总和中,按照以下程序所示的顺序。
program OptimizationBug;
{$OPTIMIZATION ON}
{$APPTYPE CONSOLE}
{$OVERFLOWCHECKS ON}
function f: integer;
var i: integer;
b: byte;
begin
i:=0;
b:=1;
Result:=i+i+b;
end;
{$OVERFLOWCHECKS OFF}
function g: integer;
var i: integer;
b: byte;
begin
i:=0;
b:=1;
Result:=i+i+b;
end;
begin
writeLn(f); //wrong, prints "2" in D2006
writeLn(g); //good, prints "1"
readLn;
end.
注意:溢出检查必须编码到源文件中,而不是通过项目选项实现。 这导致另一个错误:通过项目选项进行的溢出检测无效。
如CPU窗口所示,优化器会被零扩展movzx(将8位值扩展为32位值)和溢出检查分散注意力,忘记在单独的寄存器上加载字节b,覆盖先前的内容,从而使b加到自己而不是2i。下面汇编代码的上半部分属于出错的函数,而下半部分属于正常构造。
OptimizationBug.dpr.20: i:=0;
00403EAC 33C0 xor eax,eax
OptimizationBug.dpr.21: b:=1;
00403EAE B201 mov dl,$01
OptimizationBug.dpr.22: Result:=i+i+b;
00403EB0 03C0 add eax,eax
00403EB2 7105 jno $00403eb9
00403EB4 E82BF5FFFF call @IntOver
00403EB9 0FB6C2 movzx eax,dl //BUG: should have x-moved DL to EDX register!
00403EBC 03C0 add eax,eax // (and added EDX to EAX)
00403EBE 7105 jno $00403ec5
00403EC0 E81FF5FFFF call @IntOver
OptimizationBug.dpr.23: end;
00403EC5 C3 ret
00403EC6 8BC0 mov eax,eax
OptimizationBug.dpr.30: i:=0;
00403EC8 33C0 xor eax,eax
OptimizationBug.dpr.31: b:=1;
00403ECA B201 mov dl,$01
OptimizationBug.dpr.32: Result:=i+i+b;
00403ECC 03C0 add eax,eax
00403ECE 0FB6D2 movzx edx,dl //OK!
00403ED1 03C2 add eax,edx //ok
OptimizationBug.dpr.33: end;
00403ED3 C3 ret
顺便提一下,这段代码不是一个病态的例子。我在将D·Knuth宏伟的TeX程序改编为现代Pascal时找到了它。当检查启用优化和关闭所有编译器检查对最终*.exe大小的影响时,它不能正确地解释磁盘保存的哈希表(这些哈希表是在关闭优化的情况下生成的),我追溯到上述错误,这部分是生成表格的部分。
问题很简单,有人能在更新的Delphi版本上检查一下这个程序吗?