diff --git a/Common/RiscVEmitter.cpp b/Common/RiscVEmitter.cpp index 8232eb1d51..f02666a113 100644 --- a/Common/RiscVEmitter.cpp +++ b/Common/RiscVEmitter.cpp @@ -941,13 +941,17 @@ static inline u16 EncodeCJ(Opcode16 op, s32 simm12, Funct3 funct3) { static inline u16 EncodeCLB(Opcode16 op, RiscCReg rd, u8 uimm2, RiscCReg rs1, Funct6 funct6) { _assert_msg_(SupportsCompressed(), "Compressed instructions unsupported"); _assert_msg_((uimm2 & 3) == uimm2, "CLB immediate must be 2 bit: %d", uimm2); - return (u16)op | ((u16)rd << 2) | ((u16)uimm2 << 5) | ((u16)rs1 << 7) | ((u16)funct6 << 10); + // uimm[0] goes in bit 6 and uimm[1] in bit 5. + u16 imm = ((uimm2 & 1) << 1) | (uimm2 >> 1); + return (u16)op | ((u16)rd << 2) | (imm << 5) | ((u16)rs1 << 7) | ((u16)funct6 << 10); } static inline u16 EncodeCSB(Opcode16 op, RiscCReg rs2, u8 uimm2, RiscCReg rs1, Funct6 funct6) { _assert_msg_(SupportsCompressed(), "Compressed instructions unsupported"); _assert_msg_((uimm2 & 3) == uimm2, "CSB immediate must be 2 bit: %d", uimm2); - return (u16)op | ((u16)rs2 << 2) | ((u16)uimm2 << 5) | ((u16)rs1 << 7) | ((u16)funct6 << 10); + // uimm[0] goes in bit 6 and uimm[1] in bit 5. + u16 imm = ((uimm2 & 1) << 1) | (uimm2 >> 1); + return (u16)op | ((u16)rs2 << 2) | (imm << 5) | ((u16)rs1 << 7) | ((u16)funct6 << 10); } static inline u16 EncodeCLH(Opcode16 op, RiscCReg rd, u8 uimm1, bool funct1, RiscCReg rs1, Funct6 funct6) { @@ -2414,6 +2418,12 @@ static RiscVReg EncodeFLImm(int bits, double v) { bool RiscVEmitter::CanFLI(int bits, double v) const { if (!SupportsFloatExtra()) return false; + // FLI's only NaN is the canonical one. Any other would match it, since converting a NaN + // (to float in EncodeFLImm) may quiet it or drop its payload. + uint64_t dbits; + memcpy(&dbits, &v, sizeof(dbits)); + if (v != v && dbits != 0x7FF8000000000000ULL) + return false; if (bits == 16 && !SupportsFloatHalf()) return false; if (bits > FloatBitsSupported()) @@ -2427,6 +2437,15 @@ bool RiscVEmitter::CanFLI(int bits, uint32_t pattern) const { return CanFLI(bits, f); } +bool RiscVEmitter::CanFLI(int bits, float v) const { + // Checked here as well, since converting to double can already change a NaN. + uint32_t fbits; + memcpy(&fbits, &v, sizeof(fbits)); + if (v != v && fbits != 0x7FC00000) + return false; + return CanFLI(bits, (double)v); +} + void RiscVEmitter::FLI(int bits, RiscVReg rd, double v) { _assert_msg_(SupportsFloatExtra(), "%s cannot be used without Zfa", __func__); _assert_msg_(bits <= FloatBitsSupported(), "FLI cannot be used for %d bits, only %d/%d supported", bits, BitsSupported(), FloatBitsSupported()); diff --git a/Common/RiscVEmitter.h b/Common/RiscVEmitter.h index be6e2e986d..2cf10636a3 100644 --- a/Common/RiscVEmitter.h +++ b/Common/RiscVEmitter.h @@ -435,9 +435,7 @@ public: // Additional floating point (Zfa.) bool CanFLI(int bits, double v) const; bool CanFLI(int bits, uint32_t pattern) const; - bool CanFLI(int bits, float v) const { - return CanFLI(bits, (double)v); - } + bool CanFLI(int bits, float v) const; void FLI(int bits, RiscVReg rd, double v); void FLI(int bits, RiscVReg rd, uint32_t pattern); void FLI(int bits, RiscVReg rd, float v) { diff --git a/unittest/TestRiscVEmitter.cpp b/unittest/TestRiscVEmitter.cpp index 5dc6766996..4b681f2d30 100644 --- a/unittest/TestRiscVEmitter.cpp +++ b/unittest/TestRiscVEmitter.cpp @@ -70,5 +70,40 @@ bool TestRiscVEmitter() { EXPECT_EQ_HEX(code[i], expected[i]); } + // Zcb loads and stores, which split the offset bits differently by size. + cpu_info.RiscV_Zcb = true; + u16 *code16 = (u16 *)code; + emitter.SetCodePointer((u8 *)code, (u8 *)code); + emitter.C_LBU(X10, X11, 1); + emitter.C_LBU(X10, X11, 2); + emitter.C_SB(X10, X11, 1); + emitter.C_SB(X10, X11, 2); + emitter.C_LHU(X10, X11, 2); + emitter.C_SH(X10, X11, 2); + + // From GNU as. + static constexpr uint16_t expectedZcb[] = { + 0x81c8, + 0x81a8, + 0x89c8, + 0x89a8, + 0x85a8, + 0x8da8, + }; + + len = (u16 *)emitter.GetWritableCodePtr() - code16; + EXPECT_EQ_INT(len, ARRAY_SIZE(expectedZcb)); + + for (ptrdiff_t i = 0; i < len; ++i) { + EXPECT_EQ_HEX(code16[i], expectedZcb[i]); + } + + // FLI has only the canonical NaN. + cpu_info.RiscV_Zfa = true; + EXPECT_TRUE(emitter.CanFLI(32, (uint32_t)0x7FC00000)); + EXPECT_FALSE(emitter.CanFLI(32, (uint32_t)0xFFC00000)); + EXPECT_FALSE(emitter.CanFLI(32, (uint32_t)0x7F800001)); + EXPECT_TRUE(emitter.CanFLI(32, 1.0f)); + return true; }