summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Sanders <daniel_l_sanders@apple.com>2017-08-02 11:03:36 +0000
committerDaniel Sanders <daniel_l_sanders@apple.com>2017-08-02 11:03:36 +0000
commit078572b6b1def7297405ee62a46cd8005f5ff4fc (patch)
treef15a850dda8b406ac41cc502ffda8752d2d5286c
parent59e8ab511298abaf1c94b3d0ee43b6586bfe32d8 (diff)
downloadbcm5719-llvm-078572b6b1def7297405ee62a46cd8005f5ff4fc.tar.gz
bcm5719-llvm-078572b6b1def7297405ee62a46cd8005f5ff4fc.zip
[globalisel][tablegen] Do not merge memoperands from instructions that weren't in the match.
Summary: Fix a bug discovered in an out-of-tree target where memoperands from pseudo-instructions that weren't part of the match were being merged into the result instructions as part of GIR_MergeMemOperands. This bug was caused by a change to the handling of State.MIs between rules when the state machine tables were fused into a single table. Previously, each rule would reset State.MIs using State.MIs.resize(1) but this is no longer done, as a result stale data is occasionally left in some elements of State.MIs. Most opcodes aren't affected by this but GIR_MergeMemOperands merges all memoperands from the intructions recorded in State.MIs into the result instruction. Suppose for example, we processed but rejected the following pattern: (signextend (load x)) at this point, State.MIs contains the signextend and the load. Now suppose we process and accept this pattern: (add x, y) at this point, State.MIs contains the add as well as the (now irrelevant) load. When GIR_MergeMemOperands is processed, the memoperands from that irrelevant load will be merged into the result instruction even though it was not part of the match. Bringing back the State.MIs.resize(1) would fix the problem but it would limit our ability to optimize the table in the future. Instead, this patch fixes the problem by explicitly stating which instructions should be merged into the result. There's no direct test case in this commit because a test case would be very brittle. However, at the time of writing this should fix the failures in http://green.lab.llvm.org/green/job/Compiler_Verifiers_GlobalISEL/ as well as a failure in test/CodeGen/ARM/GlobalISel/arm-isel.ll when expensive checks are enabled. Reviewers: ab, t.p.northover, qcolombet, rovka, aditya_nandakumar Subscribers: fhahn, kristof.beyls, igorb, llvm-commits Differential Revision: https://reviews.llvm.org/D36094 llvm-svn: 309804
-rw-r--r--llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h8
-rw-r--r--llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h14
-rw-r--r--llvm/test/TableGen/GlobalISelEmitter.td28
-rw-r--r--llvm/utils/TableGen/GlobalISelEmitter.cpp29
4 files changed, 60 insertions, 19 deletions
diff --git a/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h b/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h
index 78e1b3f135e..e105379b5db 100644
--- a/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h
+++ b/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h
@@ -195,6 +195,8 @@ enum {
GIR_ConstrainSelectedInstOperands,
/// Merge all memory operands into instruction.
/// - InsnID - Instruction ID to modify
+ /// - MergeInsnID... - One or more Instruction ID to merge into the result.
+ /// - -1 - Terminates the list of instructions to merge.
GIR_MergeMemOperands,
/// Erase from parent.
/// - InsnID - Instruction ID to erase
@@ -204,6 +206,12 @@ enum {
GIR_Done,
};
+enum {
+ /// Indicates the end of the variable-length MergeInsnID list in a
+ /// GIR_MergeMemOperands opcode.
+ GIU_MergeMemOperands_EndOfList = -1,
+};
+
/// Provides the logic to select generic machine instructions.
class InstructionSelector {
public:
diff --git a/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h b/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h
index 808ac4f3ab9..902c4acd7c2 100644
--- a/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h
+++ b/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h
@@ -370,11 +370,17 @@ bool InstructionSelector::executeMatchTable(
case GIR_MergeMemOperands: {
int64_t InsnID = MatchTable[CurrentIdx++];
assert(OutMIs[InsnID] && "Attempted to add to undefined instruction");
- for (const auto *FromMI : State.MIs)
- for (const auto &MMO : FromMI->memoperands())
- OutMIs[InsnID].addMemOperand(MMO);
+
DEBUG(dbgs() << CurrentIdx << ": GIR_MergeMemOperands(OutMIs[" << InsnID
- << "])\n");
+ << "]");
+ int64_t MergeInsnID = GIU_MergeMemOperands_EndOfList;
+ while ((MergeInsnID = MatchTable[CurrentIdx++]) !=
+ GIU_MergeMemOperands_EndOfList) {
+ DEBUG(dbgs() << ", MIs[" << MergeInsnID << "]");
+ for (const auto &MMO : State.MIs[MergeInsnID]->memoperands())
+ OutMIs[InsnID].addMemOperand(MMO);
+ }
+ DEBUG(dbgs() << ")\n");
break;
}
case GIR_EraseFromParent: {
diff --git a/llvm/test/TableGen/GlobalISelEmitter.td b/llvm/test/TableGen/GlobalISelEmitter.td
index 8c32615fe08..11400d8f6ae 100644
--- a/llvm/test/TableGen/GlobalISelEmitter.td
+++ b/llvm/test/TableGen/GlobalISelEmitter.td
@@ -117,7 +117,7 @@ def HasC : Predicate<"Subtarget->hasC()"> { let RecomputePerFunction = 1; }
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/1, // src1
// CHECK-NEXT: GIR_ComplexRenderer, /*InsnID*/0, /*RendererID*/1,
// CHECK-NEXT: GIR_ComplexRenderer, /*InsnID*/0, /*RendererID*/0,
-// CHECK-NEXT: GIR_MergeMemOperands, /*InsnID*/0,
+// CHECK-NEXT: GIR_MergeMemOperands, /*InsnID*/0, /*MergeInsnID's*/0, GIU_MergeMemOperands_EndOfList,
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT: GIR_Done,
@@ -168,7 +168,7 @@ def : Pat<(select GPR32:$src1, complex:$src2, (select GPR32:$src3, complex:$src4
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/1, /*OpIdx*/1, // src3
// CHECK-NEXT: GIR_ComplexRenderer, /*InsnID*/0, /*RendererID*/1,
// CHECK-NEXT: GIR_ComplexRenderer, /*InsnID*/0, /*RendererID*/2,
-// CHECK-NEXT: GIR_MergeMemOperands, /*InsnID*/0,
+// 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,
@@ -221,7 +221,7 @@ def ADD : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2),
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::MOV,
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/2, // src1
-// CHECK-NEXT: GIR_MergeMemOperands, /*InsnID*/0,
+// CHECK-NEXT: GIR_MergeMemOperands, /*InsnID*/0, /*MergeInsnID's*/0, GIU_MergeMemOperands_EndOfList,
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT: GIR_Done,
@@ -262,7 +262,7 @@ def MOV : I<(outs GPR32:$dst), (ins GPR32:$src1),
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/1, /*OpIdx*/1, // src1
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/1, /*OpIdx*/2, // src2
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/2, // src3
-// CHECK-NEXT: GIR_MergeMemOperands, /*InsnID*/0,
+// 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,
@@ -299,7 +299,7 @@ def MOV : I<(outs GPR32:$dst), (ins GPR32:$src1),
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/1, /*OpIdx*/1, // src1
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/1, /*OpIdx*/2, // src2
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/1, // src3
-// CHECK-NEXT: GIR_MergeMemOperands, /*InsnID*/0,
+// 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,
@@ -330,7 +330,7 @@ def MULADD : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2, GPR32:$src3),
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/2, // src2
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/1, // src1
-// CHECK-NEXT: GIR_MergeMemOperands, /*InsnID*/0,
+// CHECK-NEXT: GIR_MergeMemOperands, /*InsnID*/0, /*MergeInsnID's*/0, GIU_MergeMemOperands_EndOfList,
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT: GIR_Done,
@@ -384,7 +384,7 @@ def MUL : I<(outs GPR32:$dst), (ins GPR32:$src2, GPR32:$src1),
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/1, /*OpIdx*/2, // src2
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/2, /*OpIdx*/1, // src3
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/2, /*OpIdx*/2, // src4
-// CHECK-NEXT: GIR_MergeMemOperands, /*InsnID*/0,
+// CHECK-NEXT: GIR_MergeMemOperands, /*InsnID*/0, /*MergeInsnID's*/0, 1, 2, GIU_MergeMemOperands_EndOfList,
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT: GIR_Done,
@@ -415,7 +415,7 @@ def INSNBOB : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2, GPR32:$src3, G
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/1, // src1
// CHECK-NEXT: GIR_ComplexRenderer, /*InsnID*/0, /*RendererID*/0,
-// CHECK-NEXT: GIR_MergeMemOperands, /*InsnID*/0,
+// CHECK-NEXT: GIR_MergeMemOperands, /*InsnID*/0, /*MergeInsnID's*/0, GIU_MergeMemOperands_EndOfList,
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT: GIR_Done,
@@ -444,7 +444,7 @@ def : Pat<(sub GPR32:$src1, complex:$src2), (INSN1 GPR32:$src1, complex:$src2)>;
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
// CHECK-NEXT: GIR_AddImm, /*InsnID*/0, /*Imm*/-1,
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/1, // src1
-// CHECK-NEXT: GIR_MergeMemOperands, /*InsnID*/0,
+// CHECK-NEXT: GIR_MergeMemOperands, /*InsnID*/0, /*MergeInsnID's*/0, GIU_MergeMemOperands_EndOfList,
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT: GIR_Done,
@@ -474,7 +474,7 @@ def XORI : I<(outs GPR32:$dst), (ins m1:$src2, GPR32:$src1),
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
// CHECK-NEXT: GIR_AddRegister, /*InsnID*/0, MyTarget::R0,
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/1, // src1
-// CHECK-NEXT: GIR_MergeMemOperands, /*InsnID*/0,
+// CHECK-NEXT: GIR_MergeMemOperands, /*InsnID*/0, /*MergeInsnID's*/0, GIU_MergeMemOperands_EndOfList,
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT: GIR_Done,
@@ -505,7 +505,7 @@ def XOR : I<(outs GPR32:$dst), (ins Z:$src2, GPR32:$src1),
// CHECK-NEXT: GIR_AddImm, /*InsnID*/0, /*Imm*/-1,
// CHECK-NEXT: GIR_AddRegister, /*InsnID*/0, MyTarget::R0,
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/1, // src1
-// CHECK-NEXT: GIR_MergeMemOperands, /*InsnID*/0,
+// CHECK-NEXT: GIR_MergeMemOperands, /*InsnID*/0, /*MergeInsnID's*/0, GIU_MergeMemOperands_EndOfList,
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT: GIR_Done,
@@ -537,7 +537,7 @@ def XORlike : I<(outs GPR32:$dst), (ins m1Z:$src2, GPR32:$src1),
// CHECK-NEXT: GIR_AddRegister, /*InsnID*/0, MyTarget::R0,
// CHECK-NEXT: GIR_AddRegister, /*InsnID*/0, MyTarget::R0,
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/1, // src1
-// CHECK-NEXT: GIR_MergeMemOperands, /*InsnID*/0,
+// CHECK-NEXT: GIR_MergeMemOperands, /*InsnID*/0, /*MergeInsnID's*/0, GIU_MergeMemOperands_EndOfList,
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT: GIR_Done,
@@ -569,7 +569,7 @@ def XORManyDefaults : I<(outs GPR32:$dst), (ins m1Z:$src3, Z:$src2, GPR32:$src1)
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
// CHECK-NEXT: GIR_AddRegister, /*InsnID*/0, MyTarget::R0,
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/1, // Wm
-// CHECK-NEXT: GIR_MergeMemOperands, /*InsnID*/0,
+// CHECK-NEXT: GIR_MergeMemOperands, /*InsnID*/0, /*MergeInsnID's*/0, GIU_MergeMemOperands_EndOfList,
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT: GIR_Done,
@@ -612,7 +612,7 @@ def : Pat<(i32 (bitconvert FPR32:$src1)),
// CHECK-NEXT: // 1:i32 => (MOV1:i32)
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::MOV1,
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
-// CHECK-NEXT: GIR_MergeMemOperands, /*InsnID*/0,
+// CHECK-NEXT: GIR_MergeMemOperands, /*InsnID*/0, /*MergeInsnID's*/0, GIU_MergeMemOperands_EndOfList,
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT: GIR_Done,
diff --git a/llvm/utils/TableGen/GlobalISelEmitter.cpp b/llvm/utils/TableGen/GlobalISelEmitter.cpp
index 9181272adc2..13303ebc2b1 100644
--- a/llvm/utils/TableGen/GlobalISelEmitter.cpp
+++ b/llvm/utils/TableGen/GlobalISelEmitter.cpp
@@ -460,9 +460,11 @@ class RuleMatcher {
/// have succeeded.
std::vector<std::unique_ptr<MatchAction>> Actions;
+ typedef std::map<const InstructionMatcher *, unsigned>
+ DefinedInsnVariablesMap;
/// A map of instruction matchers to the local variables created by
/// emitCaptureOpcodes().
- std::map<const InstructionMatcher *, unsigned> InsnVariableIDs;
+ DefinedInsnVariablesMap InsnVariableIDs;
/// ID for the next instruction variable defined with defineInsnVar()
unsigned NextInsnVarID;
@@ -488,6 +490,16 @@ public:
unsigned defineInsnVar(MatchTable &Table, const InstructionMatcher &Matcher,
unsigned InsnVarID, unsigned OpIdx);
unsigned getInsnVarID(const InstructionMatcher &InsnMatcher) const;
+ DefinedInsnVariablesMap::const_iterator defined_insn_vars_begin() const {
+ return InsnVariableIDs.begin();
+ }
+ DefinedInsnVariablesMap::const_iterator defined_insn_vars_end() const {
+ return InsnVariableIDs.end();
+ }
+ iterator_range<typename DefinedInsnVariablesMap::const_iterator>
+ defined_insn_vars() const {
+ return make_range(defined_insn_vars_begin(), defined_insn_vars_end());
+ }
void emitCaptureOpcodes(MatchTable &Table);
@@ -1452,6 +1464,21 @@ public:
Table << MatchTable::Opcode("GIR_MergeMemOperands")
<< MatchTable::Comment("InsnID") << MatchTable::IntValue(InsnID)
+ << MatchTable::Comment("MergeInsnID's");
+ // Emit the ID's for all the instructions that are matched by this rule.
+ // TODO: Limit this to matched instructions that mayLoad/mayStore or have
+ // some other means of having a memoperand. Also limit this to emitted
+ // instructions that expect to have a memoperand too. For example,
+ // (G_SEXT (G_LOAD x)) that results in separate load and sign-extend
+ // instructions shouldn't put the memoperand on the sign-extend since
+ // it has no effect there.
+ std::vector<unsigned> MergeInsnIDs;
+ for (const auto &IDMatcherPair : Rule.defined_insn_vars())
+ MergeInsnIDs.push_back(IDMatcherPair.second);
+ std::sort(MergeInsnIDs.begin(), MergeInsnIDs.end());
+ for (const auto &MergeInsnID : MergeInsnIDs)
+ Table << MatchTable::IntValue(MergeInsnID);
+ Table << MatchTable::NamedValue("GIU_MergeMemOperands_EndOfList")
<< MatchTable::LineBreak << MatchTable::Opcode("GIR_EraseFromParent")
<< MatchTable::Comment("InsnID")
<< MatchTable::IntValue(RecycleInsnID) << MatchTable::LineBreak;
OpenPOWER on IntegriCloud