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,53 @@
|
|
1
|
+
# rbs_inline: enabled
|
2
|
+
|
3
|
+
module Wardite
|
4
|
+
# @rbs!
|
5
|
+
# interface _WasmCallable
|
6
|
+
# def call: (Store, Array[wasmValue]) -> wasmFuncReturn
|
7
|
+
# def []: (Store, Array[wasmValue]) -> wasmFuncReturn
|
8
|
+
# end
|
9
|
+
|
10
|
+
# @rbs!
|
11
|
+
# type wasmModuleSrc = Hash[Symbol, _WasmCallable] | WasmModule | HashModule
|
12
|
+
# type wasmModule = WasmModule | HashModule
|
13
|
+
|
14
|
+
module WasmModule
|
15
|
+
# @rbs fnname: Symbol
|
16
|
+
# @rbs store: Store
|
17
|
+
# @rbs args: Array[wasmValue]
|
18
|
+
# @rbs return: wasmFuncReturn
|
19
|
+
def invoke(fnname, store, *args)
|
20
|
+
self.__send__(fnname, store, args)
|
21
|
+
end
|
22
|
+
|
23
|
+
# @rbs fnname: Symbol
|
24
|
+
# @rbs return: _WasmCallable
|
25
|
+
def callable(fnname)
|
26
|
+
self.method(fnname)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class HashModule
|
31
|
+
attr_accessor :hash #: Hash[Symbol, _WasmCallable]
|
32
|
+
|
33
|
+
# @rbs ha: Hash[Symbol, _WasmCallable]
|
34
|
+
def initialize(hash)
|
35
|
+
@hash = hash
|
36
|
+
end
|
37
|
+
|
38
|
+
# @rbs fnname: Symbol
|
39
|
+
# @rbs store: Store
|
40
|
+
# @rbs args: Array[wasmValue]
|
41
|
+
# @rbs return: wasmFuncReturn
|
42
|
+
def invoke(fnname, store, *args)
|
43
|
+
fn = self.hash[fnname.to_sym]
|
44
|
+
fn.call(store, args)
|
45
|
+
end
|
46
|
+
|
47
|
+
# @rbs fnname: Symbol
|
48
|
+
# @rbs return: _WasmCallable
|
49
|
+
def callable(fnname)
|
50
|
+
self.hash[fnname.to_sym]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/wardite.rb
CHANGED
@@ -20,8 +20,6 @@ require_relative "wardite/alu_f32.generated"
|
|
20
20
|
require_relative "wardite/alu_f64.generated"
|
21
21
|
require_relative "wardite/convert.generated"
|
22
22
|
|
23
|
-
require_relative "wardite/wasi"
|
24
|
-
|
25
23
|
require "stringio"
|
26
24
|
|
27
25
|
module Wardite
|
@@ -38,13 +36,23 @@ module Wardite
|
|
38
36
|
|
39
37
|
attr_accessor :exports #: Exports
|
40
38
|
|
41
|
-
attr_reader :import_object #: Hash[Symbol,
|
39
|
+
attr_reader :import_object #: Hash[Symbol, wasmModule]
|
40
|
+
|
41
|
+
attr_accessor :wasi #: WasiSnapshotPreview1?
|
42
42
|
|
43
|
-
# @rbs import_object: Hash[Symbol,
|
43
|
+
# @rbs import_object: Hash[Symbol, wasmModuleSrc]
|
44
44
|
# @rbs &blk: (Instance) -> void
|
45
45
|
def initialize(import_object, &blk)
|
46
|
+
@wasi = nil
|
47
|
+
|
46
48
|
blk.call(self)
|
47
|
-
|
49
|
+
import_object.each_pair do |k, v|
|
50
|
+
if v.is_a?(Hash)
|
51
|
+
import_object[k] = HashModule.new(v)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
@import_object = import_object #: Hash[Symbol, wasmModule]
|
48
56
|
|
49
57
|
@store = Store.new(self)
|
50
58
|
@exports = Exports.new(self.export_section, store)
|
@@ -281,7 +289,8 @@ module Wardite
|
|
281
289
|
|
282
290
|
case fn
|
283
291
|
when WasmFunction
|
284
|
-
invoke_internal(fn)
|
292
|
+
r = invoke_internal(fn)
|
293
|
+
r
|
285
294
|
when ExternalFunction
|
286
295
|
invoke_external(fn)
|
287
296
|
else
|
@@ -313,25 +322,7 @@ module Wardite
|
|
313
322
|
raise LoadError, "stack too short"
|
314
323
|
end
|
315
324
|
self.stack = drained_stack(local_start)
|
316
|
-
|
317
|
-
wasm_function.locals_count.each_with_index do |count, i|
|
318
|
-
typ = wasm_function.locals_type[i]
|
319
|
-
count.times do
|
320
|
-
case typ
|
321
|
-
when :i32, :u32
|
322
|
-
locals.push I32(0)
|
323
|
-
when :i64, :u64
|
324
|
-
locals.push I64(0)
|
325
|
-
when :f32
|
326
|
-
locals.push F32(0.0)
|
327
|
-
when :f64
|
328
|
-
locals.push F64(0.0)
|
329
|
-
else
|
330
|
-
$stderr.puts "warning: unknown type #{typ.inspect}. default to I32"
|
331
|
-
locals.push I32(0)
|
332
|
-
end
|
333
|
-
end
|
334
|
-
end
|
325
|
+
locals.concat(wasm_function.default_locals)
|
335
326
|
|
336
327
|
arity = wasm_function.retsig.size
|
337
328
|
frame = Frame.new(-1, stack.size, wasm_function.body, arity, locals)
|
@@ -888,7 +879,7 @@ module Wardite
|
|
888
879
|
class Store
|
889
880
|
attr_accessor :funcs #: Array[WasmFunction|ExternalFunction]
|
890
881
|
|
891
|
-
|
882
|
+
attr_accessor :modules #: Hash[Symbol, wasmModule]
|
892
883
|
|
893
884
|
attr_accessor :memories #: Array[Memory]
|
894
885
|
|
@@ -907,21 +898,19 @@ module Wardite
|
|
907
898
|
|
908
899
|
import_section = inst.import_section
|
909
900
|
@funcs = []
|
901
|
+
@modules = inst.import_object
|
910
902
|
|
911
903
|
if type_section && func_section && code_section
|
912
904
|
import_section.imports.each do |desc|
|
913
905
|
callsig = type_section.defined_types[desc.sig_index]
|
914
906
|
retsig = type_section.defined_results[desc.sig_index]
|
915
|
-
|
916
|
-
if
|
907
|
+
target_module = self.modules[desc.module_name.to_sym]
|
908
|
+
if target_module.nil?
|
917
909
|
raise ::NameError, "module #{desc.module_name} not found"
|
918
910
|
end
|
919
|
-
|
920
|
-
if !imported_proc
|
921
|
-
raise ::NameError, "function #{desc.module_name}.#{desc.name} not found"
|
922
|
-
end
|
911
|
+
target_name = desc.name.to_sym
|
923
912
|
|
924
|
-
ext_function = ExternalFunction.new(callsig, retsig
|
913
|
+
ext_function = ExternalFunction.new(target_module, target_name, callsig, retsig)
|
925
914
|
self.funcs << ext_function
|
926
915
|
end
|
927
916
|
|
@@ -1213,7 +1202,10 @@ module Wardite
|
|
1213
1202
|
end
|
1214
1203
|
|
1215
1204
|
# TODO: common interface btw. WasmFunction and ExternalFunction?
|
1205
|
+
# may be _WasmCallable?
|
1216
1206
|
class WasmFunction
|
1207
|
+
include ValueHelper
|
1208
|
+
|
1217
1209
|
attr_accessor :callsig #: Array[Symbol]
|
1218
1210
|
|
1219
1211
|
attr_accessor :retsig #: Array[Symbol]
|
@@ -1222,6 +1214,8 @@ module Wardite
|
|
1222
1214
|
|
1223
1215
|
attr_accessor :findex #: Integer
|
1224
1216
|
|
1217
|
+
attr_accessor :default_locals #: Array[wasmValue]
|
1218
|
+
|
1225
1219
|
# @rbs callsig: Array[Symbol]
|
1226
1220
|
# @rbs retsig: Array[Symbol]
|
1227
1221
|
# @rbs code_body: CodeSection::CodeBody
|
@@ -1232,6 +1226,7 @@ module Wardite
|
|
1232
1226
|
|
1233
1227
|
@code_body = code_body
|
1234
1228
|
@findex = 0 # for debug
|
1229
|
+
@default_locals = construct_default_locals
|
1235
1230
|
end
|
1236
1231
|
|
1237
1232
|
# @rbs return: Array[Op]
|
@@ -1249,6 +1244,30 @@ module Wardite
|
|
1249
1244
|
code_body.locals_count
|
1250
1245
|
end
|
1251
1246
|
|
1247
|
+
# @rbs return: Array[wasmValue]
|
1248
|
+
def construct_default_locals
|
1249
|
+
locals = [] #: Array[wasmValue]
|
1250
|
+
locals_count.each_with_index do |count, i|
|
1251
|
+
typ = locals_type[i]
|
1252
|
+
count.times do
|
1253
|
+
case typ
|
1254
|
+
when :i32, :u32
|
1255
|
+
locals.push I32(0)
|
1256
|
+
when :i64, :u64
|
1257
|
+
locals.push I64(0)
|
1258
|
+
when :f32
|
1259
|
+
locals.push F32(0.0)
|
1260
|
+
when :f64
|
1261
|
+
locals.push F64(0.0)
|
1262
|
+
else
|
1263
|
+
$stderr.puts "warning: unknown type #{typ.inspect}. default to I32"
|
1264
|
+
locals.push I32(0)
|
1265
|
+
end
|
1266
|
+
end
|
1267
|
+
end
|
1268
|
+
locals
|
1269
|
+
end
|
1270
|
+
|
1252
1271
|
# @rbs override_type: Type?
|
1253
1272
|
# @rbs return: WasmFunction
|
1254
1273
|
def clone(override_type: nil)
|
@@ -1263,23 +1282,32 @@ module Wardite
|
|
1263
1282
|
|
1264
1283
|
# @rbs!
|
1265
1284
|
# type wasmFuncReturn = Object|nil
|
1266
|
-
# type wasmCallable = ^(Store, Array[wasmValue]) -> wasmFuncReturn
|
1267
1285
|
|
1268
1286
|
class ExternalFunction
|
1287
|
+
attr_accessor :target_module #: wasmModule
|
1288
|
+
|
1289
|
+
attr_accessor :name #: Symbol
|
1290
|
+
|
1269
1291
|
attr_accessor :callsig #: Array[Symbol]
|
1270
1292
|
|
1271
1293
|
attr_accessor :retsig #: Array[Symbol]
|
1272
1294
|
|
1273
|
-
attr_accessor :callable #:
|
1295
|
+
#attr_accessor :callable #: _WasmCallable
|
1274
1296
|
|
1275
1297
|
# @rbs callsig: Array[Symbol]
|
1276
1298
|
# @rbs retsig: Array[Symbol]
|
1277
|
-
# @rbs callable:
|
1299
|
+
# @rbs callable: _WasmCallable
|
1278
1300
|
# @rbs return: void
|
1279
|
-
def initialize(callsig, retsig
|
1301
|
+
def initialize(target_module, name, callsig, retsig)
|
1302
|
+
@target_module = target_module
|
1303
|
+
@name = name
|
1280
1304
|
@callsig = callsig
|
1281
1305
|
@retsig = retsig
|
1282
|
-
|
1306
|
+
end
|
1307
|
+
|
1308
|
+
# @rbs return: _WasmCallable
|
1309
|
+
def callable()
|
1310
|
+
target_module.callable(self.name)
|
1283
1311
|
end
|
1284
1312
|
|
1285
1313
|
# @rbs override_type: Type?
|
@@ -1287,9 +1315,9 @@ module Wardite
|
|
1287
1315
|
def clone(override_type: nil)
|
1288
1316
|
if override_type
|
1289
1317
|
# callable is assumed to be frozen, so we can copy its ref
|
1290
|
-
ExternalFunction.new(override_type.callsig, override_type.retsig
|
1318
|
+
ExternalFunction.new(target_module, name, override_type.callsig, override_type.retsig)
|
1291
1319
|
else
|
1292
|
-
ExternalFunction.new(callsig, retsig
|
1320
|
+
ExternalFunction.new(target_module, name, callsig, retsig)
|
1293
1321
|
end
|
1294
1322
|
end
|
1295
1323
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# RubyKaigi 2025 Proposal
|
2
|
+
|
3
|
+
## title:
|
4
|
+
|
5
|
+
Implementing WASM Runtime in Ruby - ruby.wasm works on Ruby?
|
6
|
+
|
7
|
+
## Abstract
|
8
|
+
|
9
|
+
<!--
|
10
|
+
|
11
|
+
Create an abstract of talk in English, following below:
|
12
|
+
|
13
|
+
- 作者はWarditeというRubyのWASMランタイムを開発した
|
14
|
+
- WarditeはPure Rubyで、RBSに完全対応している
|
15
|
+
- Warditeでruby.wasmを動かすことをマイルストーンにしており、その取り組みについて報告する。
|
16
|
+
- 例えば、WASI preview 1 対応、パフォーマンス改善など。
|
17
|
+
|
18
|
+
-->
|
19
|
+
|
20
|
+
The author has developed a WASM runtime named Wardite, which is implemented entirely in pure Ruby and fully annotated by RBS. The primary milestone for Wardite is to successfully run ruby.wasm. This presentation will dive deeply into the various efforts and challenges encountered in reaching this milestone. Key topics will include the implementation of support for WASI preview 1, performance enhancements, and other technical advancements. Attendees will gain insights into the current status of Wardite, its architecture, and the approaches taken to eficciently implement WebAssembly runtime in Ruby. The talk aims to provide a comprehensive overview of the progress made so far and the future directions for Wardite, highlighting its potential impact on the Ruby and WebAssembly ecosystems.
|
21
|
+
|
22
|
+
## Details
|
23
|
+
|
24
|
+
現在、以下のような内容を考えています
|
25
|
+
|
26
|
+
- なぜ、Warditeを作ったか?
|
27
|
+
- Pure Rubyであることのメリット
|
28
|
+
- cf. wazero in Go
|
29
|
+
- 簡単なWarditeの紹介
|
30
|
+
- 動作のデモ
|
31
|
+
- Warditeの実装
|
32
|
+
- WASM Core 1.0を動かすために必要な仕様の解説
|
33
|
+
- 簡単な内部設計
|
34
|
+
- 命令の概要
|
35
|
+
- RBSによる型情報の利用
|
36
|
+
- Wardite開発上の技術的チャレンジ
|
37
|
+
- パフォーマンス改善の取り組み
|
38
|
+
- 基本的な計測(ruby-prof、perf)
|
39
|
+
- オブジェクト生成の低減・最適化
|
40
|
+
- Warditeでruby.wasmを動かすための取り組み
|
41
|
+
- WASI preview 1 対応
|
42
|
+
- 今後の展望
|
43
|
+
- 更なるパフォーマンス改善
|
44
|
+
- Component 対応
|
45
|
+
|
46
|
+
## Pitch
|
data/misc/slides/fib.c
ADDED
Binary file
|
Binary file
|
Binary file
|