@@ -206,6 +206,7 @@ impl<'a> std::fmt::Display for InvariantPrinter<'a> {
206206 BOP_FREEZE => write ! ( f, "BOP_FREEZE" ) ?,
207207 BOP_UMINUS => write ! ( f, "BOP_UMINUS" ) ?,
208208 BOP_MAX => write ! ( f, "BOP_MAX" ) ?,
209+ BOP_AREF => write ! ( f, "BOP_AREF" ) ?,
209210 _ => write ! ( f, "{bop}" ) ?,
210211 }
211212 write ! ( f, ")" )
@@ -1317,6 +1318,25 @@ impl Function {
13171318 }
13181319 }
13191320
1321+ fn try_rewrite_aref ( & mut self , block : BlockId , orig_insn_id : InsnId , self_val : InsnId , idx_val : InsnId ) {
1322+ let self_type = self . type_of ( self_val) ;
1323+ let idx_type = self . type_of ( idx_val) ;
1324+ if self_type. is_subtype ( types:: ArrayExact ) {
1325+ if let Some ( array_obj) = self_type. ruby_object ( ) {
1326+ if array_obj. is_frozen ( ) {
1327+ if let Some ( idx) = idx_type. fixnum_value ( ) {
1328+ self . push_insn ( block, Insn :: PatchPoint ( Invariant :: BOPRedefined { klass : ARRAY_REDEFINED_OP_FLAG , bop : BOP_AREF } ) ) ;
1329+ let val = unsafe { rb_yarv_ary_entry_internal ( array_obj, idx) } ;
1330+ let const_insn = self . push_insn ( block, Insn :: Const { val : Const :: Value ( val) } ) ;
1331+ self . make_equal_to ( orig_insn_id, const_insn) ;
1332+ return ;
1333+ }
1334+ }
1335+ }
1336+ }
1337+ self . push_insn_id ( block, orig_insn_id) ;
1338+ }
1339+
13201340 /// Rewrite SendWithoutBlock opcodes into SendWithoutBlockDirect opcodes if we know the target
13211341 /// ISEQ statically. This removes run-time method lookups and opens the door for inlining.
13221342 fn optimize_direct_sends ( & mut self ) {
@@ -1351,6 +1371,8 @@ impl Function {
13511371 self . try_rewrite_freeze ( block, insn_id, self_val) ,
13521372 Insn :: SendWithoutBlock { self_val, call_info : CallInfo { method_name } , args, .. } if method_name == "-@" && args. len ( ) == 0 =>
13531373 self . try_rewrite_uminus ( block, insn_id, self_val) ,
1374+ Insn :: SendWithoutBlock { self_val, call_info : CallInfo { method_name } , args, .. } if method_name == "[]" && args. len ( ) == 1 =>
1375+ self . try_rewrite_aref ( block, insn_id, self_val, args[ 0 ] ) ,
13541376 Insn :: SendWithoutBlock { mut self_val, call_info, cd, args, state } => {
13551377 let frame_state = self . frame_state ( state) ;
13561378 let ( klass, guard_equal_to) = if let Some ( klass) = self . type_of ( self_val) . runtime_exact_ruby_class ( ) {
@@ -6068,4 +6090,64 @@ mod opt_tests {
60686090 SideExit
60696091 "# ] ] ) ;
60706092 }
6093+
6094+ #[ test]
6095+ fn test_eliminate_load_from_frozen_array_in_bounds ( ) {
6096+ eval ( r##"
6097+ def test = [4,5,6].freeze[1]
6098+ "## ) ;
6099+ assert_optimized_method_hir ( "test" , expect ! [ [ r#"
6100+ fn test:
6101+ bb0(v0:BasicObject):
6102+ PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE)
6103+ PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_AREF)
6104+ v11:Fixnum[5] = Const Value(5)
6105+ Return v11
6106+ "# ] ] ) ;
6107+ }
6108+
6109+ #[ test]
6110+ fn test_eliminate_load_from_frozen_array_negative ( ) {
6111+ eval ( r##"
6112+ def test = [4,5,6].freeze[-3]
6113+ "## ) ;
6114+ assert_optimized_method_hir ( "test" , expect ! [ [ r#"
6115+ fn test:
6116+ bb0(v0:BasicObject):
6117+ PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE)
6118+ PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_AREF)
6119+ v11:Fixnum[4] = Const Value(4)
6120+ Return v11
6121+ "# ] ] ) ;
6122+ }
6123+
6124+ #[ test]
6125+ fn test_eliminate_load_from_frozen_array_negative_out_of_bounds ( ) {
6126+ eval ( r##"
6127+ def test = [4,5,6].freeze[-10]
6128+ "## ) ;
6129+ assert_optimized_method_hir ( "test" , expect ! [ [ r#"
6130+ fn test:
6131+ bb0(v0:BasicObject):
6132+ PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE)
6133+ PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_AREF)
6134+ v11:NilClassExact = Const Value(nil)
6135+ Return v11
6136+ "# ] ] ) ;
6137+ }
6138+
6139+ #[ test]
6140+ fn test_eliminate_load_from_frozen_array_out_of_bounds ( ) {
6141+ eval ( r##"
6142+ def test = [4,5,6].freeze[10]
6143+ "## ) ;
6144+ assert_optimized_method_hir ( "test" , expect ! [ [ r#"
6145+ fn test:
6146+ bb0(v0:BasicObject):
6147+ PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE)
6148+ PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_AREF)
6149+ v11:NilClassExact = Const Value(nil)
6150+ Return v11
6151+ "# ] ] ) ;
6152+ }
60716153}
0 commit comments