我应该做什么或不做什么来避免Delphi“推dword”错误。

时间:2009-08-18 15:36:38

标签: delphi delphi-5

我发现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优化。我需要知道这一点,以避免一般的副作用。

3 个答案:

答案 0 :(得分:10)

确实,这是一个痛苦的问题。常量actionFlag的存在(结合4的倍数的常量计数)触发了处理数据的push / pop样式。对于那些对实际汇编程序感兴趣的人,(在cpu视图没有提供复制/粘贴的日子里手动输入后面):

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