Edited at

ZEAM開発ログ v.0.4.3 型多相かつ型安全なNIFでオーバーフローを検出する

More than 1 year has passed since last update.


はじめに

ZACKYこと山崎進です。

整数の加減乗算のオーバーフローを検出する方法がわかりましたので,報告します。

「ZEAM開発ログ 目次」はこちら


整数のオーバーフローを検出する方法

__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

ソースコード全体のGitHubレポジトリはこちら

バックエンドでどんなコードを生成したらいいかの見通しは立ったので,次回は「ZEAM開発ログ v.0.4.4 INT64判定をマクロで簡単に判定する」で,フロントエンドについて検討を始めます。お楽しみに!

:stars::stars::stars: お知らせ:Elixirもくもく会(リモート参加OK、入門トラック有)を9月28日に開催します :stars::stars::stars:

「fukuoka.ex#14:Elixir/Phoenixもくもく会~入門もあるよ」を2018年9月28日金曜日に開催します

前回は,ゲリラ的に募った「Zoomによるリモート参加」を,今回から正式に受け付けるようになりましたので,福岡以外の首都圏や地方からでも参加できます(申し込みいただいたら、追ってZoom URLをconnpassメールでお送りします)

また,これまではElixir/Phoenix経験者を対象とした,もくもく会オンリーでしたが,今回から,入門者トラックも併設し,fukuoka.exアドバイザーズ/キャストに質問できるようにアップグレードしました

私,山崎も参加します! この記事の延長線上のものを作ろうと思っています。

お申込みはコチラから

https://fukuokaex.connpass.com/event/100659/

image.png