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