@@ -113,6 +113,13 @@ pub enum Invariant {
113113 /// The method ID of the method we want to assume unchanged
114114 method : ID ,
115115 } ,
116+ /// A list of constant expression path segments that must have not been written to for the
117+ /// following code to be valid.
118+ StableConstantNames {
119+ idlist : * const ID ,
120+ } ,
121+ /// There is one ractor running. If a non-root ractor gets spawned, this is invalidated.
122+ SingleRactorMode ,
116123}
117124
118125impl Invariant {
@@ -161,6 +168,22 @@ impl<'a> std::fmt::Display for InvariantPrinter<'a> {
161168 self . ptr_map. map_id( method. 0 )
162169 )
163170 }
171+ Invariant :: StableConstantNames { idlist } => {
172+ write ! ( f, "StableConstantNames({:p}, " , self . ptr_map. map_ptr( idlist) ) ?;
173+ let mut idx = 0 ;
174+ let mut sep = "" ;
175+ loop {
176+ let id = unsafe { * idlist. wrapping_add ( idx) } ;
177+ if id. 0 == 0 {
178+ break ;
179+ }
180+ write ! ( f, "{sep}{}" , id. contents_lossy( ) ) ?;
181+ sep = "::" ;
182+ idx += 1 ;
183+ }
184+ write ! ( f, ")" )
185+ }
186+ Invariant :: SingleRactorMode => write ! ( f, "SingleRactorMode" ) ,
164187 }
165188 }
166189}
@@ -292,7 +315,7 @@ pub enum Insn {
292315 // with IfTrue/IfFalse in the backend to generate jcc.
293316 Test { val : InsnId } ,
294317 Defined { op_type : usize , obj : VALUE , pushval : VALUE , v : InsnId } ,
295- GetConstantPath { ic : * const u8 } ,
318+ GetConstantPath { ic : * const iseq_inline_constant_cache } ,
296319
297320 //NewObject?
298321 //SetIvar {},
@@ -1013,6 +1036,25 @@ impl Function {
10131036 let send_direct = self . push_insn ( block, Insn :: SendWithoutBlockDirect { self_val, call_info, cd, iseq, args, state } ) ;
10141037 self . make_equal_to ( insn_id, send_direct) ;
10151038 }
1039+ Insn :: GetConstantPath { ic } => {
1040+ let idlist: * const ID = unsafe { ( * ic) . segments } ;
1041+ let ice = unsafe { ( * ic) . entry } ;
1042+ if ice. is_null ( ) {
1043+ self . push_insn_id ( block, insn_id) ; continue ;
1044+ }
1045+ let cref_sensitive = !unsafe { ( * ice) . ic_cref } . is_null ( ) ;
1046+ let multi_ractor_mode = unsafe { rb_zjit_multi_ractor_p ( ) } ;
1047+ if cref_sensitive || multi_ractor_mode {
1048+ self . push_insn_id ( block, insn_id) ; continue ;
1049+ }
1050+ // Assume single-ractor mode.
1051+ self . push_insn ( block, Insn :: PatchPoint ( Invariant :: SingleRactorMode ) ) ;
1052+ // Invalidate output code on any constant writes associated with constants
1053+ // referenced after the PatchPoint.
1054+ self . push_insn ( block, Insn :: PatchPoint ( Invariant :: StableConstantNames { idlist } ) ) ;
1055+ let replacement = self . push_insn ( block, Insn :: Const { val : Const :: Value ( unsafe { ( * ice) . value } ) } ) ;
1056+ self . make_equal_to ( insn_id, replacement) ;
1057+ }
10161058 _ => { self . push_insn_id ( block, insn_id) ; }
10171059 }
10181060 }
@@ -1714,7 +1756,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
17141756 state. stack_push ( fun. push_insn ( block, Insn :: Defined { op_type, obj, pushval, v } ) ) ;
17151757 }
17161758 YARVINSN_opt_getconstant_path => {
1717- let ic = get_arg ( pc, 0 ) . as_ptr :: < u8 > ( ) ;
1759+ let ic = get_arg ( pc, 0 ) . as_ptr ( ) ;
17181760 state. stack_push ( fun. push_insn ( block, Insn :: GetConstantPath { ic } ) ) ;
17191761 }
17201762 YARVINSN_branchunless => {
@@ -3565,4 +3607,70 @@ mod opt_tests {
35653607 Return v7
35663608 "# ] ] ) ;
35673609 }
3610+
3611+ #[ test]
3612+ fn dont_replace_get_constant_path_with_empty_ic ( ) {
3613+ eval ( "
3614+ def test = Kernel
3615+ " ) ;
3616+ assert_optimized_method_hir ( "test" , expect ! [ [ r#"
3617+ fn test:
3618+ bb0():
3619+ v1:BasicObject = GetConstantPath 0x1000
3620+ Return v1
3621+ "# ] ] ) ;
3622+ }
3623+
3624+ #[ test]
3625+ fn dont_replace_get_constant_path_with_invalidated_ic ( ) {
3626+ eval ( "
3627+ def test = Kernel
3628+ test
3629+ Kernel = 5
3630+ " ) ;
3631+ assert_optimized_method_hir ( "test" , expect ! [ [ r#"
3632+ fn test:
3633+ bb0():
3634+ v1:BasicObject = GetConstantPath 0x1000
3635+ Return v1
3636+ "# ] ] ) ;
3637+ }
3638+
3639+ #[ test]
3640+ fn replace_get_constant_path_with_const ( ) {
3641+ eval ( "
3642+ def test = Kernel
3643+ test
3644+ " ) ;
3645+ assert_optimized_method_hir ( "test" , expect ! [ [ r#"
3646+ fn test:
3647+ bb0():
3648+ PatchPoint SingleRactorMode
3649+ PatchPoint StableConstantNames(0x1000, Kernel)
3650+ v5:BasicObject[VALUE(0x1008)] = Const Value(VALUE(0x1008))
3651+ Return v5
3652+ "# ] ] ) ;
3653+ }
3654+
3655+ #[ test]
3656+ fn replace_nested_get_constant_path_with_const ( ) {
3657+ eval ( "
3658+ module Foo
3659+ module Bar
3660+ class C
3661+ end
3662+ end
3663+ end
3664+ def test = Foo::Bar::C
3665+ test
3666+ " ) ;
3667+ assert_optimized_method_hir ( "test" , expect ! [ [ r#"
3668+ fn test:
3669+ bb0():
3670+ PatchPoint SingleRactorMode
3671+ PatchPoint StableConstantNames(0x1000, Foo::Bar::C)
3672+ v5:BasicObject[VALUE(0x1008)] = Const Value(VALUE(0x1008))
3673+ Return v5
3674+ "# ] ] ) ;
3675+ }
35683676}
0 commit comments