Index: include/llvm/CodeGen/GlobalISel/LegalizerInfo.h =================================================================== --- include/llvm/CodeGen/GlobalISel/LegalizerInfo.h +++ include/llvm/CodeGen/GlobalISel/LegalizerInfo.h @@ -19,6 +19,7 @@ #include "llvm/ADT/None.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallBitVector.h" #include "llvm/ADT/SmallVector.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/TargetOpcodes.h" @@ -37,6 +38,7 @@ class MachineInstr; class MachineIRBuilder; class MachineRegisterInfo; +class MCInstrInfo; namespace LegalizeActions { enum LegalizeAction : std::uint8_t { @@ -253,6 +255,33 @@ bool IsAliasedByAnother; SmallVector Rules; +#ifndef NDEBUG + /// If bit I is set, this rule set contains a rule that may handle (predicate + /// or perform an action upon (or both)) the type index I. The uncertainty + /// comes from free-form rules executing user-provided lambda functions. We + /// conservatively assume such rules do the right thing and cover all type + /// indices. The bitset is intentionally 1 bit wider than it absolutely needs + /// to be to distinguish such cases from the cases where all type indices are + /// individually handled. + SmallBitVector TypeIdxsCovered{MCOI::OPERAND_LAST_GENERIC - + MCOI::OPERAND_FIRST_GENERIC + 2}; +#endif + + unsigned typeIdx(unsigned TypeIdx) { + assert(TypeIdx <= + (MCOI::OPERAND_LAST_GENERIC - MCOI::OPERAND_FIRST_GENERIC) && + "Type Index is out of bounds"); +#ifndef NDEBUG + TypeIdxsCovered.set(TypeIdx); +#endif + return TypeIdx; + } + void markAllTypeIdxsAsCovered() { +#ifndef NDEBUG + TypeIdxsCovered.set(); +#endif + } + void add(const LegalizeRule &Rule) { assert(AliasOf == 0 && "RuleSet is aliased, change the representative opcode instead"); @@ -280,7 +309,7 @@ LegalizeRuleSet &actionFor(LegalizeAction Action, std::initializer_list Types) { using namespace LegalityPredicates; - return actionIf(Action, typeInSet(0, Types)); + return actionIf(Action, typeInSet(typeIdx(0), Types)); } /// Use the given action when type index 0 is any type in the given list. /// Action should be an action that requires mutation. @@ -288,16 +317,15 @@ std::initializer_list Types, LegalizeMutation Mutation) { using namespace LegalityPredicates; - return actionIf(Action, typeInSet(0, Types), Mutation); + return actionIf(Action, typeInSet(typeIdx(0), Types), Mutation); } /// Use the given action when type indexes 0 and 1 is any type pair in the /// given list. /// Action should not be an action that requires mutation. - LegalizeRuleSet & - actionFor(LegalizeAction Action, - std::initializer_list> Types) { + LegalizeRuleSet &actionFor(LegalizeAction Action, + std::initializer_list> Types) { using namespace LegalityPredicates; - return actionIf(Action, typePairInSet(0, 1, Types)); + return actionIf(Action, typePairInSet(typeIdx(0), typeIdx(1), Types)); } /// Use the given action when type indexes 0 and 1 is any type pair in the /// given list. @@ -306,7 +334,8 @@ std::initializer_list> Types, LegalizeMutation Mutation) { using namespace LegalityPredicates; - return actionIf(Action, typePairInSet(0, 1, Types), Mutation); + return actionIf(Action, typePairInSet(typeIdx(0), typeIdx(1), Types), + Mutation); } /// Use the given action when type indexes 0 and 1 are both in the given list. /// That is, the type pair is in the cartesian product of the list. @@ -314,10 +343,11 @@ LegalizeRuleSet &actionForCartesianProduct(LegalizeAction Action, std::initializer_list Types) { using namespace LegalityPredicates; - return actionIf(Action, all(typeInSet(0, Types), typeInSet(1, Types))); + return actionIf(Action, all(typeInSet(typeIdx(0), Types), + typeInSet(typeIdx(1), Types))); } - /// Use the given action when type indexes 0 and 1 are both their respective - /// lists. + /// Use the given action when type indexes 0 and 1 are both in their + /// respective lists. /// That is, the type pair is in the cartesian product of the lists /// Action should not be an action that requires mutation. LegalizeRuleSet & @@ -325,7 +355,20 @@ std::initializer_list Types0, std::initializer_list Types1) { using namespace LegalityPredicates; - return actionIf(Action, all(typeInSet(0, Types0), typeInSet(1, Types1))); + return actionIf(Action, all(typeInSet(typeIdx(0), Types0), + typeInSet(typeIdx(1), Types1))); + } + /// Use the given action when type indexes 0, 1, and 2 are all in their + /// respective lists. + /// That is, the type triple is in the cartesian product of the lists + /// Action should not be an action that requires mutation. + LegalizeRuleSet &actionForCartesianProduct( + LegalizeAction Action, std::initializer_list Types0, + std::initializer_list Types1, std::initializer_list Types2) { + using namespace LegalityPredicates; + return actionIf(Action, all(typeInSet(typeIdx(0), Types0), + all(typeInSet(typeIdx(1), Types1), + typeInSet(typeIdx(2), Types2)))); } public: @@ -343,6 +386,9 @@ /// The instruction is legal if predicate is true. LegalizeRuleSet &legalIf(LegalityPredicate Predicate) { + // We have no choice but conservatively assume that the free-form + // user-provided Predicate properly handles all type indices: + markAllTypeIdxsAsCovered(); return actionIf(LegalizeAction::Legal, Predicate); } /// The instruction is legal when type index 0 is any type in the given list. @@ -359,8 +405,9 @@ LegalizeRuleSet &legalForTypesWithMemSize( std::initializer_list TypesAndMemSize) { - return legalIf(LegalityPredicates::typePairAndMemSizeInSet( - 0, 1, /*MMOIdx*/ 0, TypesAndMemSize)); + return actionIf(LegalizeAction::Legal, + LegalityPredicates::typePairAndMemSizeInSet( + typeIdx(0), typeIdx(1), /*MMOIdx*/ 0, TypesAndMemSize)); } /// The instruction is legal when type indexes 0 and 1 are both in the given /// list. That is, the type pair is in the cartesian product of the list. @@ -377,44 +424,74 @@ /// The instruction is lowered. LegalizeRuleSet &lower() { using namespace LegalizeMutations; - return actionIf(LegalizeAction::Lower, always, changeTo(0, 0)); + // We have no choice but conservatively assume that predicate-less lowering + // properly handles all type indices by design: + markAllTypeIdxsAsCovered(); + return actionIf(LegalizeAction::Lower, always); } /// The instruction is lowered if predicate is true. Keep type index 0 as the /// same type. LegalizeRuleSet &lowerIf(LegalityPredicate Predicate) { using namespace LegalizeMutations; - return actionIf(LegalizeAction::Lower, Predicate, changeTo(0, 0)); + // We have no choice but conservatively assume that lowering with a + // free-form user provided Predicate properly handles all type indices: + markAllTypeIdxsAsCovered(); + return actionIf(LegalizeAction::Lower, Predicate); + } + /// The instruction is lowered if predicate is true. + LegalizeRuleSet &lowerIf(LegalityPredicate Predicate, + LegalizeMutation Mutation) { + // We have no choice but conservatively assume that lowering with a + // free-form user provided Predicate properly handles all type indices: + markAllTypeIdxsAsCovered(); + return actionIf(LegalizeAction::Lower, Predicate, Mutation); } /// The instruction is lowered when type index 0 is any type in the given /// list. Keep type index 0 as the same type. LegalizeRuleSet &lowerFor(std::initializer_list Types) { - using namespace LegalizeMutations; - return lowerFor(Types, changeTo(0, 0)); + return actionFor(LegalizeAction::Lower, Types, + LegalizeMutations::changeTo(0, 0)); } - /// The instruction is lowered when type indexes 0 and 1 is any type pair in the - /// given list. Keep type index 0 as the same type. + /// The instruction is lowered when type index 0 is any type in the given + /// list. + LegalizeRuleSet &lowerFor(std::initializer_list Types, + LegalizeMutation Mutation) { + return actionFor(LegalizeAction::Lower, Types, Mutation); + } + /// The instruction is lowered when type indexes 0 and 1 is any type pair in + /// the given list. Keep type index 0 as the same type. LegalizeRuleSet &lowerFor(std::initializer_list> Types) { - return lowerFor(Types, LegalizeMutations::changeTo(0, 0)); + return actionFor(LegalizeAction::Lower, Types, + LegalizeMutations::changeTo(0, 0)); } - /// The instruction is lowered when type indexes 0 and 1 is any type pair in the - /// given list. + /// The instruction is lowered when type indexes 0 and 1 is any type pair in + /// the given list. LegalizeRuleSet &lowerFor(std::initializer_list> Types, LegalizeMutation Mutation) { return actionFor(LegalizeAction::Lower, Types, Mutation); } - /// The instruction is lowered if predicate is true. - LegalizeRuleSet &lowerIf(LegalityPredicate Predicate, - LegalizeMutation Mutation) { - return actionIf(LegalizeAction::Lower, Predicate, Mutation); + /// The instruction is lowered when type indexes 0 and 1 are both in their + /// respective lists. + LegalizeRuleSet &lowerForCartesianProduct(std::initializer_list Types0, + std::initializer_list Types1) { + using namespace LegalityPredicates; + return actionForCartesianProduct(LegalizeAction::Lower, Types0, Types1); } - /// The instruction is lowered when type index 0 is any type in the given list. - LegalizeRuleSet &lowerFor(std::initializer_list Types, - LegalizeMutation Mutation) { - return actionFor(LegalizeAction::Lower, Types, Mutation); + /// The instruction is lowered when when type indexes 0, 1, and 2 are all in + /// their respective lists. + LegalizeRuleSet &lowerForCartesianProduct(std::initializer_list Types0, + std::initializer_list Types1, + std::initializer_list Types2) { + using namespace LegalityPredicates; + return actionForCartesianProduct(LegalizeAction::Lower, Types0, Types1, + Types2); } /// Like legalIf, but for the Libcall action. LegalizeRuleSet &libcallIf(LegalityPredicate Predicate) { + // We have no choice but conservatively assume that a libcall with a + // free-form user provided Predicate properly handles all type indices: + markAllTypeIdxsAsCovered(); return actionIf(LegalizeAction::Libcall, Predicate); } LegalizeRuleSet &libcallFor(std::initializer_list Types) { @@ -438,12 +515,18 @@ /// true. LegalizeRuleSet &widenScalarIf(LegalityPredicate Predicate, LegalizeMutation Mutation) { + // We have no choice but conservatively assume that an action with a + // free-form user provided Predicate properly handles all type indices: + markAllTypeIdxsAsCovered(); return actionIf(LegalizeAction::WidenScalar, Predicate, Mutation); } /// Narrow the scalar to the one selected by the mutation if the predicate is /// true. LegalizeRuleSet &narrowScalarIf(LegalityPredicate Predicate, LegalizeMutation Mutation) { + // We have no choice but conservatively assume that an action with a + // free-form user provided Predicate properly handles all type indices: + markAllTypeIdxsAsCovered(); return actionIf(LegalizeAction::NarrowScalar, Predicate, Mutation); } @@ -451,12 +534,18 @@ /// predicate is true. LegalizeRuleSet &moreElementsIf(LegalityPredicate Predicate, LegalizeMutation Mutation) { + // We have no choice but conservatively assume that an action with a + // free-form user provided Predicate properly handles all type indices: + markAllTypeIdxsAsCovered(); return actionIf(LegalizeAction::MoreElements, Predicate, Mutation); } /// Remove elements to reach the type selected by the mutation if the /// predicate is true. LegalizeRuleSet &fewerElementsIf(LegalityPredicate Predicate, LegalizeMutation Mutation) { + // We have no choice but conservatively assume that an action with a + // free-form user provided Predicate properly handles all type indices: + markAllTypeIdxsAsCovered(); return actionIf(LegalizeAction::FewerElements, Predicate, Mutation); } @@ -473,6 +562,9 @@ } LegalizeRuleSet &customIf(LegalityPredicate Predicate) { + // We have no choice but conservatively assume that a custom action with a + // free-form user provided Predicate properly handles all type indices: + markAllTypeIdxsAsCovered(); return actionIf(LegalizeAction::Custom, Predicate); } LegalizeRuleSet &customFor(std::initializer_list Types) { @@ -489,54 +581,57 @@ /// Widen the scalar to the next power of two that is at least MinSize. /// No effect if the type is not a scalar or is a power of two. - LegalizeRuleSet &widenScalarToNextPow2(unsigned TypeIdx, unsigned MinSize = 0) { + LegalizeRuleSet &widenScalarToNextPow2(unsigned TypeIdx, + unsigned MinSize = 0) { using namespace LegalityPredicates; - return widenScalarIf( - sizeNotPow2(TypeIdx), - LegalizeMutations::widenScalarToNextPow2(TypeIdx, MinSize)); + return actionIf(LegalizeAction::WidenScalar, sizeNotPow2(typeIdx(TypeIdx)), + LegalizeMutations::widenScalarToNextPow2(TypeIdx, MinSize)); } LegalizeRuleSet &narrowScalar(unsigned TypeIdx, LegalizeMutation Mutation) { using namespace LegalityPredicates; - return narrowScalarIf(isScalar(TypeIdx), Mutation); + return actionIf(LegalizeAction::NarrowScalar, isScalar(typeIdx(TypeIdx)), + Mutation); } /// Ensure the scalar is at least as wide as Ty. LegalizeRuleSet &minScalar(unsigned TypeIdx, const LLT &Ty) { using namespace LegalityPredicates; using namespace LegalizeMutations; - return widenScalarIf(narrowerThan(TypeIdx, Ty.getSizeInBits()), - changeTo(TypeIdx, Ty)); + return actionIf(LegalizeAction::WidenScalar, + narrowerThan(TypeIdx, Ty.getSizeInBits()), + changeTo(typeIdx(TypeIdx), Ty)); } /// Ensure the scalar is at most as wide as Ty. LegalizeRuleSet &maxScalar(unsigned TypeIdx, const LLT &Ty) { using namespace LegalityPredicates; using namespace LegalizeMutations; - return narrowScalarIf(widerThan(TypeIdx, Ty.getSizeInBits()), - changeTo(TypeIdx, Ty)); + return actionIf(LegalizeAction::NarrowScalar, + widerThan(TypeIdx, Ty.getSizeInBits()), + changeTo(typeIdx(TypeIdx), Ty)); } /// Conditionally limit the maximum size of the scalar. /// For example, when the maximum size of one type depends on the size of /// another such as extracting N bits from an M bit container. - LegalizeRuleSet &maxScalarIf(LegalityPredicate Predicate, unsigned TypeIdx, const LLT &Ty) { + LegalizeRuleSet &maxScalarIf(LegalityPredicate Predicate, unsigned TypeIdx, + const LLT &Ty) { using namespace LegalityPredicates; using namespace LegalizeMutations; - return narrowScalarIf( - [=](const LegalityQuery &Query) { - return widerThan(TypeIdx, Ty.getSizeInBits()) && - Predicate(Query); - }, - changeTo(TypeIdx, Ty)); + return actionIf(LegalizeAction::NarrowScalar, + [=](const LegalityQuery &Query) { + return widerThan(TypeIdx, Ty.getSizeInBits()) && + Predicate(Query); + }, + changeTo(typeIdx(TypeIdx), Ty)); } /// Limit the range of scalar sizes to MinTy and MaxTy. - LegalizeRuleSet &clampScalar(unsigned TypeIdx, const LLT &MinTy, const LLT &MaxTy) { + LegalizeRuleSet &clampScalar(unsigned TypeIdx, const LLT &MinTy, + const LLT &MaxTy) { assert(MinTy.isScalar() && MaxTy.isScalar() && "Expected scalar types"); - - return minScalar(TypeIdx, MinTy) - .maxScalar(TypeIdx, MaxTy); + return minScalar(TypeIdx, MinTy).maxScalar(TypeIdx, MaxTy); } /// Add more elements to the vector to reach the next power of two. @@ -544,14 +639,18 @@ /// two. LegalizeRuleSet &moreElementsToNextPow2(unsigned TypeIdx) { using namespace LegalityPredicates; - return moreElementsIf(numElementsNotPow2(TypeIdx), - LegalizeMutations::moreElementsToNextPow2(TypeIdx)); + return actionIf(LegalizeAction::MoreElements, + numElementsNotPow2(typeIdx(TypeIdx)), + LegalizeMutations::moreElementsToNextPow2(TypeIdx)); } /// Limit the number of elements in EltTy vectors to at least MinElements. LegalizeRuleSet &clampMinNumElements(unsigned TypeIdx, const LLT &EltTy, unsigned MinElements) { - return moreElementsIf( + // Mark the type index as covered: + typeIdx(TypeIdx); + return actionIf( + LegalizeAction::MoreElements, [=](const LegalityQuery &Query) { LLT VecTy = Query.Types[TypeIdx]; return VecTy.isVector() && VecTy.getElementType() == EltTy && @@ -566,7 +665,10 @@ /// Limit the number of elements in EltTy vectors to at most MaxElements. LegalizeRuleSet &clampMaxNumElements(unsigned TypeIdx, const LLT &EltTy, unsigned MaxElements) { - return fewerElementsIf( + // Mark the type index as covered: + typeIdx(TypeIdx); + return actionIf( + LegalizeAction::FewerElements, [=](const LegalityQuery &Query) { LLT VecTy = Query.Types[TypeIdx]; return VecTy.isVector() && VecTy.getElementType() == EltTy && @@ -601,6 +703,11 @@ return *this; } + /// Check if there is no type index which is obviously not handled by the + /// LegalizeRuleSet in any way at all. + /// \pre Type indices of the opcode form a dense [0, \p NumTypeIdxs) set. + bool verifyTypeIdxsCoverage(unsigned NumTypeIdxs) const; + /// Apply the ruleset to the given LegalityQuery. LegalizeActionStep apply(const LegalityQuery &Query) const; }; @@ -618,6 +725,10 @@ /// before any query is made or incorrect results may be returned. void computeTables(); + /// Perform simple self-diagnostic and assert if there is anything obviously + /// wrong with the actions set up. + void verify(const MCInstrInfo &MII) const; + static bool needsLegalizingToDifferentSize(const LegalizeAction Action) { using namespace LegalizeActions; switch (Action) { Index: lib/CodeGen/GlobalISel/LegalizerInfo.cpp =================================================================== --- lib/CodeGen/GlobalISel/LegalizerInfo.cpp +++ lib/CodeGen/GlobalISel/LegalizerInfo.cpp @@ -24,6 +24,7 @@ #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/CodeGen/TargetOpcodes.h" #include "llvm/MC/MCInstrDesc.h" +#include "llvm/MC/MCInstrInfo.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/LowLevelTypeImpl.h" @@ -83,6 +84,27 @@ return {LegalizeAction::Unsupported, 0, LLT{}}; } +bool LegalizeRuleSet::verifyTypeIdxsCoverage(unsigned NumTypeIdxs) const { +#ifndef NDEBUG + if (Rules.empty()) { + DEBUG(dbgs() << ".. type index coverage check SKIPPED: no rules defined\n"); + return true; + } + const int64_t FirstUncovered = TypeIdxsCovered.find_first_unset(); + if (FirstUncovered < 0) { + DEBUG(dbgs() << ".. type index coverage check SKIPPED:" + " user-defined predicate detected\n"); + return true; + } + const bool AllCovered = (FirstUncovered >= NumTypeIdxs); + DEBUG(dbgs() << ".. the first uncovered type index: " << FirstUncovered + << ", " << (AllCovered ? "OK" : "FAIL") << "\n"); + return AllCovered; +#else + return true; +#endif +} + LegalizerInfo::LegalizerInfo() : TablesInitialized(false) { // Set defaults. // FIXME: these two (G_ANYEXT and G_TRUNC?) can be legalized to the @@ -515,6 +537,35 @@ IntermediateType.getScalarSizeInBits())}; } +/// \pre Type indices of every opcode form a dense set starting from 0. +void LegalizerInfo::verify(const MCInstrInfo &MII) const { +#ifndef NDEBUG + std::vector FailedOpcodes; + for (unsigned Opcode = FirstOp; Opcode <= LastOp; ++Opcode) { + const MCInstrDesc &MCID = MII.get(Opcode); + const unsigned NumTypeIdxs = std::accumulate( + MCID.opInfo_begin(), MCID.opInfo_end(), 0U, + [](unsigned Acc, const MCOperandInfo &OpInfo) { + return OpInfo.isGenericType() + ? std::max(OpInfo.getGenericTypeIndex() + 1U, Acc) + : Acc; + }); + DEBUG(dbgs() << MII.getName(Opcode) << " (opcode " << Opcode + << "): " << NumTypeIdxs << " type ind" + << (NumTypeIdxs == 1 ? "ex" : "ices") << "\n"); + const LegalizeRuleSet &RuleSet = getActionDefinitions(Opcode); + if (!RuleSet.verifyTypeIdxsCoverage(NumTypeIdxs)) + FailedOpcodes.push_back(Opcode); + } + if (!FailedOpcodes.empty()) { + errs() << "The following opcodes have ill-defined legalization rules:"; + for (unsigned Opcode : FailedOpcodes) + errs() << " " << MII.getName(Opcode); + errs() << "\n"; + } +#endif +} + #ifndef NDEBUG // FIXME: This should be in the MachineVerifier, but it can't use the // LegalizerInfo as it's currently in the separate GlobalISel library. Index: lib/Target/AArch64/AArch64LegalizerInfo.cpp =================================================================== --- lib/Target/AArch64/AArch64LegalizerInfo.cpp +++ lib/Target/AArch64/AArch64LegalizerInfo.cpp @@ -387,6 +387,7 @@ } computeTables(); + verify(*ST.getInstrInfo()); } bool AArch64LegalizerInfo::legalizeCustom(MachineInstr &MI, Index: test/CodeGen/AArch64/GlobalISel/legalize-inttoptr-xfail-1.mir =================================================================== --- /dev/null +++ test/CodeGen/AArch64/GlobalISel/legalize-inttoptr-xfail-1.mir @@ -0,0 +1,39 @@ +# RUN: not llc -mtriple=aarch64-- -run-pass=legalizer -simplify-mir %s -o - 2>&1 | FileCheck %s +# REQUIRES: asserts +# XFAIL: * + +# This is to demonstrate what kind of bugs we're missing w/o some kind +# of validation for LegalizerInfo: G_INTTOPTR could only be legal / +# could be legalized if its destination operand has a pointer type and +# its source - a scalar type. This is reversed in this test and the +# legalizer is expected to fail on it with an appropriate error +# message. Prior to LegalizerInfo::verify AArch64 legalizer had a +# subtle bug in its definition that caused it to accept the following +# MIR as legal. Namely, it checked that type index 0 is either s64 or +# p0 (in that order) and implicitly declared any type for type index 1 +# as legal. As LegalizerInfo::verify asserts on such a definition due +# to type index 1 not being covered it forces to review the definition +# and fix the mistake: check that type index 0 is p0 and type index 1 +# is s64 (in that order). + +# CHECK: LLVM ERROR: unable to legalize instruction: +# CHECK-SAME: %{{[0-9]+}}:_(s64) = G_INTTOPTR %{{[0-9]+}}:_(p0) +# CHECK-SAME: (in function: broken) + +--- +name: broken +alignment: 2 +tracksRegLiveness: true +registers: + - { id: 0, class: _ } + - { id: 1, class: _ } +body: | + bb.1: + liveins: $x0 + + %0:_(p0) = COPY $x0 + %1:_(s64) = G_INTTOPTR %0(p0) + $x0 = COPY %1(s64) + RET_ReallyLR implicit $x0 + +... Index: test/CodeGen/AArch64/GlobalISel/legalize-inttoptr-xfail-2.mir =================================================================== --- /dev/null +++ test/CodeGen/AArch64/GlobalISel/legalize-inttoptr-xfail-2.mir @@ -0,0 +1,43 @@ +# RUN: not llc -mtriple=aarch64-- -run-pass=legalizer -simplify-mir %s -o - 2>&1 | FileCheck %s +# REQUIRES: asserts +# XFAIL: * + +# This is to demonstrate what kind of bugs we're missing w/o some kind +# of validation for LegalizerInfo: G_INTTOPTR could only be legal / +# could be legalized if its destination operand has a pointer type and +# its source - a scalar type of an appropriate size. This test meets +# the requirements for type index 0 (the pointer) and LLT-size +# requirements for type index 1 (64 bits for AArch64), but has a +# non-scalar (vector) type for type index 1. The Legalizer is expected +# to fail on it with an appropriate error message. Prior to +# LegalizerInfo::verify AArch64 legalizer had a subtle bug in its +# definition that caused it to accept the following MIR as legal. +# Namely, it checked that type index 0 is either s64 or p0 and +# implicitly declared any type for type index 1 as legal (as soon as +# its size is 64 bits). As LegalizerInfo::verify asserts on such a +# definition due to type index 1 not being covered by a specific +# action (not just `unsupportedIf`) it forces to review the definition +# and fix the mistake: check that type index 0 is p0 and type index 1 +# is s64. + +# CHECK: LLVM ERROR: unable to legalize instruction: +# CHECK-SAME: %{{[0-9]+}}:_(p0) = G_INTTOPTR %{{[0-9]+}}:_(<4 x s16>) +# CHECK-SAME: (in function: broken) + +--- +name: broken +alignment: 2 +tracksRegLiveness: true +registers: + - { id: 0, class: _ } + - { id: 1, class: _ } +body: | + bb.1: + liveins: $d0 + + %0:_(<4 x s16>) = COPY $d0 + %1:_(p0) = G_INTTOPTR %0(<4 x s16>) + $x0 = COPY %1(p0) + RET_ReallyLR implicit $x0 + +... Index: test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir =================================================================== --- /dev/null +++ test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir @@ -0,0 +1,294 @@ +# RUN: llc -mtriple=aarch64-- -run-pass=legalizer %s \ +# RUN: -mcpu=cortex-a75 -o - 2>&1 | FileCheck %s --check-prefixes=CHECK --match-full-lines + +# RUN: llc -mtriple=aarch64-- -run-pass=legalizer %s -debug-only=legalizer-info \ +# RUN: -mcpu=cortex-a75 -o - 2>&1 | FileCheck %s --check-prefixes=CHECK,DEBUG --match-full-lines + +# REQUIRES: asserts + +# The main purpose of this test is to make sure we don't over-relax +# LegalizerInfo validation and loose its ability to catch bugs. +# +# Watch out for every "SKIPPED: user-defined predicate detected" in the +# check-lines below and keep each and every one of them justified. + + +# DEBUG: G_ADD (opcode [[ADD_OPC:[0-9]+]]): 1 type index +# DEBUG-NEXT: .. the first uncovered type index: 1, OK +# +# DEBUG-NEXT: G_SUB (opcode [[SUB_OPC:[0-9]+]]): 1 type index +# DEBUG-NEXT: .. opcode [[SUB_OPC]] is aliased to [[ADD_OPC]] +# DEBUG-NEXT: .. opcode [[ADD_OPC]] is aliased to 0 +# DEBUG-NEXT: .. the first uncovered type index: 1, OK +# +# DEBUG-NEXT: G_MUL (opcode {{[0-9]+}}): 1 type index +# DEBUG: .. the first uncovered type index: 1, OK +# +# DEBUG-NEXT: G_SDIV (opcode {{[0-9]+}}): 1 type index +# DEBUG: .. the first uncovered type index: 1, OK +# +# DEBUG-NEXT: G_UDIV (opcode {{[0-9]+}}): 1 type index +# DEBUG: .. the first uncovered type index: 1, OK +# +# DEBUG-NEXT: G_SREM (opcode {{[0-9]+}}): 1 type index +# DEBUG: .. the first uncovered type index: 1, OK +# +# DEBUG-NEXT: G_UREM (opcode {{[0-9]+}}): 1 type index +# DEBUG: .. the first uncovered type index: 1, OK +# +# DEBUG-NEXT: G_AND (opcode {{[0-9]+}}): 1 type index +# DEBUG: .. the first uncovered type index: 1, OK +# +# DEBUG-NEXT: G_OR (opcode {{[0-9]+}}): 1 type index +# DEBUG: .. the first uncovered type index: 1, OK +# +# DEBUG-NEXT: G_XOR (opcode {{[0-9]+}}): 1 type index +# DEBUG: .. the first uncovered type index: 1, OK +# +# DEBUG-NEXT: G_IMPLICIT_DEF (opcode {{[0-9]+}}): 1 type index +# DEBUG: .. the first uncovered type index: 1, OK +# +# DEBUG-NEXT: G_PHI (opcode {{[0-9]+}}): 1 type index +# DEBUG: .. the first uncovered type index: 1, OK +# +# DEBUG-NEXT: G_FRAME_INDEX (opcode {{[0-9]+}}): 1 type index +# DEBUG: .. the first uncovered type index: 1, OK +# +# DEBUG-NEXT: G_GLOBAL_VALUE (opcode {{[0-9]+}}): 1 type index +# DEBUG: .. the first uncovered type index: 1, OK +# +# DEBUG-NEXT: G_EXTRACT (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. type index coverage check SKIPPED: user-defined predicate detected +# +# DEBUG-NEXT: G_UNMERGE_VALUES (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. type index coverage check SKIPPED: user-defined predicate detected +# +# DEBUG-NEXT: G_INSERT (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. type index coverage check SKIPPED: user-defined predicate detected +# +# DEBUG-NEXT: G_MERGE_VALUES (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. type index coverage check SKIPPED: user-defined predicate detected +# +# DEBUG-NEXT: G_PTRTOINT (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. the first uncovered type index: 2, OK +# +# DEBUG-NEXT: G_INTTOPTR (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. the first uncovered type index: 1, FAIL +# +# DEBUG-NEXT: G_BITCAST (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. the first uncovered type index: 2, OK +# +# DEBUG-NEXT: G_LOAD (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. type index coverage check SKIPPED: user-defined predicate detected +# +# DEBUG-NEXT: G_SEXTLOAD (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. type index coverage check SKIPPED: user-defined predicate detected +# +# DEBUG-NEXT: G_ZEXTLOAD (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. type index coverage check SKIPPED: user-defined predicate detected +# +# DEBUG-NEXT: G_STORE (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. type index coverage check SKIPPED: user-defined predicate detected +# +# DEBUG-NEXT: G_ATOMIC_CMPXCHG_WITH_SUCCESS (opcode {{[0-9]+}}): 3 type indices +# DEBUG: .. the first uncovered type index: 1, FAIL +# +# DEBUG-NEXT: G_ATOMIC_CMPXCHG (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. the first uncovered type index: 2, OK +# +# DEBUG-NEXT: G_ATOMICRMW_XCHG (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. the first uncovered type index: 2, OK +# +# DEBUG-NEXT: G_ATOMICRMW_ADD (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. the first uncovered type index: 2, OK +# +# DEBUG-NEXT: G_ATOMICRMW_SUB (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. the first uncovered type index: 2, OK +# +# DEBUG-NEXT: G_ATOMICRMW_AND (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. the first uncovered type index: 2, OK +# +# DEBUG-NEXT: G_ATOMICRMW_NAND (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. type index coverage check SKIPPED: no rules defined +# +# DEBUG-NEXT: G_ATOMICRMW_OR (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. the first uncovered type index: 2, OK +# +# DEBUG-NEXT: G_ATOMICRMW_XOR (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. the first uncovered type index: 2, OK +# +# DEBUG-NEXT: G_ATOMICRMW_MAX (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. the first uncovered type index: 2, OK +# +# DEBUG-NEXT: G_ATOMICRMW_MIN (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. the first uncovered type index: 2, OK +# +# DEBUG-NEXT: G_ATOMICRMW_UMAX (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. the first uncovered type index: 2, OK +# +# DEBUG-NEXT: G_ATOMICRMW_UMIN (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. the first uncovered type index: 2, OK +# +# DEBUG-NEXT: G_BRCOND (opcode {{[0-9]+}}): 1 type index +# DEBUG: .. the first uncovered type index: 1, OK +# +# DEBUG-NEXT: G_BRINDIRECT (opcode {{[0-9]+}}): 1 type index +# DEBUG: .. the first uncovered type index: 1, OK +# +# DEBUG-NEXT: G_INTRINSIC (opcode {{[0-9]+}}): 0 type indices +# DEBUG: .. type index coverage check SKIPPED: no rules defined +# +# DEBUG-NEXT: G_INTRINSIC_W_SIDE_EFFECTS (opcode {{[0-9]+}}): 0 type indices +# DEBUG: .. type index coverage check SKIPPED: no rules defined +# +# DEBUG-NEXT: G_ANYEXT (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. the first uncovered type index: 1, FAIL +# +# DEBUG-NEXT: G_TRUNC (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. type index coverage check SKIPPED: no rules defined +# +# DEBUG-NEXT: G_CONSTANT (opcode {{[0-9]+}}): 1 type index +# DEBUG: .. the first uncovered type index: 1, OK +# +# DEBUG-NEXT: G_FCONSTANT (opcode {{[0-9]+}}): 1 type index +# DEBUG: .. the first uncovered type index: 1, OK +# +# DEBUG-NEXT: G_VASTART (opcode {{[0-9]+}}): 1 type index +# DEBUG: .. the first uncovered type index: 1, OK +# +# DEBUG-NEXT: G_VAARG (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. the first uncovered type index: 2, OK +# +# DEBUG-NEXT: G_SEXT (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. the first uncovered type index: 1, FAIL +# +# DEBUG-NEXT: G_ZEXT (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. the first uncovered type index: 1, FAIL +# +# DEBUG-NEXT: G_SHL (opcode {{[0-9]+}}): 1 type index +# DEBUG: .. the first uncovered type index: 1, OK +# +# DEBUG-NEXT: G_LSHR (opcode {{[0-9]+}}): 1 type index +# DEBUG: .. the first uncovered type index: 1, OK +# +# DEBUG-NEXT: G_ASHR (opcode {{[0-9]+}}): 1 type index +# DEBUG: .. the first uncovered type index: 1, OK +# +# DEBUG-NEXT: G_ICMP (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. the first uncovered type index: 2, OK +# +# DEBUG-NEXT: G_FCMP (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. the first uncovered type index: 2, OK +# +# DEBUG-NEXT: G_SELECT (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. the first uncovered type index: 2, OK +# +# DEBUG-NEXT: G_UADDE (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. the first uncovered type index: 2, OK +# +# DEBUG-NEXT: G_USUBE (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. the first uncovered type index: 2, OK +# +# DEBUG-NEXT: G_SADDO (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. the first uncovered type index: 2, OK +# +# DEBUG-NEXT: G_SSUBO (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. the first uncovered type index: 2, OK +# +# DEBUG-NEXT: G_UMULO (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. the first uncovered type index: 2, OK +# +# DEBUG-NEXT: G_SMULO (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. the first uncovered type index: 2, OK +# +# DEBUG-NEXT: G_UMULH (opcode {{[0-9]+}}): 1 type index +# DEBUG: .. the first uncovered type index: 1, OK +# +# DEBUG-NEXT: G_SMULH (opcode {{[0-9]+}}): 1 type index +# DEBUG: .. the first uncovered type index: 1, OK +# +# DEBUG-NEXT: G_FADD (opcode {{[0-9]+}}): 1 type index +# DEBUG: .. the first uncovered type index: 1, OK +# +# DEBUG-NEXT: G_FSUB (opcode {{[0-9]+}}): 1 type index +# DEBUG: .. the first uncovered type index: 1, OK +# +# DEBUG-NEXT: G_FMUL (opcode {{[0-9]+}}): 1 type index +# DEBUG: .. the first uncovered type index: 1, OK +# +# DEBUG-NEXT: G_FMA (opcode {{[0-9]+}}): 1 type index +# DEBUG: .. the first uncovered type index: 1, OK +# +# DEBUG-NEXT: G_FDIV (opcode {{[0-9]+}}): 1 type index +# DEBUG: .. the first uncovered type index: 1, OK +# +# DEBUG-NEXT: G_FREM (opcode {{[0-9]+}}): 1 type index +# DEBUG: .. the first uncovered type index: 1, OK +# +# DEBUG-NEXT: G_FPOW (opcode {{[0-9]+}}): 1 type index +# DEBUG: .. the first uncovered type index: 1, OK +# +# DEBUG-NEXT: G_FEXP (opcode {{[0-9]+}}): 1 type index +# DEBUG: .. type index coverage check SKIPPED: no rules defined +# +# DEBUG-NEXT: G_FEXP2 (opcode {{[0-9]+}}): 1 type index +# DEBUG: .. type index coverage check SKIPPED: no rules defined +# +# DEBUG-NEXT: G_FLOG (opcode {{[0-9]+}}): 1 type index +# DEBUG: .. type index coverage check SKIPPED: no rules defined +# +# DEBUG-NEXT: G_FLOG2 (opcode {{[0-9]+}}): 1 type index +# DEBUG: .. type index coverage check SKIPPED: no rules defined +# +# DEBUG-NEXT: G_FNEG (opcode {{[0-9]+}}): 1 type index +# DEBUG: .. type index coverage check SKIPPED: no rules defined +# +# DEBUG-NEXT: G_FPEXT (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. the first uncovered type index: 2, OK +# +# DEBUG-NEXT: G_FPTRUNC (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. the first uncovered type index: 2, OK +# +# DEBUG-NEXT: G_FPTOSI (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. the first uncovered type index: 2, OK +# +# DEBUG-NEXT: G_FPTOUI (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. the first uncovered type index: 2, OK +# +# DEBUG-NEXT: G_SITOFP (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. the first uncovered type index: 2, OK +# +# DEBUG-NEXT: G_UITOFP (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. the first uncovered type index: 2, OK +# +# DEBUG-NEXT: G_FABS (opcode {{[0-9]+}}): 1 type index +# DEBUG: .. type index coverage check SKIPPED: no rules defined +# +# DEBUG-NEXT: G_GEP (opcode {{[0-9]+}}): 2 type indices +# DEBUG: .. the first uncovered type index: 2, OK +# +# DEBUG-NEXT: G_PTR_MASK (opcode {{[0-9]+}}): 1 type index +# DEBUG: .. the first uncovered type index: 1, OK +# +# DEBUG-NEXT: G_BR (opcode {{[0-9]+}}): 0 type indices +# DEBUG: .. type index coverage check SKIPPED: no rules defined +# +# DEBUG-NEXT: G_INSERT_VECTOR_ELT (opcode {{[0-9]+}}): 3 type indices +# DEBUG: .. type index coverage check SKIPPED: no rules defined +# +# DEBUG-NEXT: G_EXTRACT_VECTOR_ELT (opcode {{[0-9]+}}): 3 type indices +# DEBUG: .. type index coverage check SKIPPED: no rules defined +# +# DEBUG-NEXT: G_SHUFFLE_VECTOR (opcode {{[0-9]+}}): 3 type indices +# DEBUG: .. type index coverage check SKIPPED: no rules defined +# +# DEBUG-NEXT: G_BSWAP (opcode {{[0-9]+}}): 1 type index +# DEBUG: .. the first uncovered type index: 1, OK + +# CHECK: The following opcodes have ill-defined legalization rules: G_INTTOPTR G_ATOMIC_CMPXCHG_WITH_SUCCESS G_ANYEXT G_SEXT G_ZEXT + +--- +name: dummy +body: | + bb.0: +...