wardite 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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, Hash[Symbol, wasmCallable]]
39
+ attr_reader :import_object #: Hash[Symbol, wasmModule]
40
+
41
+ attr_accessor :wasi #: WasiSnapshotPreview1?
42
42
 
43
- # @rbs import_object: Hash[Symbol, Hash[Symbol, wasmCallable]]
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
- @import_object = import_object
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
- # FIXME: attr_accessor :modules
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
- imported_module = inst.import_object[desc.module_name.to_sym]
916
- if !imported_module
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
- imported_proc = imported_module[desc.name.to_sym]
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, imported_proc)
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 #: wasmCallable
1295
+ #attr_accessor :callable #: _WasmCallable
1274
1296
 
1275
1297
  # @rbs callsig: Array[Symbol]
1276
1298
  # @rbs retsig: Array[Symbol]
1277
- # @rbs callable: wasmCallable
1299
+ # @rbs callable: _WasmCallable
1278
1300
  # @rbs return: void
1279
- def initialize(callsig, retsig, callable)
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
- @callable = callable
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, callable)
1318
+ ExternalFunction.new(target_module, name, override_type.callsig, override_type.retsig)
1291
1319
  else
1292
- ExternalFunction.new(callsig, retsig, callable)
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
@@ -0,0 +1,4 @@
1
+ int fib(int n) {
2
+ if (n <= 1) return n;
3
+ return fib(n - 1) + fib(n - 2);
4
+ }
@@ -0,0 +1,13 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <script>
5
+ WebAssembly.instantiateStreaming(fetch('./fib.wasm'))
6
+ .then(obj => {
7
+ alert(`fib(20) = ${obj.instance.exports.fib(20)}`);
8
+ });
9
+ </script>
10
+ </head>
11
+ <body>
12
+ </body>
13
+ </html>
Binary file
Binary file
Binary file