3

Delphi 5は、特定の場合に無効なアセンブリコードを生成することがわかりました。一般的にどのような場合かわかりません。以下の例では、非常に奇妙な最適化が発生するため、アクセス違反が発生します。レコードまたは配列のバイトに対して、Delphiはプッシュdword [...]、pop ebx、mov ..、blを生成します。このバイトの後にデータがある場合は正しく機能しますが(dwordを正しくプッシュするには少なくとも3つ必要です)、失敗します。データにアクセスできない場合。ここでは、win32Virtual*関数を使用して厳密な境界をエミュレートしました

具体的には、FeedBytesToClassプロシージャ内でアクセスされたブロックの最後のバイトでエラーが発生します。そして、remove 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がプッシュ、ポップ、ムーブの最適化を生成するように、ここで何が非常に具体的であるかということです。一般的にそのような副作用を避けるために私はこれを知る必要があります。

4

3 に答える 3

10

痛い、確かに痛い問題。定数 actionFlag の存在 (4 の倍数の定数カウントと組み合わせて) は、データ処理のプッシュ/ポップ スタイルをトリガーします。実際のアセンブラに興味のある方は、(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 のバージョンは実際にはかなり古いものであるため、正確なフォレンジックについては詳しく説明しません。

通常、定数フラグは内側のループの一部ではなく、カウントも通常は動的になると思います。ただし、本番コードでこの問題に遭遇したことがあるため、見かけほどまれではない可能性があります。私が言えることは、私は一度もそれに遭遇したことがなく、製品コードの 90% を Delphi 5.

于 2009-08-18T18:55:01.723 に答える
3

アップグレード。Delphi5は10年前にリリースされました。

于 2009-08-18T15:40:40.103 に答える
3

あなたの質問はかなりとらえどころのないものであるため、問題が正確に何であるかはわかりません...しかし、いくつかの発言:

  • 特にブロックサイズが ByteArray に対して小さすぎる場合、なぜNewの代わりに GetMem を使用しているのですか。 私は D5 を持っていませんが、D2007 では: PByteArray = ^TByteArray; TByteArray = バイトの配列[0..32767 ];


  • メモリ割り当てにFastMM4を使用してみてください。何が起こっているのかを確認し、問題を見つけやすくなります。

ところで、D2007 では、コンパイラによって生成された asm は、Paul-Jan が投稿したものとは異なります。

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
于 2009-08-18T18:35:40.593 に答える