rlang 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|