From bae1529bd5f16f28a6e84ab4acd8f07a934f0762 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Fri, 3 Apr 2020 06:52:01 +0000 Subject: [PATCH] [WebAssembly] Support single-floating-point literal As mentioned in TODO comment, casting double to float causes NaNs to change bits. To avoid the change, this patch adds a support for single float floating point literal value. --- llvm/include/llvm/MC/MCInst.h | 20 +++++++++++++ llvm/include/llvm/MC/MCInstBuilder.h | 6 ++++ .../MCTargetDesc/WebAssemblyInstPrinter.cpp | 13 ++------- .../MCTargetDesc/WebAssemblyMCCodeEmitter.cpp | 17 ++++------- .../WebAssembly/WebAssemblyMCInstLower.cpp | 4 +-- llvm/test/CodeGen/WebAssembly/snan_literal.ll | 29 +++++++++++++++++++ 6 files changed, 64 insertions(+), 25 deletions(-) create mode 100644 llvm/test/CodeGen/WebAssembly/snan_literal.ll diff --git a/llvm/include/llvm/MC/MCInst.h b/llvm/include/llvm/MC/MCInst.h index 360dbda58fcbd..50f847456c280 100644 --- a/llvm/include/llvm/MC/MCInst.h +++ b/llvm/include/llvm/MC/MCInst.h @@ -36,6 +36,7 @@ class MCOperand { 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. @@ -45,6 +46,7 @@ class MCOperand { union { unsigned RegVal; int64_t ImmVal; + float SFPImmVal; double FPImmVal; const MCExpr *ExprVal; const MCInst *InstVal; @@ -56,6 +58,7 @@ class MCOperand { 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,6 +85,16 @@ class MCOperand { ImmVal = Val; } + float getSFPImm() const { + assert(isSFPImm() && "This is not an SFP immediate"); + return SFPImmVal; + } + + void setSFPImm(float Val) { + assert(isSFPImm() && "This is not an SFP immediate"); + SFPImmVal = Val; + } + double getFPImm() const { assert(isFPImm() && "This is not an FP immediate"); return FPImmVal; @@ -126,6 +139,13 @@ class MCOperand { return Op; } + static MCOperand createSFPImm(float Val) { + MCOperand Op; + Op.Kind = kSFPImmediate; + Op.SFPImmVal = Val; + return Op; + } + static MCOperand createFPImm(double Val) { MCOperand Op; Op.Kind = kFPImmediate; diff --git a/llvm/include/llvm/MC/MCInstBuilder.h b/llvm/include/llvm/MC/MCInstBuilder.h index 0c8e01fdc412c..7bbed467f2c55 100644 --- a/llvm/include/llvm/MC/MCInstBuilder.h +++ b/llvm/include/llvm/MC/MCInstBuilder.h @@ -39,6 +39,12 @@ class MCInstBuilder { return *this; } + /// Add a new single floating point immediate operand. + MCInstBuilder &addSFPImm(float Val) { + Inst.addOperand(MCOperand::createSFPImm(Val)); + return *this; + } + /// Add a new floating point immediate operand. MCInstBuilder &addFPImm(double Val) { Inst.addOperand(MCOperand::createFPImm(Val)); diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp index b262e06e55e72..9d467e9b3c596 100644 --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp @@ -224,17 +224,10 @@ void WebAssemblyInstPrinter::printOperand(const MCInst *MI, unsigned OpNo, O << '='; } else if (Op.isImm()) { O << Op.getImm(); + } else if (Op.isSFPImm()) { + O << ::toString(APFloat(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(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 index 1a4c57e66d2fa..a29b2d0652f5a 100644 --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp @@ -125,19 +125,12 @@ void WebAssemblyMCCodeEmitter::encodeInstruction( encodeULEB128(uint64_t(MO.getImm()), OS); } + } else if (MO.isSFPImm()) { + auto 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); - } - + double 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/WebAssemblyMCInstLower.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp index 85e4d5a7d22f3..7e69ffd5ef7e9 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp @@ -291,11 +291,9 @@ void WebAssemblyMCInstLower::lower(const MachineInstr *MI, MCOp = MCOperand::createImm(MO.getImm()); 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(); if (Imm->getType()->isFloatTy()) - MCOp = MCOperand::createFPImm(Imm->getValueAPF().convertToFloat()); + MCOp = MCOperand::createSFPImm(Imm->getValueAPF().convertToFloat()); else if (Imm->getType()->isDoubleTy()) MCOp = MCOperand::createFPImm(Imm->getValueAPF().convertToDouble()); else diff --git a/llvm/test/CodeGen/WebAssembly/snan_literal.ll b/llvm/test/CodeGen/WebAssembly/snan_literal.ll new file mode 100644 index 0000000000000..133f76b7292cf --- /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 +} +