@@ -5845,33 +5845,6 @@ impl Function {
58455845 Ok ( ( ) )
58465846 }
58475847
5848- // Validate that every instruction use is from a block-local definition, which is a temporary
5849- // constraint until we get a global register allocator.
5850- // TODO(tenderworks): Remove this
5851- fn temporary_validate_block_local_definite_assignment ( & self ) -> Result < ( ) , ValidationError > {
5852- for block in self . rpo ( ) {
5853- let mut assigned = InsnSet :: with_capacity ( self . insns . len ( ) ) ;
5854- for & param in & self . blocks [ block. 0 ] . params {
5855- assigned. insert ( param) ;
5856- }
5857- // Check that each instruction's operands are assigned
5858- for & insn_id in & self . blocks [ block. 0 ] . insns {
5859- let insn_id = self . union_find . borrow ( ) . find_const ( insn_id) ;
5860- self . insns [ insn_id. 0 ] . try_for_each_operand ( |operand| {
5861- let operand = self . union_find . borrow ( ) . find_const ( operand) ;
5862- if !assigned. get ( operand) {
5863- return Err ( ValidationError :: OperandNotDefined ( block, insn_id, operand) ) ;
5864- }
5865- Ok ( ( ) )
5866- } ) ?;
5867- if self . insns [ insn_id. 0 ] . has_output ( ) {
5868- assigned. insert ( insn_id) ;
5869- }
5870- }
5871- }
5872- Ok ( ( ) )
5873- }
5874-
58755848 /// Checks that each instruction('s representative) appears only once in the CFG.
58765849 fn validate_insn_uniqueness ( & self ) -> Result < ( ) , ValidationError > {
58775850 let mut seen = InsnSet :: with_capacity ( self . insns . len ( ) ) ;
@@ -6189,7 +6162,6 @@ impl Function {
61896162 pub fn validate ( & self ) -> Result < ( ) , ValidationError > {
61906163 self . validate_block_terminators_and_jumps ( ) ?;
61916164 self . validate_definite_assignment ( ) ?;
6192- self . temporary_validate_block_local_definite_assignment ( ) ?;
61936165 self . validate_insn_uniqueness ( ) ?;
61946166 self . validate_types ( ) ?;
61956167 Ok ( ( ) )
@@ -6756,7 +6728,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
67566728 // Keep compiling blocks until the queue becomes empty
67576729 let mut visited = HashSet :: new ( ) ;
67586730 let iseq_size = unsafe { get_iseq_encoded_size ( iseq) } ;
6759- 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 ( ) {
67606732 // Compile each block only once
67616733 if visited. contains ( & block) { continue ; }
67626734 visited. insert ( block) ;
@@ -7927,8 +7899,44 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
79277899 fun. push_insn ( block, Insn :: SideExit { state : exit_id, reason : SideExitReason :: UnhandledYARVInsn ( opcode) } ) ;
79287900 break ; // End the block
79297901 }
7930- let result = fun. push_insn ( block, Insn :: GetIvar { self_val : self_param, id, ic, state : exit_id } ) ;
7931- 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+ }
79327940 }
79337941 YARVINSN_setinstancevariable => {
79347942 let id = ID ( get_arg ( pc, 0 ) . as_u64 ( ) ) ;
@@ -8761,17 +8769,6 @@ mod validation_tests {
87618769 assert_matches_err ( function. validate_definite_assignment ( ) , ValidationError :: OperandNotDefined ( entry, val, dangling) ) ;
87628770 }
87638771
8764- #[ test]
8765- fn not_defined_within_bb_block_local ( ) {
8766- let mut function = Function :: new ( std:: ptr:: null ( ) ) ;
8767- let entry = function. entry_block ;
8768- // Create an instruction without making it belong to anything.
8769- let dangling = function. new_insn ( Insn :: Const { val : Const :: CBool ( true ) } ) ;
8770- let val = function. push_insn ( function. entry_block , Insn :: ArrayDup { val : dangling, state : InsnId ( 0usize ) } ) ;
8771- function. seal_entries ( ) ;
8772- assert_matches_err ( function. temporary_validate_block_local_definite_assignment ( ) , ValidationError :: OperandNotDefined ( entry, val, dangling) ) ;
8773- }
8774-
87758772 #[ test]
87768773 fn using_non_output_insn ( ) {
87778774 let mut function = Function :: new ( std:: ptr:: null ( ) ) ;
@@ -8784,18 +8781,6 @@ mod validation_tests {
87848781 assert_matches_err ( function. validate_definite_assignment ( ) , ValidationError :: OperandNotDefined ( entry, val, ret) ) ;
87858782 }
87868783
8787- #[ test]
8788- fn using_non_output_insn_block_local ( ) {
8789- let mut function = Function :: new ( std:: ptr:: null ( ) ) ;
8790- let entry = function. entry_block ;
8791- let const_ = function. push_insn ( function. entry_block , Insn :: Const { val : Const :: CBool ( true ) } ) ;
8792- // Ret is a non-output instruction.
8793- let ret = function. push_insn ( function. entry_block , Insn :: Return { val : const_ } ) ;
8794- let val = function. push_insn ( function. entry_block , Insn :: ArrayDup { val : ret, state : InsnId ( 0usize ) } ) ;
8795- function. seal_entries ( ) ;
8796- assert_matches_err ( function. temporary_validate_block_local_definite_assignment ( ) , ValidationError :: OperandNotDefined ( entry, val, ret) ) ;
8797- }
8798-
87998784 #[ test]
88008785 fn not_dominated_by_diamond ( ) {
88018786 // This tests that one branch is missing a definition which fails.
0 commit comments