aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAman <amankumar2198@gmail.com>2023-04-19 17:35:39 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2023-04-19 17:35:39 +0000
commitd7c7456f38ad57069efd685b58cb31d481d053ea (patch)
tree7adc9256c84a7817a5e051d382186d557afbfe40
parent7520df1208096c2a3d410a48316fe680f46ca34e (diff)
parentdf06b0c26d542b88e2cb9e6be7aecc05eea18b80 (diff)
downloadbinary_translation-d7c7456f38ad57069efd685b58cb31d481d053ea.tar.gz
interp: added Zicsr instructions. am: 229a844908 am: df06b0c26d
Original change: https://android-review.googlesource.com/c/platform/frameworks/libs/binary_translation/+/2539430 Change-Id: If50522eca9e4a0a017a3dc31cb9f540df58d16e2 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--decoder/include/berberis/decoder/riscv64/decoder.h64
-rw-r--r--decoder/include/berberis/decoder/riscv64/semantics_player.h13
-rw-r--r--guest_state/include/berberis/guest_state/guest_state_riscv64.h15
-rw-r--r--interpreter/Android.bp2
-rw-r--r--interpreter/riscv64/interpreter.cc43
-rw-r--r--interpreter/riscv64/interpreter_test.cc20
-rw-r--r--intrinsics/Android.bp7
-rw-r--r--intrinsics/include/berberis/intrinsics/guest_fpstate.h14
-rw-r--r--intrinsics/include/berberis/intrinsics/riscv64/guest_fpstate.h58
9 files changed, 230 insertions, 6 deletions
diff --git a/decoder/include/berberis/decoder/riscv64/decoder.h b/decoder/include/berberis/decoder/riscv64/decoder.h
index 677e715e..4041b417 100644
--- a/decoder/include/berberis/decoder/riscv64/decoder.h
+++ b/decoder/include/berberis/decoder/riscv64/decoder.h
@@ -112,6 +112,20 @@ class Decoder {
kMaxCompressedOpcode = 0b111'11,
};
+ enum class CsrOpcode {
+ kCsrrw = 0b01,
+ kCsrrs = 0b10,
+ kCsrrc = 0b11,
+ kMaxCsrOpcode = 0b11,
+ };
+
+ enum class CsrImmOpcode {
+ kCsrrwi = 0b01,
+ kCsrrsi = 0b10,
+ kCsrrci = 0b11,
+ kMaxCsrOpcode = 0b11,
+ };
+
enum class FenceOpcode {
kFence = 0b0000,
kFenceTso = 0b1000,
@@ -256,6 +270,13 @@ class Decoder {
kMaxBranchOpcode = 0b111,
};
+ enum class CsrRegister {
+ kFFlags = 0b00'00'0000'0001,
+ kFrm = 0b00'00'0000'0010,
+ kFCsr = 0b00'00'0000'0011,
+ kMaxCsrRegister = 0b11'11'1111'1111,
+ };
+
struct AmoArgs {
AmoOpcode opcode;
uint8_t dst;
@@ -265,6 +286,20 @@ class Decoder {
bool aq : 1;
};
+ struct CsrArgs {
+ CsrOpcode opcode;
+ uint8_t dst;
+ uint8_t src;
+ CsrRegister csr;
+ };
+
+ struct CsrImmArgs {
+ CsrImmOpcode opcode;
+ uint8_t dst;
+ uint8_t imm;
+ CsrRegister csr;
+ };
+
struct FenceArgs {
FenceOpcode opcode;
uint8_t dst;
@@ -751,11 +786,32 @@ class Decoder {
}
void DecodeSystem() {
- int32_t opcode = GetBits<uint32_t, 7, 25>();
- const SystemArgs args = {
- .opcode = SystemOpcode(opcode),
+ uint8_t low_opcode = GetBits<uint8_t, 12, 2>();
+ if (low_opcode == 0b00) {
+ int32_t opcode = GetBits<uint32_t, 7, 25>();
+ const SystemArgs args = {
+ .opcode = SystemOpcode(opcode),
+ };
+ return insn_consumer_->System(args);
+ }
+ if (GetBits<uint8_t, 14, 1>()) {
+ CsrImmOpcode opcode = CsrImmOpcode(low_opcode);
+ const CsrImmArgs args = {
+ .opcode = opcode,
+ .dst = GetBits<uint8_t, 7, 5>(),
+ .imm = GetBits<uint8_t, 15, 5>(),
+ .csr = CsrRegister(GetBits<uint16_t, 20, 12>()),
+ };
+ return insn_consumer_->Csr(args);
+ }
+ CsrOpcode opcode = CsrOpcode(low_opcode);
+ const CsrArgs args = {
+ .opcode = opcode,
+ .dst = GetBits<uint8_t, 7, 5>(),
+ .src = GetBits<uint8_t, 15, 5>(),
+ .csr = CsrRegister(GetBits<uint16_t, 20, 12>()),
};
- insn_consumer_->System(args);
+ return insn_consumer_->Csr(args);
}
void DecodeJumpAndLinkRegister() {
diff --git a/decoder/include/berberis/decoder/riscv64/semantics_player.h b/decoder/include/berberis/decoder/riscv64/semantics_player.h
index 683b6dea..40b559d3 100644
--- a/decoder/include/berberis/decoder/riscv64/semantics_player.h
+++ b/decoder/include/berberis/decoder/riscv64/semantics_player.h
@@ -35,6 +35,19 @@ class SemanticsPlayer {
// Decoder's InsnConsumer implementation.
+ void Csr(const typename Decoder::CsrArgs& args) {
+ Register result;
+ Register arg = GetRegOrZero(args.src);
+ result = listener_->Csr(args.opcode, arg, args.csr);
+ SetRegOrIgnore(args.dst, result);
+ }
+
+ void Csr(const typename Decoder::CsrImmArgs& args) {
+ Register result;
+ result = listener_->Csr(args.opcode, args.imm, args.csr);
+ SetRegOrIgnore(args.dst, result);
+ }
+
void Fence(const typename Decoder::FenceArgs& args) {
listener_->Fence(args.opcode,
args.src,
diff --git a/guest_state/include/berberis/guest_state/guest_state_riscv64.h b/guest_state/include/berberis/guest_state/guest_state_riscv64.h
index ff6a1b40..8546311f 100644
--- a/guest_state/include/berberis/guest_state/guest_state_riscv64.h
+++ b/guest_state/include/berberis/guest_state/guest_state_riscv64.h
@@ -30,6 +30,21 @@ struct CPUState {
// f0 to f31. We are using uint64_t because C++ may change values of NaN when they are passed from
// or to function and RISC-V uses NaN-boxing which would make things problematic.
uint64_t f[32];
+ // RISC-V has five rounding modes, while x86-64 has only four.
+ //
+ // Extra rounding mode (RMM in RISC-V documentation) is emulated but requires the use of
+ // FE_TOWARDZERO mode for correct work.
+ //
+ // Additionally RISC-V implementation is supposed to support three “illegal” rounding modes and
+ // when they are selected all instructions which use rounding mode trigger “undefined instruction”
+ // exception.
+ //
+ // For simplicity we always keep full rounding mode (3 bits) in the frm field and set host
+ // rounding mode to appropriate one.
+ //
+ // Exceptions, on the other hand, couldn't be stored here efficiently, instead we rely on the fact
+ // that x86-64 implements all five exceptions that RISC-V needs (and more).
+ uint8_t frm : 3;
GuestAddr insn_addr;
};
diff --git a/interpreter/Android.bp b/interpreter/Android.bp
index 32ac6435..37a87df3 100644
--- a/interpreter/Android.bp
+++ b/interpreter/Android.bp
@@ -33,6 +33,7 @@ cc_library_static {
"libberberis_decoder_riscv64_headers",
"libberberis_guest_state_headers",
"libberberis_interpreter_riscv64_headers",
+ "libberberis_intrinsics_headers",
"libberberis_kernel_api_headers",
],
export_header_lib_headers: ["libberberis_interpreter_riscv64_headers"],
@@ -47,6 +48,7 @@ cc_test_library {
"libberberis_base_headers",
"libberberis_guest_state_headers",
"libberberis_interpreter_riscv64_headers",
+ "libberberis_intrinsics_headers",
"libberberis_kernel_api_headers",
],
}
diff --git a/interpreter/riscv64/interpreter.cc b/interpreter/riscv64/interpreter.cc
index 63b55f15..f4e82fea 100644
--- a/interpreter/riscv64/interpreter.cc
+++ b/interpreter/riscv64/interpreter.cc
@@ -16,6 +16,7 @@
#include "berberis/interpreter/riscv64/interpreter.h"
+#include <cfenv>
#include <cstdint>
#include <cstring>
@@ -27,6 +28,7 @@
#include "berberis/decoder/riscv64/semantics_player.h"
#include "berberis/guest_state/guest_addr.h"
#include "berberis/guest_state/guest_state_riscv64.h"
+#include "berberis/intrinsics/riscv64/guest_fpstate.h"
#include "berberis/kernel_api/run_guest_syscall.h"
#include "atomics.h"
@@ -47,6 +49,47 @@ class Interpreter {
// Instruction implementations.
//
+ Register Csr(Decoder::CsrOpcode opcode, Register arg, Decoder::CsrRegister csr) {
+ Register (*UpdateStatus)(Register arg, Register original_csr_value);
+ switch (opcode) {
+ case Decoder::CsrOpcode::kCsrrw:
+ UpdateStatus = [](Register arg, Register /*original_csr_value*/) { return arg; };
+ break;
+ case Decoder::CsrOpcode::kCsrrs:
+ UpdateStatus = [](Register arg, Register original_csr_value) {
+ return arg | original_csr_value;
+ };
+ break;
+ case Decoder::CsrOpcode::kCsrrc:
+ UpdateStatus = [](Register arg, Register original_csr_value) {
+ return ~arg & original_csr_value;
+ };
+ break;
+ default:
+ Unimplemented();
+ return {};
+ }
+ Register result;
+ switch (csr) {
+ case Decoder::CsrRegister::kFrm:
+ result = state_->cpu.frm;
+ arg = UpdateStatus(arg, result);
+ state_->cpu.frm = arg;
+ if (arg <= FPFlags::RM_MAX) {
+ std::fesetround(intrinsics::ToHostRoundingMode(arg));
+ }
+ break;
+ default:
+ Unimplemented();
+ return {};
+ }
+ return result;
+ }
+
+ Register Csr(Decoder::CsrImmOpcode opcode, uint8_t imm, Decoder::CsrRegister csr) {
+ return Csr(Decoder::CsrOpcode(opcode), imm, csr);
+ }
+
// Note: we prefer not to use C11/C++ atomic_thread_fence or even gcc/clang builtin
// __atomic_thread_fence because all these function rely on the fact that compiler never uses
// non-temporal loads and stores and only issue “mfence” when sequentially consistent ordering is
diff --git a/interpreter/riscv64/interpreter_test.cc b/interpreter/riscv64/interpreter_test.cc
index 299b6e46..cc4a3e3f 100644
--- a/interpreter/riscv64/interpreter_test.cc
+++ b/interpreter/riscv64/interpreter_test.cc
@@ -27,6 +27,7 @@
#include "berberis/guest_state/guest_addr.h"
#include "berberis/guest_state/guest_state_riscv64.h"
#include "berberis/interpreter/riscv64/interpreter.h"
+#include "berberis/intrinsics/guest_fpstate.h"
namespace berberis {
@@ -65,6 +66,15 @@ class Riscv64InterpreterTest : public ::testing::Test {
EXPECT_EQ(state_.cpu.insn_addr, code_start + expected_offset);
}
+ void InterpretCsr(uint32_t insn_bytes, uint8_t expected_rm) {
+ auto code_start = ToGuestAddr(&insn_bytes);
+ state_.cpu.insn_addr = code_start;
+ state_.cpu.frm = 0b001u;
+ InterpretInsn(&state_);
+ EXPECT_EQ(GetXReg<2>(state_.cpu), 0b001u);
+ EXPECT_EQ(state_.cpu.frm, expected_rm);
+ }
+
void InterpretOp(uint32_t insn_bytes,
std::initializer_list<std::tuple<uint64_t, uint64_t, uint64_t>> args) {
for (auto [arg1, arg2, expected_result] : args) {
@@ -362,6 +372,16 @@ TEST_F(Riscv64InterpreterTest, CJ) {
}
}
+TEST_F(Riscv64InterpreterTest, CsrInstrctuion) {
+ ScopedRoundingMode scoped_rounding_mode;
+ // Csrrw x2, frm, 2
+ InterpretCsr(0x00215173, 2);
+ // Csrrsi x2, frm, 2
+ InterpretCsr(0x00216173, 3);
+ // Csrrci x2, frm, 1
+ InterpretCsr(0x0020f173, 0);
+}
+
TEST_F(Riscv64InterpreterTest, FenceInstructions) {
// Fence
InterpretFence(0x0ff0000f);
diff --git a/intrinsics/Android.bp b/intrinsics/Android.bp
index 44220fe0..9e6a6cc0 100644
--- a/intrinsics/Android.bp
+++ b/intrinsics/Android.bp
@@ -17,6 +17,13 @@ package {
default_applicable_licenses: ["Android-Apache-2.0"],
}
+cc_library_headers {
+ name: "libberberis_intrinsics_headers",
+ defaults: ["berberis_defaults"],
+ host_supported: true,
+ export_include_dirs: ["include"],
+}
+
cc_library_static {
name: "libberberis_intrinsics",
defaults: ["berberis_defaults"],
diff --git a/intrinsics/include/berberis/intrinsics/guest_fpstate.h b/intrinsics/include/berberis/intrinsics/guest_fpstate.h
index da07e470..23cce1be 100644
--- a/intrinsics/include/berberis/intrinsics/guest_fpstate.h
+++ b/intrinsics/include/berberis/intrinsics/guest_fpstate.h
@@ -21,8 +21,8 @@
// portion that is reflected in hosts' fp-environment.
// TODO(levarum): Rename file to reflect this.
-#include <fenv.h> // FE_TONEAREST and friends.
-#include <stdint.h>
+#include <cfenv> // FE_TONEAREST and friends.
+#include <cstdint>
namespace berberis {
@@ -39,6 +39,16 @@ static_assert(FE_TIESAWAY != FE_UPWARD);
static_assert(FE_TIESAWAY != FE_DOWNWARD);
static_assert(FE_TIESAWAY != FE_TOWARDZERO);
+class ScopedRoundingMode {
+ public:
+ ScopedRoundingMode() : saved_round_mode(std::fegetround()) {}
+ ScopedRoundingMode(int rm) : saved_round_mode(std::fegetround()) { std::fesetround(rm); }
+ ~ScopedRoundingMode() { std::fesetround(saved_round_mode); }
+
+ private:
+ int saved_round_mode;
+};
+
} // namespace berberis
#endif // BERBERIS_INTRINSICS_GUEST_FPSTATE_H_
diff --git a/intrinsics/include/berberis/intrinsics/riscv64/guest_fpstate.h b/intrinsics/include/berberis/intrinsics/riscv64/guest_fpstate.h
new file mode 100644
index 00000000..e8a68941
--- /dev/null
+++ b/intrinsics/include/berberis/intrinsics/riscv64/guest_fpstate.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BERBERIS_INTRINSICS_GUEST_RISCV64_FPSTATE_H_
+#define BERBERIS_INTRINSICS_GUEST_RISCV64_FPSTATE_H_
+
+#include <cfenv>
+#include <cstdint>
+
+namespace berberis {
+
+namespace FPFlags {
+
+inline constexpr uint64_t NV = 1 << 4;
+inline constexpr uint64_t DZ = 1 << 3;
+inline constexpr uint64_t OF = 1 << 2;
+inline constexpr uint64_t UF = 1 << 1;
+inline constexpr uint64_t NX = 1 << 0;
+inline constexpr uint64_t RM_POS = 5;
+inline constexpr uint64_t RM_MASK = 0b111;
+inline constexpr uint64_t RM_MAX = 0b100;
+inline constexpr uint64_t RNE = 0b000;
+inline constexpr uint64_t RTZ = 0b001;
+inline constexpr uint64_t RDN = 0b010;
+inline constexpr uint64_t RUP = 0b011;
+inline constexpr uint64_t RMM = 0b100;
+inline constexpr uint64_t DYN = 0b111;
+
+} // namespace FPFlags
+
+namespace intrinsics {
+
+// Note that not all RISC-V rounding modes are supported on popular architectures.
+// FE_TIESAWAY is emulated, but proper emulation needs FE_TOWARDZERO mode.
+inline int ToHostRoundingMode(int8_t rm) {
+ static constexpr int kRounding[FPFlags::RM_MAX + 1] = {
+ FE_TONEAREST, FE_TOWARDZERO, FE_DOWNWARD, FE_UPWARD, FE_TOWARDZERO};
+ return kRounding[rm];
+}
+
+} // namespace intrinsics
+
+} // namespace berberis
+
+#endif // BERBERIS_INTRINSICS_GUEST_RISCV64_FPSTATE_H_