Skip to content

Commit db3302d

Browse files
authored
[CodeGen] Fix incorrect rematerialization order in rematerializer (llvm#189485)
When rematerializing DAGs of registers wherein multiple paths exist between some regsters of the DAG, it is possible that the rematerialization determines an incorrect rematerialization order that does not ensure that a register's dependencies are rematerialized before itself; an invariant that is otherwise required. This fixes that using a simpler recursive logic to determine a correct rematerialization order that honors this invariant. A minimal unit test is added that fails on the current implementation.
1 parent cb1a912 commit db3302d

2 files changed

Lines changed: 57 additions & 33 deletions

File tree

llvm/lib/CodeGen/Rematerializer.cpp

Lines changed: 19 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -94,40 +94,26 @@ Rematerializer::rematerializeToPos(RegisterIdx RootIdx, unsigned UseRegion,
9494
assert(!DRI.DependencyMap.contains(RootIdx));
9595
LLVM_DEBUG(dbgs() << "Rematerializing " << printID(RootIdx) << '\n');
9696

97-
// Traverse the root's dependency DAG depth-first to find the set of
98-
// registers we must rematerialize along with it and a legal order to
99-
// rematerialize them in.
100-
SmallVector<RegisterIdx, 4> DepDAG{RootIdx};
101-
SmallSetVector<RegisterIdx, 8> RematOrder;
102-
RematOrder.insert(RootIdx);
103-
do {
104-
RegisterIdx RegIdx = DepDAG.pop_back_val();
105-
for (const Reg::Dependency &Dep : getReg(RegIdx).Dependencies) {
106-
// The dependency may already have a rematerialization ready to use.
107-
if (DRI.DependencyMap.contains(Dep.RegIdx))
108-
continue;
109-
// We may have already seen the dependency in the dependency DAG.
110-
if (RematOrder.contains(Dep.RegIdx))
111-
continue;
112-
DepDAG.push_back(Dep.RegIdx);
113-
RematOrder.insert(Dep.RegIdx);
114-
}
115-
} while (!DepDAG.empty());
116-
117-
// Rematerialize all necessary registers in the root's dependency DAG. At each
118-
// rematerialization, dependencies should already be available.
119-
RegisterIdx LastNewIdx;
120-
for (RegisterIdx RegIdx : reverse(RematOrder)) {
121-
assert(!DRI.DependencyMap.contains(RegIdx) && "useless remat");
122-
SmallVector<Reg::Dependency, 2> Dependencies;
123-
for (const Reg::Dependency &Dep : getReg(RegIdx).Dependencies)
124-
Dependencies.emplace_back(Dep.MOIdx, DRI.DependencyMap.at(Dep.RegIdx));
125-
LastNewIdx =
126-
rematerializeReg(RegIdx, UseRegion, InsertPos, std::move(Dependencies));
127-
DRI.DependencyMap.insert({RegIdx, LastNewIdx});
97+
SmallVector<Reg::Dependency, 2> NewDeps;
98+
// Copy all dependencies because recursive rematerialization of dependencies
99+
// may invalidate references to the backing vector of registers.
100+
SmallVector<Reg::Dependency, 2> OldDeps(getReg(RootIdx).Dependencies);
101+
for (const Reg::Dependency &Dep : OldDeps) {
102+
// Recursively rematerialize required dependencies at the same position as
103+
// the root. Registers form a DAG so the recursion is guaranteed to
104+
// terminate.
105+
auto RematIdx = DRI.DependencyMap.find(Dep.RegIdx);
106+
RegisterIdx NewDepRegIdx;
107+
if (RematIdx == DRI.DependencyMap.end())
108+
NewDepRegIdx = rematerializeToPos(Dep.RegIdx, UseRegion, InsertPos, DRI);
109+
else
110+
NewDepRegIdx = RematIdx->second;
111+
NewDeps.emplace_back(Dep.MOIdx, NewDepRegIdx);
128112
}
129-
130-
return LastNewIdx;
113+
RegisterIdx NewIdx =
114+
rematerializeReg(RootIdx, UseRegion, InsertPos, std::move(NewDeps));
115+
DRI.DependencyMap.insert({RootIdx, NewIdx});
116+
return NewIdx;
131117
}
132118

133119
void Rematerializer::transferUser(RegisterIdx FromRegIdx, RegisterIdx ToRegIdx,

llvm/unittests/CodeGen/RematerializerTest.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,44 @@ body: |
318318
EXPECT_TRUE(getMF().verify());
319319
}
320320

321+
/// To rematerialize %3 along with all its dependencies before its only use in
322+
/// bb.1, we must first rematerialize %0 and %1 (in any order), then %2, and
323+
/// finally %3. The rematerializer had a rematerialization order bug wherein,
324+
/// because %0 is also used directly in the MI defining %3, it was
325+
/// rematerialized after %2, breaking the invariant that dependencies of a
326+
/// register must always be rematerialized before the register itself.
327+
TEST_F(RematerializerTest, MultiplePathsRematOrder) {
328+
StringRef MIR = R"(
329+
name: MultiplePathsRematOrder
330+
tracksRegLiveness: true
331+
machineFunctionInfo:
332+
isEntryFunction: true
333+
body: |
334+
bb.0:
335+
%0:vgpr_32 = nofpexcept V_CVT_I32_F64_e32 0, implicit $exec, implicit $mode
336+
%1:vgpr_32 = nofpexcept V_CVT_I32_F64_e32 1, implicit $exec, implicit $mode
337+
%2:vgpr_32 = V_ADD_U32_e32 %0, %1, implicit $exec
338+
%3:vgpr_32 = V_ADD_U32_e32 %0, %2, implicit $exec
339+
340+
bb.1:
341+
S_NOP 0, implicit %3
342+
S_ENDPGM 0
343+
...
344+
)";
345+
ASSERT_TRUE(parseMIRAndInit(MIR, "MultiplePathsRematOrder"));
346+
Rematerializer &Remater = getRematerializer();
347+
Rematerializer::DependencyReuseInfo DRI;
348+
349+
const unsigned MBB1 = 1;
350+
const RegisterIdx Add02 = 3;
351+
352+
// This call would previously fail.
353+
Remater.rematerializeToRegion(Add02, MBB1, DRI);
354+
355+
Remater.updateLiveIntervals();
356+
EXPECT_TRUE(getMF().verify());
357+
}
358+
321359
/// Rematerializes a single register to multiple regions, tracking that
322360
/// rematerializations are linked correctly and making sure that the original
323361
/// register is deleted automatically when it no longer has any uses.

0 commit comments

Comments
 (0)