我应该做什么或不做什么来避免Delphi"推dword"错误.

Mak*_*see 3 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;
Run Code Online (Sandbox Code Playgroud)

最初,当我使用位图位的RGB数据访问时发生错误,但是那里的代码太复杂,所以我把它缩小到这个片段.

所以问题是这里有什么特别的东西让Delphi产生推,弹,动优化.我需要知道这一点,以避免一般的副作用.

Pau*_*Jan 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
Run Code Online (Sandbox Code Playgroud)

这样做了4096次.我检查过,Delphi 6没有这种行为.我认为我们可以安全地假设它在任何更高版本中都已修复.

作为一种解决方法,我建议只在该方法中添加{$ O - }/{$ O +}.我不会过多地考虑确切的取证,因为触发Delphi执行这种错误优化的条件似乎很少见,Delphi版本确实相当古老.

常量标志通常不会成为内循环的一部分,我怀疑你的计数通常也是动态的.但是,你已经遇到了生产代码的这个问题,所以也许它并不像它看起来那么罕见.我只能说我从来没有碰到它,我们在Delphi 5中编写了90%的生产代码.也许这只是默认内存分配的安全性.