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:
Tyler McMullen
2018-10-03 11:04:21 -06:00
committed by Dan Gohman
parent de1d82b4ba
commit 79cea5e18b
39 changed files with 627 additions and 100 deletions

View File

@@ -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(()) => {}

View File

@@ -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"
);
}