我发现Delphi 5在特定情况下会生成无效的汇编代码。一般来说,我无法理解在什么情况下。以下示例产生访问冲突,因为发生了非常奇怪的优化。对于记录或数组中的一个字节,Delphi生成push dword [...],pop ebx,mov ..,bl如果在此字节后面有数据(我们至少需要三个正确推送dword)才能正常工作,但是失败了如果数据不可访问。我在这里用win32 Virtual *函数模拟了严格的界限
具体地说,当在FeedBytesToClass过程中访问块的最后一个字节时发生错误。如果我尝试更改类似于使用数据数组而不是删除actionFlag变量的对象属性,Delphi会生成正确的汇编指令。
const
BlockSize = 4096;
type
TSomeClass = class
private
fBytes: PByteArray;
public
property Bytes: PByteArray read fBytes;
constructor Create;
destructor Destroy;override;
end;
constructor TSomeClass.Create;
begin
inherited Create;
GetMem(fBytes, BlockSize);
end;
destructor TSomeClass.Destroy;
begin
FreeMem(fBytes);
inherited;
end;
procedure FeedBytesToClass(SrcDataBytes: PByteArray; Count: integer);
var
j: integer;
Ofs: integer;
actionFlag: boolean;
AClass: TSomeClass;
begin
AClass:=TSomeClass.Create;
try
actionFlag:=true;
for j:=0 to Count-1 do
begin
Ofs:=j;
if actionFlag then
begin
AClass.Bytes[Ofs]:=SrcDataBytes[j];
end;
end;
finally
AClass.Free;
end;
end;
procedure TForm31.Button1Click(Sender: TObject);
var
SrcDataBytes: PByteArray;
begin
SrcDataBytes:=VirtualAlloc(Nil, BlockSize, MEM_COMMIT, PAGE_READWRITE);
try
if VirtualLock(SrcDataBytes, BlockSize) then
try
FeedBytesToClass(SrcDataBytes, BlockSize);
finally
VirtualUnLock(SrcDataBytes, BlockSize);
end;
finally
VirtualFree(SrcDataBytes, MEM_DECOMMIT, BlockSize);
end;
end;
最初,当我使用位图位的RGB数据访问时发生错误,但是那里的代码太复杂,所以我把它缩小到这个片段。
所以问题是这里有什么特别的东西让Delphi产生push,pop,mov优化。我需要知道这一点,以避免一般的副作用。
答案 0 :(得分:10)
AClass.Bytes[Ofs] := SrcDataBytes[j];
mov exc,[ebp-$04]
push dword ptr [ecx+eax] <- ouch
mov ecx,[ebp-$08]
mov ecx,[exc+04]
lea esi,[exc+esi]
pop ecx
mov [esi],cl
end;
inc eax
这样做了4096次。我检查过,Delphi 6没有这种行为。我认为我们可以安全地假设它在任何更新版本中都已得到修复。
作为一种解决方法,我建议只需在方法中添加{$ O - } / {$ O +}即可。我不会过多地考虑确切的取证,因为触发Delphi执行这种错误优化的条件似乎很少见,Delphi版本确实相当古老。
常量标志通常不会成为内循环的一部分,我怀疑你的计数通常也是动态的。但是,你已经遇到了生产代码的这个问题,所以也许它并不像它看起来那么罕见。我只能说我从来没有碰到它,我们在Delphi 5中编写了90%的生产代码。也许这只是默认内存分配的安全性。
答案 1 :(得分:3)
升级。 Delphi 5于10年前发布。
答案 2 :(得分:3)
Dunno究竟是什么问题,因为你的问题相当难以捉摸......但是有几点评论:
为什么使用GetMem而不是新,尤其是 blocksize对于ByteArray来说太小。
我没有D5,但在D2007中:
PByteArray = ^ TByteArray;
TByteArray =字节的数组[0 .. 32767 ];
尝试使用 FastMM4 进行内存分配。它更容易看到发生了什么,并找到问题。
BTW,在D2007中,编译器生成的asm与Paul-Jan posted不同:
Unit7.pas.67: AClass.Bytes[Ofs]:=SrcDataBytes[j];
mov ebx,[ebp-$04]
movzx ebx,[ebx+eax]
push ebx
mov ebx,[ebp-$08]
mov ebx,[ebx+$04]
lea esi,[ebx+esi]
pop ebx
mov [esi],bl
Unit7.pas.69: end;
inc eax