From 57c8885a59f5f1fd9b3a49805b5b98564d78cfd5 Mon Sep 17 00:00:00 2001 From: Emma Turner Date: Thu, 13 Mar 2025 10:26:27 +0000 Subject: [PATCH 1/4] RISC-V: top-level branching functions --- src/riscv/lib/src/instruction_context.rs | 30 +++++++++++++++++++ src/riscv/lib/src/interpreter/branching.rs | 35 ++++++++++++++++++++++ src/riscv/lib/src/jit/builder.rs | 32 ++++++++++++++++++++ 3 files changed, 97 insertions(+) diff --git a/src/riscv/lib/src/instruction_context.rs b/src/riscv/lib/src/instruction_context.rs index 55823e432965..c74b0db64000 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)] diff --git a/src/riscv/lib/src/interpreter/branching.rs b/src/riscv/lib/src/interpreter/branching.rs index 2f8e40b52d64..bd73c13f9e75 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,6 +125,39 @@ pub fn run_add_immediate_to_pc(icb: &mut impl ICB, imm: i64, rd: NonZeroXRegiste icb.xregister_write_nz(rd, result); } +#[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) +} + +#[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 crate::backend_test; diff --git a/src/riscv/lib/src/jit/builder.rs b/src/riscv/lib/src/jit/builder.rs index 4bbbc22ad2fc..458aebd52b4c 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; @@ -249,6 +251,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); + + let snapshot = self.dynamic; + 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 } -- GitLab From 4fa45aeccafc915816c0fdad02ea53b843950e84 Mon Sep 17 00:00:00 2001 From: Emma Turner Date: Thu, 13 Mar 2025 10:38:53 +0000 Subject: [PATCH 2/4] RISC-V: lower all branching instructions in JIT --- src/riscv/lib/src/instruction_context.rs | 12 + src/riscv/lib/src/interpreter/branching.rs | 269 +++++++++++ src/riscv/lib/src/interpreter/rv32c.rs | 103 ----- src/riscv/lib/src/interpreter/rv32i.rs | 432 ------------------ src/riscv/lib/src/jit/builder.rs | 9 +- .../lib/src/machine_state/instruction.rs | 91 ++-- 6 files changed, 345 insertions(+), 571 deletions(-) diff --git a/src/riscv/lib/src/instruction_context.rs b/src/riscv/lib/src/instruction_context.rs index c74b0db64000..bb581eda3e95 100644 --- a/src/riscv/lib/src/instruction_context.rs +++ b/src/riscv/lib/src/instruction_context.rs @@ -270,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 { @@ -279,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 bd73c13f9e75..3ddd8c2d8c93 100644 --- a/src/riscv/lib/src/interpreter/branching.rs +++ b/src/riscv/lib/src/interpreter/branching.rs @@ -125,6 +125,17 @@ 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, @@ -142,6 +153,19 @@ pub fn run_branch( 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, @@ -160,11 +184,15 @@ pub fn run_branch_compare_zero( #[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; @@ -253,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 cf4f73b66147..a59dc257fa46 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 e7a627609706..59f66cefeaf9 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/builder.rs b/src/riscv/lib/src/jit/builder.rs index 458aebd52b4c..7f2f53487860 100644 --- a/src/riscv/lib/src/jit/builder.rs +++ b/src/riscv/lib/src/jit/builder.rs @@ -139,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(); } @@ -269,9 +270,9 @@ impl<'a, MC: MemoryConfig, JSA: JitStateAccess> ICB for Builder<'a, MC, JSA> { // Handle branching block self.builder.switch_to_block(branch_block); - let snapshot = self.dynamic; self.complete_step(block_state::PCUpdate::Offset(offset)); self.jump_to_end(); + self.builder.seal_block(branch_block); // Continue on the fallthrough block @@ -303,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 82264ea46b91..e58c3bf5f478 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; @@ -662,6 +663,19 @@ 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::Beq => Some(Args::run_beq), + Self::Beqz => Some(Args::run_beqz), + Self::Bne => Some(Args::run_bne), + Self::Bnez => Some(Args::run_bnez), + Self::Blt => Some(Args::run_blt), + Self::Bltu => Some(Args::run_bltu), + Self::Bltz => Some(Args::run_bltz), + Self::Bltez => Some(Args::run_bltez), + Self::Bge => Some(Args::run_bge), + Self::Bgeu => Some(Args::run_bgeu), + Self::Bgez => Some(Args::run_bgez), + Self::Bgz => Some(Args::run_bgz), _ => None, } } @@ -950,17 +964,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 +1087,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 +1310,19 @@ 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_beq, Predicate::Equal); + impl_branch!(run_bne, Predicate::NotEqual); + impl_branch!(run_blt, Predicate::LessThanSigned); + impl_branch!(run_bltu, Predicate::LessThanUnsigned); + impl_branch!(run_bge, Predicate::GreaterThanOrEqualSigned); + impl_branch!(run_bgeu, Predicate::GreaterThanOrEqualUnsigned); + impl_branch_compare_zero!(run_beqz, Predicate::Equal); + impl_branch_compare_zero!(run_bnez, Predicate::NotEqual); + impl_branch_compare_zero!(run_bltz, Predicate::LessThanSigned); + impl_branch_compare_zero!(run_bgez, Predicate::GreaterThanOrEqualSigned); + impl_branch_compare_zero!(run_bltez, Predicate::LessThanOrEqualSigned); + impl_branch_compare_zero!(run_bgz, Predicate::GreaterThanSigned); // RV64I U-type instructions /// SAFETY: This function must only be called on an `Args` belonging @@ -1446,12 +1473,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 { -- GitLab From 42327c90115868304816fc7dc4b18d90dcefcbb1 Mon Sep 17 00:00:00 2001 From: Emma Turner Date: Thu, 20 Mar 2025 09:58:54 +0000 Subject: [PATCH 3/4] RISC-V: test branches in JIT --- src/riscv/lib/src/jit.rs | 107 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/src/riscv/lib/src/jit.rs b/src/riscv/lib/src/jit.rs index 50bba67df41f..40c2af52182a 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,112 @@ 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_beq, I::new_bne, 2, 3), + test_branch(I::new_bne, I::new_beq, 2, 2), + test_branch(I::new_beq, I::new_bne, 2, -3), + // LessThanUnsigned + GreaterThanOrEqualUnsigned + test_branch(I::new_bltu, I::new_bgeu, 3, 2), + test_branch(I::new_bltu, I::new_bgeu, 2, 2), + test_branch(I::new_bgeu, I::new_bltu, 2, -3), + // LessThanSigned + GreaterThanOrEqualSigned + test_branch(I::new_blt, I::new_bge, 3, 2), + test_branch(I::new_blt, I::new_bge, 2, 2), + test_branch(I::new_blt, I::new_bge, 2, -3), + test_branch(I::new_bge, I::new_blt, -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_beqz, I::new_bnez, 12), + test_branch_compare_zero(I::new_bnez, I::new_beqz, 0), + test_branch_compare_zero(I::new_beqz, I::new_bnez, -12), + // LessThan + GreaterThanOrEqual + test_branch_compare_zero(I::new_bltz, I::new_bgez, 12), + test_branch_compare_zero(I::new_bltz, I::new_bgez, 0), + test_branch_compare_zero(I::new_bgez, I::new_bltz, -12), + // LessThanOrEqual + GreaterThan + test_branch_compare_zero(I::new_bltez, I::new_bgz, 12), + test_branch_compare_zero(I::new_bgz, I::new_bltez, 0), + test_branch_compare_zero(I::new_bgz, I::new_bltez, -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::*; -- GitLab From b601234ed862b2d82daa93f4a5ad552cbbd136ae Mon Sep 17 00:00:00 2001 From: Emma Turner Date: Wed, 19 Mar 2025 16:37:47 +0000 Subject: [PATCH 4/4] RISC-V: rename internal branching opcodes --- src/riscv/lib/src/jit.rs | 97 ++++++++-- .../lib/src/machine_state/instruction.rs | 117 +++++++----- .../machine_state/instruction/constructors.rs | 180 ++++++++++++------ .../instruction/tagged_instruction.rs | 55 +++++- 4 files changed, 316 insertions(+), 133 deletions(-) diff --git a/src/riscv/lib/src/jit.rs b/src/riscv/lib/src/jit.rs index 40c2af52182a..6762905eb577 100644 --- a/src/riscv/lib/src/jit.rs +++ b/src/riscv/lib/src/jit.rs @@ -1149,18 +1149,53 @@ mod tests { let scenarios: &[Scenario] = &[ // Equality - test_branch(I::new_beq, I::new_bne, 2, 3), - test_branch(I::new_bne, I::new_beq, 2, 2), - test_branch(I::new_beq, I::new_bne, 2, -3), + 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_bltu, I::new_bgeu, 3, 2), - test_branch(I::new_bltu, I::new_bgeu, 2, 2), - test_branch(I::new_bgeu, I::new_bltu, 2, -3), + 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_blt, I::new_bge, 3, 2), - test_branch(I::new_blt, I::new_bge, 2, 2), - test_branch(I::new_blt, I::new_bge, 2, -3), - test_branch(I::new_bge, I::new_blt, -4, -3), + 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(); @@ -1201,17 +1236,41 @@ mod tests { let scenarios: &[Scenario] = &[ // Equality - test_branch_compare_zero(I::new_beqz, I::new_bnez, 12), - test_branch_compare_zero(I::new_bnez, I::new_beqz, 0), - test_branch_compare_zero(I::new_beqz, I::new_bnez, -12), + 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_bltz, I::new_bgez, 12), - test_branch_compare_zero(I::new_bltz, I::new_bgez, 0), - test_branch_compare_zero(I::new_bgez, I::new_bltz, -12), + 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_bltez, I::new_bgz, 12), - test_branch_compare_zero(I::new_bgz, I::new_bltez, 0), - test_branch_compare_zero(I::new_bgz, I::new_bltez, -12), + 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(); diff --git a/src/riscv/lib/src/machine_state/instruction.rs b/src/riscv/lib/src/machine_state/instruction.rs index e58c3bf5f478..42efdbfb74ea 100644 --- a/src/riscv/lib/src/machine_state/instruction.rs +++ b/src/riscv/lib/src/machine_state/instruction.rs @@ -247,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, @@ -393,8 +393,8 @@ pub enum OpCode { CFsdsp, // Internal OpCodes - Beqz, - Bnez, + BranchEqualZero, + BranchNotEqualZero, J, Mv, Li, @@ -425,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 { @@ -491,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, @@ -613,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, @@ -664,18 +666,25 @@ impl OpCode { Self::SetLessThanImmediateSigned => Some(Args::run_set_less_than_immediate_signed), Self::SetLessThanImmediateUnsigned => Some(Args::run_set_less_than_immediate_unsigned), // Branching instructions - Self::Beq => Some(Args::run_beq), - Self::Beqz => Some(Args::run_beqz), - Self::Bne => Some(Args::run_bne), - Self::Bnez => Some(Args::run_bnez), - Self::Blt => Some(Args::run_blt), - Self::Bltu => Some(Args::run_bltu), - Self::Bltz => Some(Args::run_bltz), - Self::Bltez => Some(Args::run_bltez), - Self::Bge => Some(Args::run_bge), - Self::Bgeu => Some(Args::run_bgeu), - Self::Bgez => Some(Args::run_bgez), - Self::Bgz => Some(Args::run_bgz), + 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, } } @@ -1311,18 +1320,30 @@ impl Args { impl_store_type!(run_sbnz, non_zero); // Branching instructions - impl_branch!(run_beq, Predicate::Equal); - impl_branch!(run_bne, Predicate::NotEqual); - impl_branch!(run_blt, Predicate::LessThanSigned); - impl_branch!(run_bltu, Predicate::LessThanUnsigned); - impl_branch!(run_bge, Predicate::GreaterThanOrEqualSigned); - impl_branch!(run_bgeu, Predicate::GreaterThanOrEqualUnsigned); - impl_branch_compare_zero!(run_beqz, Predicate::Equal); - impl_branch_compare_zero!(run_bnez, Predicate::NotEqual); - impl_branch_compare_zero!(run_bltz, Predicate::LessThanSigned); - impl_branch_compare_zero!(run_bgez, Predicate::GreaterThanOrEqualSigned); - impl_branch_compare_zero!(run_bltez, Predicate::LessThanOrEqualSigned); - impl_branch_compare_zero!(run_bgz, Predicate::GreaterThanSigned); + 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 diff --git a/src/riscv/lib/src/machine_state/instruction/constructors.rs b/src/riscv/lib/src/machine_state/instruction/constructors.rs index 9597fa700d79..8f73d8661e77 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 905bbaf83bf1..54e644d83d8b 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 -- GitLab