Index: lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp =================================================================== --- lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp +++ lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp @@ -560,9 +560,9 @@ {"FCVT_W_S", 0xFFF0007F, 0xC0000053, DecodeIType}, {"FCVT_WU_S", 0xFFF0007F, 0xC0100053, DecodeIType}, {"FMV_X_W", 0xFFF0707F, 0xE0000053, DecodeIType}, - {"FEQ_S", 0xFE00707F, 0xA2002053, DecodeRType}, - {"FLT_S", 0xFE00707F, 0xA2001053, DecodeRType}, - {"FLE_S", 0xFE00707F, 0xA2000053, DecodeRType}, + {"FEQ_S", 0xFE00707F, 0xA0002053, DecodeRType}, + {"FLT_S", 0xFE00707F, 0xA0001053, DecodeRType}, + {"FLE_S", 0xFE00707F, 0xA0000053, DecodeRType}, {"FCLASS_S", 0xFFF0707F, 0xE0001053, DecodeIType}, {"FCVT_S_W", 0xFFF0007F, 0xD0000053, DecodeIType}, {"FCVT_S_WU", 0xFFF0007F, 0xD0100053, DecodeIType}, @@ -573,6 +573,42 @@ {"FCVT_LU_S", 0xFFF0007F, 0xC0300053, DecodeIType}, {"FCVT_S_L", 0xFFF0007F, 0xD0200053, DecodeIType}, {"FCVT_S_LU", 0xFFF0007F, 0xD0300053, DecodeIType}, + + // RV32D (Extension for Double-Precision Floating-Point) // + {"FLD", 0x707F, 0x3007, DecodeIType}, + {"FSD", 0x707F, 0x3027, DecodeSType}, + {"FMADD_D", 0x600007F, 0x2000043, DecodeR4Type}, + {"FMSUB_D", 0x600007F, 0x2000047, DecodeR4Type}, + {"FNMSUB_D", 0x600007F, 0x200004B, DecodeR4Type}, + {"FNMADD_D", 0x600007F, 0x200004F, DecodeR4Type}, + {"FADD_D", 0xFE00007F, 0x2000053, DecodeRType}, + {"FSUB_D", 0xFE00007F, 0xA000053, DecodeRType}, + {"FMUL_D", 0xFE00007F, 0x12000053, DecodeRType}, + {"FDIV_D", 0xFE00007F, 0x1A000053, DecodeRType}, + {"FSQRT_D", 0xFFF0007F, 0x5A000053, DecodeIType}, + {"FSGNJ_D", 0xFE00707F, 0x22000053, DecodeRType}, + {"FSGNJN_D", 0xFE00707F, 0x22001053, DecodeRType}, + {"FSGNJX_D", 0xFE00707F, 0x22002053, DecodeRType}, + {"FMIN_D", 0xFE00707F, 0x2A000053, DecodeRType}, + {"FMAX_D", 0xFE00707F, 0x2A001053, DecodeRType}, + {"FCVT_S_D", 0xFFF0007F, 0x40100053, DecodeIType}, + {"FCVT_D_S", 0xFFF0007F, 0x42000053, DecodeIType}, + {"FEQ_D", 0xFE00707F, 0xA2002053, DecodeRType}, + {"FLT_D", 0xFE00707F, 0xA2001053, DecodeRType}, + {"FLE_D", 0xFE00707F, 0xA2000053, DecodeRType}, + {"FCLASS_D", 0xFFF0707F, 0xE2001053, DecodeIType}, + {"FCVT_W_D", 0xFFF0007F, 0xC2000053, DecodeIType}, + {"FCVT_WU_D", 0xFFF0007F, 0xC2100053, DecodeIType}, + {"FCVT_D_W", 0xFFF0007F, 0xD2000053, DecodeIType}, + {"FCVT_D_WU", 0xFFF0007F, 0xD2100053, DecodeIType}, + + // RV64D (Extension for Double-Precision Floating-Point) // + {"FCVT_L_D", 0xFFF0007F, 0xC2200053, DecodeIType}, + {"FCVT_LU_D", 0xFFF0007F, 0xC2300053, DecodeIType}, + {"FMV_X_D", 0xFFF0707F, 0xE2000053, DecodeIType}, + {"FCVT_D_L", 0xFFF0007F, 0xD2200053, DecodeIType}, + {"FCVT_D_LU", 0xFFF0007F, 0xD2300053, DecodeIType}, + {"FMV_D_X", 0xFFF0707F, 0xF2000053, DecodeIType}, }; Optional EmulateInstructionRISCV::Decode(uint32_t inst) { @@ -1172,160 +1208,103 @@ m_emu, inst, 8, ZextD, [](uint64_t a, uint64_t b) { return std::max(a, b); }); } - bool operator()(FLW inst) { - return transformOptional( - inst.rs1.Read(m_emu), - [&](auto &&rs1) { - uint64_t addr = rs1 + uint64_t(inst.imm); - uint64_t bits = m_emu.ReadMem(addr).value(); - APFloat f(APFloat::IEEEsingle(), APInt(32, bits)); - return inst.rd.WriteAPFloat(m_emu, f); - }) - .value_or(false); - } - bool operator()(FSW inst) { - return transformOptional( - zipOpt(inst.rs1.Read(m_emu), inst.rs2.ReadAPFloat(m_emu, false)), - [&](auto &&tup) { - auto [rs1, rs2] = tup; - uint64_t addr = rs1 + uint64_t(inst.imm); - uint64_t bits = rs2.bitcastToAPInt().getZExtValue(); - return m_emu.WriteMem(addr, bits); - }) - .value_or(false); - } - bool operator()(FMADD_S inst) { - return transformOptional(zipOpt(inst.rs1.ReadAPFloat(m_emu, false), - inst.rs2.ReadAPFloat(m_emu, false), - inst.rs3.ReadAPFloat(m_emu, false)), - [&](auto &&tup) { - auto [rs1, rs2, rs3] = tup; - auto res = rs1.fusedMultiplyAdd( - rs2, rs3, m_emu.GetRoundingMode()); - inst.rd.WriteAPFloat(m_emu, rs1); - return m_emu.SetAccruedExceptions(res); - }) - .value_or(false); - } - bool operator()(FMSUB_S inst) { - return transformOptional(zipOpt(inst.rs1.ReadAPFloat(m_emu, false), - inst.rs2.ReadAPFloat(m_emu, false), - inst.rs3.ReadAPFloat(m_emu, false)), - [&](auto &&tup) { - auto [rs1, rs2, rs3] = tup; - auto res = rs1.fusedMultiplyAdd( - rs2, -rs3, m_emu.GetRoundingMode()); - inst.rd.WriteAPFloat(m_emu, rs1); - return m_emu.SetAccruedExceptions(res); - }) - .value_or(false); - } - bool operator()(FNMSUB_S inst) { - return transformOptional(zipOpt(inst.rs1.ReadAPFloat(m_emu, false), - inst.rs2.ReadAPFloat(m_emu, false), - inst.rs3.ReadAPFloat(m_emu, false)), - [&](auto &&tup) { - auto [rs1, rs2, rs3] = tup; - auto res = rs1.fusedMultiplyAdd( - -rs2, rs3, m_emu.GetRoundingMode()); - inst.rd.WriteAPFloat(m_emu, rs1); - return m_emu.SetAccruedExceptions(res); - }) - .value_or(false); - } - bool operator()(FNMADD_S inst) { - return transformOptional(zipOpt(inst.rs1.ReadAPFloat(m_emu, false), - inst.rs2.ReadAPFloat(m_emu, false), - inst.rs3.ReadAPFloat(m_emu, false)), - [&](auto &&tup) { - auto [rs1, rs2, rs3] = tup; - auto res = rs1.fusedMultiplyAdd( - -rs2, -rs3, m_emu.GetRoundingMode()); - inst.rd.WriteAPFloat(m_emu, rs1); - return m_emu.SetAccruedExceptions(res); + template + bool F_Load(T inst, const fltSemantics &(*semantics)(), + unsigned int numBits) { + return transformOptional(inst.rs1.Read(m_emu), + [&](auto &&rs1) { + uint64_t addr = rs1 + uint64_t(inst.imm); + uint64_t bits = + m_emu.ReadMem(addr).value(); + APFloat f(semantics(), APInt(numBits, bits)); + return inst.rd.WriteAPFloat(m_emu, f); }) .value_or(false); } - bool operator()(FADD_S inst) { - return transformOptional(zipOpt(inst.rs1.ReadAPFloat(m_emu, false), - inst.rs2.ReadAPFloat(m_emu, false)), + bool operator()(FLW inst) { return F_Load(inst, &APFloat::IEEEsingle, 32); } + template bool F_Store(T inst, bool isDouble) { + return transformOptional(zipOpt(inst.rs1.Read(m_emu), + inst.rs2.ReadAPFloat(m_emu, isDouble)), [&](auto &&tup) { auto [rs1, rs2] = tup; - auto res = rs1.add(rs2, m_emu.GetRoundingMode()); - inst.rd.WriteAPFloat(m_emu, rs1); - return m_emu.SetAccruedExceptions(res); + uint64_t addr = rs1 + uint64_t(inst.imm); + uint64_t bits = + rs2.bitcastToAPInt().getZExtValue(); + return m_emu.WriteMem(addr, bits); }) .value_or(false); } - bool operator()(FSUB_S inst) { - return transformOptional(zipOpt(inst.rs1.ReadAPFloat(m_emu, false), - inst.rs2.ReadAPFloat(m_emu, false)), - [&](auto &&tup) { - auto [rs1, rs2] = tup; - auto res = - rs1.subtract(rs2, m_emu.GetRoundingMode()); - inst.rd.WriteAPFloat(m_emu, rs1); - return m_emu.SetAccruedExceptions(res); - }) - .value_or(false); + bool operator()(FSW inst) { return F_Store(inst, false); } + std::tuple FusedMultiplyAdd(APFloat rs1, APFloat rs2, + APFloat rs3) { + auto opStatus = rs1.fusedMultiplyAdd(rs2, rs3, m_emu.GetRoundingMode()); + auto res = m_emu.SetAccruedExceptions(opStatus); + return {res, rs1}; } - bool operator()(FMUL_S inst) { - return transformOptional(zipOpt(inst.rs1.ReadAPFloat(m_emu, false), - inst.rs2.ReadAPFloat(m_emu, false)), + template + bool FMA(T inst, bool isDouble, float rs2_sign, float rs3_sign) { + return transformOptional(zipOpt(inst.rs1.ReadAPFloat(m_emu, isDouble), + inst.rs2.ReadAPFloat(m_emu, isDouble), + inst.rs3.ReadAPFloat(m_emu, isDouble)), [&](auto &&tup) { - auto [rs1, rs2] = tup; - auto res = - rs1.multiply(rs2, m_emu.GetRoundingMode()); - inst.rd.WriteAPFloat(m_emu, rs1); - return m_emu.SetAccruedExceptions(res); + auto [rs1, rs2, rs3] = tup; + rs2.copySign(APFloat(rs2_sign)); + rs3.copySign(APFloat(rs3_sign)); + auto [res, f] = FusedMultiplyAdd(rs1, rs2, rs3); + return res && inst.rd.WriteAPFloat(m_emu, f); }) .value_or(false); } - bool operator()(FDIV_S inst) { - return transformOptional(zipOpt(inst.rs1.ReadAPFloat(m_emu, false), - inst.rs2.ReadAPFloat(m_emu, false)), + bool operator()(FMADD_S inst) { return FMA(inst, false, 1.0f, 1.0f); } + bool operator()(FMSUB_S inst) { return FMA(inst, false, 1.0f, -1.0f); } + bool operator()(FNMSUB_S inst) { return FMA(inst, false, -1.0f, 1.0f); } + bool operator()(FNMADD_S inst) { return FMA(inst, false, -1.0f, -1.0f); } + template + bool F_Op(T inst, bool isDouble, + APFloat::opStatus (APFloat::*f)(const APFloat &RHS, + APFloat::roundingMode RM)) { + return transformOptional(zipOpt(inst.rs1.ReadAPFloat(m_emu, isDouble), + inst.rs2.ReadAPFloat(m_emu, isDouble)), [&](auto &&tup) { auto [rs1, rs2] = tup; auto res = - rs1.divide(rs2, m_emu.GetRoundingMode()); + ((&rs1)->*f)(rs2, m_emu.GetRoundingMode()); inst.rd.WriteAPFloat(m_emu, rs1); return m_emu.SetAccruedExceptions(res); }) .value_or(false); } + bool operator()(FADD_S inst) { return F_Op(inst, false, &APFloat::add); } + bool operator()(FSUB_S inst) { return F_Op(inst, false, &APFloat::subtract); } + bool operator()(FMUL_S inst) { return F_Op(inst, false, &APFloat::multiply); } + bool operator()(FDIV_S inst) { return F_Op(inst, false, &APFloat::divide); } bool operator()(FSQRT_S inst) { // TODO: APFloat doesn't have a sqrt function. return false; } - bool operator()(FSGNJ_S inst) { - return transformOptional(zipOpt(inst.rs1.ReadAPFloat(m_emu, false), - inst.rs2.ReadAPFloat(m_emu, false)), + template bool F_SignInj(T inst, bool isDouble, bool isNegate) { + return transformOptional(zipOpt(inst.rs1.ReadAPFloat(m_emu, isDouble), + inst.rs2.ReadAPFloat(m_emu, isDouble)), [&](auto &&tup) { auto [rs1, rs2] = tup; + if (isNegate) + rs2.changeSign(); rs1.copySign(rs2); return inst.rd.WriteAPFloat(m_emu, rs1); }) .value_or(false); } - bool operator()(FSGNJN_S inst) { - return transformOptional(zipOpt(inst.rs1.ReadAPFloat(m_emu, false), - inst.rs2.ReadAPFloat(m_emu, false)), - [&](auto &&tup) { - auto [rs1, rs2] = tup; - rs1.copySign(-rs2); - return inst.rd.WriteAPFloat(m_emu, rs1); - }) - .value_or(false); - } - bool operator()(FSGNJX_S inst) { - return transformOptional(zipOpt(inst.rs1.ReadAPFloat(m_emu, false), - inst.rs2.ReadAPFloat(m_emu, false)), + bool operator()(FSGNJ_S inst) { return F_SignInj(inst, false, false); } + bool operator()(FSGNJN_S inst) { return F_SignInj(inst, false, true); } + template bool F_SignInjXor(T inst, bool isDouble) { + return transformOptional(zipOpt(inst.rs1.ReadAPFloat(m_emu, isDouble), + inst.rs2.ReadAPFloat(m_emu, isDouble)), [&](auto &&tup) { auto [rs1, rs2] = tup; - // spec: the sign bit is the XOR of the - // sign bits of rs1 and rs2. if rs1 and rs2 - // have the same signs set rs1 to positive - // else set rs1 to negative + // spec: the sign bit is the XOR of the sign bits + // of rs1 and rs2. if rs1 and rs2 have the same + // signs set rs1 to positive else set rs1 to + // negative if (rs1.isNegative() == rs2.isNegative()) { rs1.clearSign(); } else { @@ -1336,10 +1315,13 @@ }) .value_or(false); } - bool operator()(FMIN_S inst) { + bool operator()(FSGNJX_S inst) { return F_SignInjXor(inst, false); } + template + bool F_MAX_MIN(T inst, bool isDouble, + APFloat (*f)(const APFloat &A, const APFloat &B)) { return transformOptional( - zipOpt(inst.rs1.ReadAPFloat(m_emu, false), - inst.rs2.ReadAPFloat(m_emu, false)), + zipOpt(inst.rs1.ReadAPFloat(m_emu, isDouble), + inst.rs2.ReadAPFloat(m_emu, isDouble)), [&](auto &&tup) { auto [rs1, rs2] = tup; // If both inputs are NaNs, the result is the canonical NaN. @@ -1352,102 +1334,83 @@ auto canonicalNaN = APFloat::getQNaN(rs1.getSemantics()); return inst.rd.WriteAPFloat(m_emu, canonicalNaN); } - return inst.rd.WriteAPFloat(m_emu, minnum(rs1, rs2)); - }) - .value_or(false); - } - bool operator()(FMAX_S inst) { - return transformOptional( - zipOpt(inst.rs1.ReadAPFloat(m_emu, false), - inst.rs2.ReadAPFloat(m_emu, false)), - [&](auto &&tup) { - auto [rs1, rs2] = tup; - if (rs1.isNaN() || rs2.isNaN()) - m_emu.SetAccruedExceptions(APFloat::opInvalidOp); - if (rs1.isNaN() && rs2.isNaN()) { - auto canonicalNaN = APFloat::getQNaN(rs1.getSemantics()); - return inst.rd.WriteAPFloat(m_emu, canonicalNaN); - } - return inst.rd.WriteAPFloat(m_emu, maxnum(rs1, rs2)); + return inst.rd.WriteAPFloat(m_emu, f(rs1, rs2)); }) .value_or(false); } + bool operator()(FMIN_S inst) { return F_MAX_MIN(inst, false, minnum); } + bool operator()(FMAX_S inst) { return F_MAX_MIN(inst, false, maxnum); } bool operator()(FCVT_W_S inst) { - return transformOptional(inst.rs1.ReadAPFloat(m_emu, false), - [&](auto &&rs1) { - int32_t res = rs1.convertToFloat(); - return inst.rd.Write(m_emu, uint64_t(res)); - }) - .value_or(false); + return FCVT_i2f(inst, false, + &APFloat::convertToFloat); } bool operator()(FCVT_WU_S inst) { - return transformOptional(inst.rs1.ReadAPFloat(m_emu, false), - [&](auto &&rs1) { - uint32_t res = rs1.convertToFloat(); - return inst.rd.Write(m_emu, uint64_t(res)); - }) - .value_or(false); - } - bool operator()(FMV_X_W inst) { - return transformOptional(inst.rs1.ReadAPFloat(m_emu, false), - [&](auto &&rs1) { - if (rs1.isNaN()) - return inst.rd.Write(m_emu, 0x7fc00000); - auto bits = rs1.bitcastToAPInt(); - return inst.rd.Write( - m_emu, - NanBoxing(uint64_t(bits.getSExtValue()))); - }) - .value_or(false); + return FCVT_i2f(inst, false, + &APFloat::convertToFloat); } - bool operator()(FEQ_S inst) { + template bool FMV_f2i(T inst, bool isDouble) { return transformOptional( - zipOpt(inst.rs1.ReadAPFloat(m_emu, false), - inst.rs2.ReadAPFloat(m_emu, false)), - [&](auto &&tup) { - auto [rs1, rs2] = tup; - if (rs1.isNaN() || rs2.isNaN()) { - if (rs1.isSignaling() || rs2.isSignaling()) - m_emu.SetAccruedExceptions(APFloat::opInvalidOp); - return inst.rd.Write(m_emu, 0); + inst.rs1.ReadAPFloat(m_emu, isDouble), + [&](auto &&rs1) { + if (rs1.isNaN()) { + if (isDouble) + return inst.rd.Write(m_emu, 0x7ff8'0000'0000'0000); + else + return inst.rd.Write(m_emu, 0x7fc0'0000); } - return inst.rd.Write(m_emu, - rs1.compare(rs2) == APFloat::cmpEqual); + auto bits = rs1.bitcastToAPInt().getZExtValue(); + if (isDouble) + return inst.rd.Write(m_emu, bits); + else + return inst.rd.Write(m_emu, uint64_t(bits & 0xffff'ffff)); }) .value_or(false); } - bool operator()(FLT_S inst) { + bool operator()(FMV_X_W inst) { return FMV_f2i(inst, false); } + enum F_CMP { + FEQ, + FLT, + FLE, + }; + template bool F_Compare(T inst, bool isDouble, F_CMP cmp) { return transformOptional( - zipOpt(inst.rs1.ReadAPFloat(m_emu, false), - inst.rs2.ReadAPFloat(m_emu, false)), + zipOpt(inst.rs1.ReadAPFloat(m_emu, isDouble), + inst.rs2.ReadAPFloat(m_emu, isDouble)), [&](auto &&tup) { auto [rs1, rs2] = tup; if (rs1.isNaN() || rs2.isNaN()) { - m_emu.SetAccruedExceptions(APFloat::opInvalidOp); - return inst.rd.Write(m_emu, 0); + if (cmp == FEQ) { + if (rs1.isSignaling() || rs2.isSignaling()) { + auto res = + m_emu.SetAccruedExceptions(APFloat::opInvalidOp); + return res && inst.rd.Write(m_emu, 0); + } + } + auto res = m_emu.SetAccruedExceptions(APFloat::opInvalidOp); + return res && inst.rd.Write(m_emu, 0); } - return inst.rd.Write(m_emu, - rs1.compare(rs2) == APFloat::cmpLessThan); - }) - .value_or(false); - } - bool operator()(FLE_S inst) { - return transformOptional( - zipOpt(inst.rs1.ReadAPFloat(m_emu, false), - inst.rs2.ReadAPFloat(m_emu, false)), - [&](auto &&tup) { - auto [rs1, rs2] = tup; - if (rs1.isNaN() || rs2.isNaN()) { - m_emu.SetAccruedExceptions(APFloat::opInvalidOp); - return inst.rd.Write(m_emu, 0); + switch (cmp) { + case FEQ: + return inst.rd.Write(m_emu, + rs1.compare(rs2) == APFloat::cmpEqual); + case FLT: + return inst.rd.Write(m_emu, rs1.compare(rs2) == + APFloat::cmpLessThan); + case FLE: + return inst.rd.Write(m_emu, rs1.compare(rs2) != + APFloat::cmpGreaterThan); + default: + llvm_unreachable("Invalid F_CMP"); } - return inst.rd.Write(m_emu, rs1.compare(rs2) != - APFloat::cmpGreaterThan); }) .value_or(false); } - bool operator()(FCLASS_S inst) { - return transformOptional(inst.rs1.ReadAPFloat(m_emu, false), + + bool operator()(FEQ_S inst) { return F_Compare(inst, false, FEQ); } + bool operator()(FLT_S inst) { return F_Compare(inst, false, FLT); } + bool operator()(FLE_S inst) { return F_Compare(inst, false, FLE); } + template bool FCLASS(T inst, bool isDouble) { + return transformOptional(inst.rs1.ReadAPFloat(m_emu, isDouble), [&](auto &&rs1) { uint64_t result = 0; if (rs1.isInfinity() && rs1.isNegative()) @@ -1480,63 +1443,134 @@ }) .value_or(false); } - bool operator()(FCVT_S_W inst) { - return transformOptional(inst.rs1.ReadI32(m_emu), + bool operator()(FCLASS_S inst) { return FCLASS(inst, false); } + template + bool FCVT_f2i(T inst, Optional (Rs::*f)(EmulateInstructionRISCV &emu), + const fltSemantics &semantics) { + return transformOptional(((&inst.rs1)->*f)(m_emu), [&](auto &&rs1) { - APFloat apf(APFloat::IEEEsingle(), rs1); + APFloat apf(semantics, rs1); return inst.rd.WriteAPFloat(m_emu, apf); }) .value_or(false); } + bool operator()(FCVT_S_W inst) { + return FCVT_f2i(inst, &Rs::ReadI32, APFloat::IEEEsingle()); + } bool operator()(FCVT_S_WU inst) { - return transformOptional(inst.rs1.ReadU32(m_emu), - [&](auto &&rs1) { - APFloat apf(APFloat::IEEEsingle(), rs1); - return inst.rd.WriteAPFloat(m_emu, apf); - }) - .value_or(false); + return FCVT_f2i(inst, &Rs::ReadU32, APFloat::IEEEsingle()); } - bool operator()(FMV_W_X inst) { + template + bool FMV_i2f(T inst, unsigned int numBits, E (APInt::*f)() const) { return transformOptional(inst.rs1.Read(m_emu), [&](auto &&rs1) { - APInt apInt(32, NanUnBoxing(rs1)); - APFloat apf(apInt.bitsToFloat()); + APInt apInt(numBits, rs1); + if (numBits == 32) // a.k.a. float + apInt = APInt(numBits, NanUnBoxing(rs1)); + APFloat apf((&apInt->*f)()); return inst.rd.WriteAPFloat(m_emu, apf); }) .value_or(false); } - bool operator()(FCVT_L_S inst) { - return transformOptional(inst.rs1.ReadAPFloat(m_emu, false), + bool operator()(FMV_W_X inst) { + return FMV_i2f(inst, 32, &APInt::bitsToFloat); + } + template + bool FCVT_i2f(I inst, bool isDouble, T (APFloat::*f)() const) { + return transformOptional(inst.rs1.ReadAPFloat(m_emu, isDouble), [&](auto &&rs1) { - int64_t res = rs1.convertToFloat(); + E res = E((&rs1->*f)()); return inst.rd.Write(m_emu, uint64_t(res)); }) .value_or(false); } + bool operator()(FCVT_L_S inst) { + return FCVT_i2f(inst, false, + &APFloat::convertToFloat); + } bool operator()(FCVT_LU_S inst) { - return transformOptional(inst.rs1.ReadAPFloat(m_emu, false), - [&](auto &&rs1) { - uint64_t res = rs1.convertToFloat(); - return inst.rd.Write(m_emu, res); - }) - .value_or(false); + return FCVT_i2f(inst, false, + &APFloat::convertToFloat); } bool operator()(FCVT_S_L inst) { - return transformOptional(inst.rs1.ReadI64(m_emu), + return FCVT_f2i(inst, &Rs::ReadI64, APFloat::IEEEsingle()); + } + bool operator()(FCVT_S_LU inst) { + return FCVT_f2i(inst, &Rs::Read, APFloat::IEEEsingle()); + } + bool operator()(FLD inst) { return F_Load(inst, &APFloat::IEEEdouble, 64); } + bool operator()(FSD inst) { return F_Store(inst, true); } + bool operator()(FMADD_D inst) { return FMA(inst, true, 1.0f, 1.0f); } + bool operator()(FMSUB_D inst) { return FMA(inst, true, 1.0f, -1.0f); } + bool operator()(FNMSUB_D inst) { return FMA(inst, true, -1.0f, 1.0f); } + bool operator()(FNMADD_D inst) { return FMA(inst, true, -1.0f, -1.0f); } + bool operator()(FADD_D inst) { return F_Op(inst, true, &APFloat::add); } + bool operator()(FSUB_D inst) { return F_Op(inst, true, &APFloat::subtract); } + bool operator()(FMUL_D inst) { return F_Op(inst, true, &APFloat::multiply); } + bool operator()(FDIV_D inst) { return F_Op(inst, true, &APFloat::divide); } + bool operator()(FSQRT_D inst) { + // TODO: APFloat doesn't have a sqrt function. + return false; + } + bool operator()(FSGNJ_D inst) { return F_SignInj(inst, true, false); } + bool operator()(FSGNJN_D inst) { return F_SignInj(inst, true, true); } + bool operator()(FSGNJX_D inst) { return F_SignInjXor(inst, true); } + bool operator()(FMIN_D inst) { return F_MAX_MIN(inst, true, minnum); } + bool operator()(FMAX_D inst) { return F_MAX_MIN(inst, true, maxnum); } + bool operator()(FCVT_S_D inst) { + return transformOptional(inst.rs1.ReadAPFloat(m_emu, true), [&](auto &&rs1) { - APFloat apf(APFloat::IEEEsingle(), rs1); + double d = rs1.convertToDouble(); + APFloat apf((float(d))); return inst.rd.WriteAPFloat(m_emu, apf); }) .value_or(false); } - bool operator()(FCVT_S_LU inst) { - return transformOptional(inst.rs1.Read(m_emu), + bool operator()(FCVT_D_S inst) { + return transformOptional(inst.rs1.ReadAPFloat(m_emu, false), [&](auto &&rs1) { - APFloat apf(APFloat::IEEEsingle(), rs1); + float f = rs1.convertToFloat(); + APFloat apf((double(f))); return inst.rd.WriteAPFloat(m_emu, apf); }) .value_or(false); } + bool operator()(FEQ_D inst) { return F_Compare(inst, true, FEQ); } + bool operator()(FLT_D inst) { return F_Compare(inst, true, FLT); } + bool operator()(FLE_D inst) { return F_Compare(inst, true, FLE); } + bool operator()(FCLASS_D inst) { return FCLASS(inst, true); } + bool operator()(FCVT_W_D inst) { + return FCVT_i2f(inst, true, + &APFloat::convertToDouble); + } + bool operator()(FCVT_WU_D inst) { + return FCVT_i2f(inst, true, + &APFloat::convertToDouble); + } + bool operator()(FCVT_D_W inst) { + return FCVT_f2i(inst, &Rs::ReadI32, APFloat::IEEEdouble()); + } + bool operator()(FCVT_D_WU inst) { + return FCVT_f2i(inst, &Rs::ReadU32, APFloat::IEEEdouble()); + } + bool operator()(FCVT_L_D inst) { + return FCVT_i2f(inst, true, + &APFloat::convertToDouble); + } + bool operator()(FCVT_LU_D inst) { + return FCVT_i2f(inst, true, + &APFloat::convertToDouble); + } + bool operator()(FMV_X_D inst) { return FMV_f2i(inst, true); } + bool operator()(FCVT_D_L inst) { + return FCVT_f2i(inst, &Rs::ReadI64, APFloat::IEEEdouble()); + } + bool operator()(FCVT_D_LU inst) { + return FCVT_f2i(inst, &Rs::Read, APFloat::IEEEdouble()); + } + bool operator()(FMV_D_X inst) { + return FMV_i2f(inst, 64, &APInt::bitsToDouble); + } bool operator()(INVALID inst) { return false; } bool operator()(RESERVED inst) { return false; } bool operator()(EBREAK inst) { return false; } Index: lldb/source/Plugins/Instruction/RISCV/RISCVInstructions.h =================================================================== --- lldb/source/Plugins/Instruction/RISCV/RISCVInstructions.h +++ lldb/source/Plugins/Instruction/RISCV/RISCVInstructions.h @@ -216,6 +216,42 @@ I_TYPE_INST(FCVT_S_L); I_TYPE_INST(FCVT_S_LU); +// RV32D inst (Extension for Double-Precision Floating-Point) +I_TYPE_INST(FLD); +S_TYPE_INST(FSD); +R4_TYPE_INST(FMADD_D); +R4_TYPE_INST(FMSUB_D); +R4_TYPE_INST(FNMSUB_D); +R4_TYPE_INST(FNMADD_D); +R_TYPE_INST(FADD_D); +R_TYPE_INST(FSUB_D); +R_TYPE_INST(FMUL_D); +R_TYPE_INST(FDIV_D); +I_TYPE_INST(FSQRT_D); +R_TYPE_INST(FSGNJ_D); +R_TYPE_INST(FSGNJN_D); +R_TYPE_INST(FSGNJX_D); +R_TYPE_INST(FMIN_D); +R_TYPE_INST(FMAX_D); +I_TYPE_INST(FCVT_S_D); +I_TYPE_INST(FCVT_D_S); +R_TYPE_INST(FEQ_D); +R_TYPE_INST(FLT_D); +R_TYPE_INST(FLE_D); +I_TYPE_INST(FCLASS_D); +I_TYPE_INST(FCVT_W_D); +I_TYPE_INST(FCVT_WU_D); +I_TYPE_INST(FCVT_D_W); +I_TYPE_INST(FCVT_D_WU); + +// RV64D inst (Extension for Double-Precision Floating-Point) +I_TYPE_INST(FCVT_L_D); +I_TYPE_INST(FCVT_LU_D); +I_TYPE_INST(FMV_X_D); +I_TYPE_INST(FCVT_D_L); +I_TYPE_INST(FCVT_D_LU); +I_TYPE_INST(FMV_D_X); + /// Invalid and reserved instructions, the `inst` fields are used for debugging. INVALID_INST(INVALID); INVALID_INST(RESERVED); @@ -234,8 +270,12 @@ AMOMAXU_D, FLW, FSW, FMADD_S, FMSUB_S, FNMADD_S, FNMSUB_S, FADD_S, FSUB_S, FMUL_S, FDIV_S, FSQRT_S, FSGNJ_S, FSGNJN_S, FSGNJX_S, FMIN_S, FMAX_S, FCVT_W_S, FCVT_WU_S, FMV_X_W, FEQ_S, FLT_S, FLE_S, FCLASS_S, FCVT_S_W, - FCVT_S_WU, FMV_W_X, FCVT_L_S, FCVT_LU_S, FCVT_S_L, FCVT_S_LU, INVALID, - EBREAK, RESERVED, HINT, NOP>; + FCVT_S_WU, FMV_W_X, FCVT_L_S, FCVT_LU_S, FCVT_S_L, FCVT_S_LU, FLD, FSD, + FMADD_D, FMSUB_D, FNMSUB_D, FNMADD_D, FADD_D, FSUB_D, FMUL_D, FDIV_D, + FSQRT_D, FSGNJ_D, FSGNJN_D, FSGNJX_D, FMIN_D, FMAX_D, FCVT_S_D, FCVT_D_S, + FEQ_D, FLT_D, FLE_D, FCLASS_D, FCVT_W_D, FCVT_WU_D, FCVT_D_W, FCVT_D_WU, + FCVT_L_D, FCVT_LU_D, FMV_X_D, FCVT_D_L, FCVT_D_LU, FMV_D_X, INVALID, EBREAK, + RESERVED, HINT, NOP>; constexpr uint8_t RV32 = 1; constexpr uint8_t RV64 = 2; Index: lldb/unittests/Instruction/RISCV/TestRISCVEmulator.cpp =================================================================== --- lldb/unittests/Instruction/RISCV/TestRISCVEmulator.cpp +++ lldb/unittests/Instruction/RISCV/TestRISCVEmulator.cpp @@ -487,19 +487,25 @@ TestAtomic(this, 0xE0F7382F, 0x1, 0x2, 0x1, 0x2); } -struct FloatCalInst { +template struct F_D_CalInst { uint32_t inst; std::string name; - float rs1_val; - float rs2_val; - float rd_val; + T rs1_val; + T rs2_val; + T rd_val; }; -static void TestFloatCalInst(RISCVEmulatorTester *tester, DecodeResult inst, - float rs1_val, float rs2_val, float rd_exp) { - std::vector FloatCMP = {"FEQ_S", "FLT_S", "FLE_S"}; - std::vector FloatCal3 = {"FMADD_S", "FMSUB_S", "FNMSUB_S", - "FNMADD_S"}; +using FloatCalInst = F_D_CalInst; +using DoubleCalInst = F_D_CalInst; + +template +static void TestF_D_CalInst(RISCVEmulatorTester *tester, DecodeResult inst, + T rs1_val, T rs2_val, T rd_exp) { + std::vector CMPs = {"FEQ_S", "FLT_S", "FLE_S", + "FEQ_D", "FLT_D", "FLE_D"}; + std::vector FMAs = {"FMADD_S", "FMSUB_S", "FNMSUB_S", + "FNMADD_S", "FMADD_D", "FMSUB_D", + "FNMSUB_D", "FNMADD_D"}; uint32_t rd = DecodeRD(inst.inst); uint32_t rs1 = DecodeRS1(inst.inst); @@ -507,29 +513,42 @@ APFloat ap_rs1_val(rs1_val); APFloat ap_rs2_val(rs2_val); - APFloat ap_rs3_val(0.5f); + APFloat ap_rs3_val(0.0f); + static_assert(std::is_same_v || std::is_same_v, + "T should be float or double"); + if constexpr (std::is_same_v) + ap_rs3_val = APFloat(0.5f); + if constexpr (std::is_same_v) + ap_rs3_val = APFloat(0.5); if (rs1) tester->fpr.fpr[rs1] = ap_rs1_val.bitcastToAPInt().getZExtValue(); if (rs2) tester->fpr.fpr[rs2] = ap_rs2_val.bitcastToAPInt().getZExtValue(); - for (auto i : FloatCal3) { + for (auto i : FMAs) { if (inst.pattern.name == i) { uint32_t rs3 = DecodeRS3(inst.inst); tester->fpr.fpr[rs3] = ap_rs3_val.bitcastToAPInt().getZExtValue(); } } ASSERT_TRUE(tester->Execute(inst, false)); - for (auto i : FloatCMP) { + for (auto i : CMPs) { if (inst.pattern.name == i) { ASSERT_EQ(tester->gpr.gpr[rd], rd_exp); return; } } - APInt apInt(32, tester->fpr.fpr[rd]); - APFloat rd_val(apInt.bitsToFloat()); - ASSERT_EQ(rd_val.convertToFloat(), rd_exp); + if constexpr (std::is_same_v) { + APInt apInt(32, tester->fpr.fpr[rd]); + APFloat rd_val(apInt.bitsToFloat()); + ASSERT_EQ(rd_val.convertToFloat(), rd_exp); + } + if constexpr (std::is_same_v) { + APInt apInt(64, tester->fpr.fpr[rd]); + APFloat rd_val(apInt.bitsToDouble()); + ASSERT_EQ(rd_val.convertToDouble(), rd_exp); + } } TEST_F(RISCVEmulatorTester, TestFloatInst) { @@ -548,14 +567,14 @@ {0x28219253, "FMAX_S", -0.5f, -0.6f, -0.5f}, {0x28219253, "FMAX_S", 0.5f, 0.6f, 0.6f}, {0x28219253, "FMAX_S", 0.5f, -0.6f, 0.5f}, - {0xA221A253, "FEQ_S", 0.5f, 0.5f, 1}, - {0xA221A253, "FEQ_S", 0.5f, -0.5f, 0}, - {0xA221A253, "FEQ_S", -0.5f, 0.5f, 0}, - {0xA221A253, "FEQ_S", 0.4f, 0.5f, 0}, - {0xA2219253, "FLT_S", 0.4f, 0.5f, 1}, - {0xA2219253, "FLT_S", 0.5f, 0.5f, 0}, - {0xA2218253, "FLE_S", 0.4f, 0.5f, 1}, - {0xA2218253, "FLE_S", 0.5f, 0.5f, 1}, + {0xA021A253, "FEQ_S", 0.5f, 0.5f, 1}, + {0xA021A253, "FEQ_S", 0.5f, -0.5f, 0}, + {0xA021A253, "FEQ_S", -0.5f, 0.5f, 0}, + {0xA021A253, "FEQ_S", 0.4f, 0.5f, 0}, + {0xA0219253, "FLT_S", 0.4f, 0.5f, 1}, + {0xA0219253, "FLT_S", 0.5f, 0.5f, 0}, + {0xA0218253, "FLE_S", 0.4f, 0.5f, 1}, + {0xA0218253, "FLE_S", 0.5f, 0.5f, 1}, {0x4021F243, "FMADD_S", 0.5f, 0.5f, 0.75f}, {0x4021F247, "FMSUB_S", 0.5f, 0.5f, -0.25f}, {0x4021F24B, "FNMSUB_S", 0.5f, 0.5f, 0.25f}, @@ -566,60 +585,132 @@ ASSERT_TRUE(decode.has_value()); std::string name = decode->pattern.name; ASSERT_EQ(name, i.name); - TestFloatCalInst(this, decode.value(), i.rs1_val, i.rs2_val, i.rd_val); + TestF_D_CalInst(this, decode.value(), i.rs1_val, i.rs2_val, i.rd_val); } } -static void TestFCVT(RISCVEmulatorTester *tester, DecodeResult inst) { - std::vector FloatToInt = {"FCVT_W_S", "FCVT_WU_S"}; - std::vector IntToFloat = {"FCVT_S_W", "FCVT_S_WU"}; - - uint32_t rd = DecodeRD(inst.inst); - uint32_t rs1 = DecodeRS1(inst.inst); - - for (auto i : FloatToInt) { - if (inst.pattern.name == i) { - APFloat apf_rs1_val(12.0f); - tester->fpr.fpr[rs1] = apf_rs1_val.bitcastToAPInt().getZExtValue(); - ASSERT_TRUE(tester->Execute(inst, false)); - ASSERT_EQ(tester->gpr.gpr[rd], uint64_t(12)); - return; - } +TEST_F(RISCVEmulatorTester, TestDoubleInst) { + std::vector tests = { + {0x221F253, "FADD_D", 0.5, 0.5, 1.0}, + {0xA21F253, "FSUB_D", 1.0, 0.5, 0.5}, + {0x1221F253, "FMUL_D", 0.5, 0.5, 0.25}, + {0x1A21F253, "FDIV_D", 0.1, 0.1, 1.0}, + {0x22218253, "FSGNJ_D", 0.5, 0.2, 0.5}, + {0x22219253, "FSGNJN_D", 0.5, -1.0, 0.5}, + {0x2221A253, "FSGNJX_D", -0.5, -0.5, 0.5}, + {0x2221A253, "FSGNJX_D", -0.5, 0.5, -0.5}, + {0x2A218253, "FMIN_D", -0.5, 0.5, -0.5}, + {0x2A218253, "FMIN_D", -0.5, -0.6, -0.6}, + {0x2A218253, "FMIN_D", 0.5, 0.6, 0.5}, + {0x2A219253, "FMAX_D", -0.5, -0.6, -0.5}, + {0x2A219253, "FMAX_D", 0.5, 0.6, 0.6}, + {0x2A219253, "FMAX_D", 0.5, -0.6, 0.5}, + {0xA221A253, "FEQ_D", 0.5, 0.5, 1}, + {0xA221A253, "FEQ_D", 0.5, -0.5, 0}, + {0xA221A253, "FEQ_D", -0.5, 0.5, 0}, + {0xA221A253, "FEQ_D", 0.4, 0.5, 0}, + {0xA2219253, "FLT_D", 0.4, 0.5, 1}, + {0xA2219253, "FLT_D", 0.5, 0.5, 0}, + {0xA2218253, "FLE_D", 0.4, 0.5, 1}, + {0xA2218253, "FLE_D", 0.5, 0.5, 1}, + {0x4221F243, "FMADD_D", 0.5, 0.5, 0.75}, + {0x4221F247, "FMSUB_D", 0.5, 0.5, -0.25}, + {0x4221F24B, "FNMSUB_D", 0.5, 0.5, 0.25}, + {0x4221F24F, "FNMADD_D", 0.5, 0.5, -0.75}, + }; + for (auto i : tests) { + auto decode = this->Decode(i.inst); + ASSERT_TRUE(decode.has_value()); + std::string name = decode->pattern.name; + ASSERT_EQ(name, i.name); + TestF_D_CalInst(this, decode.value(), i.rs1_val, i.rs2_val, i.rd_val); } +} - for (auto i : IntToFloat) { - if (inst.pattern.name == i) { - tester->gpr.gpr[rs1] = 12; - ASSERT_TRUE(tester->Execute(inst, false)); - APInt apInt(32, tester->fpr.fpr[rd]); - APFloat rd_val(apInt.bitsToFloat()); - ASSERT_EQ(rd_val.convertToFloat(), 12.0f); - return; - } - } +template +static void TestInverse(RISCVEmulatorTester *tester, uint32_t f_reg, + uint32_t x_reg, DecodeResult f2i, DecodeResult i2f, + APFloat apf_val) { + uint64_t exp_x; + if constexpr (std::is_same_v) + exp_x = uint64_t(apf_val.convertToFloat()); + if constexpr (std::is_same_v) + exp_x = uint64_t(apf_val.convertToDouble()); + T exp_f = T(exp_x); + + // convert float/double to int. + tester->fpr.fpr[f_reg] = apf_val.bitcastToAPInt().getZExtValue(); + ASSERT_TRUE(tester->Execute(f2i, false)); + ASSERT_EQ(tester->gpr.gpr[x_reg], exp_x); + + // then convert int to float/double back. + ASSERT_TRUE(tester->Execute(i2f, false)); + ASSERT_EQ(tester->fpr.fpr[f_reg], + APFloat(exp_f).bitcastToAPInt().getZExtValue()); } struct FCVTInst { - uint32_t inst; - std::string name; + uint32_t f2i; + uint32_t i2f; + APFloat data; + bool isDouble; }; -TEST_F(RISCVEmulatorTester, TestFCVTInst) { - std::vector tests = { - {0xC001F253, "FCVT_W_S"}, {0xC011F253, "FCVT_WU_S"}, - {0xD001F253, "FCVT_S_W"}, {0xD011F253, "FCVT_S_WU"}, - {0xC021F253, "FCVT_L_S"}, {0xC031F253, "FCVT_LU_S"}, - {0xD021F253, "FCVT_S_L"}, {0xD031F253, "FCVT_S_LU"}, +TEST_F(RISCVEmulatorTester, TestFCVT) { + std::vector tests{ + // FCVT_W_S and FCVT_S_W + {0xC000F0D3, 0xD000F0D3, APFloat(12.0f), false}, + // FCVT_WU_S and FCVT_S_WU + {0xC010F0D3, 0xD010F0D3, APFloat(12.0f), false}, + // FCVT_L_S and FCVT_S_L + {0xC020F0D3, 0xD020F0D3, APFloat(12.0f), false}, + // FCVT_LU_S and FCVT_S_LU + {0xC030F0D3, 0xD030F0D3, APFloat(12.0f), false}, + // FCVT_W_D and FCVT_D_W + {0xC200F0D3, 0xD200F0D3, APFloat(12.0), true}, + // FCVT_WU_D and FCVT_D_WU + {0xC210F0D3, 0xD210F0D3, APFloat(12.0), true}, + // FCVT_L_D and FCVT_D_L + {0xC220F0D3, 0xD220F0D3, APFloat(12.0), true}, + // FCVT_LU_D and FCVT_D_LU + {0xC230F0D3, 0xD230F0D3, APFloat(12.0), true}, }; for (auto i : tests) { - auto decode = this->Decode(i.inst); - ASSERT_TRUE(decode.has_value()); - std::string name = decode->pattern.name; - ASSERT_EQ(name, i.name); - TestFCVT(this, decode.value()); + auto f2i = this->Decode(i.f2i); + auto i2f = this->Decode(i.i2f); + ASSERT_TRUE(f2i.has_value()); + ASSERT_TRUE(i2f.has_value()); + uint32_t f_reg = DecodeRS1((*f2i).inst); + uint32_t x_reg = DecodeRS1((*i2f).inst); + if (i.isDouble) + TestInverse(this, f_reg, x_reg, *f2i, *i2f, i.data); + else + TestInverse(this, f_reg, x_reg, *f2i, *i2f, i.data); } } +TEST_F(RISCVEmulatorTester, TestFDInverse) { + // FCVT_S_D + auto d2f = this->Decode(0x4010F0D3); + // FCVT_S_D + auto f2d = this->Decode(0x4200F0D3); + ASSERT_TRUE(d2f.has_value()); + ASSERT_TRUE(f2d.has_value()); + auto data = APFloat(12.0); + uint32_t reg = DecodeRS1((*d2f).inst); + float exp_f = 12.0f; + double exp_d = 12.0; + + // double to float + this->fpr.fpr[reg] = data.bitcastToAPInt().getZExtValue(); + ASSERT_TRUE(this->Execute(*d2f, false)); + ASSERT_EQ(this->fpr.fpr[reg], APFloat(exp_f).bitcastToAPInt().getZExtValue()); + + // float to double + ASSERT_TRUE(this->Execute(*f2d, false)); + ASSERT_EQ(this->fpr.fpr[reg], APFloat(exp_d).bitcastToAPInt().getZExtValue()); +} + TEST_F(RISCVEmulatorTester, TestFloatLSInst) { uint32_t FLWInst = 0x1A207; // imm = 0 uint32_t FSWInst = 0x21A827; // imm = 16 @@ -644,30 +735,82 @@ ASSERT_EQ(*(uint32_t *)(this->memory + 16), bits); } +TEST_F(RISCVEmulatorTester, TestDoubleLSInst) { + uint32_t FLDInst = 0x1B207; // imm = 0 + uint32_t FSDInst = 0x21B827; // imm = 16 + + APFloat apf(12.0); + uint64_t bits = apf.bitcastToAPInt().getZExtValue(); + + *(uint64_t *)this->memory = bits; + auto decode = this->Decode(FLDInst); + ASSERT_TRUE(decode.has_value()); + std::string name = decode->pattern.name; + ASSERT_EQ(name, "FLD"); + ASSERT_TRUE(this->Execute(decode.value(), false)); + ASSERT_EQ(this->fpr.fpr[DecodeRD(FLDInst)], bits); + + this->fpr.fpr[DecodeRS2(FSDInst)] = bits; + decode = this->Decode(FSDInst); + ASSERT_TRUE(decode.has_value()); + name = decode->pattern.name; + ASSERT_EQ(name, "FSD"); + ASSERT_TRUE(this->Execute(decode.value(), false)); + ASSERT_EQ(*(uint64_t *)(this->memory + 16), bits); +} + TEST_F(RISCVEmulatorTester, TestFMV_X_WInst) { auto FMV_X_WInst = 0xE0018253; APFloat apf(12.0f); - auto bits = NanBoxing(apf.bitcastToAPInt().getZExtValue()); - this->fpr.fpr[DecodeRS1(FMV_X_WInst)] = bits; + auto exp_bits = apf.bitcastToAPInt().getZExtValue(); + this->fpr.fpr[DecodeRS1(FMV_X_WInst)] = NanBoxing(exp_bits); auto decode = this->Decode(FMV_X_WInst); ASSERT_TRUE(decode.has_value()); std::string name = decode->pattern.name; ASSERT_EQ(name, "FMV_X_W"); ASSERT_TRUE(this->Execute(decode.value(), false)); - ASSERT_EQ(this->gpr.gpr[DecodeRD(FMV_X_WInst)], bits); + ASSERT_EQ(this->gpr.gpr[DecodeRD(FMV_X_WInst)], exp_bits); +} + +TEST_F(RISCVEmulatorTester, TestFMV_X_DInst) { + auto FMV_X_DInst = 0xE2018253; + + APFloat apf(12.0); + auto exp_bits = apf.bitcastToAPInt().getZExtValue(); + this->fpr.fpr[DecodeRS1(FMV_X_DInst)] = exp_bits; + auto decode = this->Decode(FMV_X_DInst); + ASSERT_TRUE(decode.has_value()); + std::string name = decode->pattern.name; + ASSERT_EQ(name, "FMV_X_D"); + ASSERT_TRUE(this->Execute(decode.value(), false)); + ASSERT_EQ(this->gpr.gpr[DecodeRD(FMV_X_DInst)], exp_bits); } TEST_F(RISCVEmulatorTester, TestFMV_W_XInst) { auto FMV_W_XInst = 0xF0018253; APFloat apf(12.0f); - uint64_t bits = NanUnBoxing(apf.bitcastToAPInt().getZExtValue()); - this->gpr.gpr[DecodeRS1(FMV_W_XInst)] = bits; + uint64_t exp_bits = NanUnBoxing(apf.bitcastToAPInt().getZExtValue()); + this->gpr.gpr[DecodeRS1(FMV_W_XInst)] = exp_bits; auto decode = this->Decode(FMV_W_XInst); ASSERT_TRUE(decode.has_value()); std::string name = decode->pattern.name; ASSERT_EQ(name, "FMV_W_X"); ASSERT_TRUE(this->Execute(decode.value(), false)); - ASSERT_EQ(this->fpr.fpr[DecodeRD(FMV_W_XInst)], bits); + ASSERT_EQ(this->fpr.fpr[DecodeRD(FMV_W_XInst)], exp_bits); +} + +TEST_F(RISCVEmulatorTester, TestFMV_D_XInst) { + auto FMV_D_XInst = 0xF2018253; + + APFloat apf(12.0); + uint64_t bits = apf.bitcastToAPInt().getZExtValue(); + this->gpr.gpr[DecodeRS1(FMV_D_XInst)] = bits; + auto decode = this->Decode(FMV_D_XInst); + ASSERT_TRUE(decode.has_value()); + std::string name = decode->pattern.name; + ASSERT_EQ(name, "FMV_D_X"); + ASSERT_TRUE(this->Execute(decode.value(), false)); + ASSERT_EQ(this->fpr.fpr[DecodeRD(FMV_D_XInst)], bits); }