はじめに
ZACKYこと山崎進です。
整数の加減乗算のオーバーフローを検出する方法がわかりましたので,報告します。
整数のオーバーフローを検出する方法
__builtin_*_overflow
という関数を使います。
符号付き64ビット整数だったら,__builtin_saddl_overflow
になります。
ドキュメントはこちら。
Clang Language Extensions — Clang 4 documentation
C言語のコードはこんな感じです。
static
ERL_NIF_TERM asm_1_nif_ii(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
long a, b;
if(__builtin_expect((enif_get_int64(env, argv[0], &a) == 0), 0)) {
goto error;
}
if(__builtin_expect((enif_get_int64(env, argv[1], &b) == 0), 0)) {
goto error;
}
long result;
if(__builtin_expect(__builtin_saddl_overflow(a, b, &result), 0)) {
goto error2;
}
return enif_make_int64(env, result);
error:
return arithmetic_error;
error2:
return error_atom;
}
__built_in_expect
を使うと分岐予測を考慮して最適化してくれます。詳しくは「ZEAM開発ログ v.0.4.2 型多相かつ型安全なNIFのコードを分岐予測の観点で最適化」を参照ください。
実行結果
def main do
IO.puts asm_1(1, 2)
IO.puts asm_1(1.0, 2)
IO.puts asm_1(1, 2.0)
IO.puts asm_1(1.0, 2.0)
IO.puts asm_1(@max_int, 0)
IO.puts asm_1(@min_int, 0)
try do
IO.puts asm_1(@max_int, 1)
rescue
error in [ArithmeticError] -> IO.puts "it needs BigNum!: #{Exception.message(error)}"
end
try do
IO.puts asm_1(@max_int + 1, 1)
rescue
error in [ArithmeticError] -> IO.puts "it needs BigNum!: #{Exception.message(error)}"
end
end
実行してみると,オーバーフローで例外が発生します。
$ mix run -e "NifLlvm.main"
make: `priv/libnifllvm.so' is up to date.
3
3.0
3.0
3.0
9223372036854775807
-9223372036854775808
it needs BigNum!: bad argument in arithmetic expression
it needs BigNum!: bad argument in arithmetic expression
アセンブリコードを見ると意図通りオーバーフローのジャンプ命令が生成されています。
##DEBUG_VALUE: asm_1_nif_ii:a <- %rsi
.loc 3 26 22 is_stmt 0 ## native/lib.c:26:22
addq -24(%rbp), %rsi
Ltmp27:
.loc 3 26 5 ## native/lib.c:26:5
jo LBB4_5
LLVMだとこんな感じにコーディングするようです。
%19 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %17, i64 %18), !dbg !181
%20 = extractvalue { i64, i1 } %19, 1, !dbg !181
call void @llvm.dbg.value(metadata i64 %22, metadata !160, metadata !DIExpression()), !dbg !182
br i1 %20, label %26, label %21, !dbg !183, !prof !171
バックエンドでどんなコードを生成したらいいかの見通しは立ったので,次回は「ZEAM開発ログ v.0.4.4 INT64判定をマクロで簡単に判定する」で,フロントエンドについて検討を始めます。お楽しみに!
お知らせ:Elixirもくもく会(リモート参加OK、入門トラック有)を9月28日に開催します
「fukuoka.ex#14:Elixir/Phoenixもくもく会~入門もあるよ」を2018年9月28日金曜日に開催します
前回は,ゲリラ的に募った「Zoomによるリモート参加」を,今回から正式に受け付けるようになりましたので,福岡以外の首都圏や地方からでも参加できます(申し込みいただいたら、追ってZoom URLをconnpassメールでお送りします)
また,これまではElixir/Phoenix経験者を対象とした,もくもく会オンリーでしたが,今回から,入門者トラックも併設し,fukuoka.exアドバイザーズ/キャストに質問できるようにアップグレードしました
私,山崎も参加します! この記事の延長線上のものを作ろうと思っています。
お申込みはコチラから
https://fukuokaex.connpass.com/event/100659/