GCCの-ftrapv

gcc -ftrapv


最近のGCC(>=3.x)には-ftrapvというオプションがあって、これを有効にすると「符号あり整数同士の加算・減算・乗算でオーバーフロー/アンダーフローが発生したとき、プロセスをabort()」してくれます。例えば、INT_MAX + 1 とか INT_MIN - 1 とか INT_MIN * (-1) なんて計算をしようとすると、即abort()してくれるようになるわけです*1。符号無し整数の場合はチェックされませんのでご注意。


さて、

int test_trap(int a, int b)
{
 int d = a + b;
 int e = a - b;
 int f = d * e;
 return f;
}

こんなコードを gcc -ftrapv -fverbose-asm -S すると、

test_trap:
       pushl   %ebp    #
       movl    %esp, %ebp      #,
       subl    $24, %esp       #,
       subl    $8, %esp        #,
       pushl   12(%ebp)        # b
       pushl   8(%ebp) # a
       call    __addvsi3       #
       addl    $16, %esp       #,
       movl    %eax, -12(%ebp) # tmp60, d
       subl    $8, %esp        #,
       pushl   12(%ebp)        # b
       pushl   8(%ebp) # a
       call    __subvsi3       #
       addl    $16, %esp       #,
       movl    %eax, -8(%ebp)  # tmp61, e
       subl    $8, %esp        #,
       pushl   -8(%ebp)        # e
       pushl   -12(%ebp)       # d
       call    __mulvsi3       #
       addl    $16, %esp       #,
       movl    %eax, -4(%ebp)  # tmp62, f
       movl    -4(%ebp), %eax  # f, D.1171
       leave
       ret

こういう出力になります。3つのcall命令に注目してください。addl/subl/mullといったCPUの加減乗命令のかわりに、関数が呼ばれています。これらの関数はlibgcc2.a の中にあり、ソースコードは、gcc-4.0.2/gcc/libgcc2.c です。__addvsi3 の実装は次のような感じ。

SItype
__addvsi3 (SItype a, SItype b)
{
 const SItype w = a + b;

 if (b >= 0 ? w < a : w > a)
   abort ();

 return w;
}

オーバーフロー時にabort()というのは完全にハードコードで、挙動を変更することはできないようですし、パフォーマンスの問題もあるので、最終製品での使用は難しいかもしれません。でも、デバッグにはよさそうですね。

VC++ /RTC


詳しく調べていませんが、VC++には/RTCという似たようなオプションがあるそうです。

*1:最後のヤツは処理系のINT_MINの絶対値のほうがINT_MAXの絶対値よりも大きい場合(ISO Cの規格は2の補数を使う処理系でもそれを要請していない)に限りますけどね。x86/LinuxはINT_MINのほうが大きいです。詳しくはC99の§ 5.2.4.2.1 および 6.2.6.2 を参照のこと