@@ -550,6 +550,7 @@ pub enum Insn {
550
550
// Distinct from `SendWithoutBlock` with `mid:to_s` because does not have a patch point for String to_s being redefined
551
551
ObjToString { val : InsnId , call_info : CallInfo , cd : * const rb_call_data , state : InsnId } ,
552
552
AnyToString { val : InsnId , str : InsnId , state : InsnId } ,
553
+ ConcatStrings { strings : Vec < InsnId > , state : InsnId } ,
553
554
554
555
/// Side-exit if val doesn't have the expected type.
555
556
GuardType { val : InsnId , guard_type : Type , state : InsnId } ,
@@ -730,7 +731,14 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
730
731
write ! ( f, ", {arg}" ) ?;
731
732
}
732
733
Ok ( ( ) )
733
- } ,
734
+ }
735
+ Insn :: ConcatStrings { strings, state : _ } => {
736
+ write ! ( f, "ConcatStrings {}" , strings[ 0 ] ) ?;
737
+ for string in strings. iter ( ) . skip ( 1 ) {
738
+ write ! ( f, ", {string}" ) ?;
739
+ }
740
+ Ok ( ( ) )
741
+ }
734
742
Insn :: Snapshot { state } => write ! ( f, "Snapshot {}" , state) ,
735
743
Insn :: Defined { op_type, v, .. } => {
736
744
// op_type (enum defined_type) printing logic from iseq.c.
@@ -1138,6 +1146,7 @@ impl Function {
1138
1146
str : find ! ( * str ) ,
1139
1147
state : * state,
1140
1148
} ,
1149
+ ConcatStrings { state, strings } => ConcatStrings { state : * state, strings : find_vec ! ( strings) } ,
1141
1150
SendWithoutBlock { self_val, call_info, cd, args, state } => SendWithoutBlock {
1142
1151
self_val : find ! ( * self_val) ,
1143
1152
call_info : call_info. clone ( ) ,
@@ -1271,6 +1280,7 @@ impl Function {
1271
1280
Insn :: ToArray { .. } => types:: ArrayExact ,
1272
1281
Insn :: ObjToString { .. } => types:: BasicObject ,
1273
1282
Insn :: AnyToString { .. } => types:: String ,
1283
+ Insn :: ConcatStrings { .. } => types:: StringExact ,
1274
1284
Insn :: GetLocal { .. } => types:: BasicObject ,
1275
1285
// The type of Snapshot doesn't really matter; it's never materialized. It's used only
1276
1286
// as a reference for FrameState, which we use to generate side-exit code.
@@ -1902,7 +1912,8 @@ impl Function {
1902
1912
worklist. extend ( args) ;
1903
1913
worklist. push_back ( state) ;
1904
1914
}
1905
- & Insn :: InvokeBuiltin { ref args, state, .. } => {
1915
+ & Insn :: InvokeBuiltin { ref args, state, .. }
1916
+ | & Insn :: ConcatStrings { strings : ref args, state } => {
1906
1917
worklist. extend ( args) ;
1907
1918
worklist. push_back ( state)
1908
1919
}
@@ -3162,6 +3173,16 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
3162
3173
let anytostring = fun. push_insn ( block, Insn :: AnyToString { val, str, state : exit_id } ) ;
3163
3174
state. stack_push ( anytostring) ;
3164
3175
}
3176
+ YARVINSN_concatstrings => {
3177
+ let num = get_arg ( pc, 0 ) . as_u32 ( ) ;
3178
+ let mut strings = Vec :: with_capacity ( num. as_usize ( ) ) ;
3179
+ for _ in 0 ..num {
3180
+ strings. push ( state. stack_pop ( ) ?) ;
3181
+ }
3182
+ strings. reverse ( ) ;
3183
+ let snapshot = fun. push_insn ( block, Insn :: Snapshot { state : exit_state } ) ;
3184
+ state. stack_push ( fun. push_insn ( block, Insn :: ConcatStrings { state : snapshot, strings } ) ) ;
3185
+ }
3165
3186
_ => {
3166
3187
// Unknown opcode; side-exit into the interpreter
3167
3188
let exit_id = fun. push_insn ( block, Insn :: Snapshot { state : exit_state } ) ;
@@ -5046,7 +5067,8 @@ mod tests {
5046
5067
v3:Fixnum[1] = Const Value(1)
5047
5068
v5:BasicObject = ObjToString v3
5048
5069
v7:String = AnyToString v3, str: v5
5049
- SideExit UnknownOpcode(concatstrings)
5070
+ v9:StringExact = ConcatStrings v2, v7
5071
+ Return v9
5050
5072
"# ] ] ) ;
5051
5073
}
5052
5074
@@ -6827,7 +6849,8 @@ mod opt_tests {
6827
6849
v2:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000))
6828
6850
v3:StringExact[VALUE(0x1008)] = Const Value(VALUE(0x1008))
6829
6851
v4:StringExact = StringCopy v3
6830
- SideExit UnknownOpcode(concatstrings)
6852
+ v10:StringExact = ConcatStrings v2, v4
6853
+ Return v10
6831
6854
"# ] ] ) ;
6832
6855
}
6833
6856
@@ -6841,9 +6864,10 @@ mod opt_tests {
6841
6864
bb0(v0:BasicObject):
6842
6865
v2:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000))
6843
6866
v3:Fixnum[1] = Const Value(1)
6844
- v10:BasicObject = SendWithoutBlock v3, :to_s
6845
- v7:String = AnyToString v3, str: v10
6846
- SideExit UnknownOpcode(concatstrings)
6867
+ v11:BasicObject = SendWithoutBlock v3, :to_s
6868
+ v7:String = AnyToString v3, str: v11
6869
+ v9:StringExact = ConcatStrings v2, v7
6870
+ Return v9
6847
6871
"# ] ] ) ;
6848
6872
}
6849
6873
0 commit comments