diff options
author | Aman <amankumar2198@gmail.com> | 2023-04-17 00:03:40 +0530 |
---|---|---|
committer | Aman <amankumar2198@gmail.com> | 2023-04-19 14:32:16 +0530 |
commit | 229a84490815806f6715cff863ad4140548c0585 (patch) | |
tree | 7adc9256c84a7817a5e051d382186d557afbfe40 | |
parent | 57a120cf6589e0d45738da935700001af463bcd1 (diff) | |
download | binary_translation-229a84490815806f6715cff863ad4140548c0585.tar.gz |
interp: added Zicsr instructions.
Bug: 265372622
Test: berberis_host_tests/berberis_host_tests
Change-Id: I5e6e0e23c82fc6c2a282e313e3118bee1c1ba9d9
-rw-r--r-- | decoder/include/berberis/decoder/riscv64/decoder.h | 64 | ||||
-rw-r--r-- | decoder/include/berberis/decoder/riscv64/semantics_player.h | 13 | ||||
-rw-r--r-- | guest_state/include/berberis/guest_state/guest_state_riscv64.h | 15 | ||||
-rw-r--r-- | interpreter/Android.bp | 2 | ||||
-rw-r--r-- | interpreter/riscv64/interpreter.cc | 43 | ||||
-rw-r--r-- | interpreter/riscv64/interpreter_test.cc | 20 | ||||
-rw-r--r-- | intrinsics/Android.bp | 7 | ||||
-rw-r--r-- | intrinsics/include/berberis/intrinsics/guest_fpstate.h | 14 | ||||
-rw-r--r-- | intrinsics/include/berberis/intrinsics/riscv64/guest_fpstate.h | 58 |
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_ |