Skip to content

Commit 4709bf4

Browse files
author
Joe Shajrawi
authored
Merge pull request #20232 from shajrawi/dom_new_scope
[Exclusivity] Add new dominating access checks in loop pre-headers
2 parents 7af28e8 + 2e237f4 commit 4709bf4

File tree

2 files changed

+385
-9
lines changed

2 files changed

+385
-9
lines changed

lib/SILOptimizer/Transforms/AccessEnforcementDom.cpp

Lines changed: 218 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212
///
13+
/// This pass contains two optimizations:
14+
///
15+
/// 1) DominatedAccessRemoval:
1316
/// This function pass removes dynamic access enforcement based on dominance.
1417
///
1518
/// General case:
@@ -20,19 +23,46 @@
2023
/// begin_access A [no_nested_conflict] // dominated by the first access
2124
/// load/store
2225
/// end_access A
26+
///
2327
/// The second access scope does not need to be emitted.
2428
///
25-
/// Note: This optimization must be aware of all possible access to a Class or
29+
/// 2) LoopDominatingAccessAdder:
30+
/// This function pass adds new dominating accesses to loop's dynamic accesses
31+
///
32+
/// General case:
33+
/// <loop preheader>
34+
/// A = ref_element_addr
35+
/// <loop>
36+
/// begin_access A [dynamic] [no_nested_conflict]
37+
///
38+
/// Adding an empty begin_access A in the preheader would allow us to
39+
/// turn the loop's access to [static]
40+
///
41+
/// Note 1: This optimization must be aware of all possible access to a Class or
2642
/// Global address. This includes unpaired access instructions and keypath
2743
/// entry points. Ignoring any access pattern would weaken enforcement.
44+
///
45+
/// Note 2: We either need to also flag the dominating access specially, or,
46+
/// we need to do this in the last-chance pipeline, with a guarantee that no,
47+
/// access marker removal is done after it.
48+
///
49+
/// Note 3: LoopDominatedAccessAdder makes some key assumptions:
50+
/// 1) Precondition: There are no unpaired accesses (or keypath accesses)
51+
/// anywhere in the function.
52+
/// 2) Invariant: An access scope cannot begin outside the loop and end on any
53+
/// path between the loop header and the original dominated access.
54+
/// 3) Expectation: Accesses will be hoisted across nested loops during
55+
/// bottom-up processing.
2856
//===----------------------------------------------------------------------===//
2957

3058
#define DEBUG_TYPE "access-enforcement-dom"
3159

3260
#include "swift/SIL/DebugUtils.h"
3361
#include "swift/SIL/MemAccessUtils.h"
62+
#include "swift/SIL/SILBuilder.h"
3463
#include "swift/SIL/SILFunction.h"
3564
#include "swift/SILOptimizer/Analysis/DominanceAnalysis.h"
65+
#include "swift/SILOptimizer/Analysis/LoopAnalysis.h"
3666
#include "swift/SILOptimizer/PassManager/Transforms.h"
3767
#include "swift/SILOptimizer/Utils/Local.h"
3868

@@ -53,7 +83,7 @@ class DominatedAccessRemoval {
5383
DominatedAccessRemoval(SILFunction &func, DominanceInfo *domInfo)
5484
: func(func), domInfo(domInfo) {}
5585

56-
void perform();
86+
bool perform();
5787

5888
protected:
5989
bool visitInstruction(SILInstruction *instr,
@@ -154,6 +184,13 @@ void DominatedAccessRemoval::visitBeginAccess(
154184
return;
155185
}
156186

187+
auto *parentBegin = visitedAccessesIt->first;
188+
if (beginAccess->getAccessKind() > parentBegin->getAccessKind()) {
189+
// we can't change to static: dominating access has a > access kind
190+
// change parent's access kind so we would be able to do so
191+
parentBegin->setAccessKind(beginAccess->getAccessKind());
192+
}
193+
157194
domInstrs.push_back(beginAccess);
158195
}
159196

@@ -228,25 +265,198 @@ void DominatedAccessRemoval::optimize() {
228265
}
229266
}
230267

231-
void DominatedAccessRemoval::perform() {
268+
// Returns a bool: If we should bail on this function
269+
// we return false - else true
270+
bool DominatedAccessRemoval::perform() {
232271
if (func.empty())
233-
return;
272+
return false;
234273

235274
if (!analyze()) {
236275
LLVM_DEBUG(llvm::dbgs()
237276
<< "Bailed on function: " << func.getName() << "\n");
238-
return;
277+
return false;
239278
}
240279
optimize();
280+
return true;
281+
}
282+
283+
namespace {
284+
class LoopDominatingAccessAdder {
285+
SmallVector<SILLoop *, 8> bottomUpWorkList;
286+
DominanceInfo *domInfo;
287+
288+
public:
289+
LoopDominatingAccessAdder(SILLoop *topLevelLoop, DominanceInfo *domInfo)
290+
: domInfo(domInfo) {
291+
// Collect loops for a recursive bottom-up traversal in the loop tree.
292+
bottomUpWorkList.push_back(topLevelLoop);
293+
for (unsigned i = 0; i < bottomUpWorkList.size(); ++i) {
294+
auto *loop = bottomUpWorkList[i];
295+
for (auto *subLoop : *loop) {
296+
bottomUpWorkList.push_back(subLoop);
297+
}
298+
}
299+
}
300+
301+
void perform();
302+
303+
protected:
304+
/// \brief Collect a set of instructions that can be dominated
305+
void
306+
analyzeCurrentLoop(SILLoop *currentLoop,
307+
SmallVectorImpl<BeginAccessInst *> &dominatableInstrVec);
308+
309+
/// \brief Optimize the current loop nest.
310+
void optimizeLoop(SILLoop *currnetLoop,
311+
SmallVectorImpl<BeginAccessInst *> &dominatableInstrVec);
312+
};
313+
} // namespace
314+
315+
void LoopDominatingAccessAdder::analyzeCurrentLoop(
316+
SILLoop *currentLoop,
317+
SmallVectorImpl<BeginAccessInst *> &dominatableInstrVec) {
318+
auto *preheader = currentLoop->getLoopPreheader();
319+
if (!preheader) {
320+
// Can't dominate this loop - bail
321+
return;
322+
}
323+
for (auto *block : currentLoop->getBlocks()) {
324+
for (auto &instr : *block) {
325+
auto *beginAccess = dyn_cast<BeginAccessInst>(&instr);
326+
if (!beginAccess) {
327+
continue;
328+
}
329+
if (beginAccess->getEnforcement() != SILAccessEnforcement::Dynamic) {
330+
continue;
331+
}
332+
if (!beginAccess->hasNoNestedConflict()) {
333+
continue;
334+
}
335+
auto operand = beginAccess->getOperand();
336+
auto *parentBB = operand->getParentBlock();
337+
if (currentLoop->contains(parentBB)) {
338+
// Loop varaint argument
339+
continue;
340+
}
341+
if (!domInfo->properlyDominates(parentBB, preheader) &&
342+
parentBB != preheader) {
343+
// Argument not dominated by the preheader
344+
// TODO: Should this be a fatal error? Invalid control flow?
345+
continue;
346+
}
347+
dominatableInstrVec.push_back(beginAccess);
348+
}
349+
}
350+
}
351+
352+
void LoopDominatingAccessAdder::optimizeLoop(
353+
SILLoop *currnetLoop,
354+
SmallVectorImpl<BeginAccessInst *> &dominatableInstrVec) {
355+
auto *preheader = currnetLoop->getLoopPreheader();
356+
if (!preheader) {
357+
// Can't dominate this loop - bail
358+
return;
359+
}
360+
// Get the last instruction in the pre-header, this is
361+
// We insert new access scopes before it.
362+
// We also use it for our currently-optimizeable begin_access:
363+
// We can only create a dominating scope if the
364+
// dominatableInstr's operand dominates this instruction:
365+
TermInst *preheaderTerm = preheader->getTerminator();
366+
367+
// Same operand may appear more than once in dominatableInstrVec
368+
// No reason to create multiple dominating instructions
369+
// The following set keeps track of operands we can safely skip
370+
// i.e. we already created a dominating begin_access for
371+
// We also need to keep track of the new dominating begin_access:
372+
// First inner loop access might be [read] while a second one
373+
// Might be [modify], the dominating access should be [modify]
374+
// in that case
375+
llvm::SmallDenseMap<SILValue, BeginAccessInst *> domOpToBeginAccess;
376+
377+
while (!dominatableInstrVec.empty()) {
378+
BeginAccessInst *beginAccess = dominatableInstrVec.pop_back_val();
379+
auto operand = beginAccess->getOperand();
380+
auto domIt = domOpToBeginAccess.find(operand);
381+
if (domIt != domOpToBeginAccess.end()) {
382+
BeginAccessInst *domBegin = domIt->getSecond();
383+
if (domBegin->getAccessKind() < beginAccess->getAccessKind()) {
384+
LLVM_DEBUG(llvm::dbgs()
385+
<< "Changing " << *domBegin << " access kind to "
386+
<< *beginAccess << " access kind\n");
387+
domBegin->setAccessKind(beginAccess->getAccessKind());
388+
}
389+
LLVM_DEBUG(llvm::dbgs()
390+
<< "Already have a dominating access scope for: "
391+
<< *beginAccess
392+
<< ", settting the inner access' enforcement to static\n");
393+
beginAccess->setEnforcement(SILAccessEnforcement::Static);
394+
continue;
395+
}
396+
// We can go ahead with the optimization - do so:
397+
SILBuilderWithScope scopeBuilder(preheaderTerm);
398+
BeginAccessInst *newBegin = scopeBuilder.createBeginAccess(
399+
beginAccess->getLoc(), operand, beginAccess->getAccessKind(),
400+
SILAccessEnforcement::Dynamic, true /*no nested conflict*/,
401+
beginAccess->isFromBuiltin());
402+
scopeBuilder.createEndAccess(beginAccess->getLoc(), newBegin, false);
403+
domOpToBeginAccess.insert(std::make_pair(operand, newBegin));
404+
LLVM_DEBUG(llvm::dbgs()
405+
<< "Created a dominating access scope for: " << *beginAccess
406+
<< ", settting the inner access' enforcement to static\n");
407+
beginAccess->setEnforcement(SILAccessEnforcement::Static);
408+
}
409+
}
410+
411+
void LoopDominatingAccessAdder::perform() {
412+
// Process loops bottom up in the loop tree.
413+
while (!bottomUpWorkList.empty()) {
414+
SILLoop *currnetLoop = bottomUpWorkList.pop_back_val();
415+
LLVM_DEBUG(llvm::dbgs() << "Processing loop " << *currnetLoop);
416+
417+
llvm::SmallVector<BeginAccessInst *, 8> dominatableInstrVec;
418+
analyzeCurrentLoop(currnetLoop, dominatableInstrVec);
419+
optimizeLoop(currnetLoop, dominatableInstrVec);
420+
}
241421
}
242422

243423
namespace {
244424
struct AccessEnforcementDom : public SILFunctionTransform {
245425
void run() override {
426+
SILFunction *func = getFunction();
427+
246428
DominanceAnalysis *domAnalysis = getAnalysis<DominanceAnalysis>();
247-
DominanceInfo *domInfo = domAnalysis->get(getFunction());
248-
DominatedAccessRemoval eliminationPass(*getFunction(), domInfo);
249-
eliminationPass.perform();
429+
DominanceInfo *domInfo = domAnalysis->get(func);
430+
DominatedAccessRemoval eliminationPass(*func, domInfo);
431+
if (!eliminationPass.perform()) {
432+
// Bailed on the function due to key-path and/or other reasons
433+
// Too risky to do anything else, don't repeat
434+
// the analysis in subsequent optimizations
435+
return;
436+
}
437+
438+
SILLoopAnalysis *loopAnalysis = PM->getAnalysis<SILLoopAnalysis>();
439+
SILLoopInfo *loopInfo = loopAnalysis->get(func);
440+
if (loopInfo->empty()) {
441+
LLVM_DEBUG(llvm::dbgs() << "No loops in " << func->getName() << "\n");
442+
return;
443+
}
444+
445+
LLVM_DEBUG(llvm::dbgs() << "LoopDominatingAccessAdder: Processing loops in "
446+
<< func->getName() << "\n");
447+
448+
for (auto *topLevelLoop : *loopInfo) {
449+
LoopDominatingAccessAdder additionPass(topLevelLoop, domInfo);
450+
additionPass.perform();
451+
}
452+
453+
// TODO: Do we need to rerun the normal dominated check removal here?
454+
// I don’t think we need to do so: there shouldn’t be any redundant
455+
// accesses in the preheader that we can eliminate:
456+
// We only create a single access no matter how many accesses to
457+
// the same storage are inside the loop.
458+
// If there was an access outside of the loop then no new potential
459+
// would have been exposed by LoopDominatingAccessAdder?
250460
}
251461
};
252462
} // namespace

0 commit comments

Comments
 (0)