cranelift: add a new resumable_trapnz instruction;

This is useful to have to allow resumable_trap to happen in loop
headers, for instance. This is the correct way to implement interrupt
checks in Spidermonkey, which are effectively resumable traps. Previous
implementation was using traps, which is wrong, since traps semantically
can't be resumed after.
This commit is contained in:
Benjamin Bouvier
2020-06-12 12:58:36 +02:00
parent 60d55a3483
commit dad56a2488
4 changed files with 36 additions and 12 deletions

View File

@@ -340,7 +340,21 @@ fn define_control_flow(
r#" r#"
Trap when non-zero. Trap when non-zero.
if ``c`` is zero, execution continues at the following instruction. If ``c`` is zero, execution continues at the following instruction.
"#,
&formats.cond_trap,
)
.operands_in(vec![c, code])
.can_trap(true),
);
ig.push(
Inst::new(
"resumable_trapnz",
r#"
A resumable trap to be called when the passed condition is non-zero.
If ``c`` is zero, execution continues at the following instruction.
"#, "#,
&formats.cond_trap, &formats.cond_trap,
) )

View File

@@ -99,6 +99,7 @@ pub(crate) fn define(insts: &InstructionGroup, imm: &Immediates) -> TransformGro
let jump = insts.by_name("jump"); let jump = insts.by_name("jump");
let load = insts.by_name("load"); let load = insts.by_name("load");
let popcnt = insts.by_name("popcnt"); let popcnt = insts.by_name("popcnt");
let resumable_trapnz = insts.by_name("resumable_trapnz");
let rotl = insts.by_name("rotl"); let rotl = insts.by_name("rotl");
let rotl_imm = insts.by_name("rotl_imm"); let rotl_imm = insts.by_name("rotl_imm");
let rotr = insts.by_name("rotr"); let rotr = insts.by_name("rotr");
@@ -138,6 +139,7 @@ pub(crate) fn define(insts: &InstructionGroup, imm: &Immediates) -> TransformGro
// TODO: Add sufficient XForm syntax that we don't need to hand-code these. // TODO: Add sufficient XForm syntax that we don't need to hand-code these.
expand.custom_legalize(trapz, "expand_cond_trap"); expand.custom_legalize(trapz, "expand_cond_trap");
expand.custom_legalize(trapnz, "expand_cond_trap"); expand.custom_legalize(trapnz, "expand_cond_trap");
expand.custom_legalize(resumable_trapnz, "expand_cond_trap");
expand.custom_legalize(br_table, "expand_br_table"); expand.custom_legalize(br_table, "expand_br_table");
expand.custom_legalize(select, "expand_select"); expand.custom_legalize(select, "expand_select");
widen.custom_legalize(select, "expand_select"); // small ints widen.custom_legalize(select, "expand_select"); // small ints

View File

@@ -1334,7 +1334,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
ctx.emit(Inst::Brk); ctx.emit(Inst::Brk);
} }
Opcode::Trap => { Opcode::Trap | Opcode::ResumableTrap => {
let trap_info = (ctx.srcloc(insn), inst_trapcode(ctx.data(insn)).unwrap()); let trap_info = (ctx.srcloc(insn), inst_trapcode(ctx.data(insn)).unwrap());
ctx.emit(Inst::Udf { trap_info }) ctx.emit(Inst::Udf { trap_info })
} }
@@ -1385,12 +1385,8 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
panic!("safepoint support not implemented!"); panic!("safepoint support not implemented!");
} }
Opcode::Trapz | Opcode::Trapnz => { Opcode::Trapz | Opcode::Trapnz | Opcode::ResumableTrapnz => {
panic!("trapz / trapnz should have been removed by legalization!"); panic!("trapz / trapnz / resumable_trapnz should have been removed by legalization!");
}
Opcode::ResumableTrap => {
panic!("Resumable traps not supported");
} }
Opcode::FuncAddr => { Opcode::FuncAddr => {
@@ -2277,6 +2273,7 @@ pub(crate) fn lower_branch<C: LowerCtx<I = Inst>>(
dest: BranchTarget::Label(targets[0]), dest: BranchTarget::Label(targets[0]),
}); });
} }
Opcode::BrTable => { Opcode::BrTable => {
// Expand `br_table index, default, JT` to: // Expand `br_table index, default, JT` to:
// //

View File

@@ -214,6 +214,7 @@ pub fn simple_legalize(func: &mut ir::Function, cfg: &mut ControlFlowGraph, isa:
| ir::Opcode::TableAddr | ir::Opcode::TableAddr
| ir::Opcode::Trapnz | ir::Opcode::Trapnz
| ir::Opcode::Trapz | ir::Opcode::Trapz
| ir::Opcode::ResumableTrapnz
| ir::Opcode::BandImm | ir::Opcode::BandImm
| ir::Opcode::BorImm | ir::Opcode::BorImm
| ir::Opcode::BxorImm | ir::Opcode::BxorImm
@@ -261,15 +262,15 @@ fn expand_cond_trap(
) { ) {
// Parse the instruction. // Parse the instruction.
let trapz; let trapz;
let (arg, code) = match func.dfg[inst] { let (arg, code, opcode) = match func.dfg[inst] {
ir::InstructionData::CondTrap { opcode, arg, code } => { ir::InstructionData::CondTrap { opcode, arg, code } => {
// We want to branch *over* an unconditional trap. // We want to branch *over* an unconditional trap.
trapz = match opcode { trapz = match opcode {
ir::Opcode::Trapz => true, ir::Opcode::Trapz => true,
ir::Opcode::Trapnz => false, ir::Opcode::Trapnz | ir::Opcode::ResumableTrapnz => false,
_ => panic!("Expected cond trap: {}", func.dfg.display_inst(inst, None)), _ => panic!("Expected cond trap: {}", func.dfg.display_inst(inst, None)),
}; };
(arg, code) (arg, code, opcode)
} }
_ => panic!("Expected cond trap: {}", func.dfg.display_inst(inst, None)), _ => panic!("Expected cond trap: {}", func.dfg.display_inst(inst, None)),
}; };
@@ -307,7 +308,17 @@ fn expand_cond_trap(
// Insert the new label and the unconditional trap terminator. // Insert the new label and the unconditional trap terminator.
pos.insert_block(new_block_trap); pos.insert_block(new_block_trap);
match opcode {
ir::Opcode::Trapz | ir::Opcode::Trapnz => {
pos.ins().trap(code); pos.ins().trap(code);
}
ir::Opcode::ResumableTrapnz => {
pos.ins().resumable_trap(code);
pos.ins().jump(new_block_resume, &[]);
}
_ => unreachable!(),
}
// Insert the new label and resume the execution when the trap fails. // Insert the new label and resume the execution when the trap fails.
pos.insert_block(new_block_resume); pos.insert_block(new_block_resume);