LoginSignup
2
0

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-09-19

はじめに

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

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0