diff --git a/llvm/test/TableGen/GlobalISelEmitter-unannotated-dst-pattern-ops.td b/llvm/test/TableGen/GlobalISelEmitter-unannotated-dst-pattern-ops.td new file mode 100644 --- /dev/null +++ b/llvm/test/TableGen/GlobalISelEmitter-unannotated-dst-pattern-ops.td @@ -0,0 +1,42 @@ +// RUN: llvm-tblgen %s -gen-global-isel -optimize-match-table=true -I %p/../../include -I %p/Common -o - | FileCheck %s + +include "llvm/Target/Target.td" +include "GlobalISelEmitterCommon.td" + +def ADD : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2), []>; + +def X0 : Register<"x0"> { let Namespace = "MyTarget"; } +def X32 : RegisterClass<"MyTarget", [i32], 32, (add X0)>; + +let isAllocatable = 0 in +def UnallocRC : RegisterClass<"MyTarget", [i32], 32, (add R0)>; +def UnionRC : RegisterClass<"MyTarget", [i32], 32, (add GPR32, X32)>; + +def ADDUnallocRC : I<(outs UnallocRC:$dst), + (ins UnallocRC:$src1, UnallocRC:$src2), + []>; +def ADDUnionRC : I<(outs UnionRC:$dst), (ins UnionRC:$src1, UnionRC:$src2), + []>; + +// CHECK: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_ADD, +// CHECK-NEXT: GIM_CheckType, /*MI*/0, /*Op*/0, /*Type*/GILLT_s32, +// CHECK-NEXT: GIM_CheckType, /*MI*/0, /*Op*/1, /*Type*/GILLT_s32, +// CHECK-NEXT: GIM_CheckType, /*MI*/0, /*Op*/2, /*Type*/GILLT_s32, +// CHECK-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/0, /*RC*/MyTarget::GPR32RegClassID, +// CHECK-NEXT: // (add:{ *:[i32] } i32:{ *:[i32] }:$src1, i32:{ *:[i32] }:$src2) => (ADD:{ *:[i32] } ?:{ *:[i32] }:$src1, ?:{ *:[i32] }:$src2) +// CHECK-NEXT: GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/MyTarget::ADD, +// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0, +// CHECK-NEXT: // GIR_Coverage, 0, +// CHECK-NEXT: GIR_Done, +def : Pat<(add i32:$src1, i32:$src2), + (ADD $src1, $src2)>; + +//// This should be rejected +//// CHECK-NOT: ADDUnallocRC +def : Pat<(add i32:$src1, i32:$src2), + (ADDUnallocRC $src1, $src2)>; + +// This should be rejected +// CHECK-NOT: ADDUnionRC +def : Pat<(add i32:$src1, i32:$src2), + (ADDUnionRC $src1, $src2)>; diff --git a/llvm/utils/TableGen/GlobalISelEmitter.cpp b/llvm/utils/TableGen/GlobalISelEmitter.cpp --- a/llvm/utils/TableGen/GlobalISelEmitter.cpp +++ b/llvm/utils/TableGen/GlobalISelEmitter.cpp @@ -3457,7 +3457,8 @@ Expected importExplicitUseRenderer(action_iterator InsertPt, RuleMatcher &Rule, BuildMIAction &DstMIBuilder, - TreePatternNode *DstChild); + TreePatternNode *DstChild, + Record *InstrOpNodeRec); Error importDefaultOperandRenderers(action_iterator InsertPt, RuleMatcher &M, BuildMIAction &DstMIBuilder, DagInit *DefaultOps) const; @@ -4155,9 +4156,33 @@ return failedImport("Src pattern child is an unsupported kind"); } +static unsigned +getNumRegClassesInDag(const DagInit *Dag) { + std::string Op = Dag->getOperator()->getAsString(); + if (Op.compare("add") == 0) { + unsigned Count = 0; + for (auto *Arg : Dag->getArgs()) { + if (getInitValueAsRegClass(Arg)) { + Count++; + } + else if (auto *ArgDag = dyn_cast(Arg)) { + Count += getNumRegClassesInDag(ArgDag); + } + } + + return Count; + } + else if (Op.compare("sequence") == 0) { + return 0; + } + else { + llvm_unreachable("Unknown DAG operator"); + } +} + Expected GlobalISelEmitter::importExplicitUseRenderer( action_iterator InsertPt, RuleMatcher &Rule, BuildMIAction &DstMIBuilder, - TreePatternNode *DstChild) { + TreePatternNode *DstChild, Record *InstrOpNodeRec) { const auto &SubOperand = Rule.getComplexSubOperand(DstChild->getName()); if (SubOperand.hasValue()) { @@ -4245,9 +4270,35 @@ } // Otherwise, we're looking for a bog-standard RegisterClass operand. - if (auto *ChildDefInit = dyn_cast(DstChild->getLeafValue())) { - auto *ChildRec = ChildDefInit->getDef(); + Record *ChildRec = nullptr; + if (auto *ChildDefInit = dyn_cast(DstChild->getLeafValue())) + ChildRec = ChildDefInit->getDef(); + else if (dyn_cast(DstChild->getLeafValue())) { + // Operand has uninitialized leaf value (meaning the operand has not been + // prefixed with anything), so try getting info from corresponding + // instruction operand (if provided). + if (InstrOpNodeRec) { + // If the record is a register class, check that it represents an + // allocatable class and is not a union of other register classes + if (InstrOpNodeRec->isSubClassOf("RegisterClass")) { + if (InstrOpNodeRec->getValue("isAllocatable") && + InstrOpNodeRec->getValueAsBit("isAllocatable") == 0) { + return failedImport( + "Dst pattern child trying to use an unallocatable " \ + "register class"); + } + if (InstrOpNodeRec->getValue("MemberList") && + getNumRegClassesInDag( + InstrOpNodeRec->getValueAsDag("MemberList")) > 1) { + return failedImport( + "Dst pattern child trying to use an union register class "); + } + } + ChildRec = InstrOpNodeRec; + } + } + if (ChildRec) { ArrayRef ChildTypes = DstChild->getExtTypes(); if (ChildTypes.size() != 1) return failedImport("Dst pattern child has multiple results"); @@ -4592,7 +4643,8 @@ CodeGenSubRegIndex *SubIdx = CGRegs.getSubRegIdx(SubRegInit->getDef()); auto InsertPtOrError = - importExplicitUseRenderer(InsertPt, M, DstMIBuilder, ValChild); + importExplicitUseRenderer(InsertPt, M, DstMIBuilder, ValChild, + nullptr); if (auto Error = InsertPtOrError.takeError()) return std::move(Error); InsertPt = InsertPtOrError.get(); @@ -4661,7 +4713,8 @@ } auto InsertPtOrError = importExplicitUseRenderer(InsertPt, M, DstMIBuilder, - Dst->getChild(Child)); + Dst->getChild(Child), + OperandNode); if (auto Error = InsertPtOrError.takeError()) return std::move(Error); InsertPt = InsertPtOrError.get();