@@ -2638,6 +2638,22 @@ impl Function {
26382638 }
26392639 }
26402640
2641+ fn polymorphic_summary < ' profile > ( & self , profiles : & ' profile ProfileOracle , recv : InsnId , insn_idx : usize ) -> Option < & ' profile TypeDistributionSummary > {
2642+ let Some ( entries) = profiles. types . get ( & insn_idx) else {
2643+ return None ;
2644+ } ;
2645+ let recv = self . chase_insn ( recv) ;
2646+ for ( entry_insn, entry_type_summary) in entries {
2647+ if self . union_find . borrow ( ) . find_const ( * entry_insn) == recv {
2648+ if entry_type_summary. is_polymorphic ( ) {
2649+ return Some ( entry_type_summary) ;
2650+ }
2651+ return None ;
2652+ }
2653+ }
2654+ None
2655+ }
2656+
26412657 /// Prepare arguments for a direct send, handling keyword argument reordering and default synthesis.
26422658 /// Returns the (state, processed_args, kw_bits) to use for the SendWithoutBlockDirect instruction,
26432659 /// or Err with the fallback reason if direct send isn't possible.
@@ -5929,6 +5945,25 @@ fn invalidates_locals(opcode: u32, operands: *const VALUE) -> bool {
59295945/// The index of the self parameter in the HIR function
59305946pub const SELF_PARAM_IDX : usize = 0 ;
59315947
5948+ // TODO place this better
5949+ fn new_branch_block (
5950+ fun : & mut Function ,
5951+ insn_idx : u32 ,
5952+ exit_state : & FrameState ,
5953+ locals_count : usize ,
5954+ stack_count : usize ,
5955+ ) -> ( BlockId , InsnId , FrameState , InsnId ) {
5956+ let block = fun. new_block ( insn_idx) ;
5957+ let self_param = fun. push_insn ( block, Insn :: Param ) ;
5958+ let mut state = exit_state. clone ( ) ;
5959+ state. locals . clear ( ) ;
5960+ state. stack . clear ( ) ;
5961+ state. locals . extend ( ( 0 ..locals_count) . map ( |_| fun. push_insn ( block, Insn :: Param ) ) ) ;
5962+ state. stack . extend ( ( 0 ..stack_count) . map ( |_| fun. push_insn ( block, Insn :: Param ) ) ) ;
5963+ let snapshot = fun. push_insn ( block, Insn :: Snapshot { state : state. clone ( ) } ) ;
5964+ ( block, self_param, state, snapshot)
5965+ }
5966+
59325967/// Compile ISEQ into High-level IR
59335968pub fn iseq_to_hir ( iseq : * const rb_iseq_t ) -> Result < Function , ParseError > {
59345969 if !ZJITState :: can_compile_iseq ( iseq) {
@@ -6456,24 +6491,6 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
64566491 state. stack_push ( fun. push_insn ( block, Insn :: Const { val : Const :: Value ( unsafe { rb_block_param_proxy } ) } ) ) ;
64576492 }
64586493 YARVINSN_getblockparam => {
6459- fn new_branch_block (
6460- fun : & mut Function ,
6461- insn_idx : u32 ,
6462- exit_state : & FrameState ,
6463- locals_count : usize ,
6464- stack_count : usize ,
6465- ) -> ( BlockId , InsnId , FrameState , InsnId ) {
6466- let block = fun. new_block ( insn_idx) ;
6467- let self_param = fun. push_insn ( block, Insn :: Param ) ;
6468- let mut state = exit_state. clone ( ) ;
6469- state. locals . clear ( ) ;
6470- state. stack . clear ( ) ;
6471- state. locals . extend ( ( 0 ..locals_count) . map ( |_| fun. push_insn ( block, Insn :: Param ) ) ) ;
6472- state. stack . extend ( ( 0 ..stack_count) . map ( |_| fun. push_insn ( block, Insn :: Param ) ) ) ;
6473- let snapshot = fun. push_insn ( block, Insn :: Snapshot { state : state. clone ( ) } ) ;
6474- ( block, self_param, state, snapshot)
6475- }
6476-
64776494 fn finish_getblockparam_branch (
64786495 fun : & mut Function ,
64796496 block : BlockId ,
@@ -6834,8 +6851,84 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
68346851 fun. push_insn ( block, Insn :: SideExit { state : exit_id, reason : SideExitReason :: UnhandledYARVInsn ( opcode) } ) ;
68356852 break ; // End the block
68366853 }
6837- let result = fun. push_insn ( block, Insn :: GetIvar { self_val : self_param, id, ic, state : exit_id } ) ;
6838- state. stack_push ( result) ;
6854+
6855+ let mut t_object_varying_shapes = true ;
6856+ if let Some ( summary) = fun. polymorphic_summary ( & profiles, self_param, exit_state. insn_idx ) {
6857+ // Only split in cases that vary in shape for now -- all T_OBJECT
6858+ for & profiled_type in summary. buckets ( ) {
6859+ if profiled_type. is_empty ( ) { break ; }
6860+ if !profiled_type. flags ( ) . is_t_object ( ) {
6861+ t_object_varying_shapes = false ;
6862+ }
6863+ }
6864+
6865+ // Just a GetIvar if not splitting
6866+ if !t_object_varying_shapes {
6867+ let result = fun. push_insn ( block, Insn :: GetIvar { self_val : self_param, id, ic, state : exit_id } ) ;
6868+ state. stack_push ( result) ;
6869+ } else {
6870+ let self_val = fun. push_insn ( block, Insn :: GuardType { val : self_param, guard_type : types:: HeapBasicObject , state : exit_id } ) ;
6871+ let shape = fun. load_shape ( block, self_val) ;
6872+ let join_block = insn_idx_to_block. get ( & insn_idx) . copied ( ) . unwrap_or_else ( || fun. new_block ( insn_idx) ) ;
6873+
6874+ // For each profiled shape, make a block that handle it
6875+ for & profiled_type in summary. buckets ( ) {
6876+ if profiled_type. is_empty ( ) { break ; }
6877+ let mut ivar_index: u16 = 0 ;
6878+ if ! unsafe { rb_shape_get_iv_index ( profiled_type. shape ( ) . 0 , id, & mut ivar_index) } {
6879+ // TODO revisit this, can just return nil.
6880+ // If there is no IVAR index, then the ivar was undefined when we
6881+ // entered the compiler. That means we can just return nil for this
6882+ // shape + iv name
6883+ continue ;
6884+ }
6885+ let branch_insn_idx = exit_state. insn_idx as u32 ;
6886+ let locals_count = state. locals . len ( ) ;
6887+ let stack_count = state. stack . len ( ) ;
6888+ let ( get_field_block, field_block_self, mut get_field_state, _) = new_branch_block ( & mut fun, branch_insn_idx, & exit_state, locals_count, stack_count) ;
6889+ let expected_shape = fun. push_insn ( block, Insn :: Const { val : Const :: CShape ( profiled_type. shape ( ) ) } ) ;
6890+ let test_id = fun. push_insn ( block, Insn :: IsBitEqual { left : shape, right : expected_shape } ) ;
6891+ fun. push_insn ( block, Insn :: IfTrue {
6892+ val : test_id,
6893+ target : BranchEdge {
6894+ target : get_field_block,
6895+ args : state. as_args ( self_param)
6896+ }
6897+ } ) ;
6898+
6899+ let ivar = if profiled_type. flags ( ) . is_embedded ( ) {
6900+ // See ROBJECT_FIELDS() from include/ruby/internal/core/robject.h
6901+ let offset = ROBJECT_OFFSET_AS_ARY as i32 + ( SIZEOF_VALUE * ivar_index. to_usize ( ) ) as i32 ;
6902+ fun. push_insn ( get_field_block, Insn :: LoadField { recv : self_val, id, offset, return_type : types:: BasicObject } )
6903+ } else {
6904+ let as_heap = fun. push_insn ( get_field_block, Insn :: LoadField { recv : self_val, id : ID ! ( _as_heap) , offset : ROBJECT_OFFSET_AS_HEAP_FIELDS as i32 , return_type : types:: CPtr } ) ;
6905+
6906+ let offset = SIZEOF_VALUE_I32 * ivar_index as i32 ;
6907+ fun. push_insn ( get_field_block, Insn :: LoadField { recv : as_heap, id, offset, return_type : types:: BasicObject } )
6908+ } ;
6909+
6910+ get_field_state. stack_push ( ivar) ;
6911+ fun. push_insn ( get_field_block, Insn :: Jump ( BranchEdge {
6912+ target : join_block,
6913+ args : get_field_state. as_args ( field_block_self) ,
6914+ } ) ) ;
6915+ }
6916+
6917+ // The fallback case, just getivar
6918+ let ivar = fun. push_insn ( block, Insn :: GetIvar { self_val : self_param, id, ic, state : exit_id } ) ;
6919+ state. stack_push ( ivar) ;
6920+ fun. push_insn ( block, Insn :: Jump ( BranchEdge {
6921+ target : join_block,
6922+ args : state. as_args ( self_param) ,
6923+ } ) ) ;
6924+ queue. push_back ( ( state, join_block, insn_idx, local_inval) ) ;
6925+ break ; // End the block
6926+ }
6927+ } else {
6928+ // no profile
6929+ let result = fun. push_insn ( block, Insn :: GetIvar { self_val : self_param, id, ic, state : exit_id } ) ;
6930+ state. stack_push ( result) ;
6931+ }
68396932 }
68406933 YARVINSN_setinstancevariable => {
68416934 let id = ID ( get_arg ( pc, 0 ) . as_u64 ( ) ) ;
0 commit comments