オーバーフローチェックをONにすると番地がずれる

解決


OTA  2021-03-12 11:11:41  No: 149611  IP: [192.*.*.*]

お世話になります、OTAと申します。
Win10 Pro x64環境にてDelphi 10.2を使用しているのですが、32ビット用アプリケーションで不可解なコードが生成されて頭をかかえています。
何か思いつく方は、お助け頂ければ幸いです。
以下、手順です。

プロジェクトオプションのオーバーフローのチェックをONにします(※OFFだと発生しません)。
Formに1つボタンを置き、下記のようなコードを作成して実行すると、ボタンクリック時に'Err'が表示されます。

type
 TTotalRec = packed record
   Enabled : Boolean;
   Value : Double;
 public
   procedure SetValue( const num : Double );
 end;

procedure TTotalRec.SetValue( const num : Double );
begin
  Value := num;
  Enabled := True;
end;

procedure TForm8.Button1Click(Sender: TObject);
var
  i, deg : Integer;
  trec : array[0..0] of TTotalRec;
begin
  FillChar( trec, SizeOf( trec ), #0 );
  i := 0;
  deg := 45;
  trec[i].SetValue( DegToRad( deg ) );
  if ( trec[i].Enabled = False ) or
     ( trec[i].Value <> DegToRad( deg ) ) then begin
    Caption := 'Err';
  end;
end;

私の期待するところは、Captionに'Err'とは表示されないことです。
DelphiのCPU Windowを見る限り、TTotalRec.SetValueが呼ばれた時Selfの番地が正しくないようです。
下記、005D4EBCの行にあるようにtrecの番地は[ebp-$15]だと思うのですが、

Unit8.pas.45: FillChar( trec, SizeOf( trec ), #0 );
005D4EBC 8D45EB           lea eax,[ebp-$15]
005D4EBF 33C9             xor ecx,ecx
005D4EC1 BA09000000       mov edx,$00000009
005D4EC6 E80129E3FF       call @FillChar

下記の005D4F14の行では、[ebp+eax-$25]をeaxにセットしてTTotalRec.SetValueがCallされています。
この時eaxは0なので最終的に[ebp-$25]となり、どうやら番地を$10ほど引きすぎています。
(実際、call TTotalRec.SetValueの直前で、eaxに[ebp$-15]の値をセットしてやると'Err'は表示されませんでした)

Unit8.pas.48: trec[i].SetValue( DegToRad( deg ) );
(省略)
005D4F08 9B               wait 
005D4F09 6B45F809         imul eax,dword ptr [ebp-$08],$09
005D4F0D 7105             jno $005d4f14
005D4F0F E8982CE3FF       call @IntOver
005D4F14 8D4405DB         lea eax,[ebp+eax-$25]
005D4F18 E86FFFFFFF       call TTotalRec.SetValue

コンパイラの不具合のようにも見えますが、私は何かまずい組み方、または勘違いをしているでしょうか。

編集 削除
take  2021-03-15 00:03:03  No: 149613  IP: [192.*.*.*]

trec[i].SetValue( DegToRad( deg ) )
を実行した時点で trec[0] の値はどうなっていますか?

古いDelphiですがValueには DegToRad( 45 )の計算結果が
Enabled には trueが入ります。

その後のif文のどっちの条件に引っかかっていますかね?
Enabled が false ならコンパイラ含めて問題ありそうですが

DegToRad( 45 )が SetValueと if文の中で精度の違いなどで返す値が異なる可能性もあるかなと。

編集 削除
OTA  2021-03-15 00:47:00  No: 149614  IP: [192.*.*.*]

takeさん、ご返信ありがとうございます。
もしかするとスタックの汚れ具合によってTrueとなることもあるかもしれませんが、私が確認したところEnabled=Falseで弾かれているようです。

SetValueが呼ばれる時、下記で言う領域5がSelfポインタとして渡され、if文で評価されるときは領域1が比較されている感じ(SetValueの処理対象領域が異なる)なので、まぁそうなりますね。
スタックのポインタ(ebp)
ebp-$25: 領域5
ebp-$24: 領域4
       :
ebp-$17: 領域3
ebp-$16: 領域2
ebp-$15: 領域1

こういう意図しないところで問題が起きると気付きにくいので、何か原因があるなら潰したいのですが、結局コンパイラとか環境の気がしてきますね。

編集 削除
igy  2021-03-15 01:05:57  No: 149615  IP: [192.*.*.*]

Delphi 10.2 は、最新の10.2.3でしょうか?

10.2.1では、
[RSP-18985] CodeGen bug with overflow checking
https://quality.embarcadero.com/browse/RSP-18985
があるようです。

編集 削除
OTA  2021-03-15 09:49:24  No: 149616  IP: [192.*.*.*]

igyさん、ご返信、貴重な情報ありがとうございます。
私が使用しているのは、Delphi 10.2 Version 25.0.27659.1188 です(バージョン情報より)。

[RSP-18985]の件は今回のケースとすこし違うように見えますが、オーバーフローチェックが何かしら修正されているのであれば、本件もあわせて修正されている可能性はありますね。
ただ残念ながら、私のDelphi環境をアップデートすることはできないので、直っているかは確かめようがありませんが…。

もう少し調査してみます。

編集 削除
HFUKUSHI  2021-03-15 12:29:17  No: 149617  IP: [192.*.*.*]

Delphi 10.2.3 (25.0.31059.3231)で上のコードをコンパイルしてみましたが、SetValueを呼び出しているあたりの生成コードはこんな感じになりました。

Unit1.pas.47: deg := 45;
005CE6B8 C745F42D000000   mov [ebp-$0c],$0000002d
Unit1.pas.48: trec[i].SetValue( DegToRad( deg ) );
005CE6BF 6B45F809         imul eax,dword ptr [ebp-$08],$09
005CE6C3 7105             jno $005ce6ca
005CE6C5 E8E294E3FF       call @IntOver
005CE6CA 8945E0           mov [ebp-$20],eax
005CE6CD DB45F4           fild dword ptr [ebp-$0c]
005CE6D0 D95DC4           fstp dword ptr [ebp-$3c]
005CE6D3 9B               wait 
005CE6D4 D945C4           fld dword ptr [ebp-$3c]
005CE6D7 DB2D60E75C00     fld tbyte ptr [$005ce760]
005CE6DD DEC9             fmulp st(1)
005CE6DF D95DE4           fstp dword ptr [ebp-$1c]
005CE6E2 9B               wait 
005CE6E3 D945E4           fld dword ptr [ebp-$1c]
005CE6E6 83C4F8           add esp,-$08
005CE6E9 DD1C24           fstp qword ptr [esp]
005CE6EC 9B               wait 
005CE6ED 8B45E0           mov eax,[ebp-$20]
005CE6F0 8D4405EB         lea eax,[ebp+eax-$15]
005CE6F4 E87BFFFFFF       call TTotalRec.SetValue

また動作は正常でした(Caption := 'Err';は実行されない)。igyさん指摘の通り、10.2.2で不具合が修正されたものと思われます。
参考にしてください。

編集 削除
OTA  2021-03-16 01:01:27  No: 149618  IP: [192.*.*.*]

HFUKUSHIさん、ご返信 & わざわざコードを頂きありがとうございます。
コンパイラの不具合で決まりですね。

貼っていただいたコードを確認すると、コードがシンプルになっていますね。
問題のアドレス間違いも解決しているようです。
005CE6F0 8D4405EB         lea eax,[ebp+eax-$15]

すくなくとも古いバージョンを使い続ける限りは不可避の問題ということで理解いたしました。
本件解決とさせていただきます、ありがとうございました。

編集 削除