Skip to content

Commit de55452

Browse files
authored
ZJIT: Drop the legacy implementation of spilled params (ruby#17138)
ZJIT: Abort unsupported JIT entry params
1 parent 92c31fe commit de55452

1 file changed

Lines changed: 42 additions & 20 deletions

File tree

zjit/src/backend/lir.rs

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)