憂鬱な午後のひととき

10月29日_

WASMでi128

以前試したときはエラーになっていたけど最新版では使えるようになっていた. まだ直接mclをやろうとしてもエラーになるのだけど一部は確認できた.

define void @add(i128* %pz, i128* %px, i128* %py)
{
  %x = load i128, i128* %px
  %y = load i128, i128* %py

  %z = add i128 %x, %x
  store i128 %z, i128* %pz
  ret void
}
// emcc -s WASM=1 -O3 -s "MODULARIZE=2" -s "EXPORTED_FUNCTIONS=['_add']"
// wasm-objdump -dx a.out.wasm
get_local 1
i64.load 3 8
set_local 3
get_local 0
get_local 1
i64.load 3 0
tee_local 4
get_local 4
i64.add
tee_local 5
i64.store 3 0
get_local 0
get_local 3
get_local 3
i64.add
get_local 5
get_local 4
i64.lt_u
i64.extend_u/i32
i64.add
i64.store 3 8
end

ああ, 64bit乗算はやっぱり駄目だった.

define i128 @mul(i64 %x, i64 %y) {
    %x0 = zext i64 %x to i128
    %y0 = zext i64 %y to i128
    %z = mul i128 %x0, %y0
    ret i128 %z
}

Unsupported:   %z = mul i128 %x0, %y0
LLVM ERROR: Binary operator type not yet supported
for integer types larger than 64 bits
10月28日_

WASMのCからJSを呼び出す

大きく二つの方法があるようだ.

一つ目はemscripten.hをincludeしてEM_ASMを使う(INTの戻り値が欲しいならEM_ASM_INT, doubleならEM_ASM_DOUBLE). このマクロの実体はemscripten_asm_const_int関数. 第一引数にJSのコードの文字列を渡す. 後ろの引数はその文字列に渡す変数.

たとえばx + yを返すJSをCの中で記述するには

#include <emscripten.h>
int callJS(int x, int y)
{
    return EM_ASM_INT((return $0 + $1), x, y);
}

とする. JSの中で引数は$0, $1, ...で参照する.

複雑なコードを書くとエラーになったのでWASMインスタンスmodに実際処理したい関数を記述し, EM_ASMの中でそれを呼ぶ方が楽かもしれない.

// JS側
mod.callAdd1 = function(x, y) {
    return x + y
}
// C側
void fill2(int x, int y)
{
    EM_ASM((mod.callAdd($0, $1)), x, y);
}

もう一つはJSで書いたコードを--pre-jsオプションでemccの生成コードの中に埋め込み, それをCの中でextern関数として呼ぶ方法.

// pre.js
var _call2JS = function(x, y) { /* _が必要 */
    return x + y
}
// C側
extern int call2JS(int x, int y);
int call2(int x, int y)
{
    return call2JS(x, y);
}
// emccでbuildするときに--pre-js pre.jsオプションを追加.

warning: unresolved symbolが出るけど動いてるようだ.

pre.jsの中ではES6のコードは書けないようでたとえば変数はletでなくvarを使う(正確にはasm.jsでサポートしている範囲? よくわからない). そうでないと意味不明なエラーに悩む(かなり時間を食ってしまった).

subprocess.CalledProcessError: Command
  '['emsdk/node/4.1.1_64bit/bin/node',
    'emsdk/emscripten/1.37.22/tools/js-optimizer.js',
    '/tmp/xxx/fill.bc.o.js.pp.js.mem.js',
    'noPrintMetadata',
    'JSDCE',
    'last',
    'minifyWhitespace']' returned non-zero exit status 1

最適化オプション-O1ではこのパスは通らないのでエラーにならない(-O2, -O3でエラー).

足し算する関数をいくつかの方法でざっくりとベンチマークを取ってみた(run.js). ベンチマークは本当にざっくりとであり, テストするたびに数割~2倍程度のぶれは発生するのであくまでそんなこともあったという程度の数値.

addするJSを呼ぶ[usec]
testJS test0 test1 test2
Firefox 5.5 88 116 97
Chrome 5.1 22 29 23
Edge 5.6 96 120 103

ChromeのWASMの呼び出しコストはかなり低いのに驚いた. 毎回evalしてそうなtest1よりtest2の方が速いのだろうと予想したが一応そんな傾向はある.

ただ1msec程度かかる処理を呼ぶならどちらでもよい. JSの制約に悩む可能性を考えるとEM_ASMの方が安全かもしれない.

EM_ASMはCの中での途中経過の出力をconsole.logに出す方法としては気軽にできてよさそうだ.

10月27日_

emscriptenにおける/dev/urandomの扱い

疑似乱数生成にLinuxでは/dev/urandomを使っているのだけどemccでどう変換されているのか不安だったので追いかけてみた. 結論から言うと大丈夫そう.

emccはファイルシステムをエミュレーションしていて

FS.createDevice ("/dev", "urandom", random_device);

していた. このデバイスを開くとrandom_deviceにいくのだろうと, そちらを見ると

var random_device;
if (typeof crypto !== "undefined") {
    var randomBuffer = new Uint8Array(1);
    random_device = (function() {
        crypto.getRandomValues(randomBuffer);
        return randomBuffer[0]
    })
} else if (ENVIRONMENT_IS_NODE) {
    random_device = (function() {
        return require("crypto").randomBytes(1)[0]
    })
} else {
    random_device = (function() {
        return Math.random() * 256 | 0
    })
}

window.cryptoはWASMが動く環境ならあるので. crypto.getRandomValuesを使うから暗号用途に使って大丈夫.

10月26日_

she.js

大分慣れてきれいに書けるようになってきたかな. emccはちょっと変わったことをしようとするとすぐツールが例外を出すの辛い. 次はNode.jsから呼ぶ方法を調べる.

10月25日_

『日本会議の正体』(青木理)

最近名前をよく見るなと思って読んでみた. 「にっぽんかいぎ」というそうな. 20年ほど前に複数の団体が合流してできた宗教団体のようなもので, 全国各地に支部があり神社なども多数関わっているとのこと.

「美しい日本の再建と誇りある国づくり」のために着実に邁進している. おそろしい.

10月24日_

『「海の宝石」とよばれる生き物ウミウシの大研究』(濱谷巌)

↓で紹介されていたのとは別のウミウシの本だけど, 色鮮やかで不思議な生態のものがたくさん紹介されていた. 写真もきれい.

ウミウシしか食べないウミウシとか, 海藻を食べてその葉緑体を体に取り込み光合成したエネルギーを使うウミウシとか凄まじい.

なんでこんなにさまざまな形に分化したのだろう.

10月23日_

『翼を持つ少女 BISビブリオバトル部』(山本弘)

ちょっと前にビブリオバトルを初めて見て, そこで紹介されていたので読んでみた. 多すぎて困るぐらいおもしろそうな本が紹介されている.

10月20日_

H2O

ポインタのNULL判定を間違えていたところがあったのでpull reqした.

10月19日_

鼻水

急に寒くてくしゃみばかり. 寝不足や疲れもたたってしんどい. 早く寝よう.

10月18日_

emscripten & WebAssembly night !! #5で発表

久しぶりに外部でしゃべるのでめちゃくちゃ緊張した. しかも六本木と六本木一丁目間違えて入館ぎりぎりになってしまったしエレベータわからんし.

発表自体はまあまあおもしろがってもらえたようだ. よかった.

ブラウザで動く準同型暗号と実際のデモ

Homomorphic Encryption demo

ブラウザで完結するとデモがやりやすくてよいな.

10月17日_

JavaScript

明日の準備のためにいろいろ作ってるけどしょうもないところでミスって動かなくて原因追求に悩むのが多発. 早く新しい技術を勉強しないとして効率化しないといかんな.

10月16日_

ROCA:Vlunerable RSA generation

CVE-2017-15361

Infineon Technologies社のチップなどで作られたRSA鍵に脆弱性. OpenSSLで作ったものは大丈夫らしい.

公開鍵nを小さい素数(3~167)で割った余りが特定の値になったときに危ないもよう. rocaで確認できる.

該当する公開鍵か否かをチェックしているコードはhas_fingerprint_realあたり.

primes # 3から167までの素数のテーブル
prints # 対応するなんらかの数字

n # 公開鍵
for i in range(len(primes)):
     if (1 << (n % primes[i])) & prints[i] == 0:
         return False # 安全
return True # 安全じゃない

とりあえず自分に関係してるものは大丈夫そうだった.

10月10日_

wrapper class

BLS demoのAPIがWASMの_malloc, _freeを使ってるのでとても使いにくかったのでC++ライクなクラスを被せた.

classがmallocのポインタを保持してるとdestructorでfreeしないといけないのでUint32Array a_で持つことにした.

freeは気にしなくてもよいがメソッド呼び出しの度にmallocしてそのポインタにa_にコピーしないといけない. でも使い勝手はずっとよくなるのでその方がよいだろう.

うまく行きそうなのでSHEでもその方針で進める.

10月8日_

strnlen

strnlen(const char *s, size_t n)はsが区間[0, n)に'\0'を含んでいないときはnを返すのだけど, PVS-Studioは常にn未満を返すという前提の警告を出していた. そのせいで正しいコードがエラーになっていたので指摘して次のバーションで修正してもらうことになった.

10月6日_

128bit整数に対するbsr

元ネタはナンプレの正規化 ステップ・バイ・ステップ その5.

__builtin_clzlはbsrのintrinsicなのだけど0かどうかを事前にチェックしないといけない. bsr自体はその機能を持っているので冗長. 更に結果を反転させてるのでそれも無駄だから嫌いなintrinsicの一つ(cf. bsfとbsr). 実際, (値の受け渡しは呼び出し規約が分かりにくくなるのでポインタに変更した上で),

// typedef unsigned __int128 mbit;
void left_most_bit(mbit* v) {
  uint64_t *a = reinterpret_cast<uint64_t*>(v);
  if (a[1]) {
    a[0] = 0;
    a[1] = 1ull << (63 - __builtin_clzl(a[1]));
  } else if (a[0]) {
    a[0] = 1ull << (63 - __builtin_clzl(a[0]));
    a[1] = 0;
  }
}

はclangだと

left_most_bit:
    mov    rax,[rdi+0x8]
    test   rax,rax
    je     .low
    mov    QWORD [rdi],0x0
    lzcnt  rax,rax
    xor    eax,0x3f
    mov    ecx,0x1
    shlx   rax,rcx,rax
    mov    [rdi+0x8],rax
    ret
.low:
    mov    rax,[rdi]
    test   rax,rax
    je     .zero
    lzcnt  rax,rax
    xor    eax,0x3f
    mov    ecx,0x1
    shlx   rax,rcx,rax
    mov    QWORD PTR [rdi],rax
    mov    QWORD PTR [rdi+0x8],0x0
.zero:
    ret

となった. 今回の場合はVCの_BitScanReverse64がスマートだと思う.

void left_most_bit2(mbit* v) {
    uint64_t *a = reinterpret_cast<uint64_t*>(v);
    unsigned long idx;
    if (_BitScanReverse64(&idx, a[1])) {
        a[0] = 0;
        a[1] = uint64_t(1) << idx;
    } else if (_BitScanReverse64(&idx, a[0])) {
        a[0] = uint64_t(1) << idx;
        a[1] = 0;
    }
}

これならxorも条件分岐も減ったコードが生成される. x64 Linux用のNASMで書くとこう.

global left_most_bit

segment .text
left_most_bit:
    bsr rcx, [rdi + 8]
    mov edx, 1
    je .low
    mov qword [rdi], 0
    shl rdx, cl
    mov [rdi + 8], rdx
    ret
.low:
    bsr rcx, [rdi]
    je .zero
    mov qword [rdi + 8], 0
    shl rdx, cl
    mov [rdi], rdx
.zero:
    ret
10月5日_

PVS-Studio

去年Microsoft MVPを受賞したときに1年間使わせてもらっていた. いろいろバグが見つかってよかったのだがライセンスが切れてしまった.

それでLinux版が欲しいので値段を聞いたら何故かもう1年分ライセンスをくれた.

とてもありがたいので早速WalBで試したらいくつかポカミスが見つかったので修正. 多分そのうちマージされる.

こないだのblueborneのAndroidのRCEのバグもこれを試してたら見つけられていたかもしれない.

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>

typedef struct {
    char a;
    int b;
    long long int c;
} X;

X *func() {
    X *p = (X*)malloc(sizeof(X));
    if (p == NULL) return 0;
    memset(((unsigned char*) p + 1), 0, sizeof(X));
    return p;
}

A call of the 'memset' function will lead to overflow of the buffer '(unsigned char*)p + 1'.

10月4日_

最適化

ate-pairingmclは殆ど同じロジック・コードなのになんで約1.5倍も速度差があるのかずっと気になってた.

それぞれの関数自体はそんなに差がない. いろいろ細かく調べてみたところ, どうやらate-pairingでやってる低レイヤーの関数の呼び出し規約を独自にしてレジスタの退避復元を省略している部分が結構効いているようだ.

うーん, それをLLVMでやるのは辛そう.

10月2日_

ブラウザ上でSHE

ペアリングベースの一度だけ乗算可能なSHEが動いた(she.html). テーブルサイズの設定を工夫しないと初期化に時間がかかったりエラーになるけどまずはめでたい.

10月1日_

WASM

int64_tを受け渡しするAPIだと途中でint32_tに切られたりしていろいろ不整合がでるようだ. 32ビット整数しかやりとりしないのが無難.

JavaScriptには64ビット整数が無いのにWASMにはあるのがまたややこしい.


御意見は光成滋生<herumi@nifty.com>までお願いします