aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTreehugger Robot <android-test-infra-autosubmit@system.gserviceaccount.com>2024-05-17 21:36:49 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2024-05-17 21:36:49 +0000
commit832a1100d55a5ea7c25551a7ea52903797ef1ba1 (patch)
treeb8ac5bceedda448aaf4379c717f64d0ea0a88e02
parent7d1645f1b9404381dd61bb9ed1b7b3a296af805a (diff)
parentba5a5ab199c44d4a9c9181246a6de3a60d862de3 (diff)
downloadbinary_translation-master.tar.gz
Merge "Add more granular instruction tests for fflags" into mainHEADmastermain
-rw-r--r--test_utils/include/berberis/test_utils/insn_tests_riscv64-inl.h236
1 files changed, 148 insertions, 88 deletions
diff --git a/test_utils/include/berberis/test_utils/insn_tests_riscv64-inl.h b/test_utils/include/berberis/test_utils/insn_tests_riscv64-inl.h
index da2a50f9..379d167f 100644
--- a/test_utils/include/berberis/test_utils/insn_tests_riscv64-inl.h
+++ b/test_utils/include/berberis/test_utils/insn_tests_riscv64-inl.h
@@ -45,6 +45,8 @@
#error "One of TESTING_INTERPRETER, TESTING_LITE_TRANSLATOR, TESTING_HEAVY_OPTIMIZER must be defined
#endif
+namespace {
+
// TODO(b/276787675): remove these files from interpreter when they are no longer needed there.
// Maybe extract FPvalueToFPReg and TupleMap to a separate header?
inline constexpr class FPValueToFPReg {
@@ -83,12 +85,48 @@ decltype(auto) TupleMap(const ContainerType& container, const Transformer& trans
return result;
}
+void RaiseFeExceptForGuestFlags(uint8_t riscv_fflags) {
+ EXPECT_EQ(feclearexcept(FE_ALL_EXCEPT), 0);
+ if (riscv_fflags & FPFlags::NX) {
+ EXPECT_EQ(feraiseexcept(FE_INEXACT), 0);
+ }
+ if (riscv_fflags & FPFlags::UF) {
+ EXPECT_EQ(feraiseexcept(FE_UNDERFLOW), 0);
+ }
+ if (riscv_fflags & FPFlags::OF) {
+ EXPECT_EQ(feraiseexcept(FE_OVERFLOW), 0);
+ }
+ if (riscv_fflags & FPFlags::DZ) {
+ EXPECT_EQ(feraiseexcept(FE_DIVBYZERO), 0);
+ }
+ if (riscv_fflags & FPFlags::NV) {
+ EXPECT_EQ(feraiseexcept(FE_INVALID), 0);
+ }
+}
+
+void TestFeExceptForGuestFlags(uint8_t riscv_fflags) {
+ EXPECT_EQ(bool(riscv_fflags & FPFlags::NX), bool(fetestexcept(FE_INEXACT)));
+ EXPECT_EQ(bool(riscv_fflags & FPFlags::UF), bool(fetestexcept(FE_UNDERFLOW)));
+ EXPECT_EQ(bool(riscv_fflags & FPFlags::OF), bool(fetestexcept(FE_OVERFLOW)));
+ EXPECT_EQ(bool(riscv_fflags & FPFlags::DZ), bool(fetestexcept(FE_DIVBYZERO)));
+ EXPECT_EQ(bool(riscv_fflags & FPFlags::NV), bool(fetestexcept(FE_INVALID)));
+}
+
+} // namespace
+
class TESTSUITE : public ::testing::Test {
public:
TESTSUITE()
: state_{
.cpu = {.vtype = uint64_t{1} << 63, .frm = intrinsics::GuestModeFromHostRounding()}} {}
+ template <uint8_t kInsnSize = 4>
+ void RunInstruction(uint32_t insn_bytes) {
+ auto code_start = ToGuestAddr(&insn_bytes);
+ state_.cpu.insn_addr = code_start;
+ EXPECT_TRUE(RunOneInstruction(&state_, state_.cpu.insn_addr + kInsnSize));
+ }
+
// Compressed Instructions.
template <RegisterType register_type, uint64_t expected_result, uint8_t kTargetReg>
@@ -201,6 +239,15 @@ class TESTSUITE : public ::testing::Test {
// Non-Compressed Instructions.
+ void TestFFlagsOnGuestAndHost(uint8_t expected_guest_fflags) {
+ // Read fflags register.
+ RunInstruction(0x00102173); // frflags x2
+ EXPECT_EQ(GetXReg<2>(state_.cpu), expected_guest_fflags);
+
+ // Check corresponding fenv exception flags on host.
+ TestFeExceptForGuestFlags(expected_guest_fflags);
+ }
+
void TestFCsr(uint32_t insn_bytes,
uint8_t fcsr_to_set,
uint8_t expected_fcsr,
@@ -215,14 +262,6 @@ class TESTSUITE : public ::testing::Test {
EXPECT_EQ(state_.cpu.frm, expected_cpustate_frm);
}
- void TestFFlags(uint32_t insn_bytes, uint8_t fflags_to_set, uint8_t expected_fflags) {
- auto code_start = ToGuestAddr(&insn_bytes);
- state_.cpu.insn_addr = code_start;
- SetXReg<3>(state_.cpu, fflags_to_set);
- EXPECT_TRUE(RunOneInstruction(&state_, state_.cpu.insn_addr + 4));
- EXPECT_EQ(GetXReg<2>(state_.cpu), expected_fflags);
- }
-
void TestFrm(uint32_t insn_bytes, uint8_t frm_to_set, uint8_t expected_rm) {
auto code_start = ToGuestAddr(&insn_bytes);
state_.cpu.insn_addr = code_start;
@@ -1182,107 +1221,128 @@ TEST_F(TESTSUITE, CsrInstructions) {
TestFrm(0x0020f173, 0, 0);
}
-TEST_F(TESTSUITE, FCsrRegister) {
- fenv_t saved_environment;
- EXPECT_EQ(fegetenv(&saved_environment), 0);
+constexpr uint8_t kFPFlagsAll = FPFlags::NX | FPFlags::UF | FPFlags::OF | FPFlags::DZ | FPFlags::NV;
+// Ensure all trailing bits are set in kFPFlagsAll so that all combinations are possible.
+static_assert(__builtin_ctz(~kFPFlagsAll) == 5);
- for (uint8_t riscv_fflags = 0; riscv_fflags < 32; riscv_fflags += 1) {
- EXPECT_EQ(feclearexcept(FE_ALL_EXCEPT), 0);
- if (riscv_fflags & FPFlags::NX) {
- EXPECT_EQ(feraiseexcept(FE_INEXACT), 0);
- }
- if (riscv_fflags & FPFlags::UF) {
- EXPECT_EQ(feraiseexcept(FE_UNDERFLOW), 0);
- }
- if (riscv_fflags & FPFlags::OF) {
- EXPECT_EQ(feraiseexcept(FE_OVERFLOW), 0);
- }
- if (riscv_fflags & FPFlags::DZ) {
- EXPECT_EQ(feraiseexcept(FE_DIVBYZERO), 0);
- }
- if (riscv_fflags & FPFlags::NV) {
- EXPECT_EQ(feraiseexcept(FE_INVALID), 0);
- }
- TestFCsr(0x00319173, 0, riscv_fflags, 0);
+// Automatically saves and restores fenv throughout the lifetime of a parent scope.
+class ScopedFenv {
+ public:
+ ScopedFenv() { EXPECT_EQ(fegetenv(&env_), 0); }
+ ~ScopedFenv() { EXPECT_EQ(fesetenv(&env_), 0); }
+
+ private:
+ fenv_t env_;
+};
+
+TEST_F(TESTSUITE, FFlagsRead) {
+ ScopedFenv fenv;
+ for (uint8_t fflags = 0; fflags <= kFPFlagsAll; fflags++) {
+ RaiseFeExceptForGuestFlags(fflags);
+ RunInstruction(0x00102173); // frflags x2
+ EXPECT_EQ(GetXReg<2>(state_.cpu), fflags);
}
+}
- for (bool immediate_source : {true, false}) {
- for (uint8_t riscv_fflags = 0; riscv_fflags < 32; ++riscv_fflags) {
- EXPECT_EQ(feclearexcept(FE_ALL_EXCEPT), 0);
- if (immediate_source) {
- TestFCsr(0x00305173 | (riscv_fflags << 15), 0, 0, 0);
- } else {
- TestFCsr(0x00319173, 0b100'0000 | riscv_fflags, 0, 2);
- }
- EXPECT_EQ(bool(riscv_fflags & FPFlags::NX), bool(fetestexcept(FE_INEXACT)));
- EXPECT_EQ(bool(riscv_fflags & FPFlags::UF), bool(fetestexcept(FE_UNDERFLOW)));
- EXPECT_EQ(bool(riscv_fflags & FPFlags::OF), bool(fetestexcept(FE_OVERFLOW)));
- EXPECT_EQ(bool(riscv_fflags & FPFlags::DZ), bool(fetestexcept(FE_DIVBYZERO)));
- EXPECT_EQ(bool(riscv_fflags & FPFlags::NV), bool(fetestexcept(FE_INVALID)));
- }
+TEST_F(TESTSUITE, FFlagsSwap) {
+ ScopedFenv fenv;
+ for (uint8_t fflags = 0; fflags <= kFPFlagsAll; fflags++) {
+ RaiseFeExceptForGuestFlags(fflags);
+ // After swapping in 0 for flags, read fflags to verify.
+ SetXReg<3>(state_.cpu, 0);
+ RunInstruction(0x00119173); // fsflags x2, x3
+ EXPECT_EQ(GetXReg<2>(state_.cpu), fflags);
+ TestFFlagsOnGuestAndHost(0u);
}
+}
- EXPECT_EQ(fesetenv(&saved_environment), 0);
+TEST_F(TESTSUITE, FFlagsSwapImmediate) {
+ ScopedFenv fenv;
+ for (uint8_t fflags = 0; fflags <= kFPFlagsAll; fflags++) {
+ RaiseFeExceptForGuestFlags(fflags);
+ // After swapping in 0 for flags, read fflags to verify.
+ RunInstruction(0x00105173); // fsflags x2, 0
+ EXPECT_EQ(GetXReg<2>(state_.cpu), fflags);
+ TestFFlagsOnGuestAndHost(0u);
+ }
}
-TEST_F(TESTSUITE, FFlagsRegister) {
- fenv_t saved_environment;
- EXPECT_EQ(fegetenv(&saved_environment), 0);
+TEST_F(TESTSUITE, FFlagsWrite) {
+ ScopedFenv fenv;
+ for (uint8_t fflags = 0; fflags <= kFPFlagsAll; fflags++) {
+ SetXReg<3>(state_.cpu, fflags);
+ RunInstruction(0x00119073); // fsflags x3
+ TestFFlagsOnGuestAndHost(fflags);
+ }
+}
- for (uint8_t riscv_fflags = 0; riscv_fflags < 32; riscv_fflags += 1) {
- EXPECT_EQ(feclearexcept(FE_ALL_EXCEPT), 0);
- if (riscv_fflags & FPFlags::NX) {
- EXPECT_EQ(feraiseexcept(FE_INEXACT), 0);
- }
- if (riscv_fflags & FPFlags::UF) {
- EXPECT_EQ(feraiseexcept(FE_UNDERFLOW), 0);
- }
- if (riscv_fflags & FPFlags::OF) {
- EXPECT_EQ(feraiseexcept(FE_OVERFLOW), 0);
- }
- if (riscv_fflags & FPFlags::DZ) {
- EXPECT_EQ(feraiseexcept(FE_DIVBYZERO), 0);
- }
- if (riscv_fflags & FPFlags::NV) {
- EXPECT_EQ(feraiseexcept(FE_INVALID), 0);
- }
- TestFFlags(0x00105173, 0, riscv_fflags);
+TEST_F(TESTSUITE, FFlagsWriteImmediate) {
+ ScopedFenv fenv;
+ for (uint8_t fflags = 0; fflags <= kFPFlagsAll; fflags++) {
+ RunInstruction(0x00105073 | fflags << 15); // fsflagsi 0 (+ fflags)
+ TestFFlagsOnGuestAndHost(fflags);
+ }
+}
+
+TEST_F(TESTSUITE, FFlagsClearBits) {
+ ScopedFenv fenv;
+ for (uint8_t fflags = 0; fflags <= kFPFlagsAll; fflags++) {
+ RaiseFeExceptForGuestFlags(kFPFlagsAll);
+ SetXReg<3>(state_.cpu, fflags);
+ RunInstruction(0x0011b073); // csrc fflags, x3
+ // Read fflags to verify previous bitwise clear operation.
+ TestFFlagsOnGuestAndHost(static_cast<uint8_t>(~fflags & kFPFlagsAll));
+ }
+}
+
+TEST_F(TESTSUITE, FFlagsClearBitsImmediate) {
+ ScopedFenv fenv;
+ for (uint8_t fflags = 0; fflags <= kFPFlagsAll; fflags++) {
+ RaiseFeExceptForGuestFlags(kFPFlagsAll);
+ RunInstruction(0x00107073 | fflags << 15); // csrci fflags, 0 (+ fflags)
+ // Read fflags to verify previous bitwise clear operation.
+ TestFFlagsOnGuestAndHost(static_cast<uint8_t>(~fflags & kFPFlagsAll));
+ }
+}
+
+TEST_F(TESTSUITE, FCsrRegister) {
+ ScopedFenv fenv;
+ for (uint8_t fflags = 0; fflags <= kFPFlagsAll; fflags++) {
+ RaiseFeExceptForGuestFlags(fflags);
+
+ // Read and verify fflags, then replace with all flags.
+ TestFCsr(0x00319173 /* fscsr x2,x3 */, fflags, fflags, 0);
+
+ // Only read fcsr and verify fflags.
+ TestFCsr(0x00302173 /* frcsr x2 */, /* ignored */ 0, fflags, /* expected_frm= */ 0b100u);
}
for (bool immediate_source : {true, false}) {
- for (uint8_t riscv_fflags = 0; riscv_fflags < 32; ++riscv_fflags) {
+ for (uint8_t fflags = 0; fflags <= kFPFlagsAll; fflags++) {
EXPECT_EQ(feclearexcept(FE_ALL_EXCEPT), 0);
if (immediate_source) {
- TestFFlags(0x00105173 | (riscv_fflags << 15), 0, 0);
+ TestFCsr(0x00305173 /* csrrwi x2,fcsr,0 */ | (fflags << 15), 0, 0, 0);
} else {
- TestFFlags(0x00119173, riscv_fflags, 0);
+ TestFCsr(0x00319173 /* fscsr x2,x3 */, 0b100'0000 | fflags, 0, /* expected_frm= */ 0b010u);
}
- EXPECT_EQ(bool(riscv_fflags & FPFlags::NX), bool(fetestexcept(FE_INEXACT)));
- EXPECT_EQ(bool(riscv_fflags & FPFlags::UF), bool(fetestexcept(FE_UNDERFLOW)));
- EXPECT_EQ(bool(riscv_fflags & FPFlags::OF), bool(fetestexcept(FE_OVERFLOW)));
- EXPECT_EQ(bool(riscv_fflags & FPFlags::DZ), bool(fetestexcept(FE_DIVBYZERO)));
- EXPECT_EQ(bool(riscv_fflags & FPFlags::NV), bool(fetestexcept(FE_INVALID)));
+ TestFFlagsOnGuestAndHost(fflags);
}
}
-
- EXPECT_EQ(fesetenv(&saved_environment), 0);
}
TEST_F(TESTSUITE, FsrRegister) {
ScopedRoundingMode scoped_rounding_mode;
- int rounding[][2] = {
- {0, FE_TONEAREST},
- {1, FE_TOWARDZERO},
- {2, FE_DOWNWARD},
- {3, FE_UPWARD},
- {4, FE_TOWARDZERO},
- // Only low three bits must be affecting output (for forward compatibility).
- {8, FE_TONEAREST},
- {9, FE_TOWARDZERO},
- {10, FE_DOWNWARD},
- {11, FE_UPWARD},
- {12, FE_TOWARDZERO}
- };
+ int rounding[][2] = {{0, FE_TONEAREST},
+ {1, FE_TOWARDZERO},
+ {2, FE_DOWNWARD},
+ {3, FE_UPWARD},
+ {4, FE_TOWARDZERO},
+ // Only low three bits must be affecting output (for forward compatibility).
+ {8, FE_TONEAREST},
+ {9, FE_TOWARDZERO},
+ {10, FE_DOWNWARD},
+ {11, FE_UPWARD},
+ {12, FE_TOWARDZERO}};
for (bool immediate_source : {true, false}) {
for (auto [guest_rounding, host_rounding] : rounding) {
if (immediate_source) {