Delphiにはオーバフローチェックと範囲チェックというコンパイラオプションがあります。
オーバーフローのチェック(Delphi) - RAD Studio XE3
範囲チェック - RAD Studio XE3
いずれもプロジェクトオプションのコンパイラで指定するか、{$OVERFLOWCHECKS ON|OFF}{$RANGECHECKS ON|OFF}で任意の範囲に対して指定することができます。しかしデフォルトの状態ではどちらもOFFなので、ONにした場合の
オーバーフローのチェックを有効にすると,プログラムは遅くなり,またいくらか大きくなるため,{$Q+} はデバッグにのみ使用してください。
範囲チェックを有効にすると、プログラムの処理速度が遅くなり、サイズも多少大きくなります。
が実際にどのようなことを示しているのかを知っている人は意外に少ないのではないでしょうか。そこでこれらのオプションによるペナルティがどのようなものなのかを確認してみます(Delphi XE2/Windows x86/Debugビルドで検証)。
まず{$OVERFLOWCHECKS ON}(短縮形{$Q+})です。
var
a: Integer;
b: Integer;
c: Integer;
begin
{$OVERFLOWCHECKS ON}
a := $44444444;
b := $44444444;
c := a + b;
{$OVERFLOWCHECKS OFF}
end;
このコードは以下のように展開されます。Unit1.pas.36: a := $44444444;
0051139C C745F844444444 mov [ebp-$08],$44444444
Unit1.pas.37: b := $44444444;
005113A3 C745F444444444 mov [ebp-$0c],$44444444
Unit1.pas.38: c := a + b;
005113AA 8B45F8 mov eax,[ebp-$08]
005113AD 0345F4 add eax,[ebp-$0c]
005113B0 7105 jno $005113b7
005113B2 E8BD39EFFF call @IntOver
005113B7 8945F0 mov [ebp-$10],eax
特定の整数算術演算(+,-,*,Abs,Sqr,Succ,Pred,Inc,および Dec)毎にこのコードが追加で生成される、ということのようです。
一方{$RANGECHECKS ON}(短縮形{$R+})ですが、これには配列および文字列の添字のチェックとスカラ型/部分範囲型変数への代入時の範囲チェックの2つの効果があります。まず添字のチェックですが、ちょっとわかりづらいので{$RANGECHECKS OFF}のものと両方を並べてみます。
var
s: String;
c: Char;
begin
s := 'TEST';
c := s[6];
{$RANGECHECKS ON}
c := s[6];
{$RANGECHECKS OFF}
end;
このコードは以下のように展開されます。Unit1.pas.52: c := s[6];
00511464 8B45F8 mov eax,[ebp-$08]
00511467 668B400A mov ax,[eax+$0a]
0051146B 668945F6 mov [ebp-$0a],ax
Unit1.pas.54: c := s[6];
0051146F B806000000 mov eax,$00000006
00511474 8B55F8 mov edx,[ebp-$08]
00511477 48 dec eax
00511478 85D2 test edx,edx
0051147A 7405 jz $00511481
0051147C 3B42FC cmp eax,[edx-$04]
0051147F 7205 jb $00511486
00511481 E8E638EFFF call @BoundErr
00511486 40 inc eax
00511487 668B4442FE mov ax,[edx+eax*2-$02]
0051148C 668945F6 mov [ebp-$0a],ax
最後に部分範囲型の変数への代入を見てみます。
type
TSubInt = 8..15;
var
a: TSubInt;
b: TSubInt;
c: TSubInt;
begin
{$RANGECHECKS ON}
a := 8;
b := 8;
c := a + b;
a := 9;
b := 8;
c := a - b;
{$RANGECHECKS OFF}
end;
加算と減算の両方を確認してみます。Unit1.pas.69: a := 8;
005114D8 C645FB08 mov byte ptr [ebp-$05],$08
Unit1.pas.70: b := 8;
005114DC C645FA08 mov byte ptr [ebp-$06],$08
Unit1.pas.71: c := a + b;
005114E0 33C0 xor eax,eax
005114E2 8A45FB mov al,[ebp-$05]
005114E5 33D2 xor edx,edx
005114E7 8A55FA mov dl,[ebp-$06]
005114EA 03C2 add eax,edx
005114EC 83C0F8 add eax,-$08
005114EF 83F807 cmp eax,$07
005114F2 7605 jbe $005114f9
005114F4 E87338EFFF call @BoundErr
005114F9 83C008 add eax,$08
005114FC 8845F9 mov [ebp-$07],al
Unit1.pas.72: a := 9;
005114FF C645FB09 mov byte ptr [ebp-$05],$09
Unit1.pas.73: b := 8;
00511503 C645FA08 mov byte ptr [ebp-$06],$08
Unit1.pas.74: c := a - b;
00511507 33C0 xor eax,eax
00511509 8A45FB mov al,[ebp-$05]
0051150C 33D2 xor edx,edx
0051150E 8A55FA mov dl,[ebp-$06]
00511511 2BC2 sub eax,edx
00511513 83C0F8 add eax,-$08
00511516 83F807 cmp eax,$07
00511519 7605 jbe $00511520
0051151B E84C38EFFF call @BoundErr
00511520 83C008 add eax,$08
00511523 8845F9 mov [ebp-$07],al
ということで、オーバフローチェックはFLAGSレジスタのOFを検査するだけなので比較的ペナルティは小さく、オーバフローを検出したい状況では積極的にONにしても問題なさそうです。一方範囲チェックは範囲の最小値が0でないと処理が複雑になることもあり、明示的に必要な範囲チェックのコードを記述したほうがいいような気もします。
0 件のコメント:
コメントを投稿