diff --git a/src/riscv/lib/src/instruction_context.rs b/src/riscv/lib/src/instruction_context.rs index 55823e43296524c6a3bf8dd5f7a3878389885187..bb581eda3e951cac694847db675a1338fc6b936d 100644 --- a/src/riscv/lib/src/instruction_context.rs +++ b/src/riscv/lib/src/instruction_context.rs @@ -15,6 +15,7 @@ use crate::machine_state::registers::NonZeroXRegister; use crate::machine_state::registers::XRegister; use crate::machine_state::registers::XValue; use crate::parser::XRegisterParsed; +use crate::parser::instruction::InstrWidth; use crate::parser::split_x0; use crate::state_backend::ManagerReadWrite; use crate::traps::Exception; @@ -90,6 +91,19 @@ pub trait ICB { /// - `false -> 0` fn xvalue_from_bool(&mut self, value: Self::Bool) -> Self::XValue; + /// Branching instruction. + /// + /// If `predicate` is true, the branch will be taken. The PC update + /// will be to the address returned by `take_branch`. + /// + /// If false, the PC update is to the next instruction. + fn branch( + &mut self, + condition: Self::Bool, + offset: i64, + instr_width: InstrWidth, + ) -> ProgramCounterUpdate; + /// Representation for the manipulation of fallible operations. type IResult; @@ -214,6 +228,22 @@ impl ICB for MachineCoreState { value as XValue } + #[inline(always)] + fn branch( + &mut self, + predicate: Self::Bool, + offset: i64, + instr_width: InstrWidth, + ) -> ProgramCounterUpdate { + if predicate { + let pc = self.pc_read(); + let address = pc.wrapping_add(offset as u64); + ProgramCounterUpdate::Set(address) + } else { + ProgramCounterUpdate::Next(instr_width) + } + } + type IResult = Result; #[inline(always)] @@ -240,8 +270,14 @@ impl ICB for MachineCoreState { /// Operators for producing a boolean from two values. pub enum Predicate { + Equal, + NotEqual, LessThanSigned, LessThanUnsigned, + LessThanOrEqualSigned, + GreaterThanSigned, + GreaterThanOrEqualSigned, + GreaterThanOrEqualUnsigned, } impl Predicate { @@ -249,8 +285,14 @@ impl Predicate { #[inline(always)] fn eval(self, lhs: XValue, rhs: XValue) -> bool { match self { + Self::Equal => lhs == rhs, + Self::NotEqual => lhs != rhs, Self::LessThanSigned => (lhs as i64) < (rhs as i64), Self::LessThanUnsigned => lhs < rhs, + Self::LessThanOrEqualSigned => (lhs as i64) <= (rhs as i64), + Self::GreaterThanSigned => (lhs as i64) > (rhs as i64), + Self::GreaterThanOrEqualSigned => (lhs as i64) >= (rhs as i64), + Self::GreaterThanOrEqualUnsigned => lhs >= rhs, } } } diff --git a/src/riscv/lib/src/interpreter/branching.rs b/src/riscv/lib/src/interpreter/branching.rs index 2f8e40b52d64c37e152228cd29c1fea90daac9d5..3ddd8c2d8c9364d7da5b72c42b4b2f4045ff9996 100644 --- a/src/riscv/lib/src/interpreter/branching.rs +++ b/src/riscv/lib/src/interpreter/branching.rs @@ -6,6 +6,8 @@ // TODO: RV-520: Update remaining 'jump' handlers in the file to work over the ICB. use crate::instruction_context::ICB; +use crate::instruction_context::Predicate; +use crate::machine_state::ProgramCounterUpdate; use crate::machine_state::hart_state::HartState; use crate::machine_state::memory::Address; use crate::machine_state::registers::NonZeroXRegister; @@ -123,13 +125,74 @@ pub fn run_add_immediate_to_pc(icb: &mut impl ICB, imm: i64, rd: NonZeroXRegiste icb.xregister_write_nz(rd, result); } +/// Performs a conditional ( `predicate(val(rs1), val(rs2))` ) control transfer. +/// If condition met, the offset is sign-extended and added to the pc to form the branch +/// target address that is then set, otherwise indicates to proceed to the next instruction. +/// +/// Relevant RISC-V opcodes: +/// - `BEQ` +/// - `BNE` +/// - `BLT` +/// - `BLTU` +/// - `BGE` +/// - `BGEU` +#[inline(always)] +pub fn run_branch( + icb: &mut I, + predicate: Predicate, + imm: i64, + rs1: NonZeroXRegister, + rs2: NonZeroXRegister, + width: InstrWidth, +) -> ProgramCounterUpdate<::XValue> { + let lhs = icb.xregister_read_nz(rs1); + let rhs = icb.xregister_read_nz(rs2); + + let cond = icb.xvalue_compare(predicate, lhs, rhs); + + icb.branch(cond, imm, width) +} + +/// Performs a conditional ( `predicate(val(rs1), 0)` ) control transfer. +/// If condition met, the offset is sign-extended and added to the pc to form the branch +/// target address that is then set, otherwise indicates to proceed to the next instruction. +/// +/// Relevant RISC-V opcodes: +/// - `BEQ` +/// - `BNE` +/// - `BLT` +/// - `BLTU` +/// - `BGE` +/// - `BGEU` +/// - `C.BEQZ` +/// - `C.BNEZ` +#[inline(always)] +pub fn run_branch_compare_zero( + icb: &mut I, + predicate: Predicate, + imm: i64, + rs1: NonZeroXRegister, + width: InstrWidth, +) -> ProgramCounterUpdate<::XValue> { + let lhs = icb.xregister_read_nz(rs1); + let rhs = icb.xvalue_of_imm(0); + + let cond = icb.xvalue_compare(predicate, lhs, rhs); + + icb.branch(cond, imm, width) +} + #[cfg(test)] mod tests { + use proptest::prelude::*; + use crate::backend_test; use crate::create_state; + use crate::instruction_context::Predicate; use crate::interpreter::branching::run_jr_imm; use crate::machine_state::MachineCoreState; use crate::machine_state::MachineCoreStateLayout; + use crate::machine_state::ProgramCounterUpdate; use crate::machine_state::memory::M4K; use crate::machine_state::registers::nz; use crate::parser::instruction::InstrWidth; @@ -218,4 +281,245 @@ mod tests { assert_eq!(read_pc, res); } }); + + macro_rules! test_branch_compare_zero { + ($state:ident, $predicate:expr, $imm:expr, + $rs1:path, $r1_val:expr, $width:expr, + $init_pc:ident, $expected_pc:expr + ) => { + $state.hart.pc.write($init_pc); + $state.hart.xregisters.write_nz($rs1, $r1_val); + + let new_pc = + super::run_branch_compare_zero(&mut $state, $predicate, $imm, $rs1, $width); + prop_assert_eq!(&new_pc, $expected_pc); + }; + } + + macro_rules! test_branch { + ($state:ident, $predicate:expr, $imm:expr, + $rs1:path, $r1_val:expr, + $rs2:path, $r2_val:expr, $width:expr, + $init_pc:ident, $expected_pc:expr + ) => { + $state.hart.pc.write($init_pc); + $state.hart.xregisters.write_nz($rs1, $r1_val); + $state.hart.xregisters.write_nz($rs2, $r2_val); + + let new_pc = super::run_branch(&mut $state, $predicate, $imm, $rs1, $rs2, $width); + prop_assert_eq!(&new_pc, $expected_pc); + }; + } + + backend_test!(test_beq_bne, F, { + proptest!(|( + init_pc in any::(), + imm in any::(), + r1_val in any::(), + r2_val in any::(), + )| { + // to ensure different behaviour for tests + prop_assume!(r1_val != r2_val); + // to ensure branch_pc, init_pc, next_pc are different + prop_assume!(imm > 10); + let branch_pcu = ProgramCounterUpdate::Set(init_pc.wrapping_add(imm as u64)); + let width = InstrWidth::Uncompressed; + let next_pcu = ProgramCounterUpdate::Next(InstrWidth::Uncompressed); + let init_pcu = ProgramCounterUpdate::Set(init_pc); + + let mut state = create_state!(MachineCoreState, MachineCoreStateLayout, F, M4K); + + // BEQ: different + test_branch!(state, Predicate::Equal, imm, nz::t1, r1_val, nz::t2, r2_val, width, init_pc, &next_pcu); + // BEQ: equal + test_branch!(state, Predicate::Equal, imm, nz::t1, r1_val, nz::t2, r1_val, width, init_pc, &branch_pcu); + + // BNE: different + test_branch!(state, Predicate::NotEqual, imm, nz::t1, r1_val, nz::t2, r2_val, width, init_pc, &branch_pcu); + // BNE: equal + test_branch!(state, Predicate::NotEqual, imm, nz::t1, r1_val, nz::t2, r1_val, width, init_pc, &next_pcu); + + // BEQ: different - imm = 0 + test_branch!(state, Predicate::Equal, 0, nz::t1, r1_val, nz::t2, r2_val, width, init_pc, &next_pcu); + // BEQ: equal - imm = 0 + test_branch!(state, Predicate::Equal, 0, nz::t1, r1_val, nz::t2, r1_val, width, init_pc, &init_pcu); + + // BNE: different - imm = 0 + test_branch!(state, Predicate::NotEqual, 0, nz::t1, r1_val, nz::t2, r2_val, width, init_pc, &init_pcu); + // BNE: equal - imm = 0 + test_branch!(state, Predicate::NotEqual, 0, nz::t1, r1_val, nz::t2, r1_val, width, init_pc, &next_pcu); + + // BEQ: same register - imm = 0 + test_branch!(state, Predicate::Equal, 0, nz::t1, r1_val, nz::t1, r2_val, width, init_pc, &init_pcu); + // BEQ: same register + test_branch!(state, Predicate::Equal, imm, nz::t1, r1_val, nz::t1, r2_val, width, init_pc, &branch_pcu); + + // BNE: same register - imm = 0 + test_branch!(state, Predicate::NotEqual, 0, nz::t1, r1_val, nz::t1, r2_val, width, init_pc, &next_pcu); + // BNE: same register + test_branch!(state, Predicate::NotEqual, imm, nz::t1, r1_val, nz::t1, r2_val, width, init_pc, &next_pcu); + }); + }); + + backend_test!(test_bge_blt, F, { + proptest!(|( + init_pc in any::(), + imm in any::(), + )| { + // to ensure branch_pc and init_pc are different + prop_assume!(imm > 10); + let branch_pcu = ProgramCounterUpdate::Set(init_pc.wrapping_add(imm as u64)); + let width = InstrWidth::Uncompressed; + let next_pcu = ProgramCounterUpdate::Next(InstrWidth::Uncompressed); + let init_pcu = ProgramCounterUpdate::Set(init_pc); + + let mut state = create_state!(MachineCoreState, MachineCoreStateLayout, F, M4K); + + // lhs < rhs + test_branch!(state, Predicate::LessThanSigned, imm, nz::t1, 0, nz::t2, 1, width, init_pc, &branch_pcu); + test_branch!(state, Predicate::GreaterThanOrEqualSigned, imm, nz::t1, i64::MIN as u64, nz::t2, i64::MAX as u64, width, init_pc, &next_pcu); + + // lhs > rhs + test_branch!(state, Predicate::LessThanSigned, imm, nz::t1, -1_i64 as u64, nz::t2, i64::MAX as u64, width, init_pc, &branch_pcu); + test_branch!(state, Predicate::GreaterThanOrEqualSigned, imm, nz::t1, 0, nz::t2, -123_123i64 as u64, width, init_pc, &branch_pcu); + + // lhs = rhs + test_branch!(state, Predicate::LessThanSigned, imm, nz::t1, 0, nz::t2, 0, width, init_pc, &next_pcu); + test_branch!(state, Predicate::GreaterThanOrEqualSigned, imm, nz::t1, i64::MAX as u64, nz::t2, i64::MAX as u64, width, init_pc, &branch_pcu); + + // same register + test_branch!(state, Predicate::LessThanSigned, imm, nz::t1, -1_i64 as u64, nz::t1, -1_i64 as u64, width, init_pc, &next_pcu); + test_branch!(state, Predicate::GreaterThanOrEqualSigned, imm, nz::t2, 0, nz::t2, 0, width, init_pc, &branch_pcu); + + // imm 0 + // lhs < rhs + test_branch!(state, Predicate::LessThanSigned, 0, nz::t1, 100, nz::t2, i64::MAX as u64, width, init_pc, &init_pcu); + test_branch!(state, Predicate::GreaterThanOrEqualSigned, 0, nz::t1, -1_i64 as u64, nz::t2, i64::MIN as u64, width, init_pc, &init_pcu); + + // same register + test_branch!(state, Predicate::LessThanSigned, 0, nz::t1, 123_123_123, nz::t1, 123_123_123, width, init_pc, &next_pcu); + test_branch!(state, Predicate::GreaterThanOrEqualSigned, 0, nz::t2, -1_i64 as u64, nz::t2, -1_i64 as u64, width, init_pc, &init_pcu); + }); + }); + + backend_test!(test_b, F, { + proptest!(|( + init_pc in any::(), + imm in any::(), + )| { + // to ensure branch_pc and init_pc are different + prop_assume!(imm > 10); + let branch_pcu = ProgramCounterUpdate::Set(init_pc.wrapping_add(imm as u64)); + let width = InstrWidth::Uncompressed; + let next_pcu = ProgramCounterUpdate::Next(InstrWidth::Uncompressed); + + let mut state = create_state!(MachineCoreState, MachineCoreStateLayout, F, M4K); + + // lhs < 0 + test_branch_compare_zero!(state, Predicate::LessThanSigned, imm, nz::t1, -1_i64 as u64, width, init_pc, &branch_pcu); + test_branch_compare_zero!(state, Predicate::GreaterThanOrEqualSigned, imm, nz::t1, -1_i64 as u64, width, init_pc, &next_pcu); + test_branch_compare_zero!(state, Predicate::LessThanOrEqualSigned, imm, nz::t1, -1_i64 as u64, width, init_pc, &branch_pcu); + test_branch_compare_zero!(state, Predicate::GreaterThanSigned, imm, nz::t1, -1_i64 as u64, width, init_pc, &next_pcu); + + // lhs > 0 + test_branch_compare_zero!(state, Predicate::LessThanSigned, imm, nz::t1, 1, width, init_pc, &next_pcu); + test_branch_compare_zero!(state, Predicate::GreaterThanOrEqualSigned, imm, nz::t1, 1, width, init_pc, &branch_pcu); + test_branch_compare_zero!(state, Predicate::LessThanOrEqualSigned, imm, nz::t1, 1, width, init_pc, &next_pcu); + test_branch_compare_zero!(state, Predicate::GreaterThanSigned, imm, nz::t1, 1, width, init_pc, &branch_pcu); + + // lhs = 0 + test_branch_compare_zero!(state, Predicate::LessThanSigned, imm, nz::t1, 0, width, init_pc, &next_pcu); + test_branch_compare_zero!(state, Predicate::GreaterThanOrEqualSigned, imm, nz::t1, 0, width, init_pc, &branch_pcu); + test_branch_compare_zero!(state, Predicate::LessThanOrEqualSigned, imm, nz::t1, 0, width, init_pc, &branch_pcu); + test_branch_compare_zero!(state, Predicate::GreaterThanSigned, imm, nz::t1, 0, width, init_pc, &next_pcu); + }) + }); + + backend_test!(test_bge_ble_u, F, { + proptest!(|( + init_pc in any::(), + imm in any::(), + r1_val in any::(), + r2_val in any::(), + )| { + prop_assume!(r1_val < r2_val); + // to ensure branch_pc and init_pc are different + prop_assume!(imm > 10); + let branch_pcu = ProgramCounterUpdate::Set(init_pc.wrapping_add(imm as u64)); + let width = InstrWidth::Uncompressed; + let next_pcu = ProgramCounterUpdate::Next(InstrWidth::Uncompressed); + let pc_update_init_pcu = ProgramCounterUpdate::Set(init_pc); + + let mut state = create_state!(MachineCoreState, MachineCoreStateLayout, F, M4K); + + // lhs < rhs + test_branch!(state, Predicate::LessThanUnsigned, imm, nz::t1, r1_val, nz::t2, r2_val, width, init_pc, &branch_pcu); + test_branch!(state, Predicate::GreaterThanOrEqualUnsigned, imm, nz::t1, r1_val, nz::t2, r2_val, width, init_pc, &next_pcu); + + // lhs > rhs + test_branch!(state, Predicate::LessThanUnsigned, imm, nz::t1, r2_val, nz::t2, r1_val, width, init_pc, &next_pcu); + test_branch!(state, Predicate::GreaterThanOrEqualUnsigned, imm, nz::t1, r2_val, nz::t2, r1_val, width, init_pc, &branch_pcu); + + // lhs = rhs + test_branch!(state, Predicate::LessThanUnsigned, imm, nz::t1, r1_val, nz::t2, r1_val, width, init_pc, &next_pcu); + test_branch!(state, Predicate::GreaterThanOrEqualUnsigned, imm, nz::t1, r2_val, nz::t2, r2_val, width, init_pc, &branch_pcu); + + // same register + test_branch!(state, Predicate::LessThanUnsigned, imm, nz::t1, r1_val, nz::t1, r1_val, width, init_pc, &next_pcu); + test_branch!(state, Predicate::GreaterThanOrEqualUnsigned, imm, nz::t2, r1_val, nz::t2, r1_val, width, init_pc, &branch_pcu); + + // imm 0 + // lhs < rhs + test_branch!(state, Predicate::LessThanUnsigned, 0, nz::t1, r1_val, nz::t2, r2_val, width, init_pc, &pc_update_init_pcu); + test_branch!(state, Predicate::GreaterThanOrEqualUnsigned, 0, nz::t1, r1_val, nz::t2, r2_val, width, init_pc, &next_pcu); + + // lhs > rhs + test_branch!(state, Predicate::LessThanUnsigned, 0, nz::t1, r2_val, nz::t2, r1_val, width, init_pc, &next_pcu); + test_branch!(state, Predicate::GreaterThanOrEqualUnsigned, 0, nz::t1, r2_val, nz::t2, r1_val, width, init_pc, &pc_update_init_pcu); + + // lhs = rhs + test_branch!(state, Predicate::LessThanUnsigned, 0, nz::t1, r1_val, nz::t2, r1_val, width, init_pc, &next_pcu); + test_branch!(state, Predicate::GreaterThanOrEqualUnsigned, 0, nz::t2, r2_val, nz::t1, r2_val, width, init_pc, &pc_update_init_pcu); + + // same register + test_branch!(state, Predicate::LessThanUnsigned, 0, nz::t1, r1_val, nz::t1, r1_val, width, init_pc, &next_pcu); + test_branch!(state, Predicate::GreaterThanOrEqualUnsigned, 0, nz::t2, r1_val, nz::t2, r1_val, width, init_pc, &pc_update_init_pcu); + + }); + }); + + backend_test!(test_beqz_bnez, F, { + proptest!(|( + init_pc in any::(), + imm in any::(), + r1_val in any::(), + )| { + // to ensure branch_pc, init_pc, next_pc are different + prop_assume!(imm > 10); + let branch_pcu = ProgramCounterUpdate::Set(init_pc.wrapping_add(imm as u64)); + let width = InstrWidth::Uncompressed; + let next_pcu = ProgramCounterUpdate::Next(InstrWidth::Uncompressed); + let init_pcu = ProgramCounterUpdate::Set(init_pc); + + let mut state = create_state!(MachineCoreState, MachineCoreStateLayout, F, M4K); + + // BEQZ + if r1_val == 0 { + test_branch_compare_zero!(state, Predicate::Equal, imm, nz::t1, r1_val, width, init_pc, &branch_pcu); + test_branch_compare_zero!(state, Predicate::NotEqual, imm, nz::t1, r1_val, width, init_pc, &next_pcu); + } else { + test_branch_compare_zero!(state, Predicate::Equal, imm, nz::t1, r1_val, width, init_pc, &next_pcu); + test_branch_compare_zero!(state, Predicate::NotEqual, imm, nz::t1, r1_val, width, init_pc, &branch_pcu); + } + + // BEQZ when imm = 0 + if r1_val == 0 { + test_branch_compare_zero!(state, Predicate::Equal, 0, nz::t1, r1_val, width, init_pc, &init_pcu); + test_branch_compare_zero!(state, Predicate::NotEqual, 0, nz::t1, r1_val, width, init_pc, &next_pcu); + } else { + test_branch_compare_zero!(state, Predicate::Equal, 0, nz::t1, r1_val, width, init_pc, &next_pcu); + test_branch_compare_zero!(state, Predicate::NotEqual, 0, nz::t1, r1_val, width, init_pc, &init_pcu); + } + }); + }); } diff --git a/src/riscv/lib/src/interpreter/rv32c.rs b/src/riscv/lib/src/interpreter/rv32c.rs index cf4f73b66147900dd74750827b50145dfa01fbd8..a59dc257fa465e45261c4ed21ac53d1a24e6478d 100644 --- a/src/riscv/lib/src/interpreter/rv32c.rs +++ b/src/riscv/lib/src/interpreter/rv32c.rs @@ -8,7 +8,6 @@ //! U:C-16 use crate::machine_state::MachineCoreState; -use crate::machine_state::ProgramCounterUpdate; use crate::machine_state::hart_state::HartState; use crate::machine_state::memory; use crate::machine_state::memory::Address; @@ -46,54 +45,6 @@ where target_address } - /// Performs a conditional ( val(`rs1`) == 0 ) control transfer. - /// If condition met, the offset is sign-extended and added to the pc to form the branch - /// target address that is then set, otherwise indicates to proceed to the next instruction. - /// - /// Relevant RISC-V opcodes: - /// - C.BEQZ - /// - BEQ - /// - BGEU - pub fn run_beqz( - &mut self, - imm: i64, - rs1: NonZeroXRegister, - width: InstrWidth, - ) -> ProgramCounterUpdate
{ - let current_pc = self.pc.read(); - - if self.xregisters.read_nz(rs1) == 0 { - ProgramCounterUpdate::Set(current_pc.wrapping_add(imm as u64)) - } else { - ProgramCounterUpdate::Next(width) - } - } - - /// Performs a conditional ( val(`rs1`) != 0 ) control transfer. - /// If condition met, the offset is sign-extended and added to the pc to form the branch - /// target address that is then set, otherwise indicates to proceed to the next instruction. - /// - /// Relevant RISC-V opcodes: - /// - C.BNEZ - /// - BNE - /// - BLTU - pub fn run_bnez( - &mut self, - imm: i64, - rs1: NonZeroXRegister, - width: InstrWidth, - ) -> ProgramCounterUpdate
{ - let current_pc = self.pc.read(); - - // Branch if `val(rs1) != val(rs2)`, jumping `imm` bytes ahead. - // Otherwise, jump the width of current instruction - if self.xregisters.read_nz(rs1) != 0 { - ProgramCounterUpdate::Set(current_pc.wrapping_add(imm as u64)) - } else { - ProgramCounterUpdate::Next(width) - } - } - /// `C.EBREAK` compressed instruction /// /// Equivalent to `EBREAK`. @@ -122,23 +73,17 @@ where #[cfg(test)] mod tests { - use proptest::prelude::*; - use proptest::prop_assert_eq; - use proptest::proptest; - use crate::backend_test; use crate::create_state; use crate::interpreter::branching::run_j; use crate::interpreter::branching::run_jr; use crate::machine_state::MachineCoreState; use crate::machine_state::MachineCoreStateLayout; - use crate::machine_state::ProgramCounterUpdate; use crate::machine_state::hart_state::HartState; use crate::machine_state::hart_state::HartStateLayout; use crate::machine_state::memory::M4K; use crate::machine_state::registers::nz; use crate::machine_state::registers::nz::a0; - use crate::machine_state::registers::t1; use crate::parser::instruction::InstrWidth; backend_test!(test_run_j, F, { @@ -223,52 +168,4 @@ mod tests { // big imm (>= 32)) test_shift_instr!(state, run_slli, 40, a0, 0x1234_ABEF, 0x34AB_EF00_0000_0000); }); - - macro_rules! test_branch_instr { - ($state:ident, $branch_fn:tt, $imm:expr, - $rs1:ident, $r1_val:expr, $width:expr, - $init_pc:ident, $expected_pc:expr - ) => { - $state.pc.write($init_pc); - $state.xregisters.write($rs1, $r1_val); - - let new_pc = $state.$branch_fn($imm, nz::$rs1, $width); - prop_assert_eq!(&new_pc, $expected_pc); - }; - } - - backend_test!(test_beqz_bnez, F, { - proptest!(|( - init_pc in any::(), - imm in any::(), - r1_val in any::(), - )| { - // to ensure branch_pc, init_pc, next_pc are different - prop_assume!(imm > 10); - let branch_pcu = ProgramCounterUpdate::Set(init_pc.wrapping_add(imm as u64)); - let width = InstrWidth::Uncompressed; - let next_pcu = ProgramCounterUpdate::Next(InstrWidth::Uncompressed); - let init_pcu = ProgramCounterUpdate::Set(init_pc); - - let mut state = create_state!(HartState, F); - - // BEQZ - if r1_val == 0 { - test_branch_instr!(state, run_beqz, imm, t1, r1_val, width, init_pc, &branch_pcu); - test_branch_instr!(state, run_bnez, imm, t1, r1_val, width, init_pc, &next_pcu); - } else { - test_branch_instr!(state, run_beqz, imm, t1, r1_val, width, init_pc, &next_pcu); - test_branch_instr!(state, run_bnez, imm, t1, r1_val, width, init_pc, &branch_pcu); - } - - // BEQZ when imm = 0 - if r1_val == 0 { - test_branch_instr!(state, run_beqz, 0, t1, r1_val, width, init_pc, &init_pcu); - test_branch_instr!(state, run_bnez, 0, t1, r1_val, width, init_pc, &next_pcu); - } else { - test_branch_instr!(state, run_beqz, 0, t1, r1_val, width, init_pc, &next_pcu); - test_branch_instr!(state, run_bnez, 0, t1, r1_val, width, init_pc, &init_pcu); - } - }); - }); } diff --git a/src/riscv/lib/src/interpreter/rv32i.rs b/src/riscv/lib/src/interpreter/rv32i.rs index e7a627609706c2d309df9225bea2ffd6ed7db979..59f66cefeaf94897e7c33cd6af3479cf2ddcd962 100644 --- a/src/riscv/lib/src/interpreter/rv32i.rs +++ b/src/riscv/lib/src/interpreter/rv32i.rs @@ -7,7 +7,6 @@ //! Chapter 2 - Unprivileged spec use crate::machine_state::MachineCoreState; -use crate::machine_state::ProgramCounterUpdate; use crate::machine_state::hart_state::HartState; use crate::machine_state::memory; use crate::machine_state::memory::Address; @@ -81,230 +80,6 @@ where current_pc.wrapping_add(imm as u64) } - - /// Performs a conditional ( `val(rs1) == val(rs2)` ) control transfer. - /// If condition met, the offset is sign-extended and added to the pc to form the branch - /// target address that is then set, otherwise indicates to proceed to the next instruction. - /// - /// Relevant RISC-V opcodes: - /// - `BEQ` - /// - `C.BEQZ` - pub fn run_beq( - &mut self, - imm: i64, - rs1: NonZeroXRegister, - rs2: NonZeroXRegister, - width: InstrWidth, - ) -> ProgramCounterUpdate
{ - let current_pc = self.pc.read(); - - if self.xregisters.read_nz(rs1) == self.xregisters.read_nz(rs2) { - ProgramCounterUpdate::Set(current_pc.wrapping_add(imm as u64)) - } else { - ProgramCounterUpdate::Next(width) - } - } - - /// Performs a conditional ( `val(rs1) != val(rs2)` ) control transfer. - /// If condition met, the offset is sign-extended and added to the pc to form the branch - /// target address that is then set, otherwise indicates to proceed to the next instruction. - /// - /// Relevant RISC-V opcodes: - /// - `BNE` - /// - `C.BNEZ` - pub fn run_bne( - &mut self, - imm: i64, - rs1: NonZeroXRegister, - rs2: NonZeroXRegister, - width: InstrWidth, - ) -> ProgramCounterUpdate
{ - if self.xregisters.read_nz(rs1) != self.xregisters.read_nz(rs2) { - let current_pc = self.pc.read(); - ProgramCounterUpdate::Set(current_pc.wrapping_add(imm as u64)) - } else { - ProgramCounterUpdate::Next(width) - } - } - - /// Sets branching address if `val(rs1) >= val(rs2)` in signed comparison, - /// otherwise proceeds to the next instruction address. - /// - /// Relevant RISC-V opcodes: - /// - `BGE` - pub fn run_bge( - &mut self, - imm: i64, - rs1: NonZeroXRegister, - rs2: NonZeroXRegister, - width: InstrWidth, - ) -> ProgramCounterUpdate
{ - let lhs = self.xregisters.read_nz(rs1) as i64; - let rhs = self.xregisters.read_nz(rs2) as i64; - - if lhs >= rhs { - let current_pc = self.pc.read(); - ProgramCounterUpdate::Set(current_pc.wrapping_add(imm as u64)) - } else { - ProgramCounterUpdate::Next(width) - } - } - - /// Sets branching address if `val(rs1) >= 0` in signed comparison, - /// otherwise proceeds to the next instruction address. - /// - /// Relevant RISC-V opcodes: - /// - `BLT` - pub fn run_bgez( - &mut self, - imm: i64, - rs1: NonZeroXRegister, - width: InstrWidth, - ) -> ProgramCounterUpdate
{ - let lhs = self.xregisters.read_nz(rs1) as i64; - - if lhs >= 0 { - let current_pc = self.pc.read(); - ProgramCounterUpdate::Set(current_pc.wrapping_add(imm as u64)) - } else { - ProgramCounterUpdate::Next(width) - } - } - - /// Sets branching address if `val(rs1) > 0` in signed comparison, - /// otherwise proceeds to the next instruction address. - /// - /// Relevant RISC-V opcodes: - /// - `BLT` - pub fn run_bgz( - &mut self, - imm: i64, - rs1: NonZeroXRegister, - width: InstrWidth, - ) -> ProgramCounterUpdate
{ - let lhs = self.xregisters.read_nz(rs1) as i64; - - if lhs > 0 { - let current_pc = self.pc.read(); - ProgramCounterUpdate::Set(current_pc.wrapping_add(imm as u64)) - } else { - ProgramCounterUpdate::Next(width) - } - } - - /// Sets branching address if `val(rs1) >= val(rs2)` in unsigned comparison, - /// otherwise proceeds to the next instruction address. - /// - /// Relevant RISC-V opcodes: - /// - `BGEU` - pub fn run_bgeu( - &mut self, - imm: i64, - rs1: NonZeroXRegister, - rs2: NonZeroXRegister, - width: InstrWidth, - ) -> ProgramCounterUpdate
{ - let lhs = self.xregisters.read_nz(rs1); - let rhs = self.xregisters.read_nz(rs2); - - if lhs >= rhs { - let current_pc = self.pc.read(); - ProgramCounterUpdate::Set(current_pc.wrapping_add(imm as u64)) - } else { - ProgramCounterUpdate::Next(width) - } - } - - /// Sets branching address if `val(rs1) < val(rs2)` in signed comparison, - /// otherwise proceeds to the next instruction address. - /// - /// Relevant RISC-V opcodes: - /// - `BLT` - pub fn run_blt( - &mut self, - imm: i64, - rs1: NonZeroXRegister, - rs2: NonZeroXRegister, - width: InstrWidth, - ) -> ProgramCounterUpdate
{ - let lhs = self.xregisters.read_nz(rs1) as i64; - let rhs = self.xregisters.read_nz(rs2) as i64; - - if lhs < rhs { - let current_pc = self.pc.read(); - ProgramCounterUpdate::Set(current_pc.wrapping_add(imm as u64)) - } else { - ProgramCounterUpdate::Next(width) - } - } - - /// Sets branching address if `val(rs1) < 0` in signed comparison, - /// otherwise proceeds to the next instruction address. - /// - /// Relevant RISC-V opcodes: - /// - `BLT` - /// - `BGE` - pub fn run_bltz( - &mut self, - imm: i64, - rs1: NonZeroXRegister, - width: InstrWidth, - ) -> ProgramCounterUpdate
{ - let lhs = self.xregisters.read_nz(rs1) as i64; - - if lhs < 0 { - let current_pc = self.pc.read(); - ProgramCounterUpdate::Set(current_pc.wrapping_add(imm as u64)) - } else { - ProgramCounterUpdate::Next(width) - } - } - - /// Sets branching address if `val(rs1) <= 0` in signed comparison, - /// otherwise proceeds to the next instruction address. - /// - /// Relevant RISC-V opcodes: - /// - `BGE` - pub fn run_bltez( - &mut self, - imm: i64, - rs1: NonZeroXRegister, - width: InstrWidth, - ) -> ProgramCounterUpdate
{ - let lhs = self.xregisters.read_nz(rs1) as i64; - - if lhs <= 0 { - let current_pc = self.pc.read(); - ProgramCounterUpdate::Set(current_pc.wrapping_add(imm as u64)) - } else { - ProgramCounterUpdate::Next(width) - } - } - - /// `BLTU` B-type instruction - /// - /// Sets branching address if `val(rs1) < val(rs2)` in unsigned comparison, - /// otherwise proceeds to the next instruction address. - /// - /// Relevant RISC-V opcodes: - /// - `BLTU` - pub fn run_bltu( - &mut self, - imm: i64, - rs1: NonZeroXRegister, - rs2: NonZeroXRegister, - width: InstrWidth, - ) -> ProgramCounterUpdate
{ - let lhs = self.xregisters.read_nz(rs1); - let rhs = self.xregisters.read_nz(rs2); - - if lhs < rhs { - let current_pc = self.pc.read(); - ProgramCounterUpdate::Set(current_pc.wrapping_add(imm as u64)) - } else { - ProgramCounterUpdate::Next(width) - } - } } impl MachineCoreState @@ -327,7 +102,6 @@ mod tests { use proptest::prelude::any; use proptest::prelude::prop; use proptest::prop_assert_eq; - use proptest::prop_assume; use proptest::proptest; use crate::backend_test; @@ -337,7 +111,6 @@ mod tests { use crate::interpreter::integer::run_or; use crate::machine_state::MachineCoreState; use crate::machine_state::MachineCoreStateLayout; - use crate::machine_state::ProgramCounterUpdate; use crate::machine_state::csregisters::CSRRepr; use crate::machine_state::csregisters::CSRegister; use crate::machine_state::csregisters::xstatus::MPPValue; @@ -360,158 +133,6 @@ mod tests { use crate::parser::instruction::InstrWidth; use crate::traps::Exception; - macro_rules! test_b_instr { - ($state:ident, $branch_fn:tt, $imm:expr, - $rs1:ident, $r1_val:expr, $width:expr, - $init_pc:ident, $expected_pc:expr - ) => { - $state.pc.write($init_pc); - $state.xregisters.write_nz(nz::$rs1, $r1_val); - - let new_pc = $state.$branch_fn($imm, nz::$rs1, $width); - prop_assert_eq!(&new_pc, $expected_pc); - }; - } - - macro_rules! test_nz_branch_instr { - ($state:ident, $branch_fn:tt, $imm:expr, - $rs1:ident, $r1_val:expr, - $rs2:ident, $r2_val:expr, $width:expr, - $init_pc:ident, $expected_pc:expr - ) => { - $state.pc.write($init_pc); - $state.xregisters.write($rs1, $r1_val); - $state.xregisters.write($rs2, $r2_val); - - let new_pc = $state.$branch_fn($imm, nz::$rs1, nz::$rs2, $width); - prop_assert_eq!(&new_pc, $expected_pc); - }; - } - - backend_test!(test_beq_bne, F, { - proptest!(|( - init_pc in any::(), - imm in any::(), - r1_val in any::(), - r2_val in any::(), - )| { - // to ensure different behaviour for tests - prop_assume!(r1_val != r2_val); - // to ensure branch_pc, init_pc, next_pc are different - prop_assume!(imm > 10); - let branch_pcu = ProgramCounterUpdate::Set(init_pc.wrapping_add(imm as u64)); - let width = InstrWidth::Uncompressed; - let next_pcu = ProgramCounterUpdate::Next(InstrWidth::Uncompressed); - let init_pcu = ProgramCounterUpdate::Set(init_pc); - - let mut state = create_state!(HartState, F); - - // BEQ - different - test_nz_branch_instr!(state, run_beq, imm, t1, r1_val, t2, r2_val, width, init_pc, &next_pcu); - // BEQ - equal - test_nz_branch_instr!(state, run_beq, imm, t1, r1_val, t2, r1_val, width, init_pc, &branch_pcu); - - // BNE - different - test_nz_branch_instr!(state, run_bne, imm, t1, r1_val, t2, r2_val, width, init_pc, &branch_pcu); - // BNE - equal - test_nz_branch_instr!(state, run_bne, imm, t1, r1_val, t2, r1_val, width, init_pc, &next_pcu); - - // BEQ - different - imm = 0 - test_nz_branch_instr!(state, run_beq, 0, t1, r1_val, t2, r2_val, width, init_pc, &next_pcu); - // BEQ - equal - imm = 0 - test_nz_branch_instr!(state, run_beq, 0, t1, r1_val, t2, r1_val, width, init_pc, &init_pcu); - - // BNE - different - imm = 0 - test_nz_branch_instr!(state, run_bne, 0, t1, r1_val, t2, r2_val, width, init_pc, &init_pcu); - // BNE - equal - imm = 0 - test_nz_branch_instr!(state, run_bne, 0, t1, r1_val, t2, r1_val, width, init_pc, &next_pcu); - - // BEQ - same register - imm = 0 - test_nz_branch_instr!(state, run_beq, 0, t1, r1_val, t1, r2_val, width, init_pc, &init_pcu); - // BEQ - same register - test_nz_branch_instr!(state, run_beq, imm, t1, r1_val, t1, r2_val, width, init_pc, &branch_pcu); - - // BNE - same register - imm = 0 - test_nz_branch_instr!(state, run_bne, 0, t1, r1_val, t1, r2_val, width, init_pc, &next_pcu); - // BNE - same register - test_nz_branch_instr!(state, run_bne, imm, t1, r1_val, t1, r2_val, width, init_pc, &next_pcu); - }); - }); - - backend_test!(test_bge_blt, F, { - proptest!(|( - init_pc in any::(), - imm in any::(), - )| { - // to ensure branch_pc and init_pc are different - prop_assume!(imm > 10); - let branch_pcu = ProgramCounterUpdate::Set(init_pc.wrapping_add(imm as u64)); - let width = InstrWidth::Uncompressed; - let next_pcu = ProgramCounterUpdate::Next(InstrWidth::Uncompressed); - let init_pcu = ProgramCounterUpdate::Set(init_pc); - - let mut state = create_state!(HartState, F); - - // lhs < rhs - test_nz_branch_instr!(state, run_blt, imm, t1, 0, t2, 1, width, init_pc, &branch_pcu); - test_nz_branch_instr!(state, run_bge, imm, t1, i64::MIN as u64, t2, i64::MAX as u64, width, init_pc, &next_pcu); - - // lhs > rhs - test_nz_branch_instr!(state, run_blt, imm, t1, -1_i64 as u64, t2, i64::MAX as u64, width, init_pc, &branch_pcu); - test_nz_branch_instr!(state, run_bge, imm, t1, 0, t2, -123_123i64 as u64, width, init_pc, &branch_pcu); - - // lhs = rhs - test_nz_branch_instr!(state, run_blt, imm, t1, 0, t2, 0, width, init_pc, &next_pcu); - test_nz_branch_instr!(state, run_bge, imm, t1, i64::MAX as u64, t2, i64::MAX as u64, width, init_pc, &branch_pcu); - - // same register - test_nz_branch_instr!(state, run_blt, imm, t1, -1_i64 as u64, t1, -1_i64 as u64, width, init_pc, &next_pcu); - test_nz_branch_instr!(state, run_bge, imm, t2, 0, t2, 0, width, init_pc, &branch_pcu); - - // imm 0 - // lhs < rhs - test_nz_branch_instr!(state, run_blt, 0, t1, 100, t2, i64::MAX as u64, width, init_pc, &init_pcu); - test_nz_branch_instr!(state, run_bge, 0, t1, -1_i64 as u64, t2, i64::MIN as u64, width, init_pc, &init_pcu); - - // same register - test_nz_branch_instr!(state, run_blt, 0, t1, 123_123_123, t1, 123_123_123, width, init_pc, &next_pcu); - test_nz_branch_instr!(state, run_bge, 0, t2, -1_i64 as u64, t2, -1_i64 as u64, width, init_pc, &init_pcu); - }); - }); - - backend_test!(test_b, F, { - proptest!(|( - init_pc in any::(), - imm in any::(), - )| { - // to ensure branch_pc and init_pc are different - prop_assume!(imm > 10); - let branch_pcu = ProgramCounterUpdate::Set(init_pc.wrapping_add(imm as u64)); - let width = InstrWidth::Uncompressed; - let next_pcu = ProgramCounterUpdate::Next(InstrWidth::Uncompressed); - - let mut state = create_state!(HartState, F); - - // lhs < 0 - test_b_instr!(state, run_bltz, imm, t1, -1_i64 as u64, width, init_pc, &branch_pcu); - test_b_instr!(state, run_bgez, imm, t1, -1_i64 as u64, width, init_pc, &next_pcu); - test_b_instr!(state, run_bltez, imm, t1, -1_i64 as u64, width, init_pc, &branch_pcu); - test_b_instr!(state, run_bgz, imm, t1, -1_i64 as u64, width, init_pc, &next_pcu); - - // lhs > 0 - test_b_instr!(state, run_bltz, imm, t1, 1, width, init_pc, &next_pcu); - test_b_instr!(state, run_bgez, imm, t1, 1, width, init_pc, &branch_pcu); - test_b_instr!(state, run_bltez, imm, t1, 1, width, init_pc, &next_pcu); - test_b_instr!(state, run_bgz, imm, t1, 1, width, init_pc, &branch_pcu); - - // lhs = 0 - test_b_instr!(state, run_bltz, imm, t1, 0, width, init_pc, &next_pcu); - test_b_instr!(state, run_bgez, imm, t1, 0, width, init_pc, &branch_pcu); - test_b_instr!(state, run_bltez, imm, t1, 0, width, init_pc, &branch_pcu); - test_b_instr!(state, run_bgz, imm, t1, 0, width, init_pc, &next_pcu); - }) - }); - backend_test!(test_bitwise, F, { proptest!(|(val in any::(), imm in any::())| { let mut state = create_state!(MachineCoreState, MachineCoreStateLayout, F, M4K); @@ -578,59 +199,6 @@ mod tests { }); }); - backend_test!(test_bge_ble_u, F, { - proptest!(|( - init_pc in any::(), - imm in any::(), - r1_val in any::(), - r2_val in any::(), - )| { - prop_assume!(r1_val < r2_val); - // to ensure branch_pc and init_pc are different - prop_assume!(imm > 10); - let branch_pcu = ProgramCounterUpdate::Set(init_pc.wrapping_add(imm as u64)); - let width = InstrWidth::Uncompressed; - let next_pcu = ProgramCounterUpdate::Next(InstrWidth::Uncompressed); - let pc_update_init_pcu = ProgramCounterUpdate::Set(init_pc); - - let mut state = create_state!(HartState, F); - - // lhs < rhs - test_nz_branch_instr!(state, run_bltu, imm, t1, r1_val, t2, r2_val, width, init_pc, &branch_pcu); - test_nz_branch_instr!(state, run_bgeu, imm, t1, r1_val, t2, r2_val, width, init_pc, &next_pcu); - - // lhs > rhs - test_nz_branch_instr!(state, run_bltu, imm, t1, r2_val, t2, r1_val, width, init_pc, &next_pcu); - test_nz_branch_instr!(state, run_bgeu, imm, t1, r2_val, t2, r1_val, width, init_pc, &branch_pcu); - - // lhs = rhs - test_nz_branch_instr!(state, run_bltu, imm, t1, r1_val, t2, r1_val, width, init_pc, &next_pcu); - test_nz_branch_instr!(state, run_bgeu, imm, t1, r2_val, t2, r2_val, width, init_pc, &branch_pcu); - - // same register - test_nz_branch_instr!(state, run_bltu, imm, t1, r1_val, t1, r1_val, width, init_pc, &next_pcu); - test_nz_branch_instr!(state, run_bgeu, imm, t2, r1_val, t2, r1_val, width, init_pc, &branch_pcu); - - // imm 0 - // lhs < rhs - test_nz_branch_instr!(state, run_bltu, 0, t1, r1_val, t2, r2_val, width, init_pc, &pc_update_init_pcu); - test_nz_branch_instr!(state, run_bgeu, 0, t1, r1_val, t2, r2_val, width, init_pc, &next_pcu); - - // lhs > rhs - test_nz_branch_instr!(state, run_bltu, 0, t1, r2_val, t2, r1_val, width, init_pc, &next_pcu); - test_nz_branch_instr!(state, run_bgeu, 0, t1, r2_val, t2, r1_val, width, init_pc, &pc_update_init_pcu); - - // lhs = rhs - test_nz_branch_instr!(state, run_bltu, 0, t1, r1_val, t2, r1_val, width, init_pc, &next_pcu); - test_nz_branch_instr!(state, run_bgeu, 0, t2, r2_val, t1, r2_val, width, init_pc, &pc_update_init_pcu); - - // same register - test_nz_branch_instr!(state, run_bltu, 0, t1, r1_val, t1, r1_val, width, init_pc, &next_pcu); - test_nz_branch_instr!(state, run_bgeu, 0, t2, r1_val, t2, r1_val, width, init_pc, &pc_update_init_pcu); - - }); - }); - backend_test!(test_ebreak, F, { let state = create_state!(MachineCoreState, MachineCoreStateLayout, F, M4K); diff --git a/src/riscv/lib/src/jit.rs b/src/riscv/lib/src/jit.rs index 50bba67df41f82967430ccdf0f4e67b902e98865..6762905eb577ab9d11a3181a8d3f634374775924 100644 --- a/src/riscv/lib/src/jit.rs +++ b/src/riscv/lib/src/jit.rs @@ -269,6 +269,7 @@ mod tests { use crate::machine_state::registers::NonZeroXRegister; use crate::machine_state::registers::XRegister; use crate::machine_state::registers::nz; + use crate::parser::instruction::InstrWidth; use crate::parser::instruction::InstrWidth::*; use crate::state_backend::FnManagerIdent; use crate::state_backend::ManagerRead; @@ -1115,6 +1116,171 @@ mod tests { } }); + backend_test!(test_branch, F, { + let test_branch = + |non_branch: fn(NonZeroXRegister, NonZeroXRegister, i64, InstrWidth) -> I, + branch: fn(NonZeroXRegister, NonZeroXRegister, i64, InstrWidth) -> I, + lhs: i64, + rhs: i64| + -> Scenario { + let initial_pc: u64 = 0x1000; + let imm: i64 = -0x2000; + let expected_pc_branch = initial_pc.wrapping_add(imm as u64).wrapping_add(8); + + ScenarioBuilder::default() + .set_initial_pc(initial_pc) + .set_instructions(&[ + I::new_li(nz::a1, lhs, InstrWidth::Compressed), + I::new_li(nz::a2, rhs, InstrWidth::Compressed), + non_branch(nz::a1, nz::a2, imm, InstrWidth::Uncompressed), + branch(nz::a1, nz::a2, imm, InstrWidth::Uncompressed), + I::new_nop(InstrWidth::Compressed), + ]) + .set_expected_steps(4) + .set_assert_hook(assert_hook!(core, F, { + assert_eq!( + expected_pc_branch, + core.hart.pc.read(), + "Expected {expected_pc_branch} pc for B*Zero cmp {lhs}, {rhs}" + ) + })) + .build() + }; + + let scenarios: &[Scenario] = &[ + // Equality + test_branch(I::new_branch_equal, I::new_branch_not_equal, 2, 3), + test_branch(I::new_branch_not_equal, I::new_branch_equal, 2, 2), + test_branch(I::new_branch_equal, I::new_branch_not_equal, 2, -3), + // LessThanUnsigned + GreaterThanOrEqualUnsigned + test_branch( + I::new_branch_less_than_unsigned, + I::new_branch_greater_than_or_equal_unsigned, + 3, + 2, + ), + test_branch( + I::new_branch_less_than_unsigned, + I::new_branch_greater_than_or_equal_unsigned, + 2, + 2, + ), + test_branch( + I::new_branch_greater_than_or_equal_unsigned, + I::new_branch_less_than_unsigned, + 2, + -3, + ), + // LessThanSigned + GreaterThanOrEqualSigned + test_branch( + I::new_branch_less_than_signed, + I::new_branch_greater_than_or_equal_signed, + 3, + 2, + ), + test_branch( + I::new_branch_less_than_signed, + I::new_branch_greater_than_or_equal_signed, + 2, + 2, + ), + test_branch( + I::new_branch_less_than_signed, + I::new_branch_greater_than_or_equal_signed, + 2, + -3, + ), + test_branch( + I::new_branch_greater_than_or_equal_signed, + I::new_branch_less_than_signed, + -4, + -3, + ), + ]; + + let mut jit = JIT::::new().unwrap(); + let mut interpreted_bb = InterpretedBlockBuilder; + + for scenario in scenarios { + scenario.run(&mut jit, &mut interpreted_bb); + } + }); + + backend_test!(test_branch_compare_zero, F, { + let test_branch_compare_zero = |non_branch: fn(NonZeroXRegister, i64, InstrWidth) -> I, + branch: fn(NonZeroXRegister, i64, InstrWidth) -> I, + val: i64| + -> Scenario { + let initial_pc: u64 = 0x1000; + let imm: i64 = 0x2000; + let expected_pc_branch = initial_pc + imm as u64 + 4; + + ScenarioBuilder::default() + .set_initial_pc(initial_pc) + .set_instructions(&[ + I::new_li(nz::ra, val, InstrWidth::Compressed), + non_branch(nz::ra, imm, InstrWidth::Compressed), + branch(nz::ra, imm, InstrWidth::Uncompressed), + I::new_nop(InstrWidth::Compressed), + ]) + .set_expected_steps(3) + .set_assert_hook(assert_hook!(core, F, { + assert_eq!( + expected_pc_branch, + core.hart.pc.read(), + "Expected {expected_pc_branch} pc for B*Zero cmp {val:?}" + ) + })) + .build() + }; + + let scenarios: &[Scenario] = &[ + // Equality + test_branch_compare_zero(I::new_branch_equal_zero, I::new_branch_not_equal_zero, 12), + test_branch_compare_zero(I::new_branch_not_equal_zero, I::new_branch_equal_zero, 0), + test_branch_compare_zero(I::new_branch_equal_zero, I::new_branch_not_equal_zero, -12), + // LessThan + GreaterThanOrEqual + test_branch_compare_zero( + I::new_branch_less_than_zero, + I::new_branch_greater_than_or_equal_zero, + 12, + ), + test_branch_compare_zero( + I::new_branch_less_than_zero, + I::new_branch_greater_than_or_equal_zero, + 0, + ), + test_branch_compare_zero( + I::new_branch_greater_than_or_equal_zero, + I::new_branch_less_than_zero, + -12, + ), + // LessThanOrEqual + GreaterThan + test_branch_compare_zero( + I::new_branch_less_than_or_equal_zero, + I::new_branch_greater_than_zero, + 12, + ), + test_branch_compare_zero( + I::new_branch_greater_than_zero, + I::new_branch_less_than_or_equal_zero, + 0, + ), + test_branch_compare_zero( + I::new_branch_greater_than_zero, + I::new_branch_less_than_or_equal_zero, + -12, + ), + ]; + + let mut jit = JIT::::new().unwrap(); + let mut interpreted_bb = InterpretedBlockBuilder; + + for scenario in scenarios { + scenario.run(&mut jit, &mut interpreted_bb); + } + }); + backend_test!(test_jit_recovers_from_compilation_failure, F, { use crate::machine_state::registers::NonZeroXRegister::*; diff --git a/src/riscv/lib/src/jit/builder.rs b/src/riscv/lib/src/jit/builder.rs index 4bbbc22ad2fc3852665cc530c4c7ebc6f25efae2..7f2f534878604d9e64d246a1704cd22bd3d207a2 100644 --- a/src/riscv/lib/src/jit/builder.rs +++ b/src/riscv/lib/src/jit/builder.rs @@ -20,8 +20,10 @@ use super::state_access::JitStateAccess; use super::state_access::JsaCalls; use crate::instruction_context::ICB; use crate::instruction_context::Predicate; +use crate::machine_state::ProgramCounterUpdate; use crate::machine_state::memory::MemoryConfig; use crate::machine_state::registers::NonZeroXRegister; +use crate::parser::instruction::InstrWidth; pub(super) mod block_state; @@ -137,6 +139,7 @@ impl<'a, MC: MemoryConfig, JSA: JitStateAccess> Builder<'a, MC, JSA> { // Clearing the context is done via `finalize`, which internally clears the // buffers to allow re-use. + self.builder.seal_all_blocks(); self.builder.finalize(); } @@ -249,6 +252,36 @@ impl<'a, MC: MemoryConfig, JSA: JitStateAccess> ICB for Builder<'a, MC, JSA> { self.builder.ins().icmp(comparison, lhs, rhs) } + fn branch( + &mut self, + condition: Self::Bool, + offset: i64, + instr_width: InstrWidth, + ) -> ProgramCounterUpdate { + let branch_block = self.builder.create_block(); + let fallthrough_block = self.builder.create_block(); + + self.builder + .ins() + .brif(condition, branch_block, &[], fallthrough_block, &[]); + + let snapshot = self.dynamic; + + // Handle branching block + self.builder.switch_to_block(branch_block); + + self.complete_step(block_state::PCUpdate::Offset(offset)); + self.jump_to_end(); + + self.builder.seal_block(branch_block); + + // Continue on the fallthrough block + self.dynamic = snapshot; + self.builder.switch_to_block(fallthrough_block); + + ProgramCounterUpdate::Next(instr_width) + } + fn ok(&mut self, val: Value) -> Self::IResult { val } @@ -271,8 +304,14 @@ impl<'a, MC: MemoryConfig, JSA: JitStateAccess> ICB for Builder<'a, MC, JSA> { impl From for IntCC { fn from(value: Predicate) -> Self { match value { + Predicate::Equal => IntCC::Equal, + Predicate::NotEqual => IntCC::NotEqual, Predicate::LessThanSigned => IntCC::SignedLessThan, Predicate::LessThanUnsigned => IntCC::UnsignedLessThan, + Predicate::LessThanOrEqualSigned => IntCC::SignedLessThanOrEqual, + Predicate::GreaterThanSigned => IntCC::SignedGreaterThan, + Predicate::GreaterThanOrEqualSigned => IntCC::SignedGreaterThanOrEqual, + Predicate::GreaterThanOrEqualUnsigned => IntCC::UnsignedGreaterThanOrEqual, } } } diff --git a/src/riscv/lib/src/machine_state/instruction.rs b/src/riscv/lib/src/machine_state/instruction.rs index 82264ea46b913d897697b2c5ea3ed318c776f779..42efdbfb74ea0648a8bdca789eb59696768ce74f 100644 --- a/src/riscv/lib/src/machine_state/instruction.rs +++ b/src/riscv/lib/src/machine_state/instruction.rs @@ -38,6 +38,7 @@ use crate::default::ConstDefault; use crate::instruction_context::ICB; use crate::instruction_context::IcbFnResult; use crate::instruction_context::IcbLoweringFn; +use crate::instruction_context::Predicate; use crate::interpreter::branching; use crate::interpreter::integer; use crate::interpreter::load_store; @@ -246,12 +247,12 @@ pub enum OpCode { Sd, // RV64I B-type instructions - Beq, - Bne, - Blt, - Bge, - Bltu, - Bgeu, + BranchEqual, + BranchNotEqual, + BranchLessThanSigned, + BranchGreaterThanOrEqualSigned, + BranchLessThanUnsigned, + BranchGreaterThanOrEqualUnsigned, // RV64I U-type instructions AddImmediateToPC, @@ -392,8 +393,8 @@ pub enum OpCode { CFsdsp, // Internal OpCodes - Beqz, - Bnez, + BranchEqualZero, + BranchNotEqualZero, J, Mv, Li, @@ -424,13 +425,13 @@ pub enum OpCode { /// Same as `Sb` but only using NonZeroXRegisters. Sbnz, /// Jump to `pc + imm` if `val(rs2) < 0`. - Bltz, + BranchLessThanZero, /// Jump to `pc + imm` if `val(rs2) >= 0`. - Bgez, + BranchGreaterThanOrEqualZero, /// Jump to `pc + imm` if `val(rs2) <= 0`. - Bltez, + BranchLessThanOrEqualZero, /// Jump to `pc + imm` if `val(rs2) > 0`. - Bgz, + BranchGreaterThanZero, } impl OpCode { @@ -490,16 +491,18 @@ impl OpCode { Self::Swnz => Args::run_swnz, Self::Sd => Args::run_sd, Self::Sdnz => Args::run_sdnz, - Self::Beq => Args::run_beq, - Self::Bne => Args::run_bne, - Self::Blt => Args::run_blt, - Self::Bge => Args::run_bge, - Self::Bltz => Args::run_bltz, - Self::Bgez => Args::run_bgez, - Self::Bltez => Args::run_bltez, - Self::Bgz => Args::run_bgz, - Self::Bltu => Args::run_bltu, - Self::Bgeu => Args::run_bgeu, + Self::BranchEqual => Args::run_branch_equal, + Self::BranchNotEqual => Args::run_branch_not_equal, + Self::BranchLessThanSigned => Args::run_branch_less_than_signed, + Self::BranchGreaterThanOrEqualSigned => Args::run_branch_greater_than_or_equal_signed, + Self::BranchLessThanZero => Args::run_branch_less_than_zero, + Self::BranchGreaterThanOrEqualZero => Args::run_branch_greater_than_or_equal_zero, + Self::BranchLessThanOrEqualZero => Args::run_branch_less_than_equal_zero, + Self::BranchGreaterThanZero => Args::run_branch_greater_than_zero, + Self::BranchLessThanUnsigned => Args::run_branch_less_than_unsigned, + Self::BranchGreaterThanOrEqualUnsigned => { + Args::run_branch_greater_than_or_equal_unsigned + } Self::AddImmediateToPC => Args::run_add_immediate_to_pc, Self::Jal => Args::run_jal, Self::JalrImm => Args::run_jalr_imm, @@ -612,8 +615,8 @@ impl OpCode { Self::JAbsolute => Args::run_j_absolute, Self::Jr => Args::run_jr, Self::Jalr => Args::run_jalr, - Self::Beqz => Args::run_beqz, - Self::Bnez => Args::run_bnez, + Self::BranchEqualZero => Args::run_branch_equal_zero, + Self::BranchNotEqualZero => Args::run_branch_not_equal_zero, Self::Li => Args::run_li, Self::Mv => Args::run_mv, Self::CAddw => Args::run_caddw, @@ -662,6 +665,26 @@ impl OpCode { Self::SetLessThanUnsigned => Some(Args::run_set_less_than_unsigned), Self::SetLessThanImmediateSigned => Some(Args::run_set_less_than_immediate_signed), Self::SetLessThanImmediateUnsigned => Some(Args::run_set_less_than_immediate_unsigned), + // Branching instructions + Self::BranchEqual => Some(Args::run_branch_equal), + Self::BranchEqualZero => Some(Args::run_branch_equal_zero), + Self::BranchNotEqual => Some(Args::run_branch_not_equal), + Self::BranchNotEqualZero => Some(Args::run_branch_not_equal_zero), + + Self::BranchLessThanSigned => Some(Args::run_branch_less_than_signed), + Self::BranchLessThanUnsigned => Some(Args::run_branch_less_than_unsigned), + Self::BranchLessThanZero => Some(Args::run_branch_less_than_zero), + Self::BranchLessThanOrEqualZero => Some(Args::run_branch_less_than_equal_zero), + + Self::BranchGreaterThanOrEqualSigned => { + Some(Args::run_branch_greater_than_or_equal_signed) + } + Self::BranchGreaterThanOrEqualUnsigned => { + Some(Args::run_branch_greater_than_or_equal_unsigned) + } + Self::BranchGreaterThanOrEqualZero => Some(Args::run_branch_greater_than_or_equal_zero), + Self::BranchGreaterThanZero => Some(Args::run_branch_greater_than_zero), + _ => None, } } @@ -950,17 +973,37 @@ macro_rules! impl_fstore_type { }; } -macro_rules! impl_b_type { - ($fn: ident) => { +macro_rules! impl_branch { + ($fn: ident, $predicate: expr) => { /// SAFETY: This function must only be called on an `Args` belonging /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn( - &self, - core: &mut MachineCoreState, - ) -> Result, Exception> { - Ok(core - .hart - .$fn(self.imm, self.rs1.nzx, self.rs2.nzx, self.width)) + unsafe fn $fn(&self, icb: &mut I) -> IcbFnResult { + let pcu = branching::run_branch( + icb, + $predicate, + self.imm, + self.rs1.nzx, + self.rs2.nzx, + self.width, + ); + icb.ok(pcu) + } + }; +} + +macro_rules! impl_branch_compare_zero { + ($fn: ident, $predicate: expr) => { + /// SAFETY: This function must only be called on an `Args` belonging + /// to the same OpCode as the OpCode used to derive this function. + unsafe fn $fn(&self, icb: &mut I) -> IcbFnResult { + let pcu = branching::run_branch_compare_zero( + icb, + $predicate, + self.imm, + self.rs1.nzx, + self.width, + ); + icb.ok(pcu) } }; } @@ -1053,19 +1096,6 @@ macro_rules! impl_cr_nz_type { }; } -macro_rules! impl_cb_type { - ($fn: ident) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn( - &self, - core: &mut MachineCoreState, - ) -> Result, Exception> { - Ok(core.hart.$fn(self.imm, self.rs1.nzx, self.width)) - } - }; -} - macro_rules! impl_fcss_type { ($fn: ident) => { /// SAFETY: This function must only be called on an `Args` belonging @@ -1289,13 +1319,31 @@ impl Args { impl_store_type!(run_shnz, non_zero); impl_store_type!(run_sbnz, non_zero); - // RV64I B-type instructions - impl_b_type!(run_beq); - impl_b_type!(run_bne); - impl_b_type!(run_blt); - impl_b_type!(run_bge); - impl_b_type!(run_bltu); - impl_b_type!(run_bgeu); + // Branching instructions + impl_branch!(run_branch_equal, Predicate::Equal); + impl_branch!(run_branch_not_equal, Predicate::NotEqual); + impl_branch!(run_branch_less_than_signed, Predicate::LessThanSigned); + impl_branch!(run_branch_less_than_unsigned, Predicate::LessThanUnsigned); + impl_branch!( + run_branch_greater_than_or_equal_signed, + Predicate::GreaterThanOrEqualSigned + ); + impl_branch!( + run_branch_greater_than_or_equal_unsigned, + Predicate::GreaterThanOrEqualUnsigned + ); + impl_branch_compare_zero!(run_branch_equal_zero, Predicate::Equal); + impl_branch_compare_zero!(run_branch_not_equal_zero, Predicate::NotEqual); + impl_branch_compare_zero!(run_branch_less_than_zero, Predicate::LessThanSigned); + impl_branch_compare_zero!( + run_branch_greater_than_or_equal_zero, + Predicate::GreaterThanOrEqualSigned + ); + impl_branch_compare_zero!( + run_branch_less_than_equal_zero, + Predicate::LessThanOrEqualSigned + ); + impl_branch_compare_zero!(run_branch_greater_than_zero, Predicate::GreaterThanSigned); // RV64I U-type instructions /// SAFETY: This function must only be called on an `Args` belonging @@ -1446,12 +1494,6 @@ impl Args { // RV32C compressed instructions impl_cr_nz_type!(integer::run_mv, run_mv); impl_cr_nz_type!(integer::run_neg, run_neg); - impl_cb_type!(run_beqz); - impl_cb_type!(run_bnez); - impl_cb_type!(run_bltz); - impl_cb_type!(run_bgez); - impl_cb_type!(run_bltez); - impl_cb_type!(run_bgz); impl_ci_type!(load_store::run_li, run_li, non_zero); fn run_j(&self, icb: &mut I) -> IcbFnResult { diff --git a/src/riscv/lib/src/machine_state/instruction/constructors.rs b/src/riscv/lib/src/machine_state/instruction/constructors.rs index 9597fa700d7996d9bfe608350fcea9e95051af1f..8f73d8661e77e91d8c583663e9d2da01c3b8ce10 100644 --- a/src/riscv/lib/src/machine_state/instruction/constructors.rs +++ b/src/riscv/lib/src/machine_state/instruction/constructors.rs @@ -459,15 +459,16 @@ impl Instruction { } } - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::Beq`]. - pub(crate) fn new_beq( + /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for + /// [`OpCode::BranchEqual`]. + pub(crate) fn new_branch_equal( rs1: NonZeroXRegister, rs2: NonZeroXRegister, imm: i64, width: InstrWidth, ) -> Self { Self { - opcode: OpCode::Beq, + opcode: OpCode::BranchEqual, args: Args { rs1: rs1.into(), rs2: rs2.into(), @@ -478,10 +479,15 @@ impl Instruction { } } - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::Beqz`]. - pub(crate) fn new_beqz(rs1: NonZeroXRegister, imm: i64, width: InstrWidth) -> Self { + /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for + /// [`OpCode::BranchEqualZero`]. + pub(crate) fn new_branch_equal_zero( + rs1: NonZeroXRegister, + imm: i64, + width: InstrWidth, + ) -> Self { Self { - opcode: OpCode::Beqz, + opcode: OpCode::BranchEqualZero, args: Args { rs1: rs1.into(), imm, @@ -491,15 +497,16 @@ impl Instruction { } } - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::Bne`]. - pub(crate) fn new_bne( + /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for + /// [`OpCode::BranchNotEqual`]. + pub(crate) fn new_branch_not_equal( rs1: NonZeroXRegister, rs2: NonZeroXRegister, imm: i64, width: InstrWidth, ) -> Self { Self { - opcode: OpCode::Bne, + opcode: OpCode::BranchNotEqual, args: Args { rs1: rs1.into(), rs2: rs2.into(), @@ -510,10 +517,15 @@ impl Instruction { } } - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::Bnez`]. - pub(crate) fn new_bnez(rs1: NonZeroXRegister, imm: i64, width: InstrWidth) -> Self { + /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for + /// [`OpCode::BranchNotEqualZero`]. + pub(crate) fn new_branch_not_equal_zero( + rs1: NonZeroXRegister, + imm: i64, + width: InstrWidth, + ) -> Self { Self { - opcode: OpCode::Bnez, + opcode: OpCode::BranchNotEqualZero, args: Args { rs1: rs1.into(), imm, @@ -869,15 +881,16 @@ impl Instruction { } } - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::Blt`]. - pub(crate) fn new_blt( + /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for + /// [`OpCode::BranchLessThanSigned`]. + pub(crate) fn new_branch_less_than_signed( rs1: NonZeroXRegister, rs2: NonZeroXRegister, imm: i64, width: InstrWidth, ) -> Self { Self { - opcode: OpCode::Blt, + opcode: OpCode::BranchLessThanSigned, args: Args { rs1: rs1.into(), rs2: rs2.into(), @@ -888,10 +901,15 @@ impl Instruction { } } - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::Bltz`]. - pub(crate) fn new_bltz(rs1: NonZeroXRegister, imm: i64, width: InstrWidth) -> Self { + /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for + /// [`OpCode::BranchLessThanZero`]. + pub(crate) fn new_branch_less_than_zero( + rs1: NonZeroXRegister, + imm: i64, + width: InstrWidth, + ) -> Self { Self { - opcode: OpCode::Bltz, + opcode: OpCode::BranchLessThanZero, args: Args { rs1: rs1.into(), imm, @@ -901,10 +919,15 @@ impl Instruction { } } - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::Bltez`]. - pub(crate) fn new_bltez(rs1: NonZeroXRegister, imm: i64, width: InstrWidth) -> Self { + /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for + /// [`OpCode::BranchLessThanOrEqualZero`]. + pub(crate) fn new_branch_less_than_or_equal_zero( + rs1: NonZeroXRegister, + imm: i64, + width: InstrWidth, + ) -> Self { Self { - opcode: OpCode::Bltez, + opcode: OpCode::BranchLessThanOrEqualZero, args: Args { rs1: rs1.into(), imm, @@ -914,15 +937,16 @@ impl Instruction { } } - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::Bge`]. - pub(crate) fn new_bge( + /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for + /// [`OpCode::BranchGreaterThanOrEqualSigned`]. + pub(crate) fn new_branch_greater_than_or_equal_signed( rs1: NonZeroXRegister, rs2: NonZeroXRegister, imm: i64, width: InstrWidth, ) -> Self { Self { - opcode: OpCode::Bge, + opcode: OpCode::BranchGreaterThanOrEqualSigned, args: Args { rs1: rs1.into(), rs2: rs2.into(), @@ -933,10 +957,15 @@ impl Instruction { } } - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::Bgez`]. - pub(crate) fn new_bgez(rs1: NonZeroXRegister, imm: i64, width: InstrWidth) -> Self { + /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for + /// [`OpCode::BranchGreaterThanOrEqualZero`]. + pub(crate) fn new_branch_greater_than_or_equal_zero( + rs1: NonZeroXRegister, + imm: i64, + width: InstrWidth, + ) -> Self { Self { - opcode: OpCode::Bgez, + opcode: OpCode::BranchGreaterThanOrEqualZero, args: Args { rs1: rs1.into(), imm, @@ -946,10 +975,15 @@ impl Instruction { } } - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::Bgz`]. - pub(crate) fn new_bgz(rs1: NonZeroXRegister, imm: i64, width: InstrWidth) -> Self { + /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for + /// [`OpCode::BranchGreaterThanZero`]. + pub(crate) fn new_branch_greater_than_zero( + rs1: NonZeroXRegister, + imm: i64, + width: InstrWidth, + ) -> Self { Self { - opcode: OpCode::Bgz, + opcode: OpCode::BranchGreaterThanZero, args: Args { rs1: rs1.into(), imm, @@ -959,15 +993,16 @@ impl Instruction { } } - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::Bltu`]. - pub(crate) fn new_bltu( + /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for + /// [`OpCode::BranchLessThanUnsigned`]. + pub(crate) fn new_branch_less_than_unsigned( rs1: NonZeroXRegister, rs2: NonZeroXRegister, imm: i64, width: InstrWidth, ) -> Self { Self { - opcode: OpCode::Bltu, + opcode: OpCode::BranchLessThanUnsigned, args: Args { rs1: rs1.into(), rs2: rs2.into(), @@ -978,15 +1013,16 @@ impl Instruction { } } - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::Bgeu`]. - pub(crate) fn new_bgeu( + /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for + /// [`OpCode::BranchGreaterThanOrEqualUnsigned`]. + pub(crate) fn new_branch_greater_than_or_equal_unsigned( rs1: NonZeroXRegister, rs2: NonZeroXRegister, imm: i64, width: InstrWidth, ) -> Self { Self { - opcode: OpCode::Bgeu, + opcode: OpCode::BranchGreaterThanOrEqualUnsigned, args: Args { rs1: rs1.into(), rs2: rs2.into(), @@ -1398,10 +1434,10 @@ impl Instruction { } // If either register is x0, then the condition to branch is whether the other register stores 0. (X::NonZero(rs1), X::X0) | (X::X0, X::NonZero(rs1)) => { - Instruction::new_beqz(rs1, args.imm, InstrWidth::Uncompressed) + Instruction::new_branch_equal_zero(rs1, args.imm, InstrWidth::Uncompressed) } (X::NonZero(rs1), X::NonZero(rs2)) => { - Instruction::new_beq(rs1, rs2, args.imm, InstrWidth::Uncompressed) + Instruction::new_branch_equal(rs1, rs2, args.imm, InstrWidth::Uncompressed) } } } @@ -1414,7 +1450,9 @@ impl Instruction { match split_x0(args.rd_rs1) { // If `rd_rs1` is zero, the result is an unconditional jump X::X0 => Instruction::new_j(args.imm, InstrWidth::Compressed), - X::NonZero(rd_rs1) => Instruction::new_beqz(rd_rs1, args.imm, InstrWidth::Compressed), + X::NonZero(rd_rs1) => { + Instruction::new_branch_equal_zero(rd_rs1, args.imm, InstrWidth::Compressed) + } } } @@ -1431,10 +1469,10 @@ impl Instruction { } // If either register is x0, then the condition to branch is whether the other register doesn't store 0. (X::NonZero(rs1), X::X0) | (X::X0, X::NonZero(rs1)) => { - Instruction::new_bnez(rs1, args.imm, InstrWidth::Uncompressed) + Instruction::new_branch_not_equal_zero(rs1, args.imm, InstrWidth::Uncompressed) } (X::NonZero(rs1), X::NonZero(rs2)) => { - Instruction::new_bne(rs1, rs2, args.imm, InstrWidth::Uncompressed) + Instruction::new_branch_not_equal(rs1, rs2, args.imm, InstrWidth::Uncompressed) } } } @@ -1447,7 +1485,9 @@ impl Instruction { match split_x0(args.rd_rs1) { // If `rd_rs1 == x0`, this will never branch. X::X0 => Instruction::new_nop(InstrWidth::Compressed), - X::NonZero(rd_rs1) => Instruction::new_bnez(rd_rs1, args.imm, InstrWidth::Compressed), + X::NonZero(rd_rs1) => { + Instruction::new_branch_not_equal_zero(rd_rs1, args.imm, InstrWidth::Compressed) + } } } @@ -1624,15 +1664,18 @@ impl Instruction { } // If rs1 is x0, the condition to branch is whether `val(rs2) > 0`. (X::X0, X::NonZero(rs1)) => { - Instruction::new_bgz(rs1, args.imm, InstrWidth::Uncompressed) + Instruction::new_branch_greater_than_zero(rs1, args.imm, InstrWidth::Uncompressed) } // If rs2 is x0, the condition to branch is whether `val(rs1) < 0`. (X::NonZero(rs1), X::X0) => { - Instruction::new_bltz(rs1, args.imm, InstrWidth::Uncompressed) - } - (X::NonZero(rs1), X::NonZero(rs2)) => { - Instruction::new_blt(rs1, rs2, args.imm, InstrWidth::Uncompressed) + Instruction::new_branch_less_than_zero(rs1, args.imm, InstrWidth::Uncompressed) } + (X::NonZero(rs1), X::NonZero(rs2)) => Instruction::new_branch_less_than_signed( + rs1, + rs2, + args.imm, + InstrWidth::Uncompressed, + ), } } @@ -1648,15 +1691,24 @@ impl Instruction { Instruction::new_j(args.imm, InstrWidth::Uncompressed) } // If rs1 is x0, the condition to branch is whether `val(rs2) <= 0`. - (X::X0, X::NonZero(rs1)) => { - Instruction::new_bltez(rs1, args.imm, InstrWidth::Uncompressed) - } + (X::X0, X::NonZero(rs1)) => Instruction::new_branch_less_than_or_equal_zero( + rs1, + args.imm, + InstrWidth::Uncompressed, + ), // If rs2 is x0, the condition to branch is whether `val(rs1) >= 0`. - (X::NonZero(rs1), X::X0) => { - Instruction::new_bgez(rs1, args.imm, InstrWidth::Uncompressed) - } + (X::NonZero(rs1), X::X0) => Instruction::new_branch_greater_than_or_equal_zero( + rs1, + args.imm, + InstrWidth::Uncompressed, + ), (X::NonZero(rs1), X::NonZero(rs2)) => { - Instruction::new_bge(rs1, rs2, args.imm, InstrWidth::Uncompressed) + Instruction::new_branch_greater_than_or_equal_signed( + rs1, + rs2, + args.imm, + InstrWidth::Uncompressed, + ) } } } @@ -1675,11 +1727,14 @@ impl Instruction { } // If rs1 is x0, the condition to branch is whether `val(rs2) != 0`. (X::X0, X::NonZero(rs1)) => { - Instruction::new_bnez(rs1, args.imm, InstrWidth::Uncompressed) - } - (X::NonZero(rs1), X::NonZero(rs2)) => { - Instruction::new_bltu(rs1, rs2, args.imm, InstrWidth::Uncompressed) + Instruction::new_branch_not_equal_zero(rs1, args.imm, InstrWidth::Uncompressed) } + (X::NonZero(rs1), X::NonZero(rs2)) => Instruction::new_branch_less_than_unsigned( + rs1, + rs2, + args.imm, + InstrWidth::Uncompressed, + ), } } @@ -1697,10 +1752,15 @@ impl Instruction { } // If rs1 is x0, the condition to branch is whether `val(rs2) == 0`. (X::X0, X::NonZero(rs1)) => { - Instruction::new_beqz(rs1, args.imm, InstrWidth::Uncompressed) + Instruction::new_branch_equal_zero(rs1, args.imm, InstrWidth::Uncompressed) } (X::NonZero(rs1), X::NonZero(rs2)) => { - Instruction::new_bgeu(rs1, rs2, args.imm, InstrWidth::Uncompressed) + Instruction::new_branch_greater_than_or_equal_unsigned( + rs1, + rs2, + args.imm, + InstrWidth::Uncompressed, + ) } } } diff --git a/src/riscv/lib/src/machine_state/instruction/tagged_instruction.rs b/src/riscv/lib/src/machine_state/instruction/tagged_instruction.rs index 905bbaf83bf1a3ec42c8b25fba55920d5b45a0c0..54e644d83d8b66b98be82ae967acff5c86327dae 100644 --- a/src/riscv/lib/src/machine_state/instruction/tagged_instruction.rs +++ b/src/riscv/lib/src/machine_state/instruction/tagged_instruction.rs @@ -359,12 +359,55 @@ pub fn opcode_to_argsshape(opcode: &OpCode) -> ArgsShape { Fsw | Fsd | CFsd | CFsdsp => ArgsShape::XSrcFSrc, - Addi | Andi | Ori | Xori | Slli | Srli | Srai | Add | Sub | Mul | Mv | Neg | And | Or - | Xor | Sll | Srl | Sra | Jal | J | JrImm | JAbsolute | JalrAbsolute | Jr | Jalr - | CAddiw | Li | Nop | Beq | Beqz | Bne | Bnez | Blt | Bge | Bltu | Bgeu | JalrImm - | Ldnz | Sdnz | Lwnz | Swnz | Lhnz | Shnz | Lbnz | Sbnz | Bltz | Bgez | Bltez | Bgz => { - ArgsShape::NZXSrcNZXDest - } + Addi + | Andi + | Ori + | Xori + | Slli + | Srli + | Srai + | Add + | Sub + | Mul + | Mv + | Neg + | And + | Or + | Xor + | Sll + | Srl + | Sra + | Jal + | J + | JrImm + | JAbsolute + | JalrAbsolute + | Jr + | Jalr + | CAddiw + | Li + | Nop + | BranchEqual + | BranchEqualZero + | BranchNotEqual + | BranchNotEqualZero + | BranchLessThanSigned + | BranchGreaterThanOrEqualSigned + | BranchLessThanUnsigned + | BranchGreaterThanOrEqualUnsigned + | BranchLessThanZero + | BranchGreaterThanOrEqualZero + | BranchLessThanOrEqualZero + | BranchGreaterThanZero + | JalrImm + | Ldnz + | Sdnz + | Lwnz + | Swnz + | Lhnz + | Shnz + | Lbnz + | Sbnz => ArgsShape::NZXSrcNZXDest, Addiw | Addw