forked from OSchip/llvm-project
[WebAssembly] Support single-floating-point immediate value
As mentioned in TODO comment, casting double to float causes NaNs to change bits. To avoid the change, this patch adds support for single-floating-point immediate value on MachineCode. Patch by Yuta Saito. Differential Revision: https://reviews.llvm.org/D77384
This commit is contained in:
parent
4cb7d03481
commit
698c6b0a09
|
@ -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 <cassert>
|
||||
#include <cstddef>
|
||||
|
@ -33,30 +34,33 @@ class raw_ostream;
|
|||
/// 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.
|
||||
kDFPImmediate, ///< Double-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 isFPImm() const { return Kind == kFPImmediate; }
|
||||
bool isSFPImm() const { return Kind == kSFPImmediate; }
|
||||
bool isDFPImm() const { return Kind == kDFPImmediate; }
|
||||
bool isExpr() const { return Kind == kExpr; }
|
||||
bool isInst() const { return Kind == kInst; }
|
||||
|
||||
|
@ -82,13 +86,23 @@ public:
|
|||
ImmVal = Val;
|
||||
}
|
||||
|
||||
double getFPImm() const {
|
||||
assert(isFPImm() && "This is not an FP immediate");
|
||||
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 getDFPImm() const {
|
||||
assert(isDFPImm() && "This is not an FP immediate");
|
||||
return FPImmVal;
|
||||
}
|
||||
|
||||
void setFPImm(double Val) {
|
||||
assert(isFPImm() && "This is not an FP immediate");
|
||||
void setDFPImm(uint64_t Val) {
|
||||
assert(isDFPImm() && "This is not an FP immediate");
|
||||
FPImmVal = Val;
|
||||
}
|
||||
|
||||
|
@ -126,9 +140,16 @@ public:
|
|||
return Op;
|
||||
}
|
||||
|
||||
static MCOperand createFPImm(double Val) {
|
||||
static MCOperand createSFPImm(uint32_t Val) {
|
||||
MCOperand Op;
|
||||
Op.Kind = kFPImmediate;
|
||||
Op.Kind = kSFPImmediate;
|
||||
Op.SFPImmVal = Val;
|
||||
return Op;
|
||||
}
|
||||
|
||||
static MCOperand createDFPImm(uint64_t Val) {
|
||||
MCOperand Op;
|
||||
Op.Kind = kDFPImmediate;
|
||||
Op.FPImmVal = Val;
|
||||
return Op;
|
||||
}
|
||||
|
|
|
@ -39,9 +39,15 @@ public:
|
|||
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) {
|
||||
Inst.addOperand(MCOperand::createFPImm(Val));
|
||||
MCInstBuilder &addDFPImm(uint64_t Val) {
|
||||
Inst.addOperand(MCOperand::createDFPImm(Val));
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,8 +25,10 @@ void MCOperand::print(raw_ostream &OS) const {
|
|||
OS << "Reg:" << getReg();
|
||||
else if (isImm())
|
||||
OS << "Imm:" << getImm();
|
||||
else if (isFPImm())
|
||||
OS << "FPImm:" << getFPImm();
|
||||
else if (isSFPImm())
|
||||
OS << "SFPImm:" << bit_cast<float>(getSFPImm());
|
||||
else if (isDFPImm())
|
||||
OS << "DFPImm:" << bit_cast<double>(getDFPImm());
|
||||
else if (isExpr()) {
|
||||
OS << "Expr:(" << *getExpr() << ")";
|
||||
} else if (isInst()) {
|
||||
|
|
|
@ -1164,8 +1164,8 @@ void AArch64InstPrinter::printFPImmOperand(const MCInst *MI, unsigned OpNum,
|
|||
const MCSubtargetInfo &STI,
|
||||
raw_ostream &O) {
|
||||
const MCOperand &MO = MI->getOperand(OpNum);
|
||||
float FPImm =
|
||||
MO.isFPImm() ? MO.getFPImm() : AArch64_AM::getFPImmFloat(MO.getImm());
|
||||
float FPImm = MO.isDFPImm() ? bit_cast<double>(MO.getDFPImm())
|
||||
: AArch64_AM::getFPImmFloat(MO.getImm());
|
||||
|
||||
// 8 decimal places are enough to perfectly represent permitted floats.
|
||||
O << format("#%.8f", FPImm);
|
||||
|
|
|
@ -656,18 +656,19 @@ void AMDGPUInstPrinter::printOperand(const MCInst *MI, unsigned OpNo,
|
|||
// custom printer.
|
||||
llvm_unreachable("unexpected immediate operand type");
|
||||
}
|
||||
} else if (Op.isFPImm()) {
|
||||
} else if (Op.isDFPImm()) {
|
||||
double Value = bit_cast<double>(Op.getDFPImm());
|
||||
// We special case 0.0 because otherwise it will be printed as an integer.
|
||||
if (Op.getFPImm() == 0.0)
|
||||
if (Value == 0.0)
|
||||
O << "0.0";
|
||||
else {
|
||||
const MCInstrDesc &Desc = MII.get(MI->getOpcode());
|
||||
int RCID = Desc.OpInfo[OpNo].RegClass;
|
||||
unsigned RCBits = AMDGPU::getRegBitWidth(MRI.getRegClass(RCID));
|
||||
if (RCBits == 32)
|
||||
printImmediate32(FloatToBits(Op.getFPImm()), STI, O);
|
||||
printImmediate32(FloatToBits(Value), STI, O);
|
||||
else if (RCBits == 64)
|
||||
printImmediate64(DoubleToBits(Op.getFPImm()), STI, O);
|
||||
printImmediate64(DoubleToBits(Value), STI, O);
|
||||
else
|
||||
llvm_unreachable("Invalid register class size");
|
||||
}
|
||||
|
@ -727,7 +728,7 @@ void AMDGPUInstPrinter::printOperandAndFPInputMods(const MCInst *MI,
|
|||
if (OpNo + 1 < MI->getNumOperands() &&
|
||||
(InputModifiers & SISrcMods::ABS) == 0) {
|
||||
const MCOperand &Op = MI->getOperand(OpNo + 1);
|
||||
NegMnemo = Op.isImm() || Op.isFPImm();
|
||||
NegMnemo = Op.isImm() || Op.isDFPImm();
|
||||
}
|
||||
if (NegMnemo) {
|
||||
O << "neg(";
|
||||
|
@ -1560,12 +1561,12 @@ void R600InstPrinter::printOperand(const MCInst *MI, unsigned OpNo,
|
|||
}
|
||||
} else if (Op.isImm()) {
|
||||
O << Op.getImm();
|
||||
} else if (Op.isFPImm()) {
|
||||
} else if (Op.isDFPImm()) {
|
||||
// We special case 0.0 because otherwise it will be printed as an integer.
|
||||
if (Op.getFPImm() == 0.0)
|
||||
if (Op.getDFPImm() == 0.0)
|
||||
O << "0.0";
|
||||
else {
|
||||
O << Op.getFPImm();
|
||||
O << bit_cast<double>(Op.getDFPImm());
|
||||
}
|
||||
} else if (Op.isExpr()) {
|
||||
const MCExpr *Exp = Op.getExpr();
|
||||
|
|
|
@ -219,7 +219,7 @@ uint32_t SIMCCodeEmitter::getLitEncoding(const MCOperand &MO,
|
|||
Imm = C->getValue();
|
||||
} else {
|
||||
|
||||
assert(!MO.isFPImm());
|
||||
assert(!MO.isDFPImm());
|
||||
|
||||
if (!MO.isImm())
|
||||
return ~0;
|
||||
|
|
|
@ -110,7 +110,7 @@ bool ARMAsmPrinter::lowerOperand(const MachineOperand &MO,
|
|||
APFloat Val = MO.getFPImm()->getValueAPF();
|
||||
bool ignored;
|
||||
Val.convert(APFloat::IEEEdouble(), APFloat::rmTowardZero, &ignored);
|
||||
MCOp = MCOperand::createFPImm(Val.convertToDouble());
|
||||
MCOp = MCOperand::createDFPImm(Val.convertToDouble());
|
||||
break;
|
||||
}
|
||||
case MachineOperand::MO_RegisterMask:
|
||||
|
|
|
@ -576,9 +576,11 @@ getMachineOpValue(const MCInst &MI, const MCOperand &MO,
|
|||
}
|
||||
} else if (MO.isImm()) {
|
||||
return static_cast<unsigned>(MO.getImm());
|
||||
} else if (MO.isFPImm()) {
|
||||
return static_cast<unsigned>(APFloat(MO.getFPImm())
|
||||
.bitcastToAPInt().getHiBits(32).getLimitedValue());
|
||||
} else if (MO.isDFPImm()) {
|
||||
return static_cast<unsigned>(APFloat(bit_cast<double>(MO.getDFPImm()))
|
||||
.bitcastToAPInt()
|
||||
.getHiBits(32)
|
||||
.getLimitedValue());
|
||||
}
|
||||
|
||||
llvm_unreachable("Unable to encode MCOperand!");
|
||||
|
|
|
@ -254,11 +254,8 @@ unsigned AVRMCCodeEmitter::getMachineOpValue(const MCInst &MI,
|
|||
if (MO.isReg()) return Ctx.getRegisterInfo()->getEncodingValue(MO.getReg());
|
||||
if (MO.isImm()) return static_cast<unsigned>(MO.getImm());
|
||||
|
||||
if (MO.isFPImm())
|
||||
return static_cast<unsigned>(APFloat(MO.getFPImm())
|
||||
.bitcastToAPInt()
|
||||
.getHiBits(32)
|
||||
.getLimitedValue());
|
||||
if (MO.isDFPImm())
|
||||
return static_cast<unsigned>(bit_cast<double>(MO.getDFPImm()));
|
||||
|
||||
// MO must be an Expr.
|
||||
assert(MO.isExpr());
|
||||
|
|
|
@ -740,9 +740,8 @@ getMachineOpValue(const MCInst &MI, const MCOperand &MO,
|
|||
return RegNo;
|
||||
} else if (MO.isImm()) {
|
||||
return static_cast<unsigned>(MO.getImm());
|
||||
} else if (MO.isFPImm()) {
|
||||
return static_cast<unsigned>(APFloat(MO.getFPImm())
|
||||
.bitcastToAPInt().getHiBits(32).getLimitedValue());
|
||||
} else if (MO.isDFPImm()) {
|
||||
return static_cast<unsigned>(bit_cast<double>(MO.getDFPImm()));
|
||||
}
|
||||
// MO must be an Expr.
|
||||
assert(MO.isExpr());
|
||||
|
|
|
@ -125,10 +125,19 @@ struct WebAssemblyOperand : public MCParsedAsmOperand {
|
|||
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::createFPImm(Flt.Val));
|
||||
Inst.addOperand(
|
||||
MCOperand::createSFPImm(bit_cast<uint32_t>(float(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::createDFPImm(bit_cast<uint64_t>(Flt.Val)));
|
||||
else
|
||||
llvm_unreachable("Should be float immediate!");
|
||||
}
|
||||
|
|
|
@ -114,7 +114,7 @@ bool parseImmediate(MCInst &MI, uint64_t &Size, ArrayRef<uint8_t> Bytes) {
|
|||
Bytes.data() + Size);
|
||||
Size += sizeof(T);
|
||||
if (std::is_floating_point<T>::value) {
|
||||
MI.addOperand(MCOperand::createFPImm(static_cast<double>(Val)));
|
||||
MI.addOperand(MCOperand::createDFPImm(static_cast<double>(Val)));
|
||||
} else {
|
||||
MI.addOperand(MCOperand::createImm(static_cast<int64_t>(Val)));
|
||||
}
|
||||
|
|
|
@ -235,17 +235,10 @@ void WebAssemblyInstPrinter::printOperand(const MCInst *MI, unsigned OpNo,
|
|||
O << '=';
|
||||
} else if (Op.isImm()) {
|
||||
O << Op.getImm();
|
||||
} 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()));
|
||||
}
|
||||
} else if (Op.isSFPImm()) {
|
||||
O << ::toString(APFloat(bit_cast<float>(Op.getSFPImm())));
|
||||
} else if (Op.isDFPImm()) {
|
||||
O << ::toString(APFloat(bit_cast<double>(Op.getDFPImm())));
|
||||
} else {
|
||||
assert(Op.isExpr() && "unknown operand kind in printOperand");
|
||||
// call_indirect instructions have a TYPEINDEX operand that we print
|
||||
|
|
|
@ -130,19 +130,12 @@ void WebAssemblyMCCodeEmitter::encodeInstruction(
|
|||
encodeULEB128(uint64_t(MO.getImm()), OS);
|
||||
}
|
||||
|
||||
} 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<float>(OS, F, support::little);
|
||||
} else {
|
||||
assert(Info.OperandType == WebAssembly::OPERAND_F64IMM);
|
||||
double D = MO.getFPImm();
|
||||
support::endian::write<double>(OS, D, support::little);
|
||||
}
|
||||
|
||||
} else if (MO.isSFPImm()) {
|
||||
uint32_t F = MO.getSFPImm();
|
||||
support::endian::write<uint32_t>(OS, F, support::little);
|
||||
} else if (MO.isDFPImm()) {
|
||||
uint64_t D = MO.getDFPImm();
|
||||
support::endian::write<uint64_t>(OS, D, support::little);
|
||||
} else if (MO.isExpr()) {
|
||||
const MCOperandInfo &Info = Desc.OpInfo[I];
|
||||
llvm::MCFixupKind FixupKind;
|
||||
|
|
|
@ -117,13 +117,13 @@ def WebAssemblycatch : SDNode<"WebAssemblyISD::CATCH", SDT_WebAssemblyCatch,
|
|||
|
||||
// Default Operand has AsmOperandClass "Imm" which is for integers (and
|
||||
// symbols), so specialize one for floats:
|
||||
def FPImmAsmOperand : AsmOperandClass {
|
||||
let Name = "FPImm";
|
||||
class FPImmAsmOperand<ValueType ty> : AsmOperandClass {
|
||||
let Name = "FPImm" # ty;
|
||||
let PredicateMethod = "isFPImm";
|
||||
}
|
||||
|
||||
class FPOperand<ValueType ty> : Operand<ty> {
|
||||
AsmOperandClass ParserMatchClass = FPImmAsmOperand;
|
||||
AsmOperandClass ParserMatchClass = FPImmAsmOperand<ty>;
|
||||
}
|
||||
|
||||
let OperandNamespace = "WebAssembly" in {
|
||||
|
|
|
@ -285,13 +285,13 @@ void WebAssemblyMCInstLower::lower(const MachineInstr *MI,
|
|||
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<uint32_t>(BitPattern));
|
||||
else if (Imm->getType()->isDoubleTy())
|
||||
MCOp = MCOperand::createFPImm(Imm->getValueAPF().convertToDouble());
|
||||
MCOp = MCOperand::createDFPImm(BitPattern);
|
||||
else
|
||||
llvm_unreachable("unknown floating point immediate type");
|
||||
break;
|
||||
|
|
|
@ -146,12 +146,9 @@ 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: .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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
@ -134,8 +134,8 @@ private:
|
|||
OS << getRegName(MCOperand.getReg());
|
||||
} else if (MCOperand.isImm()) {
|
||||
serializeIntegerOperand(OS, MCOperand.getImm());
|
||||
} else if (MCOperand.isFPImm()) {
|
||||
serializeFPOperand(OS, MCOperand.getFPImm());
|
||||
} else if (MCOperand.isDFPImm()) {
|
||||
serializeFPOperand(OS, bit_cast<double>(MCOperand.getDFPImm()));
|
||||
} else {
|
||||
OS << kInvalidOperand;
|
||||
}
|
||||
|
@ -148,7 +148,7 @@ private:
|
|||
if (tryDeserializeIntegerOperand(String, IntValue))
|
||||
return MCOperand::createImm(IntValue);
|
||||
if (tryDeserializeFPOperand(String, DoubleValue))
|
||||
return MCOperand::createFPImm(DoubleValue);
|
||||
return MCOperand::createDFPImm(bit_cast<uint64_t>(DoubleValue));
|
||||
if (auto RegNo = getRegNo(String))
|
||||
return MCOperand::createReg(*RegNo);
|
||||
if (String != kInvalidOperand)
|
||||
|
|
|
@ -374,8 +374,10 @@ void DumpMCOperand(const MCRegisterInfo &MCRegisterInfo, const MCOperand &Op,
|
|||
OS << MCRegisterInfo.getName(Op.getReg());
|
||||
else if (Op.isImm())
|
||||
OS << Op.getImm();
|
||||
else if (Op.isFPImm())
|
||||
OS << Op.getFPImm();
|
||||
else if (Op.isDFPImm())
|
||||
OS << bit_cast<double>(Op.getDFPImm());
|
||||
else if (Op.isSFPImm())
|
||||
OS << bit_cast<float>(Op.getSFPImm());
|
||||
else if (Op.isExpr())
|
||||
OS << "Expr";
|
||||
else if (Op.isInst())
|
||||
|
|
|
@ -67,7 +67,7 @@ TEST(BenchmarkResultTest, WriteToAndReadFromDisk) {
|
|||
.addReg(X86::AL)
|
||||
.addReg(X86::AH)
|
||||
.addImm(123)
|
||||
.addFPImm(0.5));
|
||||
.addDFPImm(bit_cast<uint64_t>(0.5)));
|
||||
ToDisk.Key.Config = "config";
|
||||
ToDisk.Key.RegisterInitialValues = {
|
||||
RegisterValue{X86::AL, APInt(8, "-1", 10)},
|
||||
|
|
Loading…
Reference in New Issue