Index: include/llvm/ADT/APFloat.h =================================================================== --- include/llvm/ADT/APFloat.h +++ include/llvm/ADT/APFloat.h @@ -1119,6 +1119,21 @@ llvm_unreachable("Unexpected semantics"); } + /// We don't rely on operator== working on double values, as + /// it returns true for things that are clearly not equal, like -0.0 and 0.0. + /// As such, this method can be used to do an exact bit-for-bit comparison of + /// two floating point values. + /// + /// We leave the version with the double argument here because it's just so + /// convenient to write "2.0" and the like. Without this function we'd + /// have to duplicate its logic everywhere it's called. + bool isExactlyValue(double V) const { + bool ignored; + APFloat Tmp(V); + Tmp.convert(getSemantics(), APFloat::rmNearestTiesToEven, &ignored); + return bitwiseIsEqual(Tmp); + } + unsigned int convertToHexString(char *DST, unsigned int HexDigits, bool UpperCase, roundingMode RM) const { APFLOAT_DISPATCH_ON_SEMANTICS( Index: include/llvm/CodeGen/SelectionDAGNodes.h =================================================================== --- include/llvm/CodeGen/SelectionDAGNodes.h +++ include/llvm/CodeGen/SelectionDAGNodes.h @@ -1490,11 +1490,7 @@ /// convenient to write "2.0" and the like. Without this function we'd /// have to duplicate its logic everywhere it's called. bool isExactlyValue(double V) const { - bool ignored; - APFloat Tmp(V); - Tmp.convert(Value->getValueAPF().getSemantics(), - APFloat::rmNearestTiesToEven, &ignored); - return isExactlyValue(Tmp); + return Value->getValueAPF().isExactlyValue(V); } bool isExactlyValue(const APFloat& V) const; Index: include/llvm/Target/TargetSelectionDAG.td =================================================================== --- include/llvm/Target/TargetSelectionDAG.td +++ include/llvm/Target/TargetSelectionDAG.td @@ -676,12 +676,41 @@ // If FastIsel should ignore all instructions that have an operand of this type, // the FastIselShouldIgnore flag can be set. This is an optimization to reduce // the code size of the generated fast instruction selector. -class ImmLeaf - : PatFrag<(ops), (vt imm), [{}], xform> { +class ImmLeaf + : PatFrag<(ops), (vt ImmNode), [{}], xform> { let ImmediateCode = pred; bit FastIselShouldIgnore = 0; + + // Is the data type of the immediate an APInt? + bit IsAPInt = 0; + + // Is the data type of the immediate an APFloat? + bit IsAPFloat = 0; } +// An ImmLeaf except that Imm is an APInt. This is useful when you need to +// zero-extend the immediate instead of sign-extend it. +// +// Note that FastISel does not currently understand IntImmLeaf and will not +// generate code for rules that make use of it. As such, it does not make sense +// to replace ImmLeaf with IntImmLeaf. However, replacing PatLeaf with an +// IntImmLeaf will allow GlobalISel to import the rule. +class IntImmLeaf + : ImmLeaf { + let IsAPInt = 1; + let FastIselShouldIgnore = 1; +} + +// An ImmLeaf except that Imm is an APFloat. +// +// Note that FastISel does not currently understand FPImmLeaf and will not +// generate code for rules that make use of it. +class FPImmLeaf + : ImmLeaf { + let IsAPFloat = 1; + let FastIselShouldIgnore = 1; +} // Leaf fragments. Index: lib/Target/AArch64/AArch64InstrFormats.td =================================================================== --- lib/Target/AArch64/AArch64InstrFormats.td +++ lib/Target/AArch64/AArch64InstrFormats.td @@ -500,14 +500,14 @@ let Name = "LogicalImm64Not"; } } -def logical_imm32 : Operand, PatLeaf<(imm), [{ - return AArch64_AM::isLogicalImmediate(N->getZExtValue(), 32); +def logical_imm32 : Operand, IntImmLeaf { let PrintMethod = "printLogicalImm32"; let ParserMatchClass = LogicalImm32Operand; } -def logical_imm64 : Operand, PatLeaf<(imm), [{ - return AArch64_AM::isLogicalImmediate(N->getZExtValue(), 64); +def logical_imm64 : Operand, IntImmLeaf { let PrintMethod = "printLogicalImm64"; let ParserMatchClass = LogicalImm64Operand; @@ -754,8 +754,8 @@ // Floating-point immediate. def fpimm16 : Operand, - PatLeaf<(f16 fpimm), [{ - return AArch64_AM::getFP16Imm(N->getValueAPF()) != -1; + FPImmLeafgetValueAPF(); uint32_t enc = AArch64_AM::getFP16Imm(InVal); @@ -765,8 +765,8 @@ let PrintMethod = "printFPImmOperand"; } def fpimm32 : Operand, - PatLeaf<(f32 fpimm), [{ - return AArch64_AM::getFP32Imm(N->getValueAPF()) != -1; + FPImmLeafgetValueAPF(); uint32_t enc = AArch64_AM::getFP32Imm(InVal); @@ -776,8 +776,8 @@ let PrintMethod = "printFPImmOperand"; } def fpimm64 : Operand, - PatLeaf<(f64 fpimm), [{ - return AArch64_AM::getFP64Imm(N->getValueAPF()) != -1; + FPImmLeafgetValueAPF(); uint32_t enc = AArch64_AM::getFP64Imm(InVal); @@ -792,8 +792,8 @@ let PrintMethod = "printFPImmOperand"; } -def fpimm0 : PatLeaf<(fpimm), [{ - return N->isExactlyValue(+0.0); +def fpimm0 : FPImmLeaf; // Vector lane operands @@ -847,10 +847,9 @@ // aaaaaaaa bbbbbbbb cccccccc dddddddd eeeeeeee ffffffff gggggggg hhhhhhhh // are encoded as the eight bit value 'abcdefgh'. def simdimmtype10 : Operand, - PatLeaf<(f64 fpimm), [{ - return AArch64_AM::isAdvSIMDModImmType10(N->getValueAPF() - .bitcastToAPInt() - .getZExtValue()); + FPImmLeafgetValueAPF(); uint32_t enc = AArch64_AM::encodeAdvSIMDModImmType10(N->getValueAPF() Index: utils/TableGen/CodeGenDAGPatterns.h =================================================================== --- utils/TableGen/CodeGenDAGPatterns.h +++ utils/TableGen/CodeGenDAGPatterns.h @@ -475,9 +475,14 @@ /// appropriate. std::string getCodeToRunOnSDNode() const; + /// Get the data type of the argument to getImmediatePredicateCode(). + std::string getImmType() const; + private: std::string getPredCode() const; std::string getImmCode() const; + bool immCodeUsesAPInt() const; + bool immCodeUsesAPFloat() const; }; Index: utils/TableGen/CodeGenDAGPatterns.cpp =================================================================== --- utils/TableGen/CodeGenDAGPatterns.cpp +++ utils/TableGen/CodeGenDAGPatterns.cpp @@ -862,6 +862,24 @@ return PatFragRec->getRecord()->getValueAsString("ImmediateCode"); } +bool TreePredicateFn::immCodeUsesAPInt() const { + return getOrigPatFragRecord()->getRecord()->getValueAsBit("IsAPInt"); +} + +bool TreePredicateFn::immCodeUsesAPFloat() const { + bool Unset; + // The return value will be false when IsAPFloat is unset. + return getOrigPatFragRecord()->getRecord()->getValueAsBitOrUnset("IsAPFloat", + Unset); +} + +std::string TreePredicateFn::getImmType() const { + if (immCodeUsesAPInt()) + return "const APInt &"; + if (immCodeUsesAPFloat()) + return "const APFloat &"; + return "int64_t"; +} /// isAlwaysTrue - Return true if this is a noop predicate. bool TreePredicateFn::isAlwaysTrue() const { @@ -882,8 +900,13 @@ // Handle immediate predicates first. std::string ImmCode = getImmCode(); if (!ImmCode.empty()) { - std::string Result = - " int64_t Imm = cast(Node)->getSExtValue();\n"; + std::string Result = " " + getImmType() + " Imm = "; + if (immCodeUsesAPFloat()) + Result += "cast(Node)->getValueAPF();\n"; + else if (immCodeUsesAPInt()) + Result += "cast(Node)->getAPIntValue();\n"; + else + Result += "cast(Node)->getSExtValue();\n"; return Result + ImmCode; } Index: utils/TableGen/GlobalISelEmitter.cpp =================================================================== --- utils/TableGen/GlobalISelEmitter.cpp +++ utils/TableGen/GlobalISelEmitter.cpp @@ -1913,6 +1913,9 @@ importImplicitDefRenderers(BuildMIAction &DstMIBuilder, const std::vector &ImplicitDefs) const; + void emitImmPredicates(raw_ostream &OS, + std::function Filter); + /// Analyze pattern \p P, returning a matcher for it if possible. /// Otherwise, return an Error explaining why we don't support it. Expected runOnPattern(const PatternToMatch &P); @@ -2559,6 +2562,38 @@ return std::move(M); } +// Emit imm predicate table and an enum to reference them with. +// The 'Predicate_' part of the name is redundant but eliminating it is more +// trouble than it's worth. +void GlobalISelEmitter::emitImmPredicates( + raw_ostream &OS, std::function Filter) { + std::vector MatchedRecords; + const auto &Defs = RK.getAllDerivedDefinitions("PatFrag"); + std::copy_if(Defs.begin(), Defs.end(), std::back_inserter(MatchedRecords), + [&](Record *Record) { + return !Record->getValueAsString("ImmediateCode").empty() && + Filter(Record); + }); + + OS << "// PatFrag predicates.\n" + << "enum {\n"; + StringRef EnumeratorSeparator = " = GIPFP_Invalid + 1,\n"; + for (const auto *Record : MatchedRecords) { + OS << " GIPFP_Predicate_" << Record->getName() << EnumeratorSeparator; + EnumeratorSeparator = ",\n"; + } + OS << "};\n"; + for (const auto *Record : MatchedRecords) + OS << " static bool Predicate_" << Record->getName() << "(int64_t Imm) {" + << Record->getValueAsString("ImmediateCode") << " }\n"; + OS << "static InstructionSelector::ImmediatePredicateFn ImmPredicateFns[] = " + "{\n" + << " nullptr,\n"; + for (const auto *Record : MatchedRecords) + OS << " Predicate_" << Record->getName() << ",\n"; + OS << "};\n"; +} + void GlobalISelEmitter::run(raw_ostream &OS) { // Track the GINodeEquiv definitions. gatherNodeEquivs(); @@ -2742,32 +2777,11 @@ OS << "};\n" << "// See constructor for table contents\n\n"; - // Emit imm predicate table and an enum to reference them with. - // The 'Predicate_' part of the name is redundant but eliminating it is more - // trouble than it's worth. - { - OS << "// PatFrag predicates.\n" - << "enum {\n"; - StringRef EnumeratorSeparator = " = GIPFP_Invalid + 1,\n"; - for (const auto *Record : RK.getAllDerivedDefinitions("PatFrag")) { - if (!Record->getValueAsString("ImmediateCode").empty()) { - OS << " GIPFP_Predicate_" << Record->getName() << EnumeratorSeparator; - EnumeratorSeparator = ",\n"; - } - } - OS << "};\n"; - } - for (const auto *Record : RK.getAllDerivedDefinitions("PatFrag")) - if (!Record->getValueAsString("ImmediateCode").empty()) - OS << " static bool Predicate_" << Record->getName() << "(int64_t Imm) {" - << Record->getValueAsString("ImmediateCode") << " }\n"; - OS << "static InstructionSelector::ImmediatePredicateFn ImmPredicateFns[] = " - "{\n" - << " nullptr,\n"; - for (const auto *Record : RK.getAllDerivedDefinitions("PatFrag")) - if (!Record->getValueAsString("ImmediateCode").empty()) - OS << " Predicate_" << Record->getName() << ",\n"; - OS << "};\n"; + emitImmPredicates(OS, [](const Record *R) { + bool Unset; + return !R->getValueAsBitOrUnset("IsAPFloat", Unset) && + !R->getValueAsBit("IsAPInt"); + }); OS << "bool " << Target.getName() << "InstructionSelector::selectImpl(MachineInstr &I) const {\n"