@@ -6728,7 +6728,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
67286728 // Keep compiling blocks until the queue becomes empty
67296729 let mut visited = HashSet :: new ( ) ;
67306730 let iseq_size = unsafe { get_iseq_encoded_size ( iseq) } ;
6731- while let Some ( ( incoming_state, block, mut insn_idx, mut local_inval) ) = queue. pop_front ( ) {
6731+ while let Some ( ( incoming_state, mut block, mut insn_idx, mut local_inval) ) = queue. pop_front ( ) {
67326732 // Compile each block only once
67336733 if visited. contains ( & block) { continue ; }
67346734 visited. insert ( block) ;
@@ -7899,8 +7899,44 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
78997899 fun. push_insn ( block, Insn :: SideExit { state : exit_id, reason : SideExitReason :: UnhandledYARVInsn ( opcode) } ) ;
79007900 break ; // End the block
79017901 }
7902- let result = fun. push_insn ( block, Insn :: GetIvar { self_val : self_param, id, ic, state : exit_id } ) ;
7903- state. stack_push ( result) ;
7902+ if let Some ( summary) = fun. polymorphic_summary ( & profiles, self_param, exit_state. insn_idx ) {
7903+ self_param = fun. push_insn ( block, Insn :: GuardType { val : self_param, guard_type : types:: HeapBasicObject , state : exit_id } ) ;
7904+ let shape = fun. load_shape ( block, self_param) ;
7905+ let join_block = insn_idx_to_block. get ( & insn_idx) . copied ( ) . unwrap_or_else ( || fun. new_block ( insn_idx) ) ;
7906+ let join_param = fun. push_insn ( join_block, Insn :: Param ) ;
7907+ // Dedup by expected shape so objects with different classes but the same shape can share code
7908+ let mut seen_shapes = Vec :: with_capacity ( summary. buckets ( ) . len ( ) ) ;
7909+ for & profiled_type in summary. buckets ( ) {
7910+ // End of the buckets
7911+ if profiled_type. is_empty ( ) { break ; }
7912+ // Instance variable lookups on immediate values are always nil; don't bother
7913+ if profiled_type. flags ( ) . is_immediate ( ) { continue ; }
7914+ let expected_shape = profiled_type. shape ( ) ;
7915+ assert ! ( expected_shape. is_valid( ) ) ;
7916+ if seen_shapes. contains ( & expected_shape) { continue ; }
7917+ seen_shapes. push ( expected_shape) ;
7918+ let expected_shape_const = fun. push_insn ( block, Insn :: Const { val : Const :: CShape ( expected_shape) } ) ;
7919+ let has_shape = fun. push_insn ( block, Insn :: IsBitEqual { left : shape, right : expected_shape_const } ) ;
7920+ let iftrue_block = fun. new_block ( insn_idx) ;
7921+ let target = BranchEdge { target : iftrue_block, args : vec ! [ ] } ;
7922+ fun. push_insn ( block, Insn :: IfTrue { val : has_shape, target } ) ;
7923+ let result = fun. load_ivar ( iftrue_block, self_param, profiled_type, id, exit_id) ;
7924+ fun. push_insn ( iftrue_block, Insn :: Jump ( BranchEdge { target : join_block, args : vec ! [ result] } ) ) ;
7925+ }
7926+ // In the fallthrough case, do a generic interpreter getivar and then join.
7927+ let result = fun. push_insn ( block, Insn :: GetIvar { self_val : self_param, id, ic, state : exit_id } ) ;
7928+ fun. push_insn ( block, Insn :: Jump ( BranchEdge { target : join_block, args : vec ! [ result] } ) ) ;
7929+ state. stack_push ( join_param) ;
7930+ // Continue compilation from the join block at the next instruction.
7931+ // Make a copy of the current state without the args (pop the receiver
7932+ // and push the result) because we just use the locals/stack sizes to
7933+ // make the right number of Params
7934+ block = join_block;
7935+ } else {
7936+ // Possibly monomorphic case; handled in optimize_getivar
7937+ let result = fun. push_insn ( block, Insn :: GetIvar { self_val : self_param, id, ic, state : exit_id } ) ;
7938+ state. stack_push ( result) ;
7939+ }
79047940 }
79057941 YARVINSN_setinstancevariable => {
79067942 let id = ID ( get_arg ( pc, 0 ) . as_u64 ( ) ) ;
0 commit comments