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