@@ -54,20 +54,23 @@ def initialize(
54
54
frozen_string_literal : false ,
55
55
inline_const_cache : true ,
56
56
operands_unification : true ,
57
- specialized_instruction : true
57
+ specialized_instruction : true ,
58
+ tailcall_optimization : false
58
59
)
59
60
@frozen_string_literal = frozen_string_literal
60
61
@inline_const_cache = inline_const_cache
61
62
@operands_unification = operands_unification
62
63
@specialized_instruction = specialized_instruction
64
+ @tailcall_optimization = tailcall_optimization
63
65
end
64
66
65
67
def to_hash
66
68
{
67
69
frozen_string_literal : @frozen_string_literal ,
68
70
inline_const_cache : @inline_const_cache ,
69
71
operands_unification : @operands_unification ,
70
- specialized_instruction : @specialized_instruction
72
+ specialized_instruction : @specialized_instruction ,
73
+ tailcall_optimization : @tailcall_optimization
71
74
}
72
75
end
73
76
@@ -90,6 +93,10 @@ def operands_unification?
90
93
def specialized_instruction?
91
94
@specialized_instruction
92
95
end
96
+
97
+ def tailcall_optimization?
98
+ @tailcall_optimization
99
+ end
93
100
end
94
101
95
102
# This visitor is responsible for converting Syntax Tree nodes into their
@@ -716,12 +723,17 @@ def visit_call(node)
716
723
end
717
724
end
718
725
726
+ # Track whether or not this is a method call on a block proxy receiver.
727
+ # If it is, we can potentially do tailcall optimizations on it.
728
+ block_receiver = false
729
+
719
730
if node . receiver
720
731
if node . receiver . is_a? ( VarRef )
721
732
lookup = iseq . local_variable ( node . receiver . value . value . to_sym )
722
733
723
734
if lookup . local . is_a? ( LocalTable ::BlockLocal )
724
735
iseq . getblockparamproxy ( lookup . index , lookup . level )
736
+ block_receiver = true
725
737
else
726
738
visit ( node . receiver )
727
739
end
@@ -752,6 +764,7 @@ def visit_call(node)
752
764
when ArgsForward
753
765
flag |= CallData ::CALL_ARGS_SPLAT
754
766
flag |= CallData ::CALL_ARGS_BLOCKARG
767
+ flag |= CallData ::CALL_TAILCALL if options . tailcall_optimization?
755
768
756
769
lookup = iseq . local_table . find ( :* )
757
770
iseq . getlocal ( lookup . index , lookup . level )
@@ -768,9 +781,22 @@ def visit_call(node)
768
781
end
769
782
770
783
block_iseq = visit ( node . block ) if node . block
784
+
785
+ # If there's no block and we don't already have any special flags set,
786
+ # then we can safely call this simple arguments. Note that has to be the
787
+ # first flag we set after looking at the arguments to get the flags
788
+ # correct.
771
789
flag |= CallData ::CALL_ARGS_SIMPLE if block_iseq . nil? && flag == 0
790
+
791
+ # If there's no receiver, then this is an "fcall".
772
792
flag |= CallData ::CALL_FCALL if node . receiver . nil?
773
793
794
+ # If we're calling a method on the passed block object and we have
795
+ # tailcall optimizations turned on, then we can set the tailcall flag.
796
+ if block_receiver && options . tailcall_optimization?
797
+ flag |= CallData ::CALL_TAILCALL
798
+ end
799
+
774
800
iseq . send (
775
801
YARV . calldata ( node . message . value . to_sym , argc , flag ) ,
776
802
block_iseq
0 commit comments