diff --git a/src/riscv/lib/src/interpreter/branching.rs b/src/riscv/lib/src/interpreter/branching.rs index 7b2684798fdcfefb8526277c7acc35d88a1de2cc..49c677810006f1986bc219d6b2bab13850c2ca5a 100644 --- a/src/riscv/lib/src/interpreter/branching.rs +++ b/src/riscv/lib/src/interpreter/branching.rs @@ -9,71 +9,8 @@ use crate::instruction_context::ICB; use crate::instruction_context::Predicate; use crate::instruction_context::arithmetic::Arithmetic; use crate::machine_state::ProgramCounterUpdate; -use crate::machine_state::hart_state::HartState; -use crate::machine_state::memory::Address; use crate::machine_state::registers::NonZeroXRegister; use crate::parser::instruction::InstrWidth; -use crate::state_backend as backend; - -impl HartState -where - M: backend::ManagerReadWrite, -{ - /// Jump to Absolute Address `imm`. - /// Performs an unconditional control transfer to the target address, - /// formed by setting the least significant bit to zero. - /// - /// Relevant RISC-V opcodes: - /// - JALR - pub fn run_j_absolute(&mut self, imm: i64) -> Address { - imm as u64 & !1 - } - - /// Store the next instruction address in `rd` and jump to the target address. - /// Always returns the target address (val(rs1) + imm) - /// - /// Relevant RISC-V opcodes: - /// - `JALR` - pub fn run_jalr_imm( - &mut self, - imm: i64, - rs1: NonZeroXRegister, - rd: NonZeroXRegister, - width: InstrWidth, - ) -> Address { - // The return address to be saved in rd - let return_address = self.pc.read().wrapping_add(width as u64); - - // The target address is obtained by adding the sign-extended - // 12-bit I-immediate to the register rs1, then setting - // the least-significant bit of the result to zero - let target_address = self.xregisters.read_nz(rs1).wrapping_add(imm as u64) & !1; - - self.xregisters.write_nz(rd, return_address); - - target_address - } - - /// Jump to absolute address `imm` and link register. - /// Store the next instruction address in `rd` and jump to the target address. - /// Always returns the target address formed by sign extending the immediate and setting - /// the least significant bit to 0. - /// - /// Relevant RISC-V opcodes: - /// - `JALR` - pub fn run_jalr_absolute( - &mut self, - imm: i64, - rd: NonZeroXRegister, - width: InstrWidth, - ) -> Address { - // The return address to be saved in rd - let return_address = self.pc.read().wrapping_add(width as u64); - self.xregisters.write_nz(rd, return_address); - - imm as u64 & !1 - } -} /// Performs an unconditional control transfer. The immediate is added to /// the pc to form the jump target address. @@ -90,10 +27,6 @@ pub fn run_j(icb: &mut I, imm: i64) -> ::XValue { } /// Performs an unconditional control transfer to the address in register `rs1`. -/// -/// Relevant RISC-V opcodes: -/// - JALR -/// - C.JR pub fn run_jr(icb: &mut I, rs1: NonZeroXRegister) -> ::XValue { // The target address is obtained by setting the // least-significant bit of the address in rs1 to zero @@ -103,10 +36,6 @@ pub fn run_jr(icb: &mut I, rs1: NonZeroXRegister) -> ::XValue } /// Performs an unconditional control transfer to the target address, -/// `target_address = val(rs1) + imm` -/// -/// Relevant RISC-V opcodes: -/// - JALR pub fn run_jr_imm(icb: &mut I, imm: i64, rs1: NonZeroXRegister) -> ::XValue { let lhs = icb.xregister_read_nz(rs1); let rhs = icb.xvalue_of_imm(imm); @@ -118,12 +47,40 @@ pub fn run_jr_imm(icb: &mut I, imm: i64, rs1: NonZeroXRegister) -> (icb: &mut I, imm: i64) -> ::XValue { + let imm = icb.xvalue_of_imm(imm); + let mask = icb.xvalue_of_imm(!1); + imm.and(mask, icb) +} + +/// Store the next instruction address in `rd` and jump to the target address. +/// Always returns the target address (current program counter + imm) +pub fn run_jal( + icb: &mut I, + imm: i64, + rd: NonZeroXRegister, + width: InstrWidth, +) -> ::XValue { + // The return address to be saved in `rd` is that of the instruction following this one + let current_pc = icb.pc_read(); + let width = icb.xvalue_of_imm(width as i64); + let return_address = current_pc.add(width, icb); + + let imm = icb.xvalue_of_imm(imm); + // The target address is obtained by adding the imm to the current PC + let target_address = current_pc.add(imm, icb); + + // Store the return address in rd + icb.xregister_write_nz(rd, return_address); + + target_address +} + /// Performs an unconditional control transfer to the address in register `rs1` /// and stores the address of the instruction following the jump in register `rd`. -/// -/// Relevant RISC-V opcodes: -/// - JALR -/// - C.JALR pub fn run_jalr( icb: &mut I, rd: NonZeroXRegister, @@ -147,6 +104,63 @@ pub fn run_jalr( target_address } +/// Performs an unconditional control transfer to the target address, +/// `target_address = val(rs1) + imm` and stores the address of the instruction +/// following the jump in register `rd`. +pub fn run_jalr_imm( + icb: &mut I, + imm: i64, + rs1: NonZeroXRegister, + rd: NonZeroXRegister, + width: InstrWidth, +) -> ::XValue { + // The return address to be saved in `rd` is that of the instruction following this one + let current_pc = icb.pc_read(); + let width = icb.xvalue_of_imm(width as i64); + let return_address = current_pc.add(width, icb); + + // The target address is obtained by adding the sign-extended + // 12-bit I-immediate to the register rs1, then setting + // the least-significant bit of the result to zero + let target_address = icb.xregister_read_nz(rs1); + let imm = icb.xvalue_of_imm(imm); + let target_address = target_address.add(imm, icb); + let mask = icb.xvalue_of_imm(!1); + let target_address = target_address.and(mask, icb); + + // Store the return address in rd + icb.xregister_write_nz(rd, return_address); + + target_address +} + +/// Jump to absolute address `imm` and link register. +/// Store the next instruction address in `rd` and jump to the target address. +/// Always returns the target address formed by sign extending the immediate and setting +/// the least significant bit to 0. +pub fn run_jalr_absolute( + icb: &mut I, + imm: i64, + rd: NonZeroXRegister, + width: InstrWidth, +) -> ::XValue { + // The return address to be saved in `rd` is that of the instruction following this one + let current_pc = icb.pc_read(); + let width = icb.xvalue_of_imm(width as i64); + let return_address = current_pc.add(width, icb); + + // The target address is obtained by setting the + // least-significant bit of the immediate to zero + let target_address = icb.xvalue_of_imm(imm); + let mask = icb.xvalue_of_imm(!1); + let target_address = target_address.and(mask, icb); + + // Store the return address in rd + icb.xregister_write_nz(rd, return_address); + + target_address +} + /// Add the immediate `imm` to the PC and store the result in `rd`. /// /// Relevant RISC-V opcodes: @@ -222,7 +236,11 @@ mod tests { use crate::backend_test; use crate::create_state; use crate::instruction_context::Predicate; + use crate::interpreter::branching::run_j_absolute; + use crate::interpreter::branching::run_jal; use crate::interpreter::branching::run_jalr; + use crate::interpreter::branching::run_jalr_absolute; + use crate::interpreter::branching::run_jalr_imm; use crate::interpreter::branching::run_jr_imm; use crate::machine_state::MachineCoreState; use crate::machine_state::MachineCoreStateLayout; @@ -260,9 +278,7 @@ mod tests { // TEST JalrImm state.hart.pc.write(init_pc); state.hart.xregisters.write_nz(rs1, init_rs1); - let new_pc = state - .hart - .run_jalr_imm(imm, rs1, rd, InstrWidth::Uncompressed); + let new_pc = run_jalr_imm(&mut state, imm, rs1, rd, InstrWidth::Uncompressed); assert_eq!(state.hart.pc.read(), init_pc); assert_eq!(new_pc, res_pc); @@ -270,16 +286,22 @@ mod tests { // TEST JAbsolute state.hart.pc.write(init_pc); - let new_pc = state.hart.run_j_absolute(imm); + let new_pc = run_j_absolute(&mut state, imm); assert_eq!(state.hart.pc.read(), init_pc); assert_eq!(new_pc, imm as u64 & !1); + // TEST Jal + state.hart.pc.write(init_pc); + let new_pc = run_jal(&mut state, imm, rd, InstrWidth::Uncompressed); + + assert_eq!(state.hart.pc.read(), init_pc); + assert_eq!(new_pc, init_pc.wrapping_add(imm as u64)); + assert_eq!(state.hart.xregisters.read_nz(rd), res_rd); + // TEST JalrAbsolute state.hart.pc.write(init_pc); - let new_pc = state - .hart - .run_jalr_absolute(imm, rd, InstrWidth::Uncompressed); + let new_pc = run_jalr_absolute(&mut state, imm, rd, InstrWidth::Uncompressed); assert_eq!(state.hart.pc.read(), init_pc); assert_eq!(new_pc, imm as u64 & !1); diff --git a/src/riscv/lib/src/interpreter/rv32i.rs b/src/riscv/lib/src/interpreter/rv32i.rs index 59f66cefeaf94897e7c33cd6af3479cf2ddcd962..23e0970bb025f2038049e0b708b2c41041c19c9e 100644 --- a/src/riscv/lib/src/interpreter/rv32i.rs +++ b/src/riscv/lib/src/interpreter/rv32i.rs @@ -9,12 +9,10 @@ use crate::machine_state::MachineCoreState; use crate::machine_state::hart_state::HartState; use crate::machine_state::memory; -use crate::machine_state::memory::Address; use crate::machine_state::mode::Mode; use crate::machine_state::registers::NonZeroXRegister; use crate::machine_state::registers::XRegisters; use crate::parser::instruction::FenceSet; -use crate::parser::instruction::InstrWidth; use crate::state_backend as backend; use crate::traps::Exception; @@ -68,18 +66,6 @@ where Mode::Machine => Exception::EnvCallFromMMode, } } - - /// Store the next instruction address in `rd` and jump to the target address. - /// Always returns the target address (current program counter + imm) - pub fn run_jal(&mut self, imm: i64, rd: NonZeroXRegister, width: InstrWidth) -> Address { - let current_pc = self.pc.read(); - - // Save the address after jump instruction into rd - let return_address = current_pc.wrapping_add(width as u64); - self.xregisters.write_nz(rd, return_address); - - current_pc.wrapping_add(imm as u64) - } } impl MachineCoreState @@ -130,7 +116,6 @@ mod tests { use crate::machine_state::registers::t2; use crate::machine_state::registers::t3; use crate::parser::instruction::FenceSet; - use crate::parser::instruction::InstrWidth; use crate::traps::Exception; backend_test!(test_bitwise, F, { @@ -244,32 +229,6 @@ mod tests { } }); - backend_test!(test_jal, F, { - let ipc_imm_rd_fpc_frd = [ - (42, 42, nz::t1, 84, 46), - (0, 1000, nz::t1, 1000, 4), - (50, -100, nz::t1, -50_i64 as u64, 54), - (u64::MAX - 1, 100, nz::t1, 98_i64 as u64, 2), - ( - 1_000_000_000_000, - (u64::MAX - 1_000_000_000_000 + 1) as i64, - nz::t2, - 0, - 1_000_000_000_004, - ), - ]; - for (init_pc, imm, rd, res_pc, res_rd) in ipc_imm_rd_fpc_frd { - let mut state = create_state!(HartState, F); - - state.pc.write(init_pc); - let new_pc = state.run_jal(imm, rd, InstrWidth::Uncompressed); - - assert_eq!(state.pc.read(), init_pc); - assert_eq!(new_pc, res_pc); - assert_eq!(state.xregisters.read_nz(rd), res_rd); - } - }); - backend_test!(test_xret, F, { proptest!(|( curr_pc in any::
(), diff --git a/src/riscv/lib/src/jit.rs b/src/riscv/lib/src/jit.rs index 4421c87f43e770cd6fc372141e2abadf1bdb91c8..f8ae1e269be6c60b372ef1acea3f1b997a8ec9eb 100644 --- a/src/riscv/lib/src/jit.rs +++ b/src/riscv/lib/src/jit.rs @@ -910,107 +910,160 @@ mod tests { } }); - backend_test!(test_jr, F, { + backend_test!(test_jump_instructions, F, { use crate::machine_state::registers::NonZeroXRegister::*; - let scenarios: &[Scenario] = &[ + let test_jr = |base_reg: NonZeroXRegister, + base_val: i64, + expected_pc: u64, + instruction_width: InstrWidth| + -> Scenario { ScenarioBuilder::default() - // Jumping should exit the block .set_instructions(&[ - I::new_li(x2, 10, Compressed), - I::new_jr(x2, Compressed), - I::new_nop(Compressed), + I::new_li(base_reg, base_val, instruction_width), + I::new_jr(base_reg, instruction_width), + I::new_nop(instruction_width), ]) .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.pc.read(), 10); + assert_eq!(core.hart.pc.read(), expected_pc); })) .set_expected_steps(2) - .build(), + .build() + }; + + let test_jr_imm = |base_reg: NonZeroXRegister, + base_val: i64, + offset: i64, + expected_pc: u64, + instruction_width: InstrWidth| + -> Scenario { ScenarioBuilder::default() - // Jumping to start of the block should still exit. - .set_instructions(&[I::new_li(x6, 0, Compressed), I::new_jr(x6, Compressed)]) + .set_instructions(&[ + I::new_li(base_reg, base_val, instruction_width), + I::new_jr_imm(base_reg, offset, instruction_width), + I::new_nop(instruction_width), + ]) .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.pc.read(), 0); + assert_eq!(core.hart.pc.read(), expected_pc); })) .set_expected_steps(2) - .build(), - ]; - - 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_jr_imm, F, { - use crate::machine_state::registers::NonZeroXRegister::*; + .build() + }; - let scenarios: &[Scenario] = &[ + let test_jalr = |base_reg: NonZeroXRegister, + base_val: i64, + rd: NonZeroXRegister, + expected_pc: u64, + expected_rd: u64, + instruction_width: InstrWidth| + -> Scenario { ScenarioBuilder::default() - // Jumping to the next instruction should exit the block .set_instructions(&[ - I::new_li(x2, 10, Uncompressed), - I::new_jr_imm(x2, 10, Compressed), - I::new_nop(Uncompressed), + I::new_li(base_reg, base_val, instruction_width), + I::new_jalr(rd, base_reg, instruction_width), + I::new_nop(instruction_width), ]) .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.pc.read(), 20); + assert_eq!(core.hart.pc.read(), expected_pc); + assert_eq!(core.hart.xregisters.read_nz(rd), expected_rd); })) .set_expected_steps(2) - .build(), + .build() + }; + + let test_jalr_imm = |base_reg: NonZeroXRegister, + base_val: i64, + offset: i64, + rd: NonZeroXRegister, + expected_pc: u64, + expected_rd: u64, + instruction_width: InstrWidth| + -> Scenario { ScenarioBuilder::default() - // Jumping to start of the block should still exit. .set_instructions(&[ - I::new_li(x6, 10, Compressed), - I::new_jr_imm(x6, -10, Uncompressed), + I::new_li(base_reg, base_val, instruction_width), + I::new_jalr_imm(rd, base_reg, offset, instruction_width), + I::new_nop(instruction_width), ]) .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.pc.read(), 0); + assert_eq!(core.hart.pc.read(), expected_pc); + assert_eq!(core.hart.xregisters.read_nz(rd), expected_rd); })) .set_expected_steps(2) - .build(), - ]; - - 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_jalr, F, { - use crate::machine_state::registers::NonZeroXRegister::*; + .build() + }; - let scenarios: &[Scenario] = &[ + let test_jalr_absolute = |target: i64, + rd: NonZeroXRegister, + instruction_width: InstrWidth, + expected_rd: u64| + -> Scenario { ScenarioBuilder::default() - // Jumping should exit the block .set_instructions(&[ - I::new_li(x2, 100_000, Uncompressed), - I::new_jalr(x1, x2, Uncompressed), - I::new_nop(Uncompressed), + I::new_jalr_absolute(rd, target, instruction_width), + I::new_nop(instruction_width), ]) .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.pc.read(), 100_000); - assert_eq!(core.hart.xregisters.read_nz(x1), 8); + assert_eq!(core.hart.pc.read(), target as u64); + assert_eq!(core.hart.xregisters.read_nz(rd), expected_rd); })) - .set_expected_steps(2) - .build(), + .set_expected_steps(1) + .build() + }; + + let test_j_absolute = |target: i64, instruction_width: InstrWidth| -> Scenario { ScenarioBuilder::default() - // Jumping to start of the block should still exit. .set_instructions(&[ - I::new_li(x6, 0, Compressed), - I::new_jalr(x3, x6, Compressed), - I::new_nop(Compressed), + I::new_j_absolute(target, instruction_width), + I::new_nop(instruction_width), ]) .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.pc.read(), 0); - assert_eq!(core.hart.xregisters.read_nz(x3), 4); + assert_eq!(core.hart.pc.read(), target as u64); })) - .set_expected_steps(2) - .build(), + .set_expected_steps(1) + .build() + }; + + let test_jal = |offset: i64, + initial_pc: u64, + expected_pc: u64, + expected_x1: u64, + intruction_width: InstrWidth| + -> Scenario { + ScenarioBuilder::default() + .set_instructions(&[I::new_jal(x1, offset, intruction_width)]) + .set_initial_pc(initial_pc) + .set_assert_hook(assert_hook!(core, F, { + assert_eq!(core.hart.pc.read(), expected_pc); + assert_eq!(core.hart.xregisters.read_nz(x1), expected_x1); + })) + .set_expected_steps(1) + .build() + }; + + let scenarios: &[Scenario] = &[ + // Test jr + test_jr(x2, 10, 10, Compressed), + test_jr(x6, 0, 0, Uncompressed), + // Test jr_imm + test_jr_imm(x2, 10, 10, 20, Compressed), + test_jr_imm(x6, 10, -10, 0, Uncompressed), + // Test jalr + test_jalr(x2, 100_000, x1, 100_000, 8, Uncompressed), + test_jalr(x6, 0, x3, 0, 4, Compressed), + // Test jalr_imm + test_jalr_imm(x1, 10, 10, x2, 20, 4, Compressed), + test_jalr_imm(x1, 1000, -10, x2, 990, 8, Uncompressed), + // Test jalr_absolute + test_jalr_absolute(10, x1, Compressed, 2), + test_jalr_absolute(0, x3, Uncompressed, 4), + // Test j_absolute + test_j_absolute(10, Compressed), + test_j_absolute(0, Uncompressed), + // Test jal + test_jal(10, 0, 10, 2, Compressed), + test_jal(-10, 10, 0, 12, Compressed), + test_jal(1000, 1000, 2000, 1004, Uncompressed), ]; 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 b4262985d5c55b97c7323e4149bb483428d8b0af..88a5c88bb5a2bd88617233d2b084f37014e04150 100644 --- a/src/riscv/lib/src/machine_state/instruction.rs +++ b/src/riscv/lib/src/machine_state/instruction.rs @@ -44,7 +44,6 @@ use crate::interpreter::branching; use crate::interpreter::integer; use crate::interpreter::load_store; use crate::machine_state::ProgramCounterUpdate::Next; -use crate::machine_state::ProgramCounterUpdate::Set; use crate::parser::instruction::AmoArgs; use crate::parser::instruction::CIBDTypeArgs; use crate::parser::instruction::CIBNZTypeArgs; @@ -375,7 +374,6 @@ pub enum OpCode { Csrrsi, Csrrci, - // RV32C compressed instructions /// Jumps to val(rs1) Jr, /// Effects are to store the next instruction address in rd and jump to val(rs1). @@ -663,7 +661,11 @@ impl OpCode { Self::J => Some(Args::run_j), Self::Jr => Some(Args::run_jr), Self::JrImm => Some(Args::run_jr_imm), + Self::JAbsolute => Some(Args::run_j_absolute), + Self::Jal => Some(Args::run_jal), Self::Jalr => Some(Args::run_jalr), + Self::JalrImm => Some(Args::run_jalr_imm), + Self::JalrAbsolute => Some(Args::run_jalr_absolute), Self::Addi => Some(Args::run_addi), Self::Andi => Some(Args::run_andi), Self::SetLessThanSigned => Some(Args::run_set_less_than_signed), @@ -1386,31 +1388,6 @@ impl Args { icb.ok(Next(self.width)) } - // RV64I jump instructions - // - /// 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 run_jal( - &self, - core: &mut MachineCoreState, - ) -> Result, Exception> { - Ok(Set(core.hart.run_jal(self.imm, self.rd.nzx, self.width))) - } - - /// 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 run_jalr_imm( - &self, - core: &mut MachineCoreState, - ) -> Result, Exception> { - Ok(Set(core.hart.run_jalr_imm( - self.imm, - self.rs1.nzx, - self.rd.nzx, - self.width, - ))) - } - // RV64A atomic instructions impl_amo_type!(run_lrw); impl_amo_type!(run_scw); @@ -1535,28 +1512,17 @@ impl Args { icb.ok(pcu) } - fn run_j_absolute( - &self, - core: &mut MachineCoreState, - ) -> Result, Exception> { - Ok(Set(core.hart.run_j_absolute(self.imm))) - } - - unsafe fn run_jalr_absolute( - &self, - core: &mut MachineCoreState, - ) -> Result, Exception> { - Ok(Set(core.hart.run_jalr_absolute( - self.imm, - self.rd.nzx, - self.width, - ))) + fn run_j_absolute(&self, icb: &mut I) -> IcbFnResult { + let addr = branching::run_j_absolute(icb, self.imm); + let pcu = ProgramCounterUpdate::Set(addr); + icb.ok(pcu) } /// 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 run_jr(&self, icb: &mut I) -> IcbFnResult { - let addr = branching::run_jr(icb, self.rs1.nzx); + let rs1 = unsafe { self.rs1.nzx }; + let addr = branching::run_jr(icb, rs1); let pcu = ProgramCounterUpdate::Set(addr); icb.ok(pcu) } @@ -1564,7 +1530,17 @@ impl Args { /// 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 run_jr_imm(&self, icb: &mut I) -> IcbFnResult { - let addr = branching::run_jr_imm(icb, self.imm, self.rs1.nzx); + let rs1 = unsafe { self.rs1.nzx }; + let addr = branching::run_jr_imm(icb, self.imm, rs1); + let pcu = ProgramCounterUpdate::Set(addr); + icb.ok(pcu) + } + + /// 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 run_jal(&self, icb: &mut I) -> IcbFnResult { + let rd = unsafe { self.rd.nzx }; + let addr = branching::run_jal(icb, self.imm, rd, self.width); let pcu = ProgramCounterUpdate::Set(addr); icb.ok(pcu) } @@ -1572,7 +1548,28 @@ impl Args { /// 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 run_jalr(&self, icb: &mut I) -> IcbFnResult { - let addr = branching::run_jalr(icb, self.rd.nzx, self.rs1.nzx, self.width); + let rd = unsafe { self.rd.nzx }; + let rs1 = unsafe { self.rs1.nzx }; + let addr = branching::run_jalr(icb, rd, rs1, self.width); + let pcu = ProgramCounterUpdate::Set(addr); + icb.ok(pcu) + } + + /// 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 run_jalr_imm(&self, icb: &mut I) -> IcbFnResult { + let rs1 = unsafe { self.rs1.nzx }; + let rd = unsafe { self.rd.nzx }; + let addr = branching::run_jalr_imm(icb, self.imm, rs1, rd, self.width); + let pcu = ProgramCounterUpdate::Set(addr); + icb.ok(pcu) + } + + /// 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 run_jalr_absolute(&self, icb: &mut I) -> IcbFnResult { + let rd = unsafe { self.rd.nzx }; + let addr = branching::run_jalr_absolute(icb, self.imm, rd, self.width); let pcu = ProgramCounterUpdate::Set(addr); icb.ok(pcu) }