diff --git a/llvm/include/llvm/MC/MCInst.h b/llvm/include/llvm/MC/MCInst.h --- a/llvm/include/llvm/MC/MCInst.h +++ b/llvm/include/llvm/MC/MCInst.h @@ -17,6 +17,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/bit.h" #include "llvm/Support/SMLoc.h" #include #include @@ -33,29 +34,32 @@ /// This is a simple discriminated union. class MCOperand { enum MachineOperandType : unsigned char { - kInvalid, ///< Uninitialized. - kRegister, ///< Register operand. - kImmediate, ///< Immediate operand. - kFPImmediate, ///< Floating-point immediate operand. - kExpr, ///< Relocatable immediate operand. - kInst ///< Sub-instruction operand. + kInvalid, ///< Uninitialized. + kRegister, ///< Register operand. + kImmediate, ///< Immediate operand. + kSFPImmediate, ///< Single-floating-point immediate operand. + kFPImmediate, ///< Floating-point immediate operand. + kExpr, ///< Relocatable immediate operand. + kInst ///< Sub-instruction operand. }; MachineOperandType Kind = kInvalid; union { unsigned RegVal; int64_t ImmVal; - double FPImmVal; + uint32_t SFPImmVal; + uint64_t FPImmVal; const MCExpr *ExprVal; const MCInst *InstVal; }; public: - MCOperand() : FPImmVal(0.0) {} + MCOperand() : FPImmVal(0) {} bool isValid() const { return Kind != kInvalid; } bool isReg() const { return Kind == kRegister; } bool isImm() const { return Kind == kImmediate; } + bool isSFPImm() const { return Kind == kSFPImmediate; } bool isFPImm() const { return Kind == kFPImmediate; } bool isExpr() const { return Kind == kExpr; } bool isInst() const { return Kind == kInst; } @@ -82,12 +86,22 @@ ImmVal = Val; } - double getFPImm() const { + uint32_t getSFPImm() const { + assert(isSFPImm() && "This is not an SFP immediate"); + return SFPImmVal; + } + + void setSFPImm(uint32_t Val) { + assert(isSFPImm() && "This is not an SFP immediate"); + SFPImmVal = Val; + } + + uint64_t getFPImm() const { assert(isFPImm() && "This is not an FP immediate"); return FPImmVal; } - void setFPImm(double Val) { + void setFPImm(uint64_t Val) { assert(isFPImm() && "This is not an FP immediate"); FPImmVal = Val; } @@ -126,7 +140,14 @@ return Op; } - static MCOperand createFPImm(double Val) { + static MCOperand createSFPImm(uint32_t Val) { + MCOperand Op; + Op.Kind = kSFPImmediate; + Op.SFPImmVal = Val; + return Op; + } + + static MCOperand createFPImm(uint64_t Val) { MCOperand Op; Op.Kind = kFPImmediate; Op.FPImmVal = Val; diff --git a/llvm/include/llvm/MC/MCInstBuilder.h b/llvm/include/llvm/MC/MCInstBuilder.h --- a/llvm/include/llvm/MC/MCInstBuilder.h +++ b/llvm/include/llvm/MC/MCInstBuilder.h @@ -39,8 +39,14 @@ return *this; } + /// Add a new single floating point immediate operand. + MCInstBuilder &addSFPImm(uint32_t Val) { + Inst.addOperand(MCOperand::createSFPImm(Val)); + return *this; + } + /// Add a new floating point immediate operand. - MCInstBuilder &addFPImm(double Val) { + MCInstBuilder &addFPImm(uint64_t Val) { Inst.addOperand(MCOperand::createFPImm(Val)); return *this; } diff --git a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp --- a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp +++ b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp @@ -53,7 +53,7 @@ }; struct FltOp { - double Val; + uint64_t Val; }; struct SymOp { @@ -123,7 +123,15 @@ llvm_unreachable("Should be integer immediate or symbol!"); } - void addFPImmOperands(MCInst &Inst, unsigned N) const { + void addFPImmf32Operands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + if (Kind == Float) + Inst.addOperand(MCOperand::createSFPImm(static_cast(Flt.Val))); + else + llvm_unreachable("Should be float immediate!"); + } + + void addFPImmf64Operands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); if (Kind == Float) Inst.addOperand(MCOperand::createFPImm(Flt.Val)); @@ -371,7 +379,7 @@ Val = -Val; Operands.push_back(std::make_unique( WebAssemblyOperand::Float, Flt.getLoc(), Flt.getEndLoc(), - WebAssemblyOperand::FltOp{Val})); + WebAssemblyOperand::FltOp{bit_cast(Val)})); Parser.Lex(); return false; } @@ -393,7 +401,7 @@ Val = -Val; Operands.push_back(std::make_unique( WebAssemblyOperand::Float, Flt.getLoc(), Flt.getEndLoc(), - WebAssemblyOperand::FltOp{Val})); + WebAssemblyOperand::FltOp{bit_cast(Val)})); Parser.Lex(); return false; } diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp @@ -236,17 +236,10 @@ O << '='; } else if (Op.isImm()) { O << Op.getImm(); + } else if (Op.isSFPImm()) { + O << ::toString(APFloat(bit_cast(Op.getSFPImm()))); } else if (Op.isFPImm()) { - const MCInstrDesc &Desc = MII.get(MI->getOpcode()); - const MCOperandInfo &Info = Desc.OpInfo[OpNo]; - if (Info.OperandType == WebAssembly::OPERAND_F32IMM) { - // 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_F64IMM); - O << ::toString(APFloat(Op.getFPImm())); - } + O << ::toString(APFloat(bit_cast(Op.getFPImm()))); } else { assert(Op.isExpr() && "unknown operand kind in printOperand"); // call_indirect instructions have a TYPEINDEX operand that we print diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp @@ -125,19 +125,12 @@ encodeULEB128(uint64_t(MO.getImm()), OS); } + } else if (MO.isSFPImm()) { + uint32_t F = MO.getSFPImm(); + support::endian::write(OS, F, support::little); } else if (MO.isFPImm()) { - const MCOperandInfo &Info = Desc.OpInfo[I]; - if (Info.OperandType == WebAssembly::OPERAND_F32IMM) { - // TODO: MC converts all floating point immediate operands to double. - // This is fine for numeric values, but may cause NaNs to change bits. - auto F = float(MO.getFPImm()); - support::endian::write(OS, F, support::little); - } else { - assert(Info.OperandType == WebAssembly::OPERAND_F64IMM); - double D = MO.getFPImm(); - support::endian::write(OS, D, support::little); - } - + uint64_t D = MO.getFPImm(); + support::endian::write(OS, D, support::little); } else if (MO.isExpr()) { const MCOperandInfo &Info = Desc.OpInfo[I]; llvm::MCFixupKind FixupKind; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td @@ -116,13 +116,13 @@ // Default Operand has AsmOperandClass "Imm" which is for integers (and // symbols), so specialize one for floats: -def FPImmAsmOperand : AsmOperandClass { - let Name = "FPImm"; +class FPImmAsmOperand : AsmOperandClass { + let Name = "FPImm" # ty; let PredicateMethod = "isFPImm"; } class FPOperand : Operand { - AsmOperandClass ParserMatchClass = FPImmAsmOperand; + AsmOperandClass ParserMatchClass = FPImmAsmOperand; } let OperandNamespace = "WebAssembly" in { diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp @@ -275,13 +275,12 @@ break; } case MachineOperand::MO_FPImmediate: { - // TODO: MC converts all floating point immediate operands to double. - // This is fine for numeric values, but may cause NaNs to change bits. const ConstantFP *Imm = MO.getFPImm(); + const uint64_t BitPattern = Imm->getValueAPF().bitcastToAPInt().getZExtValue(); if (Imm->getType()->isFloatTy()) - MCOp = MCOperand::createFPImm(Imm->getValueAPF().convertToFloat()); + MCOp = MCOperand::createSFPImm(static_cast(BitPattern)); else if (Imm->getType()->isDoubleTy()) - MCOp = MCOperand::createFPImm(Imm->getValueAPF().convertToDouble()); + MCOp = MCOperand::createFPImm(BitPattern); else llvm_unreachable("unknown floating point immediate type"); break; diff --git a/llvm/test/CodeGen/WebAssembly/immediates.ll b/llvm/test/CodeGen/WebAssembly/immediates.ll --- a/llvm/test/CodeGen/WebAssembly/immediates.ll +++ b/llvm/test/CodeGen/WebAssembly/immediates.ll @@ -146,12 +146,9 @@ 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: .functype custom_nans_f32 () -> (f32){{$}} -; CHECK-NEXT: f32.const $push[[NUM:[0-9]+]]=, -nan:0x6bcdef{{$}} +; CHECK-NEXT: f32.const $push[[NUM:[0-9]+]]=, -nan:0x2bcdef{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define float @custom_nans_f32() { ret float 0xFFF579BDE0000000 diff --git a/llvm/test/CodeGen/WebAssembly/snan_literal.ll b/llvm/test/CodeGen/WebAssembly/snan_literal.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/snan_literal.ll @@ -0,0 +1,29 @@ +; RUN: llc < %s --filetype=obj | llvm-objdump -d - | FileCheck %s +target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target triple = "wasm32-unknown-unknown" + +define float @float_sNaN() #0 { +entry: +; CHECK: 00 00 a0 7f + ret float 0x7ff4000000000000 +} + +define float @float_qNaN() #0 { +entry: +; CHECK: 00 00 e0 7f + ret float 0x7ffc000000000000 +} + + +define double @double_sNaN() #0 { +entry: +; CHECK: 00 00 00 00 00 00 f4 7f + ret double 0x7ff4000000000000 +} + +define double @double_qNaN() #0 { +entry: +; CHECK: 00 00 00 00 00 00 fc 7f + ret double 0x7ffc000000000000 +} +