rlang 0.3.0
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 +7 -0
- data/.gitignore +10 -0
- data/.rake_tasks~ +0 -0
- data/.travis.yml +7 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/LICENSE +373 -0
- data/README.md +61 -0
- data/Rakefile +10 -0
- data/bin/rlang +164 -0
- data/docs/RlangCompiler.md +37 -0
- data/docs/RlangManual.md +391 -0
- data/lib/builder/ext/tempfile.rb +7 -0
- data/lib/builder/ext.rb +5 -0
- data/lib/builder/rlang/builder.rb +31 -0
- data/lib/builder/rlang.rb +2 -0
- data/lib/builder/wat/builder.rb +52 -0
- data/lib/builder/wat/renderer.rb +28 -0
- data/lib/builder/wat.rb +3 -0
- data/lib/builder.rb +7 -0
- data/lib/rlang/lib/malloc.c +97 -0
- data/lib/rlang/lib/malloc.rb +169 -0
- data/lib/rlang/lib/memory.rb +11 -0
- data/lib/rlang/lib/type/i32.rb +7 -0
- data/lib/rlang/lib/type/i64.rb +7 -0
- data/lib/rlang/lib/type.rb +6 -0
- data/lib/rlang/lib/unistd.rb +47 -0
- data/lib/rlang/lib.rb +10 -0
- data/lib/rlang/parser/const.rb +15 -0
- data/lib/rlang/parser/cvar.rb +44 -0
- data/lib/rlang/parser/data.rb +105 -0
- data/lib/rlang/parser/export.rb +22 -0
- data/lib/rlang/parser/ext/integer.rb +5 -0
- data/lib/rlang/parser/ext/string.rb +5 -0
- data/lib/rlang/parser/ext/type.rb +64 -0
- data/lib/rlang/parser/global.rb +65 -0
- data/lib/rlang/parser/lvar.rb +29 -0
- data/lib/rlang/parser/marg.rb +30 -0
- data/lib/rlang/parser/method.rb +76 -0
- data/lib/rlang/parser/wattr.rb +65 -0
- data/lib/rlang/parser/wgenerator.rb +509 -0
- data/lib/rlang/parser/winstruction.rb +148 -0
- data/lib/rlang/parser/wnode.rb +455 -0
- data/lib/rlang/parser/wtree.rb +19 -0
- data/lib/rlang/parser/wtype.rb +116 -0
- data/lib/rlang/parser.rb +1842 -0
- data/lib/rlang/version.rb +3 -0
- data/lib/rlang.rb +4 -0
- data/lib/simul/classes/data.rb +80 -0
- data/lib/simul/classes/global.rb +38 -0
- data/lib/simul/classes/memory.rb +131 -0
- data/lib/utils/log.rb +32 -0
- data/rlang.gemspec +38 -0
- metadata +158 -0
@@ -0,0 +1,509 @@
|
|
1
|
+
# Rubinius WebAssembly VM
|
2
|
+
# Copyright (c) 2019, Laurent Julliard and contributors
|
3
|
+
# All rights reserved.
|
4
|
+
|
5
|
+
# WAT generator for Rlang
|
6
|
+
# Rlang is a subset of the Ruby language that can be transpiled
|
7
|
+
# to WAT and then compiled to WASM. The Rubinius WASM virtual
|
8
|
+
# machine is written in Rlang.
|
9
|
+
|
10
|
+
# TODO: write a short documentation about what subset of Ruby is
|
11
|
+
# supported in Rlang
|
12
|
+
|
13
|
+
require_relative '../../utils/log'
|
14
|
+
require_relative './wnode'
|
15
|
+
|
16
|
+
module Rlang::Parser
|
17
|
+
|
18
|
+
ARITHMETIC_OPS_MAP = {
|
19
|
+
:+ => :add,
|
20
|
+
:- => :sub,
|
21
|
+
:* => :mul,
|
22
|
+
:/ => :div_u,
|
23
|
+
:% => :rem_u,
|
24
|
+
:& => :and,
|
25
|
+
:| => :or,
|
26
|
+
:^ => :xor,
|
27
|
+
:>> => :shr_u,
|
28
|
+
:<< => :shl
|
29
|
+
}
|
30
|
+
|
31
|
+
RELATIONAL_OPS_MAP = {
|
32
|
+
:== => :eq,
|
33
|
+
:!= => :ne,
|
34
|
+
:'<s' => :lt_s,
|
35
|
+
:< => :lt_u,
|
36
|
+
:'>s' => :gt_s,
|
37
|
+
:> => :gt_u,
|
38
|
+
:'<=s' => :le_s,
|
39
|
+
:<= => :le_u,
|
40
|
+
:'>=s' => :ge_s,
|
41
|
+
:>= => :ge_u
|
42
|
+
}
|
43
|
+
|
44
|
+
BOOLEAN_OPS_MAP = {
|
45
|
+
:and => :and,
|
46
|
+
:or => :or
|
47
|
+
}
|
48
|
+
|
49
|
+
UNARY_OPS_MAP = {
|
50
|
+
:'!' => :eqz
|
51
|
+
}
|
52
|
+
|
53
|
+
# Matrix of how to cast a WASM type to another
|
54
|
+
CAST_OPS = {
|
55
|
+
I32: { I32: :cast_nope, I64: :cast_extend, F32: :cast_notyet, F64: :cast_notyet, Class: :cast_wtype, none: :cast_error},
|
56
|
+
I64: { I32: :cast_wrap, I64: :cast_nope, F32: :cast_notyet, F64: :cast_notyet, Class: :cast_error, none: :cast_error},
|
57
|
+
F32: { I32: :cast_notyet, I64: :cast_notyet, F32: :cast_nope, F64: :cast_notyet, Class: :cast_error, none: :cast_error},
|
58
|
+
F64: { I32: :cast_notyet, I64: :cast_notyet, F32: :cast_notyet, F64: :cast_nope, Class: :cast_error, none: :cast_error},
|
59
|
+
Class: { I32: :cast_wtype, I64: :cast_extend, F32: :cast_error, F64: :cast_error, Class: :cast_wtype, none: :cast_error},
|
60
|
+
none: { I32: :cast_error, I64: :cast_error, F32: :cast_error, F64: :cast_error, Class: :cast_error, none: :cast_error},
|
61
|
+
}
|
62
|
+
|
63
|
+
# Generate the wasm nodes and tree structure
|
64
|
+
# ***IMPORTANT NOTE***
|
65
|
+
# Unless otherwise stated all methods receive
|
66
|
+
# the parent wnode as their first argument
|
67
|
+
# and must generate child nodes of this parent
|
68
|
+
class WGenerator
|
69
|
+
include Log
|
70
|
+
attr_accessor :parser
|
71
|
+
attr_reader :root
|
72
|
+
|
73
|
+
def initialize(parser)
|
74
|
+
@parser = parser
|
75
|
+
@root = WTree.new().root
|
76
|
+
@new_count = 0
|
77
|
+
end
|
78
|
+
|
79
|
+
def klass(wnode, klass_name)
|
80
|
+
# First see if that class already exist
|
81
|
+
# If not create it.
|
82
|
+
unless (cwn = wnode.find_class(klass_name))
|
83
|
+
cwn = WNode.new(:class, wnode)
|
84
|
+
cwn.class_name = klass_name
|
85
|
+
cwn.wtype = WType.new(cwn.class_name)
|
86
|
+
WNode.root.class_wnodes << cwn
|
87
|
+
end
|
88
|
+
cwn
|
89
|
+
end
|
90
|
+
|
91
|
+
def attributes(wnode)
|
92
|
+
wnc = wnode.class_wnode
|
93
|
+
return if wnc.wattrs.empty?
|
94
|
+
# Process each declared attribute
|
95
|
+
offset = 0
|
96
|
+
wnc.wattrs.each do |wa|
|
97
|
+
logger.debug("Generating accessors for attribute #{wa}")
|
98
|
+
# Generate getter and setter methods wnode
|
99
|
+
wattr_setter(wnc, wa, offset)
|
100
|
+
wattr_getter(wnc, wa, offset)
|
101
|
+
# Update offset
|
102
|
+
offset += wa.wtype.size
|
103
|
+
end
|
104
|
+
|
105
|
+
# Also generate the Class::size method
|
106
|
+
size_method = wnc.create_method(:size, nil, WType::DEFAULT, :class)
|
107
|
+
wns = WNode.new(:insn, wnc, true)
|
108
|
+
wns.wtype = WType::DEFAULT
|
109
|
+
wns.c(:class_size, func_name: size_method.wasm_name,
|
110
|
+
wtype: wns.wasm_type, size: wnc.class_size)
|
111
|
+
end
|
112
|
+
|
113
|
+
# Generate attribute setter method wnode
|
114
|
+
def wattr_setter(wnode, wattr, offset)
|
115
|
+
wnc = wnode.class_wnode
|
116
|
+
wn_set = WNode.new(:insn, wnc, true)
|
117
|
+
wn_set.c(:wattr_writer, func_name: wattr.setter.wasm_name,
|
118
|
+
wattr_name: wattr.wasm_name, wtype: wattr.wasm_type,
|
119
|
+
offset: offset)
|
120
|
+
wn_set
|
121
|
+
end
|
122
|
+
|
123
|
+
# Generate attribute getter method wnode
|
124
|
+
def wattr_getter(wnode, wattr, offset)
|
125
|
+
wnc = wnode.class_wnode
|
126
|
+
wn_get = WNode.new(:insn, wnc, true)
|
127
|
+
wn_get.c(:wattr_reader, func_name: wattr.getter.wasm_name,
|
128
|
+
wattr_name: wattr.wasm_name, wtype: wattr.wasm_type,
|
129
|
+
offset: offset)
|
130
|
+
wn_get
|
131
|
+
end
|
132
|
+
|
133
|
+
def instance_method(wnode, method)
|
134
|
+
logger.debug("Generating wnode for instance method #{method.inspect}")
|
135
|
+
wn = WNode.new(:method, wnode)
|
136
|
+
wn.method = method # must be set before calling func_name
|
137
|
+
wn.wtype = method.wtype
|
138
|
+
wn.c(:func, func_name: wn.method.wasm_name)
|
139
|
+
# Also declare a "hidden" parameter representing the
|
140
|
+
# pointer to the instance (always default wtype)
|
141
|
+
wn.create_marg(:_self_)
|
142
|
+
logger.debug("MEthod built: #{wn.method.inspect}")
|
143
|
+
wn
|
144
|
+
end
|
145
|
+
|
146
|
+
def class_method(wnode, method)
|
147
|
+
logger.debug("Generating wnode for class method #{method}")
|
148
|
+
wn = WNode.new(:method, wnode)
|
149
|
+
wn.method = method # must be set before calling func_name
|
150
|
+
wn.wtype = method.wtype
|
151
|
+
wn.c(:func, func_name: wn.method.wasm_name)
|
152
|
+
logger.debug("Building method: wn.wtype #{wn.wtype}, wn.method #{wn.method}")
|
153
|
+
wn
|
154
|
+
end
|
155
|
+
|
156
|
+
def params(wnode)
|
157
|
+
wnode = wnode.method_wnode
|
158
|
+
# use reverse to preserve proper param order
|
159
|
+
wnode.margs.reverse.each do |marg|
|
160
|
+
logger.debug("Prepending param #{marg}")
|
161
|
+
wn = WNode.new(:insn, wnode, true)
|
162
|
+
wn.wtype = marg.wtype
|
163
|
+
wn.c(:param, name: marg.wasm_name)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def result(wnode)
|
168
|
+
unless wnode.wtype.blank?
|
169
|
+
wn = WNode.new(:insn, wnode, true)
|
170
|
+
wn.wtype = wnode.wtype
|
171
|
+
wn.c(:result)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def locals(wnode)
|
176
|
+
wnode = wnode.method_wnode
|
177
|
+
wnode.lvars.reverse.each do |lvar|
|
178
|
+
logger.debug("Prepending local #{lvar.inspect}")
|
179
|
+
wn = WNode.new(:insn, wnode, true)
|
180
|
+
wn.wtype = lvar.wtype
|
181
|
+
wn.c(:local, name: lvar.wasm_name)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def inline(wnode, code, wtype=Type::I32)
|
186
|
+
wn = WNode.new(:insn, wnode)
|
187
|
+
wn.wtype = wnode.wtype
|
188
|
+
wn.c(:inline, code: code)
|
189
|
+
wn
|
190
|
+
end
|
191
|
+
|
192
|
+
# Constant assignment doesn't generate any code
|
193
|
+
# A Data object is instantiated and initialized
|
194
|
+
# when the Const object is created in parser
|
195
|
+
def casgn(wnode, const)
|
196
|
+
end
|
197
|
+
|
198
|
+
# Read class variable
|
199
|
+
def const(wnode, const)
|
200
|
+
(wn = WNode.new(:insn, wnode)).wtype = const.wtype
|
201
|
+
wn.c(:load, wtype: const.wtype, var_name: const.wasm_name)
|
202
|
+
WNode.new(:insn, wn).c(:addr, value: const.address)
|
203
|
+
wn
|
204
|
+
end
|
205
|
+
|
206
|
+
# Global variable assignment
|
207
|
+
def gvasgn(wnode, gvar)
|
208
|
+
(wn = WNode.new(:insn, wnode)).wtype = gvar.wtype
|
209
|
+
wn.c(:global_set, var_name: gvar.name)
|
210
|
+
wn
|
211
|
+
end
|
212
|
+
|
213
|
+
# Global variable read
|
214
|
+
def gvar(wnode, gvar)
|
215
|
+
(wn = WNode.new(:insn, wnode)).wtype = gvar.wtype
|
216
|
+
wn.c(:global_get, var_name: gvar.name)
|
217
|
+
wn
|
218
|
+
end
|
219
|
+
|
220
|
+
# Create the class variable storage node and
|
221
|
+
# an empty expression node to populate later
|
222
|
+
def cvasgn(wnode, cvar)
|
223
|
+
(wn = WNode.new(:insn, wnode)).wtype = cvar.wtype
|
224
|
+
wn.c(:store, wtype: cvar.wtype)
|
225
|
+
WNode.new(:insn, wn).c(:addr, value: cvar.address)
|
226
|
+
wn
|
227
|
+
end
|
228
|
+
|
229
|
+
# Read class variable
|
230
|
+
def cvar(wnode, cvar)
|
231
|
+
(wn = WNode.new(:insn, wnode)).wtype = cvar.wtype
|
232
|
+
wn.c(:load, wtype: cvar.wtype, var_name: cvar.wasm_name)
|
233
|
+
WNode.new(:insn, wn).c(:addr, value: cvar.address)
|
234
|
+
wn
|
235
|
+
end
|
236
|
+
|
237
|
+
# Create the local variable storage node
|
238
|
+
def lvasgn(wnode, lvar)
|
239
|
+
(wn = WNode.new(:insn, wnode)).wtype = lvar.wtype
|
240
|
+
wn.c(:local_set, wtype: lvar.wtype, var_name: lvar.wasm_name)
|
241
|
+
wn
|
242
|
+
end
|
243
|
+
|
244
|
+
# Read local variable
|
245
|
+
def lvar(wnode, lvar)
|
246
|
+
(wn = WNode.new(:insn, wnode)).wtype = lvar.wtype
|
247
|
+
wn.c(:local_get, wtype: lvar.wtype, var_name: lvar.wasm_name)
|
248
|
+
wn
|
249
|
+
end
|
250
|
+
|
251
|
+
def drop(wnode)
|
252
|
+
logger.debug "dropping result of #{wnode}, caller: #{caller_locations}"
|
253
|
+
(wn = WNode.new(:insn, wnode)).c(:drop)
|
254
|
+
wn
|
255
|
+
end
|
256
|
+
|
257
|
+
def nop(wnode)
|
258
|
+
(wn = WNode.new(:insn, wnode)).c(:nop)
|
259
|
+
wn
|
260
|
+
end
|
261
|
+
|
262
|
+
def int(wnode, wtype, value)
|
263
|
+
(wn = WNode.new(:insn, wnode)).wtype = wtype
|
264
|
+
wn.c(:const, wtype: wtype, value: value)
|
265
|
+
wn
|
266
|
+
end
|
267
|
+
|
268
|
+
def float(wnode, wtype, value)
|
269
|
+
(wn = WNode.new(:insn, wnode)).wtype = wtype
|
270
|
+
wn.c(:const, wtype: wtype, value: value)
|
271
|
+
wn
|
272
|
+
end
|
273
|
+
|
274
|
+
# All the cast_xxxx methods below returns
|
275
|
+
# the new wnode doing the cast operation
|
276
|
+
# or the same wnode if there is no additional code
|
277
|
+
# for the cast operation
|
278
|
+
def cast_nope(wnode, wtype, signed)
|
279
|
+
# Do nothing
|
280
|
+
wnode
|
281
|
+
end
|
282
|
+
|
283
|
+
def cast_extend(wnode, wtype, signed)
|
284
|
+
if (wnode.template == :const)
|
285
|
+
# it's a WASM const, simply change the wtype
|
286
|
+
wnode.wtype = wtype
|
287
|
+
wn_cast_op = wnode
|
288
|
+
else
|
289
|
+
wn_cast_op = wnode.insert(:insn)
|
290
|
+
wn_cast_op.wtype = wtype
|
291
|
+
wn_cast_op.c(signed ? :extend_i32_s : :extend_i32_u , wtype: wtype)
|
292
|
+
end
|
293
|
+
wn_cast_op
|
294
|
+
end
|
295
|
+
|
296
|
+
def cast_wtype(wnode, wtype, signed)
|
297
|
+
if (wnode.wtype.default? && wtype.class?) ||
|
298
|
+
(wnode.wtype.class? && wtype.default?) ||
|
299
|
+
(wnode.wtype.class? && wtype.class?)
|
300
|
+
wnode.wtype = wtype
|
301
|
+
else
|
302
|
+
cast_error(wnode, wtype, signed)
|
303
|
+
end
|
304
|
+
wnode
|
305
|
+
end
|
306
|
+
|
307
|
+
def cast_wrap(wnode, wtype, signed)
|
308
|
+
if (wnode.template == :const)
|
309
|
+
# it's a WASM const, simply change the wtype
|
310
|
+
wnode.wtype = wtype
|
311
|
+
wn_cast_op = wnode
|
312
|
+
else
|
313
|
+
wn_cast_op = wnode.insert(:insn)
|
314
|
+
wn_cast_op.wtype = wtype
|
315
|
+
wn_cast_op.c(:wrap_i64, wtype: wtype)
|
316
|
+
end
|
317
|
+
wn_cast_op
|
318
|
+
end
|
319
|
+
|
320
|
+
def cast_notyet(wnode, wtype, signed)
|
321
|
+
raise "Type cast from #{wnode.wtype} to #{wtype} not supported yet"
|
322
|
+
end
|
323
|
+
|
324
|
+
def cast_error(wnode, wtype, signed)
|
325
|
+
raise "Cannot cast type #{src} to #{dest}. Time to fix your code :-)"
|
326
|
+
end
|
327
|
+
|
328
|
+
# cast an expression to a different type
|
329
|
+
# if same type do nothing
|
330
|
+
# - wnode: the wnode to type cast
|
331
|
+
# - wtype: the wtype to cast wnode to
|
332
|
+
# - signed: whether the cast wnode must be interpreted as a signed value
|
333
|
+
#
|
334
|
+
# TODO: simplify this complex method (possibly by using
|
335
|
+
# a conversion table source type -> destination type)
|
336
|
+
def cast(wnode, wtype, signed=false)
|
337
|
+
logger.debug "wnode: #{wnode}, wtype: #{wtype}"
|
338
|
+
src_type = (wnode.wtype.native? ? wnode.wtype.name : :Class)
|
339
|
+
dest_type = (wtype.native? ? wtype.name : :Class)
|
340
|
+
cast_method = CAST_OPS[src_type] && CAST_OPS[src_type][dest_type] || :cast_error
|
341
|
+
|
342
|
+
wn_cast_op = self.send(cast_method, wnode, wtype, signed)
|
343
|
+
logger.debug "After type cast: wnode: #{wn_cast_op}, wtype: #{wtype}"
|
344
|
+
wn_cast_op
|
345
|
+
end
|
346
|
+
|
347
|
+
# just create a wnode for the WASM operator
|
348
|
+
# Do not set wtype or a code template yet,
|
349
|
+
# wait until operands type is known (see
|
350
|
+
# operands below)
|
351
|
+
def operator(wnode, operator, wtype=WType.new(:none))
|
352
|
+
if (op = (ARITHMETIC_OPS_MAP[operator] ||
|
353
|
+
RELATIONAL_OPS_MAP[operator] ||
|
354
|
+
BOOLEAN_OPS_MAP[operator] ||
|
355
|
+
UNARY_OPS_MAP[operator] ))
|
356
|
+
(wn_op = WNode.new(:insn, wnode)).c(:operator, operator: op)
|
357
|
+
wn_op.wtype = wtype
|
358
|
+
wn_op
|
359
|
+
else
|
360
|
+
raise "operator '#{operator}' not supported"
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
# finish the setting of the operator node and
|
365
|
+
# attach operands
|
366
|
+
def operands(wnode_op, wnode_recv, wnode_args)
|
367
|
+
raise "only 0 or 1 operand expected (got #{wnode_args.count})" if wnode_args.count > 1
|
368
|
+
op = wnode_op.wargs[:operator]
|
369
|
+
# First find out the wtype that has precedence
|
370
|
+
wtype = self.class.leading_wtype(wnode_recv, *wnode_args)
|
371
|
+
|
372
|
+
wnode_op.wtype = wtype
|
373
|
+
logger.debug "leading type cast: #{wtype}"
|
374
|
+
|
375
|
+
# Attach receiver and argument to the operator wnode
|
376
|
+
# type casting them if necessary
|
377
|
+
self.cast(wnode_recv, wtype).reparent_to(wnode_op)
|
378
|
+
self.cast(wnode_args.first, wtype).reparent_to(wnode_op) unless wnode_args.empty?
|
379
|
+
|
380
|
+
# if the receiver is a class object and not
|
381
|
+
# a native integer then pointer arithmetic
|
382
|
+
# applies (like in C)
|
383
|
+
if wnode_recv.wtype.class?
|
384
|
+
legal_ops = RELATIONAL_OPS_MAP.values + [:add, :sub]
|
385
|
+
raise "Only #{legal_ops.join(', ')} operators are supported on objects (got #{op} in #{wnode_op})" \
|
386
|
+
unless legal_ops.include?(op)
|
387
|
+
# if + or - operator then multiply arg by size of object
|
388
|
+
if [:add, :sub].include? wnode_op.wargs[:operator]
|
389
|
+
(wn_mulop = WNode.new(:insn, wnode_op)).c(:operator, operator: :mul)
|
390
|
+
WNode.new(:insn, wn_mulop).c(:const,
|
391
|
+
value: lambda { wnode_recv.find_class(wnode_recv.wtype.name).class_size })
|
392
|
+
wnode_args.first.reparent_to(wn_mulop)
|
393
|
+
else
|
394
|
+
# It's a relational operator. In this case
|
395
|
+
# the type of the operator node is always the
|
396
|
+
# default type because a comparison between
|
397
|
+
# object pointers gives a boolean (0 or 1)
|
398
|
+
wnode_op.wtype = WType::DEFAULT
|
399
|
+
end
|
400
|
+
end
|
401
|
+
wnode_op
|
402
|
+
end
|
403
|
+
|
404
|
+
# Statically allocate an object in data segment
|
405
|
+
# with the size of the class
|
406
|
+
def new(wnode, class_name)
|
407
|
+
class_wnode = wnode.find_class(class_name)
|
408
|
+
if class_wnode.class_size > 0
|
409
|
+
data_label = "#{class_name}_new_#{@new_count += 1}"
|
410
|
+
data = DAta.new(data_label.to_sym, "\x00"*class_wnode.class_size)
|
411
|
+
address = data.address
|
412
|
+
else
|
413
|
+
# TODO: point to address 0. It is not safe but normally
|
414
|
+
# this class is without attribute so the code will never
|
415
|
+
# use memory address to access attribute
|
416
|
+
address = 0
|
417
|
+
end
|
418
|
+
(wn_object_addr = WNode.new(:insn, wnode)).c(:addr, value: address)
|
419
|
+
# VERY IMPORTANT the wtype of this node is the Class name !!!
|
420
|
+
wn_object_addr.wtype = WType.new(class_name.to_sym)
|
421
|
+
wn_object_addr
|
422
|
+
end
|
423
|
+
|
424
|
+
def call(wnode, class_name, method_name, method_type)
|
425
|
+
method = wnode.find_or_create_method(method_name, class_name, method_type)
|
426
|
+
logger.debug "found method #{method.inspect}"
|
427
|
+
(wn_call = WNode.new(:insn, wnode)).c(:call, func_name: method.wasm_name)
|
428
|
+
wn_call.wtype = method.wtype
|
429
|
+
wn_call
|
430
|
+
end
|
431
|
+
|
432
|
+
# self in an instance context is passed as the first argument
|
433
|
+
# of a method call
|
434
|
+
def _self(wnode)
|
435
|
+
(wns = WNode.new(:insn, wnode)).c(:local_get, var_name: '$_self_')
|
436
|
+
wns.wtype = WType.new(wnode.class_name)
|
437
|
+
wns
|
438
|
+
end
|
439
|
+
|
440
|
+
def return(wnode)
|
441
|
+
(wn = WNode.new(:insn, wnode)).c(:return)
|
442
|
+
wn
|
443
|
+
end
|
444
|
+
|
445
|
+
def if(wnode)
|
446
|
+
(wn = WNode.new(:insn, wnode)).c(:if)
|
447
|
+
wn
|
448
|
+
end
|
449
|
+
|
450
|
+
def then(wnode)
|
451
|
+
(wn = WNode.new(:insn, wnode)).c(:then)
|
452
|
+
wn
|
453
|
+
end
|
454
|
+
|
455
|
+
def else(wnode)
|
456
|
+
(wn = WNode.new(:insn, wnode)).c(:else)
|
457
|
+
wn
|
458
|
+
end
|
459
|
+
|
460
|
+
def while(wnode)
|
461
|
+
(wnb = WNode.new(:insn, wnode)).c(:block)
|
462
|
+
(wnl = WNode.new(:insn, wnb)).c(:loop)
|
463
|
+
(wnbi = WNode.new(:insn, wnl)).c(:br_if, label: wnb.label)
|
464
|
+
return wnb,wnbi,wnl
|
465
|
+
end
|
466
|
+
|
467
|
+
# This is a post processing of the while
|
468
|
+
# exp wnode because br_if requires to
|
469
|
+
# negate the original while condition
|
470
|
+
def while_cond(wnode, wnode_cond_exp)
|
471
|
+
wn_eqz = WNode.new(:insn, wnode)
|
472
|
+
wn_eqz.c(:eqz, wtype: wnode_cond_exp.wtype)
|
473
|
+
wnode_cond_exp.reparent_to(wn_eqz)
|
474
|
+
wn_eqz
|
475
|
+
end
|
476
|
+
|
477
|
+
# add the unconditional looping branch at
|
478
|
+
# the end of the while
|
479
|
+
def while_end(wnode)
|
480
|
+
(wnwe = WNode.new(:insn, wnode)).c(:br, label: wnode.label)
|
481
|
+
wnwe
|
482
|
+
end
|
483
|
+
|
484
|
+
def break(wnode)
|
485
|
+
# look for block wnode upper in the tree
|
486
|
+
# and branch to that label
|
487
|
+
(wn = WNode.new(:insn, wnode)).c(:br, label: wnode.block_wnode.label)
|
488
|
+
wn
|
489
|
+
end
|
490
|
+
|
491
|
+
def next(wnode)
|
492
|
+
# look for loop wnode upper in the tree
|
493
|
+
# branch to that label
|
494
|
+
(wn = WNode.new(:insn, wnode)).c(:br, label: wnode.loop_wnode.label)
|
495
|
+
wn
|
496
|
+
end
|
497
|
+
|
498
|
+
private
|
499
|
+
# Determine which wasm type has precedence among
|
500
|
+
# all wnodes
|
501
|
+
def self.leading_wtype(*wnodes)
|
502
|
+
begin
|
503
|
+
WType.leading(wnodes.map(&:wtype))
|
504
|
+
rescue
|
505
|
+
raise "#{wnodes.map(&:to_s)}"
|
506
|
+
end
|
507
|
+
end
|
508
|
+
end
|
509
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
# Rubinius WebAssembly VM
|
2
|
+
# Copyright (c) 2019, Laurent Julliard and contributors
|
3
|
+
# All rights reserved.
|
4
|
+
#
|
5
|
+
# WASM instructions data
|
6
|
+
|
7
|
+
module Rlang::Parser
|
8
|
+
|
9
|
+
class WInstruction
|
10
|
+
@@instructions = {}
|
11
|
+
|
12
|
+
attr_reader :insn, :stk_ins, :stk_outs
|
13
|
+
|
14
|
+
# inputs and outputs are respectively what an
|
15
|
+
# instruction pops from the stack and what it
|
16
|
+
# pushes back as a result
|
17
|
+
def initialize(insn, stk_ins, stk_outs)
|
18
|
+
@insn = insn
|
19
|
+
@stk_ins = stk_ins
|
20
|
+
@stk_outs = stk_outs
|
21
|
+
@@instructions[insn] = self
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.load(insn_data)
|
25
|
+
insn_data.each { |elt| WInstruction.new(*elt) }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
WInstruction.load(
|
30
|
+
[
|
31
|
+
['unreachable', [:any], [:any]],
|
32
|
+
['nop', [], []],
|
33
|
+
['block', [], [:any]],
|
34
|
+
['loop', [], [:any]],
|
35
|
+
['if', [:I32], [:any]],
|
36
|
+
['then', [], [:any]],
|
37
|
+
['else', [], [:any]],
|
38
|
+
['br', [], []],
|
39
|
+
['br_if', [:I32], []],
|
40
|
+
['br_table', [:I32], []],
|
41
|
+
['return', [], [:any]],
|
42
|
+
['call', [:any], [:any]],
|
43
|
+
['call_indirect', [:any, :I32], [:any]],
|
44
|
+
['drop', [:any], []],
|
45
|
+
['select', [:one, :one, :I32], []],
|
46
|
+
['func', [], []],
|
47
|
+
['param', [], []],
|
48
|
+
['result', [], []],
|
49
|
+
['i32.load', [:I32], [:I32]],
|
50
|
+
['i64.load', [:I32], [:I64]],
|
51
|
+
['i32.load8_s', [:I32], [:I32]],
|
52
|
+
['i32.load8_u', [:I32], [:I32]],
|
53
|
+
['i32.load16_s', [:I32], [:I32]],
|
54
|
+
['i32.load16_u', [:I32], [:I32]],
|
55
|
+
['i64.load8_s', [:I32], [:I64]],
|
56
|
+
['i64.load8_u', [:I32], [:I64]],
|
57
|
+
['i64.load16_s', [:I32], [:I64]],
|
58
|
+
['i64.load16_u', [:I32], [:I64]],
|
59
|
+
['i64.load32_s', [:I32], [:I64]],
|
60
|
+
['i64.load32_u', [:I32], [:I64]],
|
61
|
+
['i32.store', [:I32, :I32], []],
|
62
|
+
['i64.store', [:I32, :I64], []],
|
63
|
+
['i32.store8', [:I32, :I32], []],
|
64
|
+
['i32.store16', [:I32, :I32], []],
|
65
|
+
['i64.store8', [:I32, :I64], []],
|
66
|
+
['i64.store16', [:I32, :I64], []],
|
67
|
+
['i64.store32', [:I32, :I64], []],
|
68
|
+
['memory.size', [], [:I32]],
|
69
|
+
['memory.grow', [:I32], [:I32]],
|
70
|
+
['i32.const', [], [:I32]],
|
71
|
+
['i64.const', [], [:I64]],
|
72
|
+
['i32.eqz', [:I32], [:I32]],
|
73
|
+
['i32.eq', [:I32, :I32], [:I32]],
|
74
|
+
['i32.lt_s', [:I32, :I32], [:I32]],
|
75
|
+
['i32.lt_u', [:I32, :I32], [:I32]],
|
76
|
+
['i32.gt_s', [:I32, :I32], [:I32]],
|
77
|
+
['i32.gt_u', [:I32, :I32], [:I32]],
|
78
|
+
['i32.le_s', [:I32, :I32], [:I32]],
|
79
|
+
['i32.le_u', [:I32, :I32], [:I32]],
|
80
|
+
['i32.ge_s', [:I32, :I32], [:I32]],
|
81
|
+
['i32.ge_u', [:I32, :I32], [:I32]],
|
82
|
+
['i64.eqz', [:I64], [:I32]],
|
83
|
+
['i64.eq', [:I64, :I64], [:I32]],
|
84
|
+
['i64.lt_s', [:I64, :I64], [:I32]],
|
85
|
+
['i64.lt_u', [:I64, :I64], [:I32]],
|
86
|
+
['i64.gt_s', [:I64, :I64], [:I32]],
|
87
|
+
['i64.gt_u', [:I64, :I64], [:I32]],
|
88
|
+
['i64.le_s', [:I64, :I64], [:I32]],
|
89
|
+
['i64.le_u', [:I64, :I64], [:I32]],
|
90
|
+
['i64.ge_s', [:I64, :I64], [:I32]],
|
91
|
+
['i64.ge_u', [:I64, :I64], [:I32]],
|
92
|
+
['call', [:any], [:any]],
|
93
|
+
['local.get', [], [:one]],
|
94
|
+
['local.set', [:one], []],
|
95
|
+
['local.tee', [:one], [:one]],
|
96
|
+
['global.get', [], [:one]],
|
97
|
+
['global.set', [:one], []],
|
98
|
+
['i32.clz', [:I32], [:I32]],
|
99
|
+
['i32.ctz', [:I32], [:I32]],
|
100
|
+
['i32.popcnt', [:I32], [:I32]],
|
101
|
+
['i32.add', [:I32, :I32], [:I32]],
|
102
|
+
['i32.sub', [:I32, :I32], [:I32]],
|
103
|
+
['i32.mul', [:I32, :I32], [:I32]],
|
104
|
+
['i32.div_s', [:I32, :I32], [:I32]],
|
105
|
+
['i32.div_u', [:I32, :I32], [:I32]],
|
106
|
+
['i32.rem_s', [:I32, :I32], [:I32]],
|
107
|
+
['i32.rem_u', [:I32, :I32], [:I32]],
|
108
|
+
['i32.and', [:I32, :I32], [:I32]],
|
109
|
+
['i32.or', [:I32, :I32], [:I32]],
|
110
|
+
['i32.or', [:I32, :I32], [:I32]],
|
111
|
+
['i32.xor', [:I32, :I32], [:I32]],
|
112
|
+
['i32.shl', [:I32, :I32], [:I32]],
|
113
|
+
['i32.shr_s', [:I32, :I32], [:I32]],
|
114
|
+
['i32.shr_u', [:I32, :I32], [:I32]],
|
115
|
+
['i32.rotl', [:I32, :I32], [:I32]],
|
116
|
+
['i32.rotr', [:I32, :I32], [:I32]],
|
117
|
+
['i64.clz', [:I64], [:I64]],
|
118
|
+
['i64.ctz', [:I64], [:I64]],
|
119
|
+
['i64.popcnt', [:I32], [:I32]],
|
120
|
+
['i64.add', [:I64, :I64], [:I64]],
|
121
|
+
['i64.sub', [:I64, :I64], [:I64]],
|
122
|
+
['i64.mul', [:I64, :I64], [:I64]],
|
123
|
+
['i64.div_s', [:I64, :I64], [:I64]],
|
124
|
+
['i64.div_u', [:I64, :I64], [:I64]],
|
125
|
+
['i64.rem_s', [:I64, :I64], [:I64]],
|
126
|
+
['i64.rem_u', [:I64, :I64], [:I64]],
|
127
|
+
['i64.and', [:I64, :I64], [:I64]],
|
128
|
+
['i64.or', [:I64, :I64], [:I64]],
|
129
|
+
['i64.or', [:I64, :I64], [:I64]],
|
130
|
+
['i64.xor', [:I64, :I64], [:I64]],
|
131
|
+
['i64.shl', [:I64, :I64], [:I64]],
|
132
|
+
['i64.shr_s', [:I64, :I64], [:I64]],
|
133
|
+
['i64.shr_u', [:I64, :I64], [:I64]],
|
134
|
+
['i64.rotl', [:I64, :I64], [:I64]],
|
135
|
+
['i64.rotr', [:I64, :I64], [:I64]],
|
136
|
+
['i32.wrap_64', [:I64], [:I32]],
|
137
|
+
['i64.extend_i32_s', [:I32], [:I64]],
|
138
|
+
['i64.extend_i32_u', [:I32], [:I64]],
|
139
|
+
['i64.trunc_f32_s', [:F32], [:I64]],
|
140
|
+
['i64.trunc_f32_u', [:F32], [:I64]],
|
141
|
+
['i64.trunc_f64_s', [:F64], [:I64]],
|
142
|
+
['i64.trunc_f64_u', [:F64], [:I64]],
|
143
|
+
['i64.trunc_f32_s', [:F64], [:I64]],
|
144
|
+
['i64.trunc_f32_u', [:F64], [:I64]],
|
145
|
+
] )
|
146
|
+
|
147
|
+
end
|
148
|
+
end
|