@@ -54,20 +54,23 @@ def initialize(
5454 frozen_string_literal : false ,
5555 inline_const_cache : true ,
5656 operands_unification : true ,
57- specialized_instruction : true
57+ specialized_instruction : true ,
58+ tailcall_optimization : false
5859 )
5960 @frozen_string_literal = frozen_string_literal
6061 @inline_const_cache = inline_const_cache
6162 @operands_unification = operands_unification
6263 @specialized_instruction = specialized_instruction
64+ @tailcall_optimization = tailcall_optimization
6365 end
6466
6567 def to_hash
6668 {
6769 frozen_string_literal : @frozen_string_literal ,
6870 inline_const_cache : @inline_const_cache ,
6971 operands_unification : @operands_unification ,
70- specialized_instruction : @specialized_instruction
72+ specialized_instruction : @specialized_instruction ,
73+ tailcall_optimization : @tailcall_optimization
7174 }
7275 end
7376
@@ -90,6 +93,10 @@ def operands_unification?
9093 def specialized_instruction?
9194 @specialized_instruction
9295 end
96+
97+ def tailcall_optimization?
98+ @tailcall_optimization
99+ end
93100 end
94101
95102 # This visitor is responsible for converting Syntax Tree nodes into their
@@ -716,12 +723,17 @@ def visit_call(node)
716723 end
717724 end
718725
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+
719730 if node . receiver
720731 if node . receiver . is_a? ( VarRef )
721732 lookup = iseq . local_variable ( node . receiver . value . value . to_sym )
722733
723734 if lookup . local . is_a? ( LocalTable ::BlockLocal )
724735 iseq . getblockparamproxy ( lookup . index , lookup . level )
736+ block_receiver = true
725737 else
726738 visit ( node . receiver )
727739 end
@@ -752,6 +764,7 @@ def visit_call(node)
752764 when ArgsForward
753765 flag |= CallData ::CALL_ARGS_SPLAT
754766 flag |= CallData ::CALL_ARGS_BLOCKARG
767+ flag |= CallData ::CALL_TAILCALL if options . tailcall_optimization?
755768
756769 lookup = iseq . local_table . find ( :* )
757770 iseq . getlocal ( lookup . index , lookup . level )
@@ -768,9 +781,22 @@ def visit_call(node)
768781 end
769782
770783 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.
771789 flag |= CallData ::CALL_ARGS_SIMPLE if block_iseq . nil? && flag == 0
790+
791+ # If there's no receiver, then this is an "fcall".
772792 flag |= CallData ::CALL_FCALL if node . receiver . nil?
773793
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+
774800 iseq . send (
775801 YARV . calldata ( node . message . value . to_sym , argc , flag ) ,
776802 block_iseq
0 commit comments