@@ -1562,23 +1562,6 @@ impl Assembler
15621562 iter
15631563 }
15641564
1565- /// Return an operand for a basic block argument at a given index.
1566- /// To simplify the implementation, we allocate a fixed register or a stack slot
1567- /// for each basic block argument.
1568- pub fn param_opnd ( idx : usize ) -> Opnd {
1569- use crate :: backend:: current:: ALLOC_REGS ;
1570- use crate :: cruby:: SIZEOF_VALUE_I32 ;
1571-
1572- if idx < ALLOC_REGS . len ( ) {
1573- Opnd :: Reg ( ALLOC_REGS [ idx] )
1574- } else {
1575- // With FrameSetup, the address that NATIVE_BASE_PTR points to stores an old value in the register.
1576- // To avoid clobbering it, we need to start from the next slot, and we also reserve one space for
1577- // JITFrame, hence `+ 2` for the index.
1578- Opnd :: mem ( 64 , NATIVE_BASE_PTR , ( idx - ALLOC_REGS . len ( ) + 2 ) as i32 * -SIZEOF_VALUE_I32 )
1579- }
1580- }
1581-
15821565 pub fn linearize_instructions ( & self ) -> Vec < Insn > {
15831566 // Emit instructions with labels, expanding branch parameters
15841567 let mut insns = Vec :: with_capacity ( ASSEMBLER_INSNS_CAPACITY ) ;
@@ -2040,11 +2023,25 @@ impl Assembler
20402023 if self . basic_blocks [ block_id. 0 ] . is_dummy ( ) { continue ; }
20412024 let params = self . basic_blocks [ block_id. 0 ] . parameters . clone ( ) ;
20422025
2026+ // JIT-to-JIT entries that would need more argument registers should
2027+ // be unreachable because can_direct_send() refuses to call them.
2028+ // Keep compiling the function body, but make the unsupported entry
2029+ // abort if control ever reaches it. TODO: Remove this (Shopify/ruby#916)
2030+ if params. len ( ) > C_ARG_OPNDS . len ( ) {
2031+ let insert_pos = self . basic_blocks [ block_id. 0 ] . insns . iter ( )
2032+ . position ( |insn| matches ! ( insn, Insn :: FrameSetup { .. } ) )
2033+ . or_else ( || self . basic_blocks [ block_id. 0 ] . insns . iter ( ) . position ( |insn| matches ! ( insn, Insn :: Label ( _) ) ) . map ( |idx| idx + 1 ) )
2034+ . unwrap_or ( 0 ) ;
2035+ self . basic_blocks [ block_id. 0 ] . insns . insert ( insert_pos, Insn :: Abort ) ;
2036+ self . basic_blocks [ block_id. 0 ] . insn_ids . insert ( insert_pos, None ) ;
2037+ continue ;
2038+ }
2039+
20432040 // Rewrite VRegs to physical registers before sequentialization
20442041 // so the parcopy algorithm can detect physical register conflicts.
20452042 let reg_copies: Vec < parcopy:: RegisterCopy < Opnd > > = params. iter ( ) . enumerate ( )
20462043 . map ( |( i, param) | parcopy:: RegisterCopy :: < Opnd > {
2047- source : Assembler :: param_opnd ( i ) ,
2044+ source : C_ARG_OPNDS [ i ] ,
20482045 destination : Self :: rewritten_opnd ( * param, assignments) ,
20492046 } )
20502047 . filter ( |copy| copy. source != copy. destination )
@@ -4216,8 +4213,8 @@ mod tests {
42164213 let ( assignments, _) = asm. linear_scan ( intervals. clone ( ) , 5 , & preferred_registers) ;
42174214
42184215 // Entry block b1 has parameters [v0, v1].
4219- // With 5 registers: v0 -> Reg(0) = regs[0], arrival = param_opnd(0) = regs[0] -> self-move, filtered
4220- // v1 -> Reg(1) = regs[1], arrival = param_opnd(1) = regs[1] -> self-move, filtered
4216+ // With 5 registers: v0 -> Reg(0) = regs[0], arrival = C_ARG_OPNDS[0] = regs[0] -> self-move, filtered
4217+ // v1 -> Reg(1) = regs[1], arrival = C_ARG_OPNDS[1] = regs[1] -> self-move, filtered
42214218 // Before resolve_ssa, b1 has: [Label, Jmp] = 2 insns
42224219 assert_eq ! ( asm. basic_blocks[ b1. 0 ] . insns. len( ) , 2 ) ;
42234220
@@ -4242,6 +4239,31 @@ mod tests {
42424239 }
42434240 }
42444241
4242+ #[ test]
4243+ fn test_resolve_ssa_entry_params_too_many_abort ( ) {
4244+ let mut asm = Assembler :: new ( ) ;
4245+ let block = asm. new_block ( hir:: BlockId ( 0 ) , true , 0 ) ;
4246+ asm. set_current_block ( block) ;
4247+ let label = asm. new_label ( "bb0" ) ;
4248+ asm. write_label ( label) ;
4249+
4250+ for _ in 0 ..=C_ARG_OPNDS . len ( ) {
4251+ let param = asm. new_vreg ( 64 ) ;
4252+ asm. basic_blocks [ block. 0 ] . add_parameter ( param) ;
4253+ }
4254+ asm. basic_blocks [ block. 0 ] . push_insn ( Insn :: CRet ( Opnd :: UImm ( 0 ) ) ) ;
4255+
4256+ let live_in = asm. analyze_liveness ( ) ;
4257+ asm. number_instructions ( 0 ) ;
4258+ let intervals = asm. build_intervals ( live_in) ;
4259+ let preferred_registers = asm. preferred_register_assignments ( & intervals) ;
4260+ let ( assignments, _) = asm. linear_scan ( intervals. clone ( ) , 5 , & preferred_registers) ;
4261+
4262+ asm. resolve_ssa ( & intervals, & assignments) ;
4263+
4264+ assert ! ( matches!( asm. basic_blocks[ block. 0 ] . insns[ 1 ] , Insn :: Abort ) ) ;
4265+ }
4266+
42454267 fn build_critical_edge ( ) -> ( Assembler , Opnd , Opnd , Opnd , Opnd , Opnd , BlockId , BlockId , BlockId ) {
42464268 let mut asm = Assembler :: new ( ) ;
42474269
0 commit comments