wardite 0.5.1 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- 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 +6 -22
- 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/proposals/rubykaigi2025.md +46 -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/load.rbs +2 -2
- data/sig/generated/wardite/value.rbs +3 -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 +15 -3
- 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
|
+
![w:800](./image.png)
|
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
|
+
![w:1000](./image-1.png)
|
330
|
+
|
331
|
+
---
|
332
|
+
|
333
|
+
## 頑張っていた過去の自分
|
334
|
+
|
335
|
+
- 楽しそうですね
|
336
|
+
|
337
|
+
![w:550](./image-2.png)
|
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
@@ -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,9 @@ 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
|
+
# @rbs value: Integer
|
36
|
+
def initialize: (?Integer value) -> untyped
|
37
|
+
|
45
38
|
# @rbs str: String
|
46
39
|
# @rbs size: Integer|nil
|
47
40
|
# @rbs signed: bool
|