rlang 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/CHANGELOG.md +10 -0
- data/Gemfile.lock +3 -5
- data/README +11 -0
- data/bin/rlang +4 -0
- data/docs/RlangCompiler.md +2 -0
- data/docs/RlangManual.md +37 -8
- data/lib/rlang/lib/array/array32.rb +13 -4
- data/lib/rlang/lib/array/array64.rb +11 -3
- data/lib/rlang/lib/base64.rb +223 -0
- data/lib/rlang/lib/io.rb +7 -2
- data/lib/rlang/lib/malloc.rb +5 -3
- data/lib/rlang/lib/object.rb +23 -0
- data/lib/rlang/lib/rlang_core.rb +2 -1
- data/lib/rlang/lib/string.rb +95 -11
- data/lib/rlang/lib/type/i32.rb +30 -12
- data/lib/rlang/lib/type/i64.rb +0 -2
- data/lib/rlang/lib/wasi.rb +57 -6
- data/lib/rlang/parser/data.rb +5 -0
- data/lib/rlang/parser/ext/integer.rb +3 -1
- data/lib/rlang/parser/ext/type.rb +30 -1
- data/lib/rlang/parser/global.rb +7 -0
- data/lib/rlang/parser/wgenerator.rb +221 -83
- data/lib/rlang/parser/wnode.rb +41 -14
- data/lib/rlang/parser/wtype.rb +12 -6
- data/lib/rlang/parser.rb +182 -130
- data/lib/rlang/version.rb +1 -1
- data/lib/ruby/mirror/rstring.rb +16 -0
- data/lib/utils/exceptions.rb +12 -0
- data/rlang.gemspec +4 -4
- metadata +16 -12
@@ -22,26 +22,29 @@ module Rlang::Parser
|
|
22
22
|
:+ => :add,
|
23
23
|
:- => :sub,
|
24
24
|
:* => :mul,
|
25
|
-
:/ => :div_u,
|
26
|
-
:% => :rem_u,
|
27
25
|
:& => :and,
|
28
26
|
:| => :or,
|
29
27
|
:^ => :xor,
|
30
|
-
:>> => :shr_u,
|
31
28
|
:<< => :shl
|
32
29
|
}
|
33
30
|
|
31
|
+
ARITHMETIC_SIGNED_OPS_MAP = {
|
32
|
+
:/ => :div_x,
|
33
|
+
:% => :rem_x,
|
34
|
+
:>> => :shr_x
|
35
|
+
}
|
36
|
+
|
34
37
|
RELATIONAL_OPS_MAP = {
|
35
38
|
:== => :eq,
|
36
39
|
:!= => :ne,
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
:>= => :
|
40
|
+
|
41
|
+
}
|
42
|
+
|
43
|
+
RELATIONAL_SIGNED_OPS_MAP = {
|
44
|
+
:< => :lt_x,
|
45
|
+
:> => :gt_x,
|
46
|
+
:<= => :le_x,
|
47
|
+
:>= => :ge_x
|
45
48
|
}
|
46
49
|
|
47
50
|
BOOLEAN_OPS_MAP = {
|
@@ -50,19 +53,40 @@ module Rlang::Parser
|
|
50
53
|
}
|
51
54
|
|
52
55
|
UNARY_OPS_MAP = {
|
53
|
-
:'!' => :eqz
|
56
|
+
:'!' => :eqz,
|
57
|
+
:'-@' => :sub # special case for unary - turned into (sub 0 x)
|
54
58
|
}
|
55
59
|
|
56
|
-
|
60
|
+
SIGNED_OPS = {
|
61
|
+
signed: {div_x: :div_s, rem_x: :rem_s, shr_x: :shr_s, lt_x: :lt_s, gt_x: :gt_s,
|
62
|
+
le_x: :le_s, ge_x: :ge_s},
|
63
|
+
unsigned: {div_x: :div_u, rem_x: :rem_u, shr_x: :shr_u, lt_x: :lt_u, gt_x: :gt_u,
|
64
|
+
le_x: :le_u, ge_x: :ge_u},
|
65
|
+
}
|
66
|
+
|
67
|
+
# Operators that can be legally used when doing
|
68
|
+
# arithmetic on class instance (= object) pointers
|
69
|
+
# Only unsigned relational operators make sense as pointers
|
70
|
+
# are by nature unsigned integers
|
71
|
+
LEGAL_CLASS_WASM_OPS = [:eq, :ne, :lt_u, :gt_u, :le_u, :ge_u, :add, :sub]
|
72
|
+
|
73
|
+
# All operators with signed / unsigned variants
|
74
|
+
ALL_SIGNED_OPS_MAP = [*ARITHMETIC_SIGNED_OPS_MAP, *RELATIONAL_SIGNED_OPS_MAP].to_h
|
75
|
+
|
76
|
+
# All operators in on hash
|
77
|
+
ALL_OPS_MAP = [*ARITHMETIC_OPS_MAP, *ARITHMETIC_SIGNED_OPS_MAP, *RELATIONAL_OPS_MAP, *RELATIONAL_SIGNED_OPS_MAP,
|
78
|
+
*BOOLEAN_OPS_MAP, *UNARY_OPS_MAP].to_h
|
57
79
|
|
58
80
|
# Matrix of how to cast a WASM type to another
|
59
81
|
CAST_OPS = {
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
82
|
+
UI32: { UI32: :cast_nope, I32: :cast_wtype, UI64: :cast_extend, I64: :cast_extend, F32: :cast_notyet, F64: :cast_notyet, Class: :cast_wtype, none: :cast_error},
|
83
|
+
I32: { UI32: :cast_wtype, I32: :cast_nope, UI64: :cast_extend, I64: :cast_extend, F32: :cast_notyet, F64: :cast_notyet, Class: :cast_wtype, none: :cast_error},
|
84
|
+
UI64: { UI32: :cast_wrap, I32: :cast_wrap, UI64: :cast_nope, I64: :cast_wtype, F32: :cast_notyet, F64: :cast_notyet, Class: :cast_error, none: :cast_error},
|
85
|
+
I64: { UI32: :cast_wrap, I32: :cast_wrap, UI64: :cast_wtype, I64: :cast_nope, F32: :cast_notyet, F64: :cast_notyet, Class: :cast_error, none: :cast_error},
|
86
|
+
F32: { UI32: :cast_notyet, I32: :cast_notyet, UI64: :cast_notyet, I64: :cast_notyet, F32: :cast_nope, F64: :cast_notyet, Class: :cast_error, none: :cast_error},
|
87
|
+
F64: { UI32: :cast_notyet, I32: :cast_notyet, UI64: :cast_notyet, I64: :cast_notyet, F32: :cast_notyet, F64: :cast_nope, Class: :cast_error, none: :cast_error},
|
88
|
+
Class: { UI32: :cast_wtype, I32: :cast_wtype, UI64: :cast_extend, I64: :cast_extend, F32: :cast_error, F64: :cast_error, Class: :cast_wtype, none: :cast_error},
|
89
|
+
none: { UI32: :cast_error, I32: :cast_error, UI64: :cast_error, I64: :cast_error, F32: :cast_error, F64: :cast_error, Class: :cast_error, none: :cast_error},
|
66
90
|
}
|
67
91
|
|
68
92
|
# Rlang class size method name
|
@@ -103,6 +127,11 @@ module Rlang::Parser
|
|
103
127
|
String.new(%{ptr}, %{length})
|
104
128
|
}
|
105
129
|
|
130
|
+
# Dynamically allocate a string object
|
131
|
+
ARRAY_NEW_TMPL = %q{
|
132
|
+
Array%{elt_size_in_bits}.new(%{ptr}, %{length})
|
133
|
+
}
|
134
|
+
|
106
135
|
# Generate the wasm nodes and tree structure
|
107
136
|
# ***IMPORTANT NOTE***
|
108
137
|
# Unless otherwise stated all methods receive
|
@@ -190,6 +219,13 @@ module Rlang::Parser
|
|
190
219
|
k.wnode
|
191
220
|
end
|
192
221
|
|
222
|
+
def comments(wnode, comments)
|
223
|
+
# The gsub below is to handle =begin...=end block comments
|
224
|
+
comments.each do |c|
|
225
|
+
WNode.new(:comment, wnode).c(:comment, text: c.text.sub(/^\s*#/,'').gsub("\n", "\n;;"))
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
193
229
|
# Create module object and module wnode
|
194
230
|
# if it doesn't exist yet
|
195
231
|
def module(wnode, module_path)
|
@@ -305,9 +341,9 @@ module Rlang::Parser
|
|
305
341
|
unless size_method.wnode
|
306
342
|
logger.debug("Generating #{size_method.klass.name}\##{size_method.name}")
|
307
343
|
wns = WNode.new(:insn, wnc)
|
308
|
-
wns.wtype = WType::DEFAULT
|
344
|
+
wns.wtype = WType::DEFAULT
|
309
345
|
wns.c(:class_size, func_name: size_method.wasm_name,
|
310
|
-
|
346
|
+
wasm_type: wns.wasm_type, size: wnc.class_size)
|
311
347
|
size_method.wnode = wns
|
312
348
|
end
|
313
349
|
end
|
@@ -317,7 +353,7 @@ module Rlang::Parser
|
|
317
353
|
wnc = wnode.class_wnode
|
318
354
|
wn_set = WNode.new(:insn, wnc, true)
|
319
355
|
wn_set.c(:attr_setter, func_name: attr.setter.wasm_name,
|
320
|
-
attr_name: attr.wasm_name,
|
356
|
+
attr_name: attr.wasm_name, wasm_type: attr.wasm_type,
|
321
357
|
offset: attr.offset)
|
322
358
|
wn_set
|
323
359
|
end
|
@@ -327,8 +363,7 @@ module Rlang::Parser
|
|
327
363
|
wnc = wnode.class_wnode
|
328
364
|
wn_get = WNode.new(:insn, wnc, true)
|
329
365
|
wn_get.c(:attr_getter, func_name: attr.getter.wasm_name,
|
330
|
-
|
331
|
-
offset: attr.offset)
|
366
|
+
wasm_type: attr.wasm_type, offset: attr.offset)
|
332
367
|
wn_get
|
333
368
|
end
|
334
369
|
|
@@ -381,7 +416,7 @@ module Rlang::Parser
|
|
381
416
|
logger.debug("Prepending param #{marg}")
|
382
417
|
wn = WNode.new(:insn, wnm, true)
|
383
418
|
wn.wtype = marg.wtype
|
384
|
-
wn.c(:param, name: marg.wasm_name)
|
419
|
+
wn.c(:param, name: marg.wasm_name, wasm_type: wn.wasm_type)
|
385
420
|
end
|
386
421
|
end
|
387
422
|
|
@@ -389,7 +424,7 @@ module Rlang::Parser
|
|
389
424
|
unless wnode.wtype.blank?
|
390
425
|
wn = WNode.new(:insn, wnode, true)
|
391
426
|
wn.wtype = wnode.wtype
|
392
|
-
wn.c(:result)
|
427
|
+
wn.c(:result, wasm_type: wn.wasm_type)
|
393
428
|
end
|
394
429
|
end
|
395
430
|
|
@@ -399,7 +434,7 @@ module Rlang::Parser
|
|
399
434
|
logger.debug("Prepending local #{lvar.inspect}")
|
400
435
|
wn = WNode.new(:insn, wnm, true)
|
401
436
|
wn.wtype = lvar.wtype
|
402
|
-
wn.c(:local, name: lvar.wasm_name)
|
437
|
+
wn.c(:local, name: lvar.wasm_name, wasm_type: wn.wasm_type)
|
403
438
|
end
|
404
439
|
end
|
405
440
|
|
@@ -413,7 +448,7 @@ module Rlang::Parser
|
|
413
448
|
# Set constant
|
414
449
|
def casgn(wnode, const)
|
415
450
|
(wn = WNode.new(:insn, wnode)).wtype = const.wtype
|
416
|
-
wn.c(:store,
|
451
|
+
wn.c(:store, wasm_type: const.wtype)
|
417
452
|
WNode.new(:insn, wn).c(:addr, value: const.address)
|
418
453
|
wn
|
419
454
|
end
|
@@ -421,7 +456,7 @@ module Rlang::Parser
|
|
421
456
|
# Get constant
|
422
457
|
def const(wnode, const)
|
423
458
|
(wn = WNode.new(:insn, wnode)).wtype = const.wtype
|
424
|
-
wn.c(:load,
|
459
|
+
wn.c(:load, wasm_type: const.wasm_type)
|
425
460
|
WNode.new(:insn, wn).c(:addr, value: const.address)
|
426
461
|
wn
|
427
462
|
end
|
@@ -468,7 +503,7 @@ module Rlang::Parser
|
|
468
503
|
# Set instance variable
|
469
504
|
def ivasgn(wnode, ivar)
|
470
505
|
(wn = WNode.new(:insn, wnode)).wtype = ivar.wtype
|
471
|
-
wn.c(:store_offset,
|
506
|
+
wn.c(:store_offset, wasm_type: ivar.wasm_type, offset: lambda { ivar.offset })
|
472
507
|
self._self_(wn)
|
473
508
|
wn
|
474
509
|
end
|
@@ -476,7 +511,7 @@ module Rlang::Parser
|
|
476
511
|
# Get instance variable.
|
477
512
|
def ivar(wnode, ivar)
|
478
513
|
(wn = WNode.new(:insn, wnode)).wtype = ivar.wtype
|
479
|
-
wn.c(:load_offset,
|
514
|
+
wn.c(:load_offset, wasm_type: ivar.wasm_type, offset: lambda { ivar.offset })
|
480
515
|
self._self_(wn)
|
481
516
|
wn
|
482
517
|
end
|
@@ -486,7 +521,7 @@ module Rlang::Parser
|
|
486
521
|
# an empty expression node to populate later
|
487
522
|
def cvasgn(wnode, cvar)
|
488
523
|
(wn = WNode.new(:insn, wnode)).wtype = cvar.wtype
|
489
|
-
wn.c(:store,
|
524
|
+
wn.c(:store, wasm_type: cvar.wasm_type)
|
490
525
|
WNode.new(:insn, wn).c(:addr, value: cvar.address)
|
491
526
|
wn
|
492
527
|
end
|
@@ -494,7 +529,7 @@ module Rlang::Parser
|
|
494
529
|
# Get class variable
|
495
530
|
def cvar(wnode, cvar)
|
496
531
|
(wn = WNode.new(:insn, wnode)).wtype = cvar.wtype
|
497
|
-
wn.c(:load,
|
532
|
+
wn.c(:load, wasm_type: cvar.wasm_type)
|
498
533
|
WNode.new(:insn, wn).c(:addr, value: cvar.address)
|
499
534
|
wn
|
500
535
|
end
|
@@ -510,14 +545,14 @@ module Rlang::Parser
|
|
510
545
|
# Create the local variable storage node
|
511
546
|
def lvasgn(wnode, lvar)
|
512
547
|
(wn = WNode.new(:insn, wnode)).wtype = lvar.wtype
|
513
|
-
wn.c(:local_set,
|
548
|
+
wn.c(:local_set, var_name: lvar.wasm_name)
|
514
549
|
wn
|
515
550
|
end
|
516
551
|
|
517
552
|
# Read local variable
|
518
553
|
def lvar(wnode, lvar)
|
519
554
|
(wn = WNode.new(:insn, wnode)).wtype = lvar.wtype
|
520
|
-
wn.c(:local_get,
|
555
|
+
wn.c(:local_get, var_name: lvar.wasm_name)
|
521
556
|
wn
|
522
557
|
end
|
523
558
|
|
@@ -534,13 +569,13 @@ module Rlang::Parser
|
|
534
569
|
|
535
570
|
def int(wnode, wtype, value)
|
536
571
|
(wn = WNode.new(:insn, wnode)).wtype = wtype
|
537
|
-
wn.c(:const,
|
572
|
+
wn.c(:const, wasm_type: wn.wasm_type, value: value)
|
538
573
|
wn
|
539
574
|
end
|
540
575
|
|
541
576
|
def float(wnode, wtype, value)
|
542
577
|
(wn = WNode.new(:insn, wnode)).wtype = wtype
|
543
|
-
wn.c(:const,
|
578
|
+
wn.c(:const, wasm_type: wn.wasm_type, value: value)
|
544
579
|
wn
|
545
580
|
end
|
546
581
|
|
@@ -551,24 +586,25 @@ module Rlang::Parser
|
|
551
586
|
WNode.new(type, wnode)
|
552
587
|
end
|
553
588
|
|
554
|
-
# Static string allocation
|
555
|
-
def
|
556
|
-
#
|
557
|
-
|
558
|
-
data_stg = DAta.append(data_label.to_sym, string)
|
559
|
-
data_stg
|
589
|
+
# Static string data allocation
|
590
|
+
def allocate_string_static_data(string, data_label)
|
591
|
+
# if string is empty do not allocate any memory space
|
592
|
+
DAta.append(data_label.to_sym, string) unless string.empty?
|
560
593
|
end
|
561
594
|
|
562
595
|
# Static new string object
|
563
596
|
def string_static_new(wnode, string)
|
564
597
|
klass = wnode.find_current_class_or_module()
|
565
598
|
data_label = "#{klass.name}_string_#{@static_count += 1}"
|
566
|
-
#
|
567
|
-
data_stg
|
599
|
+
# Allocate string data statically
|
600
|
+
# Note : data_stg is nil if string is empty
|
601
|
+
data_stg = self.allocate_string_static_data(string, data_label)
|
568
602
|
# align on :I32 boundary
|
603
|
+
# then allocate the String object attributes
|
604
|
+
# and set them up
|
569
605
|
DAta.align(4)
|
570
606
|
data_len = DAta.append("#{data_label}_len".to_sym, string.length, WType::DEFAULT)
|
571
|
-
data_ptr = DAta.append("#{data_label}_ptr".to_sym, data_stg.address, WType::DEFAULT)
|
607
|
+
data_ptr = DAta.append("#{data_label}_ptr".to_sym, data_stg ? data_stg.address : 0, WType::DEFAULT)
|
572
608
|
# Generate address wnode
|
573
609
|
(wn_object_addr = WNode.new(:insn, wnode)).c(:addr, value: data_len.address)
|
574
610
|
wn_object_addr.wtype = WType.new(:String)
|
@@ -579,25 +615,73 @@ module Rlang::Parser
|
|
579
615
|
def string_dynamic_new(wnode, string)
|
580
616
|
klass = wnode.find_current_class_or_module()
|
581
617
|
data_label = "#{klass.name}_string_#{@static_count += 1}"
|
582
|
-
data_stg
|
618
|
+
# Note : data_stg is nil if string is empty
|
619
|
+
data_stg = self.allocate_string_static_data(string, data_label)
|
583
620
|
string_new_source = STRING_NEW_TMPL % {
|
584
|
-
ptr: data_stg.address,
|
621
|
+
ptr: data_stg ? data_stg.address : 0,
|
585
622
|
length: string.length
|
586
623
|
}
|
587
624
|
#puts string_new_source;exit
|
588
|
-
|
589
|
-
|
625
|
+
self.parser.parse(string_new_source, wnode)
|
626
|
+
end
|
627
|
+
|
628
|
+
# Static array data allocation
|
629
|
+
def allocate_array_static_data(array, data_label)
|
630
|
+
# Append each array element to the same data section
|
631
|
+
label = data_label.to_sym
|
632
|
+
data_arr = nil
|
633
|
+
# Do not allocate memory space if array is empty
|
634
|
+
array.each { |elt| data_arr = DAta.append(label, elt) }
|
635
|
+
data_arr
|
636
|
+
end
|
637
|
+
|
638
|
+
# Static new array object
|
639
|
+
def array_static_new(wnode, array)
|
640
|
+
klass = wnode.find_current_class_or_module()
|
641
|
+
data_label = "#{klass.name}_array_#{@static_count += 1}"
|
642
|
+
# Allocate array data statically
|
643
|
+
# Note : data_arr is nil if string is empty
|
644
|
+
data_arr = self.allocate_array_static_data(array, data_label)
|
645
|
+
# align on :I32 boundary
|
646
|
+
# then allocate the Array object attributes
|
647
|
+
# and set them up
|
648
|
+
DAta.align(4)
|
649
|
+
data_count = DAta.append("#{data_label}_count".to_sym, array.length, WType::DEFAULT)
|
650
|
+
data_ptr = DAta.append("#{data_label}_ptr".to_sym, data_arr ? data_arr.address : 0, WType::DEFAULT)
|
651
|
+
# Generate address wnode
|
652
|
+
(wn_object_addr = WNode.new(:insn, wnode)).c(:addr, value: data_count.address)
|
653
|
+
wn_object_addr.wtype = WType.new(:Array32)
|
654
|
+
wn_object_addr
|
655
|
+
end
|
656
|
+
|
657
|
+
# Dynamic new array object
|
658
|
+
def array_dynamic_new(wnode, array)
|
659
|
+
klass = wnode.find_current_class_or_module()
|
660
|
+
data_label = "#{klass.name}_array_#{@static_count += 1}"
|
661
|
+
# Note : data_arr is nil if string is empty
|
662
|
+
data_arr = self.allocate_array_static_data(array, data_label)
|
663
|
+
array_new_source = ARRAY_NEW_TMPL % {
|
664
|
+
elt_size_in_bits: WTYPE::DEFAULT.size * 8,
|
665
|
+
ptr: data_arr ? data_arr.address : 0,
|
666
|
+
count: array.length
|
667
|
+
}
|
668
|
+
#puts array_new_source;exit
|
669
|
+
self.parser.parse(array_new_source, wnode)
|
590
670
|
end
|
591
671
|
|
672
|
+
# TYPE CASTING methods
|
592
673
|
# All the cast_xxxx methods below returns
|
593
674
|
# the new wnode doing the cast operation
|
594
675
|
# or the same wnode if there is no additional code
|
595
676
|
# for the cast operation
|
677
|
+
|
678
|
+
# No casting. Return node as is.
|
596
679
|
def cast_nope(wnode, wtype, signed)
|
597
|
-
# Do nothing
|
598
680
|
wnode
|
599
681
|
end
|
600
682
|
|
683
|
+
# Cast by extending to a wtype of larger bit size
|
684
|
+
# (e.g. I32 to I64)
|
601
685
|
def cast_extend(wnode, wtype, signed)
|
602
686
|
if (wnode.template == :const)
|
603
687
|
# it's a WASM const, simply change the wtype
|
@@ -606,15 +690,19 @@ module Rlang::Parser
|
|
606
690
|
else
|
607
691
|
wn_cast_op = wnode.insert(:insn)
|
608
692
|
wn_cast_op.wtype = wtype
|
609
|
-
wn_cast_op.c(signed ? :extend_i32_s : :extend_i32_u ,
|
693
|
+
wn_cast_op.c(signed ? :extend_i32_s : :extend_i32_u , wasm_type: wn_cast_op.wasm_type)
|
610
694
|
end
|
611
695
|
wn_cast_op
|
612
696
|
end
|
613
697
|
|
698
|
+
# Cast by simply changing the node wtype
|
699
|
+
# No change in native WASM type
|
700
|
+
# (e.g. casting an object pointer to I32)
|
614
701
|
def cast_wtype(wnode, wtype, signed)
|
615
|
-
|
616
|
-
|
617
|
-
|
702
|
+
# Don't cast blindly. Check that source and target
|
703
|
+
# have the same bit size (e.g. Object pointers, I32, UI32
|
704
|
+
# are the same size, )
|
705
|
+
if wnode.wtype.size == wtype.size
|
618
706
|
wnode.wtype = wtype
|
619
707
|
else
|
620
708
|
cast_error(wnode, wtype, signed)
|
@@ -622,6 +710,8 @@ module Rlang::Parser
|
|
622
710
|
wnode
|
623
711
|
end
|
624
712
|
|
713
|
+
# Cast by wraping a wtype to a smaller size
|
714
|
+
# (e.g. I64 to I32)
|
625
715
|
def cast_wrap(wnode, wtype, signed)
|
626
716
|
if (wnode.template == :const)
|
627
717
|
# it's a WASM const, simply change the wtype
|
@@ -630,17 +720,19 @@ module Rlang::Parser
|
|
630
720
|
else
|
631
721
|
wn_cast_op = wnode.insert(:insn)
|
632
722
|
wn_cast_op.wtype = wtype
|
633
|
-
wn_cast_op.c(:wrap_i64,
|
723
|
+
wn_cast_op.c(:wrap_i64, wasm_type: wn_cast_op.wasm_type)
|
634
724
|
end
|
635
725
|
wn_cast_op
|
636
726
|
end
|
637
727
|
|
728
|
+
# Cast operation not yet supported
|
638
729
|
def cast_notyet(wnode, wtype, signed)
|
639
730
|
raise "Type cast from #{wnode.wtype} to #{wtype} not supported yet"
|
640
731
|
end
|
641
732
|
|
733
|
+
# Cast operation is invalid
|
642
734
|
def cast_error(wnode, wtype, signed)
|
643
|
-
raise "Cannot cast type #{
|
735
|
+
raise "Cannot cast type #{wnode.wtype} to #{wtype}. Time to fix your code :-)"
|
644
736
|
end
|
645
737
|
|
646
738
|
# cast an expression to a different type
|
@@ -668,17 +760,20 @@ module Rlang::Parser
|
|
668
760
|
# operands below)
|
669
761
|
def native_operator(wnode, operator, wtype=WType.new(:none))
|
670
762
|
if (op = ALL_OPS_MAP[operator])
|
671
|
-
(wn_op = WNode.new(:insn, wnode)).
|
672
|
-
wn_op.
|
763
|
+
(wn_op = WNode.new(:insn, wnode)).wtype = wtype
|
764
|
+
wn_op.c(:operator, wasm_type: wn_op.wasm_type, operator: op)
|
673
765
|
logger.debug "Creating operator #{operator} wnode: #{wn_op}"
|
766
|
+
# special case for - unary operator transformed into (0 - x)
|
767
|
+
WNode.new(:insn, wn_op).c(:const, wasm_type: wn_op.wasm_type, value: 0) if operator == :-@
|
674
768
|
wn_op
|
675
769
|
else
|
676
770
|
raise "operator '#{operator}' not supported"
|
677
771
|
end
|
678
772
|
end
|
679
773
|
|
680
|
-
#
|
681
|
-
# attach operands
|
774
|
+
# Finish the setting of the operator node,
|
775
|
+
# attach operands and see if they need implicit
|
776
|
+
# type casting
|
682
777
|
def operands(wnode_op, wnode_recv, wnode_args)
|
683
778
|
logger.debug "Processing operands in operator wnode: #{wnode_op}..."
|
684
779
|
# Do not post process operands if the operator
|
@@ -696,26 +791,48 @@ module Rlang::Parser
|
|
696
791
|
#wnode_recv = wnode_op.children[0]
|
697
792
|
#wnode_args = wnode_op.children[1..-1]
|
698
793
|
# First find out the wtype that has precedence
|
699
|
-
|
794
|
+
leading_wtype = self.class.leading_wtype(wnode_recv, *wnode_args)
|
700
795
|
|
701
|
-
wnode_op.wtype =
|
702
|
-
logger.debug "leading type cast: #{
|
796
|
+
wnode_op.wtype = leading_wtype
|
797
|
+
logger.debug "leading type cast: #{leading_wtype}"
|
703
798
|
|
704
799
|
# Attach receiver and argument to the operator wnode
|
705
|
-
# type casting them if necessary
|
706
|
-
|
707
|
-
|
800
|
+
# type casting them if necessary
|
801
|
+
# Note : normally for an operator there is only one argument
|
802
|
+
# but process args as if they were many one day.
|
803
|
+
logger.debug "Perform implicit type casting of receviver and operator arg(s)"
|
804
|
+
self.cast(wnode_recv, leading_wtype).reparent_to(wnode_op)
|
805
|
+
wnode_args.each do |wna|
|
806
|
+
self.cast(wna, leading_wtype).reparent_to(wnode_op)
|
807
|
+
end
|
808
|
+
|
809
|
+
# Once operands casting is done, see if we need the signed or unsigned
|
810
|
+
# version of the native operator
|
811
|
+
# NOTE : At this stage, after the operands casting both of them
|
812
|
+
# should either be signed or unsigned, hence the XNOR sanity check
|
813
|
+
# below
|
814
|
+
if ALL_SIGNED_OPS_MAP.values.include? op
|
815
|
+
if !((signed = wnode_recv.wtype.signed?) ^ (wnode_args.empty? ? true : wnode_args.first.wtype.signed?))
|
816
|
+
wnode_op.wargs[:operator] = SIGNED_OPS[signed ? :signed : :unsigned][op]
|
817
|
+
logger.debug "Receiver has wtype #{wnode_recv.wtype} / Argument has wtype #{wnode_args.first.wtype}"
|
818
|
+
logger.debug "Replacing #{op} operator with #{wnode_op.wargs[:operator]}"
|
819
|
+
op = wnode_op.wargs[:operator]
|
820
|
+
else
|
821
|
+
raise "Type mismatch between operands. Receiver is #{wnode_recv.wtype} and argument is #{wnode_args.first.wtype}"
|
822
|
+
end
|
823
|
+
end
|
708
824
|
|
709
825
|
# if the receiver is a class object and not
|
710
826
|
# a native integer then pointer arithmetic
|
711
827
|
# applies (like in C)
|
712
828
|
if wnode_recv.wtype.class?
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
#
|
717
|
-
if [:add, :sub].include?
|
718
|
-
(wn_mulop = WNode.new(:insn, wnode_op)).
|
829
|
+
raise "Only #{LEGAL_CLASS_WASM_OPS.join(', ')} operators are supported on objects (got #{op} in #{wnode_op})" \
|
830
|
+
unless LEGAL_CLASS_WASM_OPS.include?(op)
|
831
|
+
# if :add or :sub operator then multiply arg by size of object
|
832
|
+
# like in C
|
833
|
+
if [:add, :sub].include? op
|
834
|
+
(wn_mulop = WNode.new(:insn, wnode_op)).wtype = WType::DEFAULT
|
835
|
+
wn_mulop.c(:operator, wasm_type: wn_mulop.wasm_type, operator: :mul)
|
719
836
|
WNode.new(:insn, wn_mulop).c(:call, func_name: "$#{wnode_recv.wtype.name}::#{SIZE_METHOD}")
|
720
837
|
wnode_args.first.reparent_to(wn_mulop)
|
721
838
|
else
|
@@ -794,14 +911,35 @@ module Rlang::Parser
|
|
794
911
|
# generate code for method call
|
795
912
|
def send_method(wnode, class_path, method_name, method_type)
|
796
913
|
logger.debug "In call generator for #{class_path}::#{method_name}"
|
797
|
-
k = wnode.find_class_or_module(class_path)
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
914
|
+
if k = wnode.find_class_or_module(class_path)
|
915
|
+
method = wnode.find_method(k, method_name, method_type)
|
916
|
+
if k.wtype.native? && ALL_OPS_MAP.has_key?(method_name)
|
917
|
+
# An Rlang method exist for this class but methods corresponding to
|
918
|
+
# Webassembly native operators applied to native Webassembly types
|
919
|
+
# (I32, I64, F32, F64) **cannot** be overriden by instance methods
|
920
|
+
# in Rlang code
|
921
|
+
if method
|
922
|
+
logger.warn "Rlang #{class_path}::#{method_name} method ignored. Native operator has precedence"
|
923
|
+
end
|
924
|
+
logger.debug "Apply native operator #{method_name} to native wtype #{class_path}"
|
925
|
+
wn_call = self.native_operator(wnode, method_name, WType.new(class_path))
|
926
|
+
elsif !k.wtype.native? && ALL_OPS_MAP.has_key?(method_name) && method.nil?
|
927
|
+
# Similarly if the Class is not a native type (a regular class)
|
928
|
+
# and the Class doesn't provide its own method implementation of the native
|
929
|
+
# operator then apply the native operands
|
930
|
+
logger.debug "Apply native operator #{method_name} to class found : #{class_path}"
|
931
|
+
wn_call = self.native_operator(wnode, method_name, WType.new(class_path))
|
932
|
+
elsif method
|
933
|
+
logger.debug "Found method #{method.name} in class #{method.klass.name}"
|
934
|
+
(wn_call = WNode.new(:insn, wnode)).c(:call, func_name: method.wasm_name)
|
935
|
+
wn_call.wtype = method.wtype
|
936
|
+
wn_call
|
937
|
+
else
|
938
|
+
raise "Unknown method '#{method_name}' in class #{class_path}"
|
939
|
+
end
|
803
940
|
elsif ALL_OPS_MAP.has_key? method_name
|
804
|
-
#
|
941
|
+
# It is a native Wasm operator
|
942
|
+
logger.debug "Native operator found : #{class_path}::#{method_name}"
|
805
943
|
wn_call = self.native_operator(wnode, method_name, WType.new(class_path))
|
806
944
|
else
|
807
945
|
raise "Unknown method '#{method_name}' in class #{class_path}"
|
@@ -838,8 +976,8 @@ module Rlang::Parser
|
|
838
976
|
end
|
839
977
|
|
840
978
|
def while(wnode)
|
841
|
-
(wnb = WNode.new(:insn, wnode)).c(:block)
|
842
|
-
(wnl = WNode.new(:insn, wnb)).c(:loop)
|
979
|
+
(wnb = WNode.new(:insn, wnode)).c(:block, label: wnb.set_label)
|
980
|
+
(wnl = WNode.new(:insn, wnb)).c(:loop, label: wnl.set_label)
|
843
981
|
(wnbi = WNode.new(:insn, wnl)).c(:br_if, label: wnb.label)
|
844
982
|
return wnb,wnbi,wnl
|
845
983
|
end
|
@@ -849,7 +987,7 @@ module Rlang::Parser
|
|
849
987
|
# negate the original while condition
|
850
988
|
def while_cond(wnode, wnode_cond_exp)
|
851
989
|
wn_eqz = WNode.new(:insn, wnode)
|
852
|
-
wn_eqz.c(:eqz,
|
990
|
+
wn_eqz.c(:eqz, wasm_type: wnode_cond_exp.wasm_type)
|
853
991
|
wnode_cond_exp.reparent_to(wn_eqz)
|
854
992
|
wn_eqz
|
855
993
|
end
|
data/lib/rlang/parser/wnode.rb
CHANGED
@@ -52,14 +52,14 @@ module Rlang::Parser
|
|
52
52
|
br_if: 'br_if %{label}',
|
53
53
|
br: 'br %{label}',
|
54
54
|
inline: '%{code}',
|
55
|
-
attr_getter: %q{func %{func_name} (param $_self_ i32) (result %{
|
56
|
-
(%{
|
57
|
-
attr_setter: %q{func %{func_name} (param $_self_ i32) (param %{attr_name} %{
|
55
|
+
attr_getter: %q{func %{func_name} (param $_self_ i32) (result %{wasm_type})
|
56
|
+
(%{wasm_type}.load offset=%{offset} (local.get $_self_))},
|
57
|
+
attr_setter: %q{func %{func_name} (param $_self_ i32) (param %{attr_name} %{wasm_type}) (result %{wasm_type})
|
58
58
|
(local.get %{attr_name})
|
59
|
-
(%{
|
60
|
-
class_size: %q{func %{func_name} (result %{
|
61
|
-
(%{
|
62
|
-
comment: ';; %{
|
59
|
+
(%{wasm_type}.store offset=%{offset} (local.get $_self_) (local.get %{attr_name}))},
|
60
|
+
class_size: %q{func %{func_name} (result %{wasm_type})
|
61
|
+
(%{wasm_type}.const %{size})},
|
62
|
+
comment: ';; %{text}',
|
63
63
|
memory: 'memory $0 %{min} %{max}',
|
64
64
|
module: 'module %{module}'
|
65
65
|
}
|
@@ -126,17 +126,37 @@ module Rlang::Parser
|
|
126
126
|
def c(template, wargs = {})
|
127
127
|
raise "Error: unknown WASM code template (#{template})" unless T.has_key? template
|
128
128
|
raise "Error: this WNode is already populated with instruction #{@template}" if @template
|
129
|
-
if [:loop, :block].include? template
|
130
|
-
|
131
|
-
end
|
129
|
+
#if [:loop, :block].include? template
|
130
|
+
# wargs[:label] = self.set_label
|
131
|
+
#end
|
132
132
|
@template = template
|
133
133
|
@wargs = wargs
|
134
134
|
end
|
135
135
|
|
136
136
|
def wasm_code
|
137
|
-
@
|
137
|
+
return '' unless T[@template]
|
138
138
|
wargs = {}
|
139
139
|
@wargs.each { |k, v| wargs[k] = (v.is_a?(Proc) ? v.call : v) }
|
140
|
+
# Because WNode#to_s generate wasm code from sometimes incomplete
|
141
|
+
# information, we must add the wasm_type key if needed by the template
|
142
|
+
if T[@template].index("%{wasm_type}")
|
143
|
+
puts "*** adding wasm_type to #{T[@template]}" unless wargs.has_key? :wasm_type
|
144
|
+
wargs[:wasm_type] ||= self.wasm_type
|
145
|
+
end
|
146
|
+
|
147
|
+
# debug code to activate if you get
|
148
|
+
# a Ruby warning on too many or too few arguments
|
149
|
+
# wargs.each do |k, v|
|
150
|
+
# if T[@template].index("%{#{k.to_s}}").nil?
|
151
|
+
# puts "**** Error wargs missing keys in wargs : template: #{@template} / wargs: #{wargs.inspect}"
|
152
|
+
# end
|
153
|
+
# end
|
154
|
+
#T[@template].scan(/%{([^}]+)}/).flatten.each do |k|
|
155
|
+
# unless wargs.has_key? k.to_sym
|
156
|
+
# puts "**** Error wargs missing keys in template: template: #{@template} / wargs: #{wargs.inspect}"
|
157
|
+
# end
|
158
|
+
# end
|
159
|
+
#logger.debug "code template : #{@template} / T : #{T[@template]} / wargs : #{wargs.inspect}" if @template
|
140
160
|
T[@template] ? T[@template] % wargs : ''
|
141
161
|
end
|
142
162
|
|
@@ -154,8 +174,13 @@ module Rlang::Parser
|
|
154
174
|
logger.debug "Setting wtype #{wtype} for wnode #{self}"
|
155
175
|
@wtype = wtype
|
156
176
|
@method.wtype = @wtype if self.method?
|
157
|
-
# update wasm_type template arg accordingly
|
158
|
-
|
177
|
+
# update wasm_type template arg accordingly if ti is already set
|
178
|
+
# (this is needed in some cases - e.g. operands - where node wtype
|
179
|
+
# is known once the wtypes of the children nodes are known )
|
180
|
+
if @wtype && @wargs.has_key?(:wasm_type)
|
181
|
+
logger.debug "Updating @wargs[:wasm_type] from '#{@wargs[:wasm_type]}' to '#{@wtype.wasm_type}'"
|
182
|
+
@wargs[:wasm_type] = @wtype.wasm_type
|
183
|
+
end
|
159
184
|
logger.debug "type #{self.type} wargs #{self.wargs} wtype #{@wtype}"
|
160
185
|
@wtype
|
161
186
|
end
|
@@ -715,7 +740,9 @@ module Rlang::Parser
|
|
715
740
|
logger.debug "children: #{self} / #{children.map(&:head)}" if self.link
|
716
741
|
|
717
742
|
case @type
|
718
|
-
# Section nodes
|
743
|
+
# Section nodes
|
744
|
+
when :comment
|
745
|
+
"\n%s%s" % [indent, self.wasm_code]
|
719
746
|
when :imports
|
720
747
|
"\n%s;;============= %s SECTION ===============\n" % [indent, @type.to_s.upcase] +
|
721
748
|
children.map { |wn| wn.transpile(depth) }.join('')
|