diff --git a/cranelift/filetests/wasm/control.cton b/cranelift/filetests/wasm/control.cton index 9d46a3577b..1d12aeff3d 100644 --- a/cranelift/filetests/wasm/control.cton +++ b/cranelift/filetests/wasm/control.cton @@ -48,3 +48,18 @@ function %undefined() { ebb0: trap user0 } + +function %br_table(i32) { +jt0 = jump_table ebb3, ebb1, 0, ebb2 + +ebb0(v0: i32): + br_table v0, jt0 + trap oob + +ebb1: + return +ebb2: + return +ebb3: + return +} diff --git a/lib/cretonne/meta/base/legalize.py b/lib/cretonne/meta/base/legalize.py index 626295e165..4f92827b24 100644 --- a/lib/cretonne/meta/base/legalize.py +++ b/lib/cretonne/meta/base/legalize.py @@ -53,6 +53,7 @@ expand.custom_legalize(insts.heap_addr, 'expand_heap_addr') # TODO: Add sufficient XForm syntax that we don't need to hand-code these. expand.custom_legalize(insts.trapz, 'expand_cond_trap') expand.custom_legalize(insts.trapnz, 'expand_cond_trap') +expand.custom_legalize(insts.br_table, 'expand_br_table') # Custom expansions for floating point constants. # These expansions require bit-casting or creating constant pool entries. diff --git a/lib/cretonne/src/ir/jumptable.rs b/lib/cretonne/src/ir/jumptable.rs index 319eeb1edf..a6cfad3346 100644 --- a/lib/cretonne/src/ir/jumptable.rs +++ b/lib/cretonne/src/ir/jumptable.rs @@ -39,6 +39,11 @@ impl JumpTableData { } } + /// Get the number of table entries. + pub fn len(&self) -> usize { + self.table.len() + } + /// Set a table entry. /// /// The table will grow as needed to fit `idx`. diff --git a/lib/cretonne/src/legalizer/mod.rs b/lib/cretonne/src/legalizer/mod.rs index 920bebde81..61a433b6ed 100644 --- a/lib/cretonne/src/legalizer/mod.rs +++ b/lib/cretonne/src/legalizer/mod.rs @@ -147,6 +147,38 @@ fn expand_cond_trap(inst: ir::Inst, func: &mut ir::Function, cfg: &mut ControlFl cfg.recompute_ebb(pos.func, new_ebb); } +/// Jump tables. +fn expand_br_table(inst: ir::Inst, func: &mut ir::Function, cfg: &mut ControlFlowGraph) { + use ir::condcodes::IntCC; + + let (arg, table) = match func.dfg[inst] { + ir::InstructionData::BranchTable { + opcode: ir::Opcode::BrTable, + arg, + table, + } => (arg, table), + _ => panic!("Expected br_table: {}", func.dfg.display_inst(inst, None)), + }; + + // This is a poor man's jump table using just a sequence of conditional branches. + // TODO: Lower into a jump table load and indirect branch. + let table_size = func.jump_tables[table].len(); + let mut pos = FuncCursor::new(func).at_inst(inst); + pos.use_srcloc(inst); + + for i in 0..table_size { + if let Some(dest) = pos.func.jump_tables[table].get_entry(i) { + let t = pos.ins().icmp_imm(IntCC::Equal, arg, i as i64); + pos.ins().brnz(t, dest, &[]); + } + } + + // `br_table` falls through when nothing matches. + let ebb = pos.current_ebb().unwrap(); + pos.remove_inst(); + cfg.recompute_ebb(pos.func, ebb); +} + /// Expand illegal `f32const` and `f64const` instructions. fn expand_fconst(inst: ir::Inst, func: &mut ir::Function, _cfg: &mut ControlFlowGraph) { let ty = func.dfg.value_type(func.dfg.first_result(inst));