Go 1.24、WebAssembly関連のアップデートを追ってみた

この記事は [フラー株式会社 Advent Calendar 2024](https://qiita.com/advent-calendar/2024/fuller-inc) の16日目の記事です。

# はじめに

[Go 1.24 Release Notes](https://tip.golang.org/doc/go1.24) を眺めていたらWebAssembly に関する機能追加・変更がありました。リリース時期は2025年2月とまだ先であり、リリースに含まれるか不確定ですが、興味があったので調べることにしました。

以下がアップデート内容です。この内、go:wasmexportディレクティブのサポートとgo:wasmimportの関数の型に使える型が増えた件を細かく見ていきます。

筆者は Goのコードから wasm をビルドした経験がありますが、手を動かした確認をとっていないため、不正確な点があるかもしれません。あらかじめご了承ください。


```text
WebAssembly¶
The go:wasmexport directive is added for Go programs to export functions to the WebAssembly host.

On WebAssembly System Interface Preview 1 (GOOS=wasip1, GOARCH=wasm), 
Go 1.24 supports building a Go program as a reactor/library, 
by specifying the -buildmode=c-shared build flag.

More types are now permitted as argument or result types for go:wasmimport functions. 
Specifically, bool, string, uintptr, and pointers to certain types are allowed (see the proposal for detail), along with 32-bit and 64-bit integer and float types, and unsafe.Pointer, which are already allowed. 
These types are also permitted as argument or result types for go:wasmexport functions.

The support files for WebAssembly have been moved to lib/wasm from misc/wasm.
```

# go:wasmexport ディレクティブの追加

Issue: [cmd/compile: add go:wasmexport directive](https://github.com/golang/go/issues/42372)

ユーザーの要望により go:wasmexport コンパイラディレクティブが追加されます。goのソースコードに以下のコメントを追加することでブラウザなどから呼び出すことができます。ちなみに、関数のみでメソッドは対応していません。

```
//go:wasmexport add
func add(x, y int) int {
    return x + y
}
```

Rustではwasm-bindgenで[既に実現された機能](https://rustwasm.github.io/wasm-bindgen/contributing/design/exporting-rust.html)のようですが、Goでは[goroutineの扱いやGCなどの様々な懸念点](https://github.com/golang/go/issues/42372#issuecomment-723445431)があり、issue が長い間塩漬けになっていました。

より詳細にみていきます。wasmで生成される初期化関数には _start, _initializeの二つがあります。これらの違いは、_start はランタイムを初期化し、mainを呼び出す一方、_initializeはランタイムを初期化し、mainを呼び出しません。_startをエクスポートするモードがコマンドモード、_initializeをエクスポートするモードをリアクター（ライブラリ）モードと言います。

_start,_initializeの詳細は[Current Unstable API](https://github.com/WebAssembly/WASI/blob/main/legacy/application-abi.md#current-unstable-abi) にあります。[WASI](https://wasi.dev/) Preview 1(GOOS=wasip1, GOARCH=wasm)では、ビルドフラグに```-buildmode=c-shared```をつけることでリアクターモードにすることができ、これが、リリースノートにあったアップデートの２行目の内容になります。

以上が go:wasmexportの説明ですが、その他にも面白い議論がされていました。
例えば、エクスポートされた関数は新しいgoroutine上で実行されます。そのgoroutineでgoroutineが実行され、ブロックされた場合はどうするかを考えたり、エクスポートされた関数からJSの関数をインポートしてその中でさらにエクスポートした関数を呼ぶ場合はどうするかを考えていたりしていました。気になる方は issue を参照ください。

ちなみに tinygoではgo:wasmexportは[一足先に導入されています。](https://github.com/tinygo-org/tinygo/releases/tag/v0.34.0)

# go:wasmimport の関数に使える型が増えた件

Issue: [cmd/compile: relax wasm/wasm32 function import signature type constraints](https://github.com/golang/go/issues/66984)

背景を正しく理解できていないのですが、正確には使える型の制限が緩和されたそうです。Go1.21 で go:wasmimportが WASI Preview 1 のサポートされました。その際、ホストとクライアント間、アーキテクチャ(32bit, 64bit)が違う場合、型変換が高度で高価であったため、使用できる型を制限していました。具体的には以下の型です。

|Go value|Wasm value|
|:----|:----|
|int32, uint32|i32|
|int64, uint64|i64|
|float32|f32|
|float64|f64|
|unsafe.Pointer|i32|

それが、[wasm32](https://github.com/golang/go/issues/63131)の登場により、アーキテクチャ間のミスマッチを解消できるようになり、型の制限を緩和できるようになったようです。
具体的な型は膨大で複雑なため、詳細はissueを参照ください。個人的には文字列をwasmからJSに渡すのが簡単になったのが嬉しいです。ちなみに go:wasmexportで使える型は緩和前のgo:wasmimport で使える型と同じです。制限が緩和されるアップデートを待ちましょう...

# おわりに
以前 Goで WebAssembly を触ったことがあるんですが、アップデートあったので内容を読んでみました。やっぱり issue を読むの楽しいです。歴史を学ぶことができるし、明解な文章は読んでて気持ちがいいです。次は17日目でまた私になります。タイトル未定です。