Implement jump tables (#453)
* Add 'jump_table_entry' and 'indirect_jump' instructions. * Update CodeSink to keep track of code size. Pretty up clif-util's disassembly output. * Only disassemble the machine portion of output. Pretty print the read-only data after it. * Update switch frontend code to use new br_table instruction w/ default.
This commit is contained in:
committed by
Dan Gohman
parent
de1d82b4ba
commit
79cea5e18b
@@ -10,7 +10,7 @@ use cranelift_codegen::entity::{EntityRef, PrimaryMap, SecondaryMap};
|
||||
use cranelift_codegen::ir::immediates::{Ieee32, Ieee64};
|
||||
use cranelift_codegen::ir::instructions::BranchInfo;
|
||||
use cranelift_codegen::ir::types::{F32, F64};
|
||||
use cranelift_codegen::ir::{Ebb, Function, Inst, InstBuilder, Type, Value};
|
||||
use cranelift_codegen::ir::{Ebb, Function, Inst, InstBuilder, InstructionData, Type, Value};
|
||||
use cranelift_codegen::packed_option::PackedOption;
|
||||
use cranelift_codegen::packed_option::ReservedValue;
|
||||
use std::mem;
|
||||
@@ -647,7 +647,7 @@ impl SSABuilder {
|
||||
func.dfg.append_inst_arg(jump_inst, val);
|
||||
None
|
||||
}
|
||||
BranchInfo::Table(jt) => {
|
||||
BranchInfo::Table(jt, default_ebb) => {
|
||||
// In the case of a jump table, the situation is tricky because br_table doesn't
|
||||
// support arguments.
|
||||
// We have to split the critical edge
|
||||
@@ -656,6 +656,21 @@ impl SSABuilder {
|
||||
let middle_block = self.declare_ebb_header_block(middle_ebb);
|
||||
self.blocks[middle_block].add_predecessor(jump_inst_block, jump_inst);
|
||||
self.mark_ebb_header_block_sealed(middle_block);
|
||||
|
||||
if let Some(default_ebb) = default_ebb {
|
||||
if dest_ebb == default_ebb {
|
||||
match func.dfg[jump_inst] {
|
||||
InstructionData::BranchTable {
|
||||
destination: ref mut dest,
|
||||
..
|
||||
} => {
|
||||
*dest = middle_ebb;
|
||||
}
|
||||
_ => panic!("should not happen"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for old_dest in func.jump_tables[jt].as_mut_slice() {
|
||||
if *old_dest == PackedOption::from(dest_ebb) {
|
||||
*old_dest = PackedOption::from(middle_ebb);
|
||||
@@ -986,20 +1001,31 @@ mod tests {
|
||||
#[test]
|
||||
fn br_table_with_args() {
|
||||
// This tests the on-demand splitting of critical edges for br_table with jump arguments
|
||||
let mut func = Function::new();
|
||||
let mut ssa = SSABuilder::new();
|
||||
let ebb0 = func.dfg.make_ebb();
|
||||
let ebb1 = func.dfg.make_ebb();
|
||||
//
|
||||
// Here is the pseudo-program we want to translate:
|
||||
//
|
||||
// function %f {
|
||||
// jt = jump_table ebb2, 0, ebb1
|
||||
// ebb0:
|
||||
// x = 0;
|
||||
// br_table x ebb1
|
||||
// x = 1
|
||||
// jump ebb1
|
||||
// x = 1;
|
||||
// br_table x, ebb2, jt
|
||||
// ebb1:
|
||||
// x = 2
|
||||
// jump ebb2
|
||||
// ebb2:
|
||||
// x = x + 1
|
||||
// return
|
||||
//
|
||||
// }
|
||||
|
||||
let mut func = Function::new();
|
||||
let mut ssa = SSABuilder::new();
|
||||
let mut jump_table = JumpTableData::new();
|
||||
let ebb0 = func.dfg.make_ebb();
|
||||
let ebb1 = func.dfg.make_ebb();
|
||||
let ebb2 = func.dfg.make_ebb();
|
||||
|
||||
// ebb0:
|
||||
// x = 1;
|
||||
let block0 = ssa.declare_ebb_header_block(ebb0);
|
||||
ssa.seal_ebb_header_block(ebb0, &mut func);
|
||||
let x_var = Variable::new(0);
|
||||
@@ -1007,42 +1033,60 @@ mod tests {
|
||||
let mut cur = FuncCursor::new(&mut func);
|
||||
cur.insert_ebb(ebb0);
|
||||
cur.insert_ebb(ebb1);
|
||||
cur.insert_ebb(ebb2);
|
||||
cur.goto_bottom(ebb0);
|
||||
cur.ins().iconst(I32, 1)
|
||||
};
|
||||
ssa.def_var(x_var, x1, block0);
|
||||
let mut data = JumpTableData::new();
|
||||
data.push_entry(ebb1);
|
||||
data.set_entry(2, ebb1);
|
||||
let jt = func.create_jump_table(data);
|
||||
|
||||
// jt = jump_table ebb2, 0, ebb1
|
||||
jump_table.push_entry(ebb2);
|
||||
jump_table.set_entry(2, ebb1);
|
||||
let jt = func.create_jump_table(jump_table);
|
||||
|
||||
// ebb0:
|
||||
// ...
|
||||
// br_table x, ebb2, jt
|
||||
ssa.use_var(&mut func, x_var, I32, block0).0;
|
||||
let br_table = {
|
||||
let mut cur = FuncCursor::new(&mut func).at_bottom(ebb0);
|
||||
cur.ins().br_table(x1, jt)
|
||||
cur.ins().br_table(x1, ebb2, jt)
|
||||
};
|
||||
let block1 = ssa.declare_ebb_body_block(block0);
|
||||
let x3 = {
|
||||
let mut cur = FuncCursor::new(&mut func).at_bottom(ebb0);
|
||||
|
||||
// ebb1:
|
||||
// x = 2
|
||||
// jump ebb2
|
||||
let block1 = ssa.declare_ebb_header_block(ebb1);
|
||||
ssa.seal_ebb_header_block(ebb1, &mut func);
|
||||
let x2 = {
|
||||
let mut cur = FuncCursor::new(&mut func).at_bottom(ebb1);
|
||||
cur.ins().iconst(I32, 2)
|
||||
};
|
||||
ssa.def_var(x_var, x3, block1);
|
||||
ssa.def_var(x_var, x2, block1);
|
||||
let jump_inst = {
|
||||
let mut cur = FuncCursor::new(&mut func).at_bottom(ebb0);
|
||||
cur.ins().jump(ebb1, &[])
|
||||
};
|
||||
let block2 = ssa.declare_ebb_header_block(ebb1);
|
||||
ssa.declare_ebb_predecessor(ebb1, block1, jump_inst);
|
||||
ssa.declare_ebb_predecessor(ebb1, block0, br_table);
|
||||
ssa.seal_ebb_header_block(ebb1, &mut func);
|
||||
let x4 = ssa.use_var(&mut func, x_var, I32, block2).0;
|
||||
{
|
||||
let mut cur = FuncCursor::new(&mut func).at_bottom(ebb1);
|
||||
cur.ins().iadd_imm(x4, 1)
|
||||
cur.ins().jump(ebb2, &[])
|
||||
};
|
||||
|
||||
// ebb2:
|
||||
// x = x + 1
|
||||
// return
|
||||
let block3 = ssa.declare_ebb_header_block(ebb2);
|
||||
ssa.declare_ebb_predecessor(ebb2, block1, jump_inst);
|
||||
ssa.declare_ebb_predecessor(ebb2, block0, br_table);
|
||||
ssa.seal_ebb_header_block(ebb2, &mut func);
|
||||
let block4 = ssa.declare_ebb_body_block(block3);
|
||||
let x3 = ssa.use_var(&mut func, x_var, I32, block4).0;
|
||||
let x4 = {
|
||||
let mut cur = FuncCursor::new(&mut func).at_bottom(ebb2);
|
||||
cur.ins().iadd_imm(x3, 1)
|
||||
};
|
||||
ssa.def_var(x_var, x4, block4);
|
||||
{
|
||||
let mut cur = FuncCursor::new(&mut func).at_bottom(ebb1);
|
||||
let mut cur = FuncCursor::new(&mut func).at_bottom(ebb2);
|
||||
cur.ins().return_(&[])
|
||||
};
|
||||
|
||||
let flags = settings::Flags::new(settings::builder());
|
||||
match verify_function(&func, &flags) {
|
||||
Ok(()) => {}
|
||||
|
||||
@@ -156,8 +156,7 @@ impl Switch {
|
||||
|
||||
bx.switch_to_block(jt_ebb);
|
||||
let discr = bx.ins().iadd_imm(val, (first_index as i64).wrapping_neg());
|
||||
bx.ins().br_table(discr, jump_table);
|
||||
bx.ins().jump(otherwise, &[]);
|
||||
bx.ins().br_table(discr, otherwise, jump_table);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,8 +255,7 @@ ebb0:
|
||||
|
||||
ebb3:
|
||||
v3 = iadd_imm.i32 v1, 0
|
||||
br_table v3, jt0
|
||||
jump ebb0"
|
||||
br_table v3, ebb0, jt0"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -308,13 +306,11 @@ ebb8:
|
||||
|
||||
ebb11:
|
||||
v7 = iadd_imm.i32 v1, 0
|
||||
br_table v7, jt0
|
||||
jump ebb0
|
||||
br_table v7, ebb0, jt0
|
||||
|
||||
ebb10:
|
||||
v8 = iadd_imm.i32 v1, -10
|
||||
br_table v8, jt1
|
||||
jump ebb0"
|
||||
br_table v8, ebb0, jt1"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user