This adds checks for target instruction alignment in jump and branch
instructions, and generates exceptions from these instructions if the
alignment is wrong.

Note, if the C subset is supported, there will be no exceptions generated.

PiperOrigin-RevId: 842742707
Change-Id: I86cf0e744e5ab7d73b5e793509a8c886da042d6f
diff --git a/riscv/riscv_i_instructions.cc b/riscv/riscv_i_instructions.cc
index 64645ec..feaf111 100644
--- a/riscv/riscv_i_instructions.cc
+++ b/riscv/riscv_i_instructions.cc
@@ -24,6 +24,7 @@
 #include "mpact/sim/generic/instruction.h"
 #include "mpact/sim/generic/register.h"
 #include "mpact/sim/generic/type_helpers.h"
+#include "riscv/riscv_csr.h"
 #include "riscv/riscv_instruction_helpers.h"
 #include "riscv/riscv_register.h"
 #include "riscv/riscv_state.h"
@@ -140,10 +141,24 @@
   UIntReg reg_base = generic::GetInstructionSource<UIntReg>(instruction, 0);
   UIntReg offset = generic::GetInstructionSource<UIntReg>(instruction, 1);
   UIntReg target = (offset + reg_base) & ~0x1;
+  // Check target for proper alignment.
+  auto* state = static_cast<RiscVState*>(instruction->state());
+  auto res =
+      state->csr_set()->GetCsr(static_cast<uint64_t>(RiscVCsrEnum::kMIsa));
+  bool has_c_extension = true;
+  if (res.ok() || res.value() != nullptr) {
+    has_c_extension = (res.value()->GetUint64() &
+                       static_cast<uint64_t>(IsaExtensions::kCompressed)) != 0;
+  }
+  if (!has_c_extension && ((target & 0x3) != 0)) {
+    state->Trap(/*is_interrupt*/ false, instruction->address(),
+                *ExceptionCode::kInstructionAddressMisaligned,
+                instruction->address(), instruction);
+    return;
+  }
   UIntReg return_address = instruction->address() + instruction->size();
   auto* db = instruction->Destination(0)->AllocateDataBuffer();
   db->SetSubmit<UIntReg>(0, target);
-  auto* state = static_cast<RiscVState*>(instruction->state());
   state->set_branch(true);
   auto* reg = static_cast<generic::RegisterDestinationOperand<UIntReg>*>(
                   instruction->Destination(1))
@@ -294,10 +309,24 @@
   UIntReg offset = generic::GetInstructionSource<UIntReg>(instruction, 1);
   UIntReg target = offset + reg_base;
   target &= (std::numeric_limits<UIntReg>::max() << 1);
+  // Check target for proper alignment.
+  auto* state = static_cast<RiscVState*>(instruction->state());
+  auto res =
+      state->csr_set()->GetCsr(static_cast<uint64_t>(RiscVCsrEnum::kMIsa));
+  bool has_c_extension = true;
+  if (res.ok() || res.value() != nullptr) {
+    has_c_extension = (res.value()->GetUint64() &
+                       static_cast<uint64_t>(IsaExtensions::kCompressed)) != 0;
+  }
+  if (!has_c_extension && ((target & 0x3) != 0)) {
+    state->Trap(/*is_interrupt*/ false, instruction->address(),
+                *ExceptionCode::kInstructionAddressMisaligned,
+                instruction->address(), instruction);
+    return;
+  }
   UIntReg return_address = instruction->address() + instruction->size();
   auto* db = instruction->Destination(0)->AllocateDataBuffer();
   db->SetSubmit<UIntReg>(0, target);
-  auto* state = static_cast<RiscVState*>(instruction->state());
   state->set_branch(true);
   auto* reg = static_cast<generic::RegisterDestinationOperand<UIntReg>*>(
                   instruction->Destination(1))
diff --git a/riscv/riscv_instruction_helpers.h b/riscv/riscv_instruction_helpers.h
index 89d6050..9435bbd 100644
--- a/riscv/riscv_instruction_helpers.h
+++ b/riscv/riscv_instruction_helpers.h
@@ -28,6 +28,7 @@
 #include "mpact/sim/generic/operand_interface.h"
 #include "mpact/sim/generic/register.h"
 #include "mpact/sim/generic/type_helpers.h"
+#include "riscv/riscv_csr.h"
 #include "riscv/riscv_fp_host.h"
 #include "riscv/riscv_fp_info.h"
 #include "riscv/riscv_fp_state.h"
@@ -348,9 +349,24 @@
   if (cond(a, b)) {
     UIntType offset = generic::GetInstructionSource<UIntType>(instruction, 2);
     UIntType target = offset + instruction->address();
+    // Check target for proper alignment.
+    auto* state = static_cast<RiscVState*>(instruction->state());
+    auto res =
+        state->csr_set()->GetCsr(static_cast<uint64_t>(RiscVCsrEnum::kMIsa));
+    bool has_c_extension = true;
+    if (res.ok() || res.value() != nullptr) {
+      has_c_extension =
+          (res.value()->GetUint64() &
+           static_cast<uint64_t>(IsaExtensions::kCompressed)) != 0;
+    }
+    if (!has_c_extension && ((target & 0x3) != 0)) {
+      state->Trap(/*is_interrupt*/ false, instruction->address(),
+                  *ExceptionCode::kInstructionAddressMisaligned,
+                  instruction->address(), instruction);
+      return;
+    }
     auto* db = instruction->Destination(0)->AllocateDataBuffer();
     db->SetSubmit<UIntType>(0, target);
-    auto state = static_cast<RiscVState*>(instruction->state());
     state->set_branch(true);
   }
 }