wardite 0.5.1 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/Steepfile +1 -0
- data/exe/wardite +11 -5
- data/lib/wardite/instruction.rb +7 -9
- data/lib/wardite/load.rb +14 -9
- data/lib/wardite/revisitor.rb +1 -1
- data/lib/wardite/value.rb +28 -23
- data/lib/wardite/version.rb +1 -1
- data/lib/wardite/wasi/consts.rb +129 -0
- data/lib/wardite/wasi.rb +268 -7
- data/lib/wardite/wasm_module.rb +53 -0
- data/lib/wardite.rb +68 -40
- data/misc/bench-value.rb +25 -0
- data/misc/slides/fib.c +4 -0
- data/misc/slides/fib.html +13 -0
- data/misc/slides/image-1.png +0 -0
- data/misc/slides/image-2.png +0 -0
- data/misc/slides/image.png +0 -0
- data/misc/slides/tokyo12.html +614 -0
- data/misc/slides/tokyo12.md +624 -0
- data/sig/fcntl.rbs +7 -0
- data/sig/generated/wardite/leb128.rbs +1 -1
- data/sig/generated/wardite/load.rbs +2 -2
- data/sig/generated/wardite/value.rbs +12 -10
- data/sig/generated/wardite/wasi/consts.rbs +137 -0
- data/sig/generated/wardite/wasi.rbs +62 -5
- data/sig/generated/wardite/wasm_module.rbs +42 -0
- data/sig/generated/wardite.rbs +24 -9
- metadata +16 -7
- data/examples/add.wasm +0 -0
@@ -0,0 +1,624 @@
|
|
1
|
+
---
|
2
|
+
marp: true
|
3
|
+
theme: default
|
4
|
+
paginate: true
|
5
|
+
backgroundColor: #fff
|
6
|
+
header: '超入門WebAssembly(のランタイムをRubyで書く方法)'
|
7
|
+
# footer: 'Page %page%'
|
8
|
+
---
|
9
|
+
|
10
|
+
# 超入門WebAssembly<br>(のランタイムをRubyで書く方法)
|
11
|
+
|
12
|
+
---
|
13
|
+
|
14
|
+
# 自己紹介
|
15
|
+
|
16
|
+
- Uchio Kondo
|
17
|
+
- @udzura
|
18
|
+
|
19
|
+
---
|
20
|
+
|
21
|
+
# 最近の生活
|
22
|
+
|
23
|
+
- 趣味: VM実装になりつつある...
|
24
|
+
|
25
|
+
---
|
26
|
+
|
27
|
+
# mruby/edge
|
28
|
+
|
29
|
+
- Rustで書いたmrubyランタイム(mruby 3.x のバイトコードを実行可能)
|
30
|
+
- 基本的にmruby/cをベースにしている(一部必要な機能はmrubyから移植したい)
|
31
|
+
|
32
|
+
---
|
33
|
+
|
34
|
+
# Wardite
|
35
|
+
|
36
|
+
- Rubyで書いたWebAssemblyランタイム
|
37
|
+
|
38
|
+
---
|
39
|
+
|
40
|
+
# 今日は Wardite の話をします
|
41
|
+
|
42
|
+
---
|
43
|
+
|
44
|
+
# 前提: WebAssembly とは
|
45
|
+
|
46
|
+
---
|
47
|
+
|
48
|
+
# WebAssembly とは
|
49
|
+
|
50
|
+
- ブラウザで動くバイナリフォーマット
|
51
|
+
- バイトコードをいろいろな言語(C, C++, Rust, ...)からコンパイルして、そのプログラムをブラウザの上で実行できる
|
52
|
+
|
53
|
+
---
|
54
|
+
|
55
|
+
# WebAssembly とは
|
56
|
+
|
57
|
+
- ~~ブラウザで~~ いろいろなところで動くバイナリフォーマット
|
58
|
+
|
59
|
+
---
|
60
|
+
|
61
|
+
# WebAssembly のメリット
|
62
|
+
|
63
|
+
- 言語を選ばずにバイナリを作れる
|
64
|
+
- そのバイナリは、ブラウザを中心にいろいろなところで動かせる
|
65
|
+
- Write Once, Run Anywhere なのじゃよ...
|
66
|
+
- 多くのランタイムで高速に動くことが期待できる
|
67
|
+
- (安全性等もメリットですが今日は割愛)
|
68
|
+
|
69
|
+
---
|
70
|
+
|
71
|
+
# さて、Wardite
|
72
|
+
|
73
|
+
---
|
74
|
+
|
75
|
+
# Wardite の特徴
|
76
|
+
|
77
|
+
- Pure Ruby 製
|
78
|
+
- 標準添付ライブラリ以外に動作上の依存ライブラリはなし
|
79
|
+
- Fully RBS annotated (rbs-inline)
|
80
|
+
|
81
|
+
<!-- ちなみに標準添付ライブラリもstringioだけです。これも削っちゃおうかな? -->
|
82
|
+
|
83
|
+
---
|
84
|
+
|
85
|
+
`<!--`
|
86
|
+
|
87
|
+
# 名前の由来
|
88
|
+
|
89
|
+
- `WA` で始まる鉱石の名前を探して、それを採用した
|
90
|
+
- ワード石: NaAl<sub>3</sub>(PO<sub>4</sub>)<sub>2</sub>(OH)<sub>4</sub>2(H<sub>2</sub>O)
|
91
|
+
- [Image CC BY-SA 4.0](https://en.wikipedia.org/wiki/Wardite#/media/File:Wardite.jpg)
|
92
|
+
|
93
|
+
`-->`
|
94
|
+
|
95
|
+
---
|
96
|
+
|
97
|
+
# なぜ Ruby で WebAssembly ランタイムを書くのか
|
98
|
+
|
99
|
+
- C拡張ベースの組み込みWebAssembly ランタイムなら実際結構ある
|
100
|
+
- Wasmtime, Wasmedge, Wasmer, ...
|
101
|
+
- 速度面でも強みはあるでしょう...
|
102
|
+
|
103
|
+
---
|
104
|
+
|
105
|
+
# でも...
|
106
|
+
|
107
|
+
- みなさん、このメッセージ好きですか?
|
108
|
+
|
109
|
+
```
|
110
|
+
Building native extensions. This could take a while...
|
111
|
+
```
|
112
|
+
|
113
|
+
---
|
114
|
+
|
115
|
+
# cf. [wazero](https://github.com/tetratelabs/wazero)
|
116
|
+
|
117
|
+
- Pure Go の WebAssembly ランタイム
|
118
|
+
- cgo を避けたいGoのコミュニティで受け入れられている
|
119
|
+
- wazero + tinygo でGoだけのプラグイン機構があったりする
|
120
|
+
- https://github.com/knqyf263/go-plugin
|
121
|
+
|
122
|
+
---
|
123
|
+
|
124
|
+
# Pure Rubyであることのメリット
|
125
|
+
|
126
|
+
- インストール・セットアップが簡単
|
127
|
+
- Rubyが動けば動く、プラットフォームに依存しにくい
|
128
|
+
- さらに、Warditeを頑張って高速化することで色々貢献も出てきそう
|
129
|
+
|
130
|
+
## とはいえ
|
131
|
+
|
132
|
+
- 最初は「勉強目的」ではあった。思ったよりちゃんと動くので色んなユースケースを考えている
|
133
|
+
|
134
|
+
---
|
135
|
+
|
136
|
+
# demo
|
137
|
+
|
138
|
+
---
|
139
|
+
|
140
|
+
# こういうCのコードがあります
|
141
|
+
|
142
|
+
```c
|
143
|
+
/* fibonacci number in C */
|
144
|
+
int fib(int n) {
|
145
|
+
if (n <= 1) return n;
|
146
|
+
return fib(n - 1) + fib(n - 2);
|
147
|
+
}
|
148
|
+
```
|
149
|
+
|
150
|
+
---
|
151
|
+
|
152
|
+
# これをWebAssemblyにコンパイルしてみましょう
|
153
|
+
|
154
|
+
```
|
155
|
+
$ clang --target=wasm32 \
|
156
|
+
--no-standard-libraries \
|
157
|
+
-Wl,--export-all -Wl,--no-entry \
|
158
|
+
-o fib.wasm \
|
159
|
+
fib.c
|
160
|
+
```
|
161
|
+
|
162
|
+
---
|
163
|
+
|
164
|
+
# ブラウザで動かす
|
165
|
+
|
166
|
+
```html
|
167
|
+
<!DOCTYPE html>
|
168
|
+
<html>
|
169
|
+
<head>
|
170
|
+
<script>
|
171
|
+
WebAssembly.instantiateStreaming(fetch('./fib.wasm'))
|
172
|
+
.then(obj => {
|
173
|
+
alert(`fib(20) = ${obj.instance.exports.fib(20)}`);
|
174
|
+
});
|
175
|
+
</script>
|
176
|
+
</head>
|
177
|
+
<body>
|
178
|
+
</body>
|
179
|
+
</html>
|
180
|
+
```
|
181
|
+
|
182
|
+
----
|
183
|
+
|
184
|
+
|
185
|
+
|
186
|
+
```javascript
|
187
|
+
|
188
|
+
// WebAssemblyをフェッチしてインスタンス化する
|
189
|
+
WebAssembly.instantiateStreaming(
|
190
|
+
fetch('./fib.wasm')
|
191
|
+
).then(obj => {
|
192
|
+
// obj.instance にインスタンスがあり、
|
193
|
+
// さっきの fib がexportされている
|
194
|
+
const value = obj.instance.exports.fib(20)
|
195
|
+
alert(`fib(20) = ${value}`);
|
196
|
+
});
|
197
|
+
|
198
|
+
```
|
199
|
+
|
200
|
+
----
|
201
|
+
|
202
|
+

|
203
|
+
|
204
|
+
----
|
205
|
+
|
206
|
+
# 手元で動作
|
207
|
+
|
208
|
+
```
|
209
|
+
$ wasmtime --invoke fib fib.wasm 20
|
210
|
+
warning: using `--invoke` with a function that takes arguments
|
211
|
+
is experimental and may break in the future
|
212
|
+
warning: using `--invoke` with a function that returns values
|
213
|
+
is experimental and may break in the future
|
214
|
+
6765
|
215
|
+
```
|
216
|
+
|
217
|
+
---
|
218
|
+
|
219
|
+
# そして... Warditeでも動作
|
220
|
+
|
221
|
+
```
|
222
|
+
$ bundle exec wardite fib.wasm fib 20
|
223
|
+
return value: I32(6765)
|
224
|
+
```
|
225
|
+
|
226
|
+
---
|
227
|
+
|
228
|
+
# Wardite を支える技術
|
229
|
+
|
230
|
+
---
|
231
|
+
|
232
|
+
# 最初の実装
|
233
|
+
|
234
|
+
- ゴリラさんの「[RustでWasm Runtimeを実装する](https://zenn.dev/skanehira/books/writing-wasm-runtime-in-rust)」を参考に実装を開始した
|
235
|
+
- 元のコードがRustなので、強烈に型で守られたかったのと、RBS自体経験が浅いので勉強も兼ね、Full RBSを目指して移植していった
|
236
|
+
|
237
|
+
---
|
238
|
+
|
239
|
+
## 当時のメモを見ながら振り返ると
|
240
|
+
|
241
|
+
- 最初はとにかくデータ構造を把握しようとした
|
242
|
+
- [Wasmバイナリの全体像](https://zenn.dev/skanehira/books/writing-wasm-runtime-in-rust/viewer/04_wasm_binary_structure#wasm%E3%83%90%E3%82%A4%E3%83%8A%E3%83%AA%E3%81%AE%E5%85%A8%E4%BD%93%E5%83%8F)
|
243
|
+
- なぜかCの構造体で表現してるメモが...
|
244
|
+
|
245
|
+
```c
|
246
|
+
struct SectionHeader {
|
247
|
+
u8 code;
|
248
|
+
u32LEB128 size; //先頭2バイトを除いたセクションデータのバイト数
|
249
|
+
union {
|
250
|
+
u32LEB128 nr_types;
|
251
|
+
u32LEB128 nr_functions;
|
252
|
+
u32LEB128 nr_memories; // num memoriesはメモリの個数だが、
|
253
|
+
// version 1の仕様ではメモリは1モジュールに1つしか定義できないので、
|
254
|
+
// 実質的にこの値は1で固定される。
|
255
|
+
u32LEB128 nr_data_segments;
|
256
|
+
u32LEB128 nr_imports;
|
257
|
+
u32LEB128 nr_exports;
|
258
|
+
}
|
259
|
+
}
|
260
|
+
```
|
261
|
+
|
262
|
+
<!-- この発表本当にRubyのコードでないなw -->
|
263
|
+
|
264
|
+
---
|
265
|
+
|
266
|
+
## > section sizeはLEB128[1]でエンコードされたu32
|
267
|
+
|
268
|
+
- LEB128っち何??????
|
269
|
+
- DWARFの中とかで使われている可変長の数値表現らしい...
|
270
|
+
|
271
|
+
---
|
272
|
+
|
273
|
+
## Rubyで実装しないといけないので...
|
274
|
+
|
275
|
+
- 自分で書いた
|
276
|
+
- 当時はまだそんなにCopilot使ってなかったんですよ...
|
277
|
+
|
278
|
+
```ruby
|
279
|
+
def to_i_by_uleb128(bytes)
|
280
|
+
dest = 0
|
281
|
+
bytes.each_with_index do |b, level|
|
282
|
+
upper, lower = (b >> 7), (b & (1 << 7) - 1)
|
283
|
+
dest |= lower << (7 * level)
|
284
|
+
if upper == 0
|
285
|
+
return dest
|
286
|
+
end
|
287
|
+
end
|
288
|
+
raise "unreachable"
|
289
|
+
end
|
290
|
+
|
291
|
+
to_i_by_uleb128 "\xB9\x64".unpack("C*")
|
292
|
+
# => 12857
|
293
|
+
```
|
294
|
+
|
295
|
+
---
|
296
|
+
|
297
|
+
## この辺りの情報をもとにバイナリパーサを書く
|
298
|
+
|
299
|
+
- StringIO/File どちらも取る想定
|
300
|
+
- バイト列を一つ一つ読み取る感じの実装になっている
|
301
|
+
|
302
|
+
```ruby
|
303
|
+
# @rbs return: Integer
|
304
|
+
def self.preamble
|
305
|
+
asm = @buf.read 4
|
306
|
+
raise LoadError, "buffer too short" if !asm
|
307
|
+
raise LoadError, "invalid preamble" asm != "\u0000asm"
|
308
|
+
vstr = @buf.read(4)
|
309
|
+
version = vstr.to_enum(:chars)
|
310
|
+
.with_index
|
311
|
+
.inject(0) {|dest, (c, i)| dest | (c.ord << i*8) }
|
312
|
+
raise LoadError, "unsupported ver: #{version}" if version != 1
|
313
|
+
|
314
|
+
version
|
315
|
+
end # ...
|
316
|
+
```
|
317
|
+
|
318
|
+
---
|
319
|
+
|
320
|
+
## 命令を把握する
|
321
|
+
|
322
|
+
- [Wasm SpecのIndex of Instructions](https://www.w3.org/TR/wasm-core-1/#a7-index-of-instructions)
|
323
|
+
- メモには「思ったより多くなかった」って書いてあるが、いや多いでしょ(190個ぐらい)
|
324
|
+
|
325
|
+
---
|
326
|
+
|
327
|
+
## [WebAssembly Opcodes Table](https://pengowray.github.io/wasm-ops/) が便利
|
328
|
+
|
329
|
+

|
330
|
+
|
331
|
+
---
|
332
|
+
|
333
|
+
## 頑張っていた過去の自分
|
334
|
+
|
335
|
+
- 楽しそうですね
|
336
|
+
|
337
|
+

|
338
|
+
|
339
|
+
---
|
340
|
+
|
341
|
+
## VM周りの実装
|
342
|
+
|
343
|
+
- [Runtime Structure](https://webassembly.github.io/spec/core/exec/runtime.html#) も見ながら
|
344
|
+
- Runtimeは以下のデータ構造を保持している
|
345
|
+
- Store と呼ばれるglobal stateを表現した構造体
|
346
|
+
- 3つのスタック
|
347
|
+
- Call Stack
|
348
|
+
- Value Stack
|
349
|
+
- Label Stack
|
350
|
+
|
351
|
+
---
|
352
|
+
|
353
|
+
## Ruby で書けばこういう感じ
|
354
|
+
|
355
|
+
```ruby
|
356
|
+
class Store
|
357
|
+
attr_accessor :funcs #: Array[WasmFunction|ExternalFunction]
|
358
|
+
attr_accessor :modules #: Hash[Symbol, wasmModule]
|
359
|
+
attr_accessor :memories #: Array[Memory]
|
360
|
+
attr_accessor :globals #: Array[Global]
|
361
|
+
attr_accessor :tables #: Array[Table]
|
362
|
+
attr_accessor :elements #: Array[[Symbol, Integer, Array[Integer]]]
|
363
|
+
#...
|
364
|
+
```
|
365
|
+
|
366
|
+
```ruby
|
367
|
+
class Runtime
|
368
|
+
attr_accessor :store #: Store
|
369
|
+
attr_accessor :stack #: Array[wasmValue]
|
370
|
+
attr_accessor :call_stack #: Array[Frame]
|
371
|
+
attr_accessor :labels #: Array[Label]
|
372
|
+
#...
|
373
|
+
```
|
374
|
+
|
375
|
+
- 実際にはもっとごちゃついた関係になってる。整理したい...
|
376
|
+
|
377
|
+
---
|
378
|
+
|
379
|
+
## 命令はパースしてこういうクラスに
|
380
|
+
|
381
|
+
```ruby
|
382
|
+
class Op
|
383
|
+
attr_accessor :namespace #: Symbol -- :i32/:i64/...
|
384
|
+
attr_accessor :code #: Symbol
|
385
|
+
attr_accessor :operand #: Array[operandItem]
|
386
|
+
attr_accessor :meta #: Hash[Symbol, Integer]
|
387
|
+
end
|
388
|
+
```
|
389
|
+
|
390
|
+
---
|
391
|
+
|
392
|
+
## VMの命令実行部分
|
393
|
+
|
394
|
+
- フレームに現在実行しているコード自体とその位置の情報がある
|
395
|
+
- それを上からやっていく
|
396
|
+
|
397
|
+
```ruby
|
398
|
+
# @rbs return: void
|
399
|
+
def execute!
|
400
|
+
loop do
|
401
|
+
cur_frame = self.call_stack.last #: Frame
|
402
|
+
if !cur_frame
|
403
|
+
break
|
404
|
+
end
|
405
|
+
cur_frame.pc += 1
|
406
|
+
insn = cur_frame.body[cur_frame.pc]
|
407
|
+
if !insn
|
408
|
+
break
|
409
|
+
end
|
410
|
+
eval_insn(cur_frame, insn)
|
411
|
+
end
|
412
|
+
end
|
413
|
+
```
|
414
|
+
|
415
|
+
----
|
416
|
+
|
417
|
+
## VM名物でっかいcase文
|
418
|
+
|
419
|
+
```ruby
|
420
|
+
# @rbs frame: Frame
|
421
|
+
# @rbs insn: Op
|
422
|
+
# @rbs return: void
|
423
|
+
def eval_insn(frame, insn)
|
424
|
+
# unmached namespace...
|
425
|
+
case insn.code
|
426
|
+
when :unreachable
|
427
|
+
raise Unreachable, "unreachable op"
|
428
|
+
when :nop
|
429
|
+
return
|
430
|
+
when :br
|
431
|
+
level = insn.operand[0]
|
432
|
+
pc = do_branch(frame.labels, stack, level)
|
433
|
+
frame.pc = pc
|
434
|
+
#...
|
435
|
+
```
|
436
|
+
|
437
|
+
---
|
438
|
+
|
439
|
+
## ところで
|
440
|
+
|
441
|
+
- i32/i64,f32/f64で共通の処理が多い
|
442
|
+
- Generatorでまとめて作るようにした
|
443
|
+
|
444
|
+
```ruby
|
445
|
+
when :i32_add
|
446
|
+
right, left = runtime.stack.pop, runtime.stack.pop
|
447
|
+
if !right.is_a?(I32) || !left.is_a?(I32)
|
448
|
+
raise EvalError, "maybe empty or invalid stack"
|
449
|
+
end
|
450
|
+
runtime.stack.push(I32(left.value + right.value))
|
451
|
+
|
452
|
+
# ...
|
453
|
+
when :i64_add
|
454
|
+
right, left = runtime.stack.pop, runtime.stack.pop
|
455
|
+
if !right.is_a?(I64) || !left.is_a?(I64)
|
456
|
+
raise EvalError, "maybe empty or invalid stack"
|
457
|
+
end
|
458
|
+
runtime.stack.push(I64(left.value + right.value))
|
459
|
+
```
|
460
|
+
|
461
|
+
---
|
462
|
+
|
463
|
+
## こういうDSLで生成するようにした
|
464
|
+
|
465
|
+
```ruby
|
466
|
+
task :generate do
|
467
|
+
GenAlu.execute(libdir + "/wardite/alu_i32.generated.rb", prefix: "i32", defined_ops: [
|
468
|
+
:load,
|
469
|
+
:load8_s,
|
470
|
+
:load8_u,
|
471
|
+
:load16_s,
|
472
|
+
:load16_u,
|
473
|
+
:store,
|
474
|
+
:store8,
|
475
|
+
:store16,
|
476
|
+
:const,
|
477
|
+
:eqz,
|
478
|
+
:eq,
|
479
|
+
:ne,
|
480
|
+
:lts,
|
481
|
+
# ...
|
482
|
+
:rotr,
|
483
|
+
])
|
484
|
+
```
|
485
|
+
|
486
|
+
- あとは `rake generate`
|
487
|
+
|
488
|
+
---
|
489
|
+
|
490
|
+
## なぜコード生成?
|
491
|
+
|
492
|
+
- メタプログラミングは今回はやめとこかと思った
|
493
|
+
- 命令はすごい数そのパスを通るので、パフォーマンス面の心配
|
494
|
+
- RBS/rbs-inline と相性が悪そうな予感(杞憂かもだが)
|
495
|
+
|
496
|
+
<!-- GoとRustを結構書いてきたので、別に生成すれば良くね?って自然な気持ちで思ったのが大きいかな... -->
|
497
|
+
|
498
|
+
---
|
499
|
+
|
500
|
+
## 関数を呼ぶときの命令の解説
|
501
|
+
|
502
|
+
- 関数インスタンスを取り出してフレームをプッシュする
|
503
|
+
|
504
|
+
```ruby
|
505
|
+
when :call
|
506
|
+
idx = insn.operand[0]
|
507
|
+
fn = self.instance.store.funcs[idx]
|
508
|
+
case fn
|
509
|
+
when WasmFunction
|
510
|
+
push_frame(fn)
|
511
|
+
# ... call external
|
512
|
+
else
|
513
|
+
raise GenericError, "got a non-function pointer"
|
514
|
+
end
|
515
|
+
```
|
516
|
+
|
517
|
+
---
|
518
|
+
|
519
|
+
## フレームをプッシュするコード
|
520
|
+
|
521
|
+
- 初期変数(引数+コードで使う変数)をフレームに積む
|
522
|
+
- pcを -1 に初期化
|
523
|
+
- フレームをコールスタックに積む
|
524
|
+
|
525
|
+
```ruby
|
526
|
+
def push_frame(wasm_function)
|
527
|
+
local_start = stack.size - wasm_function.callsig.size
|
528
|
+
locals = stack[local_start..]
|
529
|
+
self.stack = drained_stack(local_start)
|
530
|
+
locals.concat(wasm_function.default_locals)
|
531
|
+
|
532
|
+
arity = wasm_function.retsig.size
|
533
|
+
frame = Frame.new(-1, stack.size, wasm_function.body, arity, locals)
|
534
|
+
self.call_stack.push(frame)
|
535
|
+
end
|
536
|
+
```
|
537
|
+
|
538
|
+
---
|
539
|
+
|
540
|
+
## returnする時
|
541
|
+
|
542
|
+
- stackを巻き戻します
|
543
|
+
|
544
|
+
```ruby
|
545
|
+
def stack_unwind(sp, arity)
|
546
|
+
if arity > 0
|
547
|
+
if arity > 1
|
548
|
+
raise ::NotImplementedError, "return artiy >= 2 not yet supported ;;"
|
549
|
+
end
|
550
|
+
value = stack.pop
|
551
|
+
self.stack = stack[0...sp]
|
552
|
+
stack.push value
|
553
|
+
else
|
554
|
+
self.stack = stack[0...sp]
|
555
|
+
end
|
556
|
+
end
|
557
|
+
```
|
558
|
+
|
559
|
+
---
|
560
|
+
|
561
|
+
# WASI
|
562
|
+
|
563
|
+
---
|
564
|
+
|
565
|
+
## > WASI(WebAssembly System Interface)とは何であるのかについて理解が深まる話をします
|
566
|
+
|
567
|
+
- ...やばい!時間がなくてあんまできない!
|
568
|
+
- と言ってもWASIはシンプルだと思う
|
569
|
+
- 基本的にはWASM Moduleにインポートする関数群
|
570
|
+
- Rubyのような言語でWASIに対応した関数を書く場合
|
571
|
+
- 本当にシンプルに、OS側のシステムコールに対応させるだけ
|
572
|
+
- 簡単とは言ってないよ!量が多いし!
|
573
|
+
- ちなみにpreview1/preview2というのがあるが
|
574
|
+
- p2はComponent向け。まず基本p1を実装中
|
575
|
+
|
576
|
+
---
|
577
|
+
|
578
|
+
## WASI (p1) 周り
|
579
|
+
|
580
|
+
- ただのimport moduleなのでまずその仕組みを作った
|
581
|
+
- あとはこういう関数を地道に実装するだけやで...
|
582
|
+
|
583
|
+
```ruby
|
584
|
+
class WasiSnapshotPreview1
|
585
|
+
# @rbs store: Store
|
586
|
+
# @rbs args: Array[wasmValue]
|
587
|
+
# @rbs return: Object
|
588
|
+
def random_get(store, args)
|
589
|
+
buf, buflen = args[0].value, args[1].value
|
590
|
+
randoms = SecureRandom.random_bytes(buflen) #: String
|
591
|
+
store.memories[0].data[buf...(buf+buflen)] = randoms
|
592
|
+
0
|
593
|
+
end
|
594
|
+
end
|
595
|
+
```
|
596
|
+
|
597
|
+
- ちなみに、hello worldだけなら `fd_write` のみでいける
|
598
|
+
|
599
|
+
---
|
600
|
+
|
601
|
+
# という感じで地道に実装中
|
602
|
+
|
603
|
+
---
|
604
|
+
|
605
|
+
# 他、話していないこと
|
606
|
+
|
607
|
+
- WASM specに対応したテスト実行の仕方
|
608
|
+
- [ブログに少し書いた](https://udzura.hatenablog.jp/entry/2024/11/24/210124)
|
609
|
+
- 超カバレッジ低い、i32系だけ。
|
610
|
+
- パフォーマンスチューニング
|
611
|
+
- [ブログ書いた](https://udzura.hatenablog.jp/entry/2024/12/20/173728)
|
612
|
+
|
613
|
+
---
|
614
|
+
|
615
|
+
# 今後の展望
|
616
|
+
|
617
|
+
- WASI、specカバレッジ、パフォチューを地道に
|
618
|
+
- `ruby.wasm` を動かしたいぞん
|
619
|
+
- ひとまずWASIサポート関数を増やすなど頑張りが必要
|
620
|
+
- 速度は... 我慢で対応(?)
|
621
|
+
- Component も読めるようにしたい
|
622
|
+
- 簡単なものならまあ...
|
623
|
+
- もちろん、松山でWarditeの話をしたいですね
|
624
|
+
- mruby/edgeか、どちらかをね。
|
data/sig/fcntl.rbs
ADDED
@@ -6,7 +6,7 @@ module Wardite
|
|
6
6
|
# @rbs buf: File|StringIO
|
7
7
|
# @rbs max_level: Integer
|
8
8
|
# @rbs return: Integer
|
9
|
-
def fetch_uleb128: (File | StringIO buf, ?max_level: Integer) -> Integer
|
9
|
+
def self?.fetch_uleb128: (File | StringIO buf, ?max_level: Integer) -> Integer
|
10
10
|
|
11
11
|
# @rbs buf: File|StringIO
|
12
12
|
# @rbs return: Integer
|
@@ -173,10 +173,10 @@ module Wardite
|
|
173
173
|
self.@buf: File | StringIO
|
174
174
|
|
175
175
|
# @rbs buf: File|StringIO
|
176
|
-
# @rbs import_object: Hash[Symbol,
|
176
|
+
# @rbs import_object: Hash[Symbol, wasmModuleSrc]
|
177
177
|
# @rbs enable_wasi: boolish
|
178
178
|
# @rbs return: Instance
|
179
|
-
def self.load_from_buffer: (File | StringIO buf, ?import_object: Hash[Symbol,
|
179
|
+
def self.load_from_buffer: (File | StringIO buf, ?import_object: Hash[Symbol, wasmModuleSrc], ?enable_wasi: boolish) -> Instance
|
180
180
|
|
181
181
|
# @rbs return: Integer
|
182
182
|
def self.preamble: () -> Integer
|
@@ -19,16 +19,6 @@ module Wardite
|
|
19
19
|
# @rbs value: Float
|
20
20
|
# @rbs return: F64
|
21
21
|
def F64: (Float value) -> F64
|
22
|
-
|
23
|
-
private
|
24
|
-
|
25
|
-
# @rbs value: Integer
|
26
|
-
# @rbs return: Integer
|
27
|
-
def as_u32: (Integer value) -> Integer
|
28
|
-
|
29
|
-
# @rbs value: Integer
|
30
|
-
# @rbs return: Integer
|
31
|
-
def as_u64: (Integer value) -> Integer
|
32
22
|
end
|
33
23
|
|
34
24
|
extend ValueHelper
|
@@ -42,6 +32,15 @@ module Wardite
|
|
42
32
|
# when we want to access signed value, it'd be done via #value_s
|
43
33
|
attr_accessor value: Integer
|
44
34
|
|
35
|
+
@@i32_object_pool: Hash[Integer, I32]
|
36
|
+
|
37
|
+
# @rbs value: Integer
|
38
|
+
# @rbs return: I32
|
39
|
+
def self.cached_or_initialize: (Integer value) -> I32
|
40
|
+
|
41
|
+
# @rbs value: Integer
|
42
|
+
def initialize: (?Integer value) -> untyped
|
43
|
+
|
45
44
|
# @rbs str: String
|
46
45
|
# @rbs size: Integer|nil
|
47
46
|
# @rbs signed: bool
|
@@ -126,6 +125,9 @@ module Wardite
|
|
126
125
|
|
127
126
|
attr_accessor value: Integer
|
128
127
|
|
128
|
+
# @rbs value: Integer
|
129
|
+
def initialize: (?Integer value) -> untyped
|
130
|
+
|
129
131
|
# @rbs str: String
|
130
132
|
# @rbs size: Integer|nil
|
131
133
|
# @rbs signed: bool
|