rlang 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- :'<s' => :lt_s,
38
- :< => :lt_u,
39
- :'>s' => :gt_s,
40
- :> => :gt_u,
41
- :'<=s' => :le_s,
42
- :<= => :le_u,
43
- :'>=s' => :ge_s,
44
- :>= => :ge_u
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
- ALL_OPS_MAP = [*ARITHMETIC_OPS_MAP, *RELATIONAL_OPS_MAP, *BOOLEAN_OPS_MAP, *UNARY_OPS_MAP].to_h
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
- I32: { I32: :cast_nope, I64: :cast_extend, F32: :cast_notyet, F64: :cast_notyet, Class: :cast_wtype, none: :cast_error},
61
- I64: { I32: :cast_wrap, I64: :cast_nope, F32: :cast_notyet, F64: :cast_notyet, Class: :cast_error, none: :cast_error},
62
- F32: { I32: :cast_notyet, I64: :cast_notyet, F32: :cast_nope, F64: :cast_notyet, Class: :cast_error, none: :cast_error},
63
- F64: { I32: :cast_notyet, I64: :cast_notyet, F32: :cast_notyet, F64: :cast_nope, Class: :cast_error, none: :cast_error},
64
- Class: { I32: :cast_wtype, I64: :cast_extend, F32: :cast_error, F64: :cast_error, Class: :cast_wtype, none: :cast_error},
65
- none: { I32: :cast_error, I64: :cast_error, F32: :cast_error, F64: :cast_error, Class: :cast_error, none: :cast_error},
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
- wtype: wns.wasm_type, size: wnc.class_size)
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, wtype: attr.wasm_type,
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
- attr_name: attr.wasm_name, wtype: attr.wasm_type,
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, wtype: const.wtype)
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, wtype: const.wtype, var_name: const.wasm_name)
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, wtype: ivar.wasm_type, offset: lambda { ivar.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, wtype: ivar.wasm_type, offset: lambda { ivar.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, wtype: cvar.wtype)
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, wtype: cvar.wtype, var_name: cvar.wasm_name)
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, wtype: lvar.wtype, var_name: lvar.wasm_name)
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, wtype: lvar.wtype, var_name: lvar.wasm_name)
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, wtype: wtype, value: value)
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, wtype: wtype, value: value)
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 string_static(string, data_label)
556
- # Allocate string itself and the attributes
557
- # of String object pointing to that string
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
- # Statically
567
- data_stg = self.string_static(string, data_label)
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 = self.string_static(string, data_label)
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
- wn_string = self.parser.parse(string_new_source, wnode)
589
- #puts wn_string; exit
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 , wtype: wtype)
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
- if (wnode.wtype.default? && wtype.class?) ||
616
- (wnode.wtype.class? && wtype.default?) ||
617
- (wnode.wtype.class? && wtype.class?)
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, wtype: wtype)
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 #{src} to #{dest}. Time to fix your code :-)"
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)).c(:operator, operator: op)
672
- wn_op.wtype = wtype
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
- # finish the setting of the operator node and
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
- wtype = self.class.leading_wtype(wnode_recv, *wnode_args)
794
+ leading_wtype = self.class.leading_wtype(wnode_recv, *wnode_args)
700
795
 
701
- wnode_op.wtype = wtype
702
- logger.debug "leading type cast: #{wtype}"
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
- self.cast(wnode_recv, wtype).reparent_to(wnode_op)
707
- self.cast(wnode_args.first, wtype).reparent_to(wnode_op) unless wnode_args.empty?
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
- legal_ops = RELATIONAL_OPS_MAP.values + [:add, :sub]
714
- raise "Only #{legal_ops.join(', ')} operators are supported on objects (got #{op} in #{wnode_op})" \
715
- unless legal_ops.include?(op)
716
- # if + or - operator then multiply arg by size of object
717
- if [:add, :sub].include? wnode_op.wargs[:operator]
718
- (wn_mulop = WNode.new(:insn, wnode_op)).c(:operator, operator: :mul)
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
- if k && (method = wnode.find_method(k, method_name, method_type))
799
- logger.debug "Found method #{method.name} in class #{method.klass.name}"
800
- (wn_call = WNode.new(:insn, wnode)).c(:call, func_name: method.wasm_name)
801
- wn_call.wtype = method.wtype
802
- wn_call
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
- # it's a native Wasm operator
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, wtype: wnode_cond_exp.wtype)
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
@@ -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 %{wtype})
56
- (%{wtype}.load offset=%{offset} (local.get $_self_))},
57
- attr_setter: %q{func %{func_name} (param $_self_ i32) (param %{attr_name} %{wtype}) (result %{wtype})
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
- (%{wtype}.store offset=%{offset} (local.get $_self_) (local.get %{attr_name}))},
60
- class_size: %q{func %{func_name} (result %{wtype})
61
- (%{wtype}.const %{size})},
62
- comment: ';; %{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
- wargs[:label] = self.set_label
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
- @wargs[:wasm_type] ||= self.wasm_type
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
- @wargs[:wasm_type] = @wtype.wasm_type if @wtype
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('')