diff --git a/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp b/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp index 0a4227c38224..c0355aef0b35 100644 --- a/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp +++ b/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp @@ -110,7 +110,8 @@ MCDisassembler::DecodeStatus WebAssemblyDisassembler::getInstruction( MI.addOperand(MCOperand::createReg(Reg)); break; } - case WebAssembly::OPERAND_FPIMM: { + case WebAssembly::OPERAND_FP32IMM: + case WebAssembly::OPERAND_FP64IMM: { // TODO: MC converts all floating point immediate operands to double. // This is fine for numeric values, but may cause NaNs to change bits. if (Pos + sizeof(uint64_t) > Bytes.size()) diff --git a/llvm/lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.cpp b/llvm/lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.cpp index 2ecf9be23987..a58796c1ea82 100644 --- a/llvm/lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.cpp +++ b/llvm/lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.cpp @@ -110,14 +110,22 @@ void WebAssemblyInstPrinter::printInst(const MCInst *MI, raw_ostream &OS, } static std::string toString(const APFloat &FP) { + // Print NaNs with custom payloads specially. + if (FP.isNaN() && + !FP.bitwiseIsEqual(APFloat::getQNaN(FP.getSemantics())) && + !FP.bitwiseIsEqual(APFloat::getQNaN(FP.getSemantics(), /*Negative=*/true))) { + APInt AI = FP.bitcastToAPInt(); + return + std::string(AI.isNegative() ? "-" : "") + "nan:0x" + + utohexstr(AI.getZExtValue() & + (AI.getBitWidth() == 32 ? INT64_C(0x007fffff) : + INT64_C(0x000fffffffffffff)), + /*LowerCase=*/true); + } + + // Use C99's hexadecimal floating-point representation. static const size_t BufBytes = 128; char buf[BufBytes]; - if (FP.isNaN()) - assert((FP.bitwiseIsEqual(APFloat::getQNaN(FP.getSemantics())) || - FP.bitwiseIsEqual( - APFloat::getQNaN(FP.getSemantics(), /*Negative=*/true))) && - "convertToHexString handles neither SNaN nor NaN payloads"); - // Use C99's hexadecimal floating-point representation. auto Written = FP.convertToHexString( buf, /*hexDigits=*/0, /*upperCase=*/false, APFloat::rmNearestTiesToEven); (void)Written; @@ -157,10 +165,20 @@ void WebAssemblyInstPrinter::printOperand(const MCInst *MI, unsigned OpNo, // control flow stack, and it may be nice to pretty-print. O << Op.getImm(); } else if (Op.isFPImm()) { - assert((OpNo < MII.get(MI->getOpcode()).getNumOperands() || - MII.get(MI->getOpcode()).TSFlags == 0) && + const MCInstrDesc &Desc = MII.get(MI->getOpcode()); + assert(OpNo < Desc.getNumOperands() && + "Unexpected floating-point immediate as a non-fixed operand"); + assert(Desc.TSFlags == 0 && "WebAssembly variable_ops floating point ops don't use TSFlags"); - O << toString(APFloat(Op.getFPImm())); + const MCOperandInfo &Info = Desc.OpInfo[OpNo]; + if (Info.OperandType == WebAssembly::OPERAND_FP32IMM) { + // TODO: MC converts all floating point immediate operands to double. + // This is fine for numeric values, but may cause NaNs to change bits. + O << toString(APFloat(float(Op.getFPImm()))); + } else { + assert(Info.OperandType == WebAssembly::OPERAND_FP64IMM); + O << toString(APFloat(Op.getFPImm())); + } } else { assert((OpNo < MII.get(MI->getOpcode()).getNumOperands() || (MII.get(MI->getOpcode()).TSFlags & diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h index af970929bfaf..001bd7f1fc43 100644 --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h @@ -44,8 +44,10 @@ namespace WebAssembly { enum OperandType { /// Basic block label in a branch construct. OPERAND_BASIC_BLOCK = MCOI::OPERAND_FIRST_TARGET, - /// Floating-point immediate. - OPERAND_FPIMM, + /// 32-bit floating-point immediates. + OPERAND_FP32IMM, + /// 64-bit floating-point immediates. + OPERAND_FP64IMM, /// p2align immediate for load and store address alignment. OPERAND_P2ALIGN }; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td index 60e5d6ea6457..115e532b5a25 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td @@ -71,10 +71,11 @@ let OperandNamespace = "WebAssembly" in { let OperandType = "OPERAND_BASIC_BLOCK" in def bb_op : Operand; -let OperandType = "OPERAND_FPIMM" in { +let OperandType = "OPERAND_FP32IMM" in def f32imm_op : Operand; + +let OperandType = "OPERAND_FP64IMM" in def f64imm_op : Operand; -} // OperandType = "OPERAND_FPIMM" let OperandType = "OPERAND_P2ALIGN" in { def P2Align : Operand { diff --git a/llvm/test/CodeGen/WebAssembly/immediates.ll b/llvm/test/CodeGen/WebAssembly/immediates.ll index 735b386b4fc0..0ccfe0916f7a 100644 --- a/llvm/test/CodeGen/WebAssembly/immediates.ll +++ b/llvm/test/CodeGen/WebAssembly/immediates.ll @@ -133,6 +133,25 @@ define float @neginf_f32() { ret float 0xFFF0000000000000 } +; CHECK-LABEL: custom_nan_f32: +; CHECK-NEXT: .result f32{{$}} +; CHECK-NEXT: f32.const $push[[NUM:[0-9]+]]=, -nan:0x6bcdef{{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define float @custom_nan_f32() { + ret float 0xFFFD79BDE0000000 +} + +; TODO: LLVM's MC layer stores f32 operands as host doubles, requiring a +; conversion, so the bits of the NaN are not fully preserved. + +; CHECK-LABEL: custom_nans_f32: +; CHECK-NEXT: .result f32{{$}} +; CHECK-NEXT: f32.const $push[[NUM:[0-9]+]]=, -nan:0x6bcdef{{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define float @custom_nans_f32() { + ret float 0xFFF579BDE0000000 +} + ; CHECK-LABEL: negzero_f64: ; CHECK-NEXT: .result f64{{$}} ; CHECK-NEXT: f64.const $push[[NUM:[0-9]+]]=, -0x0p0{{$}} @@ -196,3 +215,19 @@ define double @inf_f64() { define double @neginf_f64() { ret double 0xFFF0000000000000 } + +; CHECK-LABEL: custom_nan_f64: +; CHECK-NEXT: .result f64{{$}} +; CHECK-NEXT: f64.const $push[[NUM:[0-9]+]]=, -nan:0xabcdef0123456{{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define double @custom_nan_f64() { + ret double 0xFFFABCDEF0123456 +} + +; CHECK-LABEL: custom_nans_f64: +; CHECK-NEXT: .result f64{{$}} +; CHECK-NEXT: f64.const $push[[NUM:[0-9]+]]=, -nan:0x2bcdef0123456{{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define double @custom_nans_f64() { + ret double 0xFFF2BCDEF0123456 +}