Skip to content

Commit 1b11ebe

Browse files
committed
[LoopInterchange] Add metadata to control loop-interchange
This patch adds metadata to enable/disable the loop-interchange for a loop nest. This is a prelude to introduce a new pragma directive for loop-interchange, like other loop optimizations (unroll, vectorize, distribute, etc.) have.
1 parent dc9d217 commit 1b11ebe

File tree

2 files changed

+401
-0
lines changed

2 files changed

+401
-0
lines changed

llvm/lib/Transforms/Scalar/LoopInterchange.cpp

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,16 @@ using namespace llvm;
5151

5252
#define DEBUG_TYPE "loop-interchange"
5353

54+
/// @{
55+
/// Metadata attribute names
56+
static const char *const LLVMLoopInterchangeFollowupAll =
57+
"llvm.loop.interchange.followup_all";
58+
static const char *const LLVMLoopInterchangeFollowupOuter =
59+
"llvm.loop.interchange.followup_outer";
60+
static const char *const LLVMLoopInterchangeFollowupInner =
61+
"llvm.loop.interchange.followup_inner";
62+
/// @}
63+
5464
STATISTIC(LoopsInterchanged, "Number of loops interchanged");
5565

5666
static cl::opt<int> LoopInterchangeCostThreshold(
@@ -65,6 +75,14 @@ static cl::opt<unsigned int> MaxMemInstrCount(
6575
"in the dependency matrix. Higher value may lead to more interchanges "
6676
"at the cost of compile-time"));
6777

78+
// Whether to apply by default.
79+
// TODO: Once this pass is enabled by default, remove this option and use the
80+
// value of PipelineTuningOptions.
81+
static cl::opt<bool> OnlyWhenForced(
82+
"loop-interchange-only-when-forced", cl::init(false), cl::ReallyHidden,
83+
cl::desc(
84+
"Apply interchanges only when explicitly specified metadata exists"));
85+
6886
namespace {
6987

7088
using LoopVector = SmallVector<Loop *, 8>;
@@ -297,6 +315,16 @@ static bool isComputableLoopNest(ScalarEvolution *SE,
297315
return true;
298316
}
299317

318+
static std::optional<bool> findMetadata(Loop *L) {
319+
auto Value = findStringMetadataForLoop(L, "llvm.loop.interchange.enable");
320+
if (!Value)
321+
return std::nullopt;
322+
323+
const MDOperand *Op = *Value;
324+
assert(Op && mdconst::hasa<ConstantInt>(*Op) && "invalid metadata");
325+
return mdconst::extract<ConstantInt>(*Op)->getZExtValue();
326+
}
327+
300328
namespace {
301329

302330
/// LoopInterchangeLegality checks if it is legal to interchange the loop.
@@ -504,6 +532,10 @@ struct LoopInterchange {
504532
CostMap[LoopCosts[i].first] = i;
505533
}
506534
}
535+
536+
if (OnlyWhenForced)
537+
return processEnabledLoop(LoopList, DependencyMatrix, CostMap);
538+
507539
// We try to achieve the globally optimal memory access for the loopnest,
508540
// and do interchange based on a bubble-sort fasion. We start from
509541
// the innermost loop, move it outwards to the best possible position
@@ -532,6 +564,8 @@ struct LoopInterchange {
532564
Loop *InnerLoop = LoopList[InnerLoopId];
533565
LLVM_DEBUG(dbgs() << "Processing InnerLoopId = " << InnerLoopId
534566
<< " and OuterLoopId = " << OuterLoopId << "\n");
567+
if (findMetadata(OuterLoop) == false || findMetadata(InnerLoop) == false)
568+
return false;
535569
LoopInterchangeLegality LIL(OuterLoop, InnerLoop, SE, ORE);
536570
if (!LIL.canInterchangeLoops(InnerLoopId, OuterLoopId, DependencyMatrix)) {
537571
LLVM_DEBUG(dbgs() << "Not interchanging loops. Cannot prove legality.\n");
@@ -569,6 +603,48 @@ struct LoopInterchange {
569603

570604
return true;
571605
}
606+
607+
bool processEnabledLoop(SmallVectorImpl<Loop *> &LoopList,
608+
std::vector<std::vector<char>> &DependencyMatrix,
609+
const DenseMap<const Loop *, unsigned> &CostMap) {
610+
bool Changed = false;
611+
for (unsigned InnerLoopId = LoopList.size() - 1; InnerLoopId > 0;
612+
InnerLoopId--) {
613+
unsigned OuterLoopId = InnerLoopId - 1;
614+
if (findMetadata(LoopList[OuterLoopId]) != true)
615+
continue;
616+
617+
MDNode *MDOrigLoopID = LoopList[OuterLoopId]->getLoopID();
618+
bool Interchanged =
619+
processLoop(LoopList[InnerLoopId], LoopList[OuterLoopId], InnerLoopId,
620+
OuterLoopId, DependencyMatrix, CostMap);
621+
622+
// TODO: Consolidate the duplicate code in `processLoopList`.
623+
if (Interchanged) {
624+
std::swap(LoopList[OuterLoopId], LoopList[InnerLoopId]);
625+
// Update the DependencyMatrix
626+
interChangeDependencies(DependencyMatrix, InnerLoopId, OuterLoopId);
627+
628+
LLVM_DEBUG(dbgs() << "Dependency matrix after interchange:\n";
629+
printDepMatrix(DependencyMatrix));
630+
}
631+
632+
std::optional<MDNode *> MDOuterLoopID =
633+
makeFollowupLoopID(MDOrigLoopID, {LLVMLoopInterchangeFollowupAll,
634+
LLVMLoopInterchangeFollowupOuter});
635+
if (MDOuterLoopID)
636+
LoopList[OuterLoopId]->setLoopID(*MDOuterLoopID);
637+
638+
std::optional<MDNode *> MDInnerLoopID =
639+
makeFollowupLoopID(MDOrigLoopID, {LLVMLoopInterchangeFollowupAll,
640+
LLVMLoopInterchangeFollowupInner});
641+
if (MDInnerLoopID)
642+
LoopList[InnerLoopId]->setLoopID(*MDInnerLoopID);
643+
644+
Changed |= Interchanged;
645+
}
646+
return Changed;
647+
}
572648
};
573649

574650
} // end anonymous namespace

0 commit comments

Comments
 (0)