summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h5
-rw-r--r--llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h2
-rw-r--r--llvm/lib/CodeGen/GlobalISel/InstructionSelector.cpp8
-rw-r--r--llvm/test/CodeGen/AArch64/GlobalISel/select-load.mir40
-rw-r--r--llvm/test/TableGen/GlobalISelEmitter.td40
-rw-r--r--llvm/utils/TableGen/CodeGenDAGPatterns.cpp10
-rw-r--r--llvm/utils/TableGen/CodeGenDAGPatterns.h8
-rw-r--r--llvm/utils/TableGen/GlobalISelEmitter.cpp77
8 files changed, 180 insertions, 10 deletions
diff --git a/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h b/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h
index 4935e900773..ebfcec133b6 100644
--- a/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h
+++ b/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h
@@ -364,7 +364,10 @@ protected:
bool isBaseWithConstantOffset(const MachineOperand &Root,
const MachineRegisterInfo &MRI) const;
- bool isObviouslySafeToFold(MachineInstr &MI) const;
+ /// Return true if MI can obviously be folded into IntoMI.
+ /// MI and IntoMI do not need to be in the same basic blocks, but MI must
+ /// preceed IntoMI.
+ bool isObviouslySafeToFold(MachineInstr &MI, MachineInstr &IntoMI) const;
};
} // end namespace llvm
diff --git a/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h b/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h
index 84b6ec9beea..0747205f447 100644
--- a/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h
+++ b/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h
@@ -401,7 +401,7 @@ bool InstructionSelector::executeMatchTable(
dbgs() << CurrentIdx << ": GIM_CheckIsSafeToFold(MIs["
<< InsnID << "])\n");
assert(State.MIs[InsnID] != nullptr && "Used insn before defined");
- if (!isObviouslySafeToFold(*State.MIs[InsnID])) {
+ if (!isObviouslySafeToFold(*State.MIs[InsnID], *State.MIs[0])) {
if (handleReject() == RejectAndGiveUp)
return false;
}
diff --git a/llvm/lib/CodeGen/GlobalISel/InstructionSelector.cpp b/llvm/lib/CodeGen/GlobalISel/InstructionSelector.cpp
index 2a563c9bf5c..b8503de36fe 100644
--- a/llvm/lib/CodeGen/GlobalISel/InstructionSelector.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/InstructionSelector.cpp
@@ -116,7 +116,13 @@ bool InstructionSelector::isBaseWithConstantOffset(
return true;
}
-bool InstructionSelector::isObviouslySafeToFold(MachineInstr &MI) const {
+bool InstructionSelector::isObviouslySafeToFold(MachineInstr &MI,
+ MachineInstr &IntoMI) const {
+ // Immediate neighbours are already folded.
+ if (MI.getParent() == IntoMI.getParent() &&
+ std::next(MI.getIterator()) == IntoMI.getIterator())
+ return true;
+
return !MI.mayLoadOrStore() && !MI.hasUnmodeledSideEffects() &&
MI.implicit_operands().begin() == MI.implicit_operands().end();
}
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/select-load.mir b/llvm/test/CodeGen/AArch64/GlobalISel/select-load.mir
index 00f6c9418b7..04fa74c5562 100644
--- a/llvm/test/CodeGen/AArch64/GlobalISel/select-load.mir
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/select-load.mir
@@ -30,6 +30,9 @@
define void @load_gep_32_s8_fpr(i8* %addr) { ret void }
define void @load_v2s32(i64 *%addr) { ret void }
+
+ define void @sextload_s32_from_s16(i16 *%addr) { ret void }
+ define void @zextload_s32_from_s16(i16 *%addr) { ret void }
...
---
@@ -468,3 +471,40 @@ body: |
%1(<2 x s32>) = G_LOAD %0 :: (load 8 from %ir.addr)
%d0 = COPY %1(<2 x s32>)
...
+---
+name: sextload_s32_from_s16
+legalized: true
+regBankSelected: true
+
+body: |
+ bb.0:
+ liveins: %w0
+
+ ; CHECK-LABEL: name: sextload_s32_from_s16
+ ; CHECK: [[COPY:%[0-9]+]]:gpr64sp = COPY %x0
+ ; CHECK: [[T0:%[0-9]+]]:gpr32 = LDRSHWui [[COPY]], 0 :: (load 2 from %ir.addr)
+ ; CHECK: %w0 = COPY [[T0]]
+ %0:gpr(p0) = COPY %x0
+ %1:gpr(s16) = G_LOAD %0 :: (load 2 from %ir.addr)
+ %2:gpr(s32) = G_SEXT %1
+ %w0 = COPY %2(s32)
+...
+
+---
+name: zextload_s32_from_s16
+legalized: true
+regBankSelected: true
+
+body: |
+ bb.0:
+ liveins: %w0
+
+ ; CHECK-LABEL: name: zextload_s32_from_s16
+ ; CHECK: [[COPY:%[0-9]+]]:gpr64sp = COPY %x0
+ ; CHECK: [[T0:%[0-9]+]]:gpr32 = LDRHHui [[COPY]], 0 :: (load 2 from %ir.addr)
+ ; CHECK: %w0 = COPY [[T0]]
+ %0:gpr(p0) = COPY %x0
+ %1:gpr(s16) = G_LOAD %0 :: (load 2 from %ir.addr)
+ %2:gpr(s32) = G_ZEXT %1
+ %w0 = COPY %2(s32)
+...
diff --git a/llvm/test/TableGen/GlobalISelEmitter.td b/llvm/test/TableGen/GlobalISelEmitter.td
index dd80640c946..f36240eec3c 100644
--- a/llvm/test/TableGen/GlobalISelEmitter.td
+++ b/llvm/test/TableGen/GlobalISelEmitter.td
@@ -89,9 +89,11 @@ def HasC : Predicate<"Subtarget->hasC()"> { let RecomputePerFunction = 1; }
// CHECK-LABEL: // LLT Objects.
// CHECK-NEXT: enum {
+// CHECK-NEXT: GILLT_s16,
// CHECK-NEXT: GILLT_s32,
// CHECK-NEXT: }
// CHECK-NEXT: const static LLT TypeObjects[] = {
+// CHECK-NEXT: LLT::scalar(16),
// CHECK-NEXT: LLT::scalar(32),
// CHECK-NEXT: };
@@ -845,9 +847,43 @@ def MOVfpimmz : I<(outs FPR32:$dst), (ins f32imm:$imm), [(set FPR32:$dst, fpimmz
def LOAD : I<(outs GPR32:$dst), (ins GPR32:$src1),
[(set GPR32:$dst, (load GPR32:$src1))]>;
-//===- Test a pattern with an MBB operand. --------------------------------===//
+
+//===- Test a simple pattern with a sextload -------------------------------===//
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 23*/ [[LABEL:[0-9]+]],
+// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/2,
+// CHECK-NEXT: GIM_RecordInsn, /*DefineMI*/1, /*MI*/0, /*OpIdx*/1, // MIs[1]
+// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/1, /*Expected*/2,
+// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_SEXT,
+// CHECK-NEXT: // MIs[0] dst
+// CHECK-NEXT: GIM_CheckType, /*MI*/0, /*Op*/0, /*Type*/GILLT_s32,
+// CHECK-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/0, /*RC*/MyTarget::GPR32RegClassID,
+// CHECK-NEXT: // MIs[0] Operand 1
+// CHECK-NEXT: GIM_CheckType, /*MI*/0, /*Op*/1, /*Type*/GILLT_s16,
+// CHECK-NEXT: GIM_CheckOpcode, /*MI*/1, TargetOpcode::G_LOAD,
+// CHECK-NEXT: GIM_CheckNonAtomic, /*MI*/1,
+// CHECK-NEXT: // MIs[1] Operand 0
+// CHECK-NEXT: GIM_CheckType, /*MI*/1, /*Op*/0, /*Type*/GILLT_s16,
+// CHECK-NEXT: // MIs[1] src1
+// CHECK-NEXT: GIM_CheckPointerToAny, /*MI*/1, /*Op*/1, /*SizeInBits*/32,
+// CHECK-NEXT: GIM_CheckRegBankForClass, /*MI*/1, /*Op*/1, /*RC*/MyTarget::GPR32RegClassID,
+// CHECK-NEXT: GIM_CheckIsSafeToFold, /*InsnID*/1,
+// CHECK-NEXT: // (sext:{ *:[i32] } (ld:{ *:[i16] } GPR32:{ *:[i32] }:$src1)<<P:Predicate_unindexedload>>) => (SEXTLOAD:{ *:[i32] } GPR32:{ *:[i32] }:$src1)
+// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::SEXTLOAD,
+// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
+// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/1, /*OpIdx*/1, // src1
+// CHECK-NEXT: GIR_MergeMemOperands, /*InsnID*/0, /*MergeInsnID's*/0, 1, GIU_MergeMemOperands_EndOfList,
+// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
+// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
+// CHECK-NEXT: GIR_Done,
+// CHECK-NEXT: // Label 23: @[[LABEL]]
+
+def SEXTLOAD : I<(outs GPR32:$dst), (ins GPR32:$src1),
+ [(set GPR32:$dst, (sextloadi16 GPR32:$src1))]>;
+
+//===- Test a pattern with an MBB operand. --------------------------------===//
+
+// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 24*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/1,
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_BR,
// CHECK-NEXT: // MIs[0] target
@@ -856,7 +892,7 @@ def LOAD : I<(outs GPR32:$dst), (ins GPR32:$src1),
// CHECK-NEXT: GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/MyTarget::BR,
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT: GIR_Done,
-// CHECK-NEXT: // Label 23: @[[LABEL]]
+// CHECK-NEXT: // Label 24: @[[LABEL]]
def BR : I<(outs), (ins unknown:$target),
[(br bb:$target)]>;
diff --git a/llvm/utils/TableGen/CodeGenDAGPatterns.cpp b/llvm/utils/TableGen/CodeGenDAGPatterns.cpp
index 3b400c1262e..c07a7267bbe 100644
--- a/llvm/utils/TableGen/CodeGenDAGPatterns.cpp
+++ b/llvm/utils/TableGen/CodeGenDAGPatterns.cpp
@@ -2744,8 +2744,10 @@ void TreePattern::dump() const { print(errs()); }
// CodeGenDAGPatterns implementation
//
-CodeGenDAGPatterns::CodeGenDAGPatterns(RecordKeeper &R) :
- Records(R), Target(R), LegalVTS(Target.getLegalValueTypes()) {
+CodeGenDAGPatterns::CodeGenDAGPatterns(RecordKeeper &R,
+ PatternRewriterFn PatternRewriter)
+ : Records(R), Target(R), LegalVTS(Target.getLegalValueTypes()),
+ PatternRewriter(PatternRewriter) {
Intrinsics = CodeGenIntrinsicTable(Records, false);
TgtIntrinsics = CodeGenIntrinsicTable(Records, true);
@@ -3536,6 +3538,8 @@ void CodeGenDAGPatterns::ParseInstructions() {
TreePattern *I = TheInst.getPattern();
if (!I) continue; // No pattern.
+ if (PatternRewriter)
+ PatternRewriter(I);
// FIXME: Assume only the first tree is the pattern. The others are clobber
// nodes.
TreePatternNode *Pattern = I->getTree(0);
@@ -3936,6 +3940,8 @@ void CodeGenDAGPatterns::ParsePatterns() {
Temp.getOnlyTree()->hasPossibleType()) {
ListInit *Preds = CurPattern->getValueAsListInit("Predicates");
int Complexity = CurPattern->getValueAsInt("AddedComplexity");
+ if (PatternRewriter)
+ PatternRewriter(Pattern);
AddPatternToMatch(
Pattern,
PatternToMatch(
diff --git a/llvm/utils/TableGen/CodeGenDAGPatterns.h b/llvm/utils/TableGen/CodeGenDAGPatterns.h
index 1f7e9fb98d3..d3bb99a5a8f 100644
--- a/llvm/utils/TableGen/CodeGenDAGPatterns.h
+++ b/llvm/utils/TableGen/CodeGenDAGPatterns.h
@@ -25,6 +25,7 @@
#include "llvm/Support/MathExtras.h"
#include <algorithm>
#include <array>
+#include <functional>
#include <map>
#include <set>
#include <vector>
@@ -780,6 +781,7 @@ public:
const std::vector<TreePatternNode*> &getTrees() const { return Trees; }
unsigned getNumTrees() const { return Trees.size(); }
TreePatternNode *getTree(unsigned i) const { return Trees[i]; }
+ void setTree(unsigned i, TreePatternNode *Tree) { Trees[i] = Tree; }
TreePatternNode *getOnlyTree() const {
assert(Trees.size() == 1 && "Doesn't have exactly one pattern!");
return Trees[0];
@@ -1029,8 +1031,12 @@ class CodeGenDAGPatterns {
TypeSetByHwMode LegalVTS;
+ using PatternRewriterFn = std::function<void (TreePattern *)>;
+ PatternRewriterFn PatternRewriter;
+
public:
- CodeGenDAGPatterns(RecordKeeper &R);
+ CodeGenDAGPatterns(RecordKeeper &R,
+ PatternRewriterFn PatternRewriter = nullptr);
CodeGenTarget &getTargetInfo() { return Target; }
const CodeGenTarget &getTargetInfo() const { return Target; }
diff --git a/llvm/utils/TableGen/GlobalISelEmitter.cpp b/llvm/utils/TableGen/GlobalISelEmitter.cpp
index 08649d7f9b5..8a0d625ca44 100644
--- a/llvm/utils/TableGen/GlobalISelEmitter.cpp
+++ b/llvm/utils/TableGen/GlobalISelEmitter.cpp
@@ -2356,6 +2356,9 @@ private:
Expected<RuleMatcher> runOnPattern(const PatternToMatch &P);
void declareSubtargetFeature(Record *Predicate);
+
+ TreePatternNode *fixupPatternNode(TreePatternNode *N);
+ void fixupPatternTrees(TreePattern *P);
};
void GlobalISelEmitter::gatherNodeEquivs() {
@@ -2377,8 +2380,8 @@ Record *GlobalISelEmitter::findNodeEquiv(Record *N) const {
}
GlobalISelEmitter::GlobalISelEmitter(RecordKeeper &RK)
- : RK(RK), CGP(RK), Target(CGP.getTargetInfo()),
- CGRegs(RK, Target.getHwModes()) {}
+ : RK(RK), CGP(RK, [&](TreePattern *P) { fixupPatternTrees(P); }),
+ Target(CGP.getTargetInfo()), CGRegs(RK, Target.getHwModes()) {}
//===- Emitter ------------------------------------------------------------===//
@@ -3233,6 +3236,7 @@ void GlobalISelEmitter::run(raw_ostream &OS) {
// Look through the SelectionDAG patterns we found, possibly emitting some.
for (const PatternToMatch &Pat : CGP.ptms()) {
++NumPatternTotal;
+
auto MatcherOrErr = runOnPattern(Pat);
// The pattern analysis can fail, indicating an unsupported pattern.
@@ -3483,6 +3487,75 @@ void GlobalISelEmitter::declareSubtargetFeature(Record *Predicate) {
Predicate, SubtargetFeatureInfo(Predicate, SubtargetFeatures.size()));
}
+TreePatternNode *GlobalISelEmitter::fixupPatternNode(TreePatternNode *N) {
+ if (!N->isLeaf()) {
+ for (unsigned I = 0, E = N->getNumChildren(); I < E; ++I) {
+ TreePatternNode *OrigChild = N->getChild(I);
+ TreePatternNode *NewChild = fixupPatternNode(OrigChild);
+ if (OrigChild != NewChild)
+ N->setChild(I, NewChild);
+ }
+
+ if (N->getOperator()->getName() == "ld") {
+ // If it's a signext-load we need to adapt the pattern slightly. We need
+ // to split the node into (sext (ld ...)), remove the <<signext>> predicate,
+ // and then apply the <<signextTY>> predicate by updating the result type
+ // of the load.
+ //
+ // For example:
+ // (ld:[i32] [iPTR])<<unindexed>><<signext>><<signexti16>>
+ // must be transformed into:
+ // (sext:[i32] (ld:[i16] [iPTR])<<unindexed>>)
+ //
+ // Likewise for zeroext-load.
+
+ std::vector<TreePredicateFn> Predicates;
+ bool IsSignExtLoad = false;
+ bool IsZeroExtLoad = false;
+ Record *MemVT = nullptr;
+ for (const auto &P : N->getPredicateFns()) {
+ if (P.isLoad() && P.isSignExtLoad()) {
+ IsSignExtLoad = true;
+ continue;
+ }
+ if (P.isLoad() && P.isZeroExtLoad()) {
+ IsZeroExtLoad = true;
+ continue;
+ }
+ if (P.isLoad() && P.getMemoryVT()) {
+ MemVT = P.getMemoryVT();
+ continue;
+ }
+ Predicates.push_back(P);
+ }
+
+ if ((IsSignExtLoad || IsZeroExtLoad) && MemVT) {
+ assert(((IsSignExtLoad && !IsZeroExtLoad) ||
+ (!IsSignExtLoad && IsZeroExtLoad)) &&
+ "IsSignExtLoad and IsZeroExtLoad are mutually exclusive");
+ TreePatternNode *Ext = new TreePatternNode(
+ RK.getDef(IsSignExtLoad ? "sext" : "zext"), {N}, 1);
+ Ext->setType(0, N->getType(0));
+ N->clearPredicateFns();
+ N->setPredicateFns(Predicates);
+ N->setType(0, getValueType(MemVT));
+ return Ext;
+ }
+ }
+ }
+
+ return N;
+}
+
+void GlobalISelEmitter::fixupPatternTrees(TreePattern *P) {
+ for (unsigned I = 0, E = P->getNumTrees(); I < E; ++I) {
+ TreePatternNode *OrigTree = P->getTree(I);
+ TreePatternNode *NewTree = fixupPatternNode(OrigTree);
+ if (OrigTree != NewTree)
+ P->setTree(I, NewTree);
+ }
+}
+
} // end anonymous namespace
//===----------------------------------------------------------------------===//
OpenPOWER on IntegriCloud