diff --git a/cranelift/filetests/filetests/runtests/br_table.clif b/cranelift/filetests/filetests/runtests/br_table.clif new file mode 100644 index 0000000000..836e5da8ec --- /dev/null +++ b/cranelift/filetests/filetests/runtests/br_table.clif @@ -0,0 +1,148 @@ +test interpret +test run +target aarch64 +target s390x + + +function %br_table_i8(i8) -> i8 { + jt0 = jump_table [block1, block2, block2, block3] + +block0(v0: i8): + br_table v0, block4, jt0 + +block1: + v1 = iconst.i8 1 + jump block5(v1) + +block2: + v2 = iconst.i8 2 + jump block5(v2) + +block3: + v3 = iconst.i8 3 + jump block5(v3) + +block4: + v4 = iconst.i8 4 + jump block5(v4) + +block5(v5: i8): + v6 = iadd.i8 v0, v5 + return v6 +} +; run: %br_table_i8(0) == 1 +; run: %br_table_i8(1) == 3 +; run: %br_table_i8(2) == 4 +; run: %br_table_i8(3) == 6 +; run: %br_table_i8(4) == 8 +; run: %br_table_i8(5) == 9 +; run: %br_table_i8(6) == 10 +; run: %br_table_i8(-1) == 3 + + +function %br_table_i16(i16) -> i16 { + jt0 = jump_table [block1, block2, block2, block3] + +block0(v0: i16): + br_table v0, block4, jt0 + +block1: + v1 = iconst.i16 1 + jump block5(v1) + +block2: + v2 = iconst.i16 2 + jump block5(v2) + +block3: + v3 = iconst.i16 3 + jump block5(v3) + +block4: + v4 = iconst.i16 4 + jump block5(v4) + +block5(v5: i16): + v6 = iadd.i16 v0, v5 + return v6 +} +; run: %br_table_i16(0) == 1 +; run: %br_table_i16(1) == 3 +; run: %br_table_i16(2) == 4 +; run: %br_table_i16(3) == 6 +; run: %br_table_i16(4) == 8 +; run: %br_table_i16(5) == 9 +; run: %br_table_i16(6) == 10 +; run: %br_table_i16(-1) == 3 + + +function %br_table_i32(i32) -> i32 { + jt0 = jump_table [block1, block2, block2, block3] + +block0(v0: i32): + br_table v0, block4, jt0 + +block1: + v1 = iconst.i32 1 + jump block5(v1) + +block2: + v2 = iconst.i32 2 + jump block5(v2) + +block3: + v3 = iconst.i32 3 + jump block5(v3) + +block4: + v4 = iconst.i32 4 + jump block5(v4) + +block5(v5: i32): + v6 = iadd.i32 v0, v5 + return v6 +} +; run: %br_table_i32(0) == 1 +; run: %br_table_i32(1) == 3 +; run: %br_table_i32(2) == 4 +; run: %br_table_i32(3) == 6 +; run: %br_table_i32(4) == 8 +; run: %br_table_i32(5) == 9 +; run: %br_table_i32(6) == 10 +; run: %br_table_i32(-1) == 3 + + +function %br_table_i64(i64) -> i64 { + jt0 = jump_table [block1, block2, block2, block3] + +block0(v0: i64): + br_table v0, block4, jt0 + +block1: + v1 = iconst.i64 1 + jump block5(v1) + +block2: + v2 = iconst.i64 2 + jump block5(v2) + +block3: + v3 = iconst.i64 3 + jump block5(v3) + +block4: + v4 = iconst.i64 4 + jump block5(v4) + +block5(v5: i64): + v6 = iadd.i64 v0, v5 + return v6 +} +; run: %br_table_i64(0) == 1 +; run: %br_table_i64(1) == 3 +; run: %br_table_i64(2) == 4 +; run: %br_table_i64(3) == 6 +; run: %br_table_i64(4) == 8 +; run: %br_table_i64(5) == 9 +; run: %br_table_i64(6) == 10 +; run: %br_table_i64(-1) == 3 diff --git a/cranelift/interpreter/src/interpreter.rs b/cranelift/interpreter/src/interpreter.rs index a153f0fb5b..11732be230 100644 --- a/cranelift/interpreter/src/interpreter.rs +++ b/cranelift/interpreter/src/interpreter.rs @@ -217,6 +217,10 @@ impl<'a> State<'a, DataValue> for InterpreterState<'a> { self.functions .get_from_func_ref(func_ref, self.frame_stack.last().unwrap().function) } + fn get_current_function(&self) -> &'a Function { + self.current_frame().function + } + fn push_frame(&mut self, function: &'a Function) { self.frame_stack.push(Frame::new(function)); } diff --git a/cranelift/interpreter/src/state.rs b/cranelift/interpreter/src/state.rs index d18e4536c8..837c2f5849 100644 --- a/cranelift/interpreter/src/state.rs +++ b/cranelift/interpreter/src/state.rs @@ -19,6 +19,8 @@ use thiserror::Error; pub trait State<'a, V> { /// Retrieve a reference to a [Function]. fn get_function(&self, func_ref: FuncRef) -> Option<&'a Function>; + /// Retrieve a reference to the currently executing [Function]. + fn get_current_function(&self) -> &'a Function; /// Record that an interpreter has called into a new [Function]. fn push_frame(&mut self, function: &'a Function); /// Record that an interpreter has returned from a called [Function]. @@ -92,6 +94,10 @@ where None } + fn get_current_function(&self) -> &'a Function { + unimplemented!() + } + fn push_frame(&mut self, _function: &'a Function) { unimplemented!() } diff --git a/cranelift/interpreter/src/step.rs b/cranelift/interpreter/src/step.rs index 2030d15a40..d3b9f56b66 100644 --- a/cranelift/interpreter/src/step.rs +++ b/cranelift/interpreter/src/step.rs @@ -9,6 +9,7 @@ use cranelift_codegen::ir::{ }; use log::trace; use smallvec::{smallvec, SmallVec}; +use std::convert::TryFrom; use std::ops::RangeFrom; use thiserror::Error; @@ -155,10 +156,25 @@ where Opcode::BrIcmp => branch_when(icmp(inst.cond_code().unwrap(), &arg(0)?, &arg(1)?)?)?, Opcode::Brif => branch_when(state.has_iflag(inst.cond_code().unwrap()))?, Opcode::Brff => branch_when(state.has_fflag(inst.fp_cond_code().unwrap()))?, - Opcode::BrTable => unimplemented!("BrTable"), - Opcode::JumpTableEntry => unimplemented!("JumpTableEntry"), - Opcode::JumpTableBase => unimplemented!("JumpTableBase"), - Opcode::IndirectJumpTableBr => unimplemented!("IndirectJumpTableBr"), + Opcode::BrTable => { + if let InstructionData::BranchTable { + table, destination, .. + } = inst + { + let jt_data = &state.get_current_function().jump_tables[table]; + + // Convert to usize to remove negative indexes from the following operations + let jump_target = usize::try_from(arg(0)?.into_int()?) + .ok() + .and_then(|i| jt_data.as_slice().get(i)) + .copied() + .unwrap_or(destination); + + ControlFlow::ContinueAt(jump_target, SmallVec::new()) + } else { + unreachable!() + } + } Opcode::Trap => ControlFlow::Trap(CraneliftTrap::User(trap_code())), Opcode::Debugtrap => ControlFlow::Trap(CraneliftTrap::Debug), Opcode::ResumableTrap => ControlFlow::Trap(CraneliftTrap::Resumable), @@ -632,6 +648,9 @@ where | Opcode::X86Palignr | Opcode::X86ElfTlsGetAddr | Opcode::X86MachoTlsGetAddr => unimplemented!("x86 instruction: {}", inst.opcode()), + Opcode::JumpTableBase | Opcode::JumpTableEntry | Opcode::IndirectJumpTableBr => { + unimplemented!("Legacy instruction: {}", inst.opcode()) + } }) }