Skip to content

Commit 2017681

Browse files
committed
[flang] Use fir.declare/fir.dummy_scope for TBAA tags attachments.
With MLIR inlining (e.g. `flang-new -mmlir -inline-all=true`) the current TBAA tags attachment is suboptimal, because we may lose information about the callee's dummy arguments (by bypassing fir.declare in AliasAnalysis::getSource). This is a conservative first step to improve the situation. This patch makes AddAliasTagsPass to account for fir.dummy_scope hierarchy after MLIR inlining and use it to place the TBAA tags into TBAA trees corresponding to different function scopes. The pass uses special mode of AliasAnalysis to find the instantiation point of a Fortran variable (a [hl]fir.decalre) when searching for the source of a memory reference. In this mode, AliasAnalysis will always stop at fir.declare operations that have dummy_scope operands - there should not be a reason to past throught it for the purpose of TBAA tags attachment.
1 parent 47d80ec commit 2017681

File tree

8 files changed

+462
-32
lines changed

8 files changed

+462
-32
lines changed

flang/include/flang/Optimizer/Analysis/AliasAnalysis.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,17 @@ struct AliasAnalysis {
120120
/// Source definition of a value.
121121
SourceUnion u;
122122

123+
/// A value definition denoting the place where the corresponding
124+
/// source variable was instantiated by the front-end.
125+
/// Currently, it is the result of [hl]fir.declare of the source,
126+
/// if we can reach it.
127+
/// It helps to identify the scope where the corresponding variable
128+
/// was defined in the original Fortran source, e.g. when MLIR
129+
/// inlining happens an inlined fir.declare of the callee's
130+
/// dummy argument identifies the scope where the source
131+
/// may be treated as a dummy argument.
132+
mlir::Value instantiationPoint;
133+
123134
/// Whether the source was reached following data or box reference
124135
bool isData{false};
125136
};
@@ -168,7 +179,10 @@ struct AliasAnalysis {
168179
mlir::ModRefResult getModRef(mlir::Operation *op, mlir::Value location);
169180

170181
/// Return the memory source of a value.
171-
Source getSource(mlir::Value);
182+
/// If getInstantiationPoint is true, the search for the source
183+
/// will stop at [hl]fir.declare if it represents a dummy
184+
/// argument declaration (i.e. it has the dummy_scope operand).
185+
Source getSource(mlir::Value, bool getInstantiationPoint = false);
172186
};
173187

174188
inline bool operator==(const AliasAnalysis::Source::SourceOrigin &lhs,

flang/include/flang/Optimizer/Analysis/TBAAForest.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,20 @@ class TBAAForrest {
9292
}
9393
return getFuncTree(func.getSymNameAttr());
9494
}
95+
// Returns the TBAA tree associated with the scope enclosed
96+
// within the given function. With MLIR inlining, there may
97+
// be multiple scopes within a single function. It is the caller's
98+
// responsibility to provide unique name for the scope.
99+
// If the scope string is empty, returns the TBAA tree for the
100+
// "root" scope of the given function.
101+
inline const TBAATree &getFuncTreeWithScope(mlir::func::FuncOp func,
102+
llvm::StringRef scope) {
103+
mlir::StringAttr name = func.getSymNameAttr();
104+
if (!scope.empty())
105+
name = mlir::StringAttr::get(name.getContext(),
106+
llvm::Twine(name) + " - " + scope);
107+
return getFuncTree(name);
108+
}
95109

96110
private:
97111
const TBAATree &getFuncTree(mlir::StringAttr symName) {

flang/lib/Optimizer/Analysis/AliasAnalysis.cpp

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,15 @@ using namespace mlir;
3030

3131
static bool isDummyArgument(mlir::Value v) {
3232
auto blockArg{mlir::dyn_cast<mlir::BlockArgument>(v)};
33-
if (!blockArg)
33+
if (!blockArg) {
34+
auto defOp = v.getDefiningOp();
35+
if (defOp) {
36+
if (auto declareOp = mlir::dyn_cast<fir::DeclareOp>(defOp))
37+
if (declareOp.getDummyScope())
38+
return true;
39+
}
3440
return false;
41+
}
3542

3643
auto *owner{blockArg.getOwner()};
3744
return owner->isEntryBlock() &&
@@ -105,6 +112,9 @@ bool AliasAnalysis::Source::isRecordWithPointerComponent() const {
105112
}
106113

107114
AliasResult AliasAnalysis::alias(Value lhs, Value rhs) {
115+
// TODO: alias() has to be aware of the function scopes.
116+
// After MLIR inlining, the current implementation may
117+
// not recognize non-aliasing entities.
108118
auto lhsSrc = getSource(lhs);
109119
auto rhsSrc = getSource(rhs);
110120
bool approximateSource = lhsSrc.approximateSource || rhsSrc.approximateSource;
@@ -242,7 +252,8 @@ getAttrsFromVariable(fir::FortranVariableOpInterface var) {
242252
return attrs;
243253
}
244254

245-
AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v) {
255+
AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v,
256+
bool getInstantiationPoint) {
246257
auto *defOp = v.getDefiningOp();
247258
SourceKind type{SourceKind::Unknown};
248259
mlir::Type ty;
@@ -254,6 +265,7 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v) {
254265
bool followingData = !isBoxRef;
255266
mlir::SymbolRefAttr global;
256267
Source::Attributes attributes;
268+
mlir::Value instantiationPoint;
257269
while (defOp && !breakFromLoop) {
258270
ty = defOp->getResultTypes()[0];
259271
llvm::TypeSwitch<Operation *>(defOp)
@@ -344,6 +356,21 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v) {
344356
breakFromLoop = true;
345357
return;
346358
}
359+
if (getInstantiationPoint) {
360+
// Fetch only the innermost instantiation point.
361+
if (!instantiationPoint)
362+
instantiationPoint = op->getResult(0);
363+
364+
if (op.getDummyScope()) {
365+
// Do not track past DeclareOp that has the dummy_scope
366+
// operand. This DeclareOp is known to represent
367+
// a dummy argument for some runtime instantiation
368+
// of a procedure.
369+
type = SourceKind::Argument;
370+
breakFromLoop = true;
371+
return;
372+
}
373+
}
347374
// TODO: Look for the fortran attributes present on the operation
348375
// Track further through the operand
349376
v = op.getMemref();
@@ -382,9 +409,17 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v) {
382409
}
383410

384411
if (type == SourceKind::Global) {
385-
return {{global, followingData}, type, ty, attributes, approximateSource};
412+
return {{global, instantiationPoint, followingData},
413+
type,
414+
ty,
415+
attributes,
416+
approximateSource};
386417
}
387-
return {{v, followingData}, type, ty, attributes, approximateSource};
418+
return {{v, instantiationPoint, followingData},
419+
type,
420+
ty,
421+
attributes,
422+
approximateSource};
388423
}
389424

390425
} // namespace fir

flang/lib/Optimizer/CodeGen/TBAABuilder.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,38 @@ TBAABuilder::TBAABuilder(MLIRContext *context, bool applyTBAA,
5252
bool forceUnifiedTree)
5353
: enableTBAA(applyTBAA && !disableTBAA),
5454
trees(/*separatePerFunction=*/perFunctionTBAATrees && !forceUnifiedTree) {
55+
// TODO: the TBAA tags created here are rooted in the root scope
56+
// of the enclosing function. This does not work best with MLIR inlining.
57+
// A better approach is to root them according to the scopes they belong to
58+
// and that were used by AddAliasTagsPass to create TBAA tags before
59+
// the CodeGen. For example:
60+
// subroutine caller(a, b, ptr)
61+
// real, target :: a(:), b(:)
62+
// integer, pointer :: ptr(:)
63+
// call callee(a, b, ptr)
64+
// end
65+
// subroutine callee(a, b, ptr)
66+
// real :: a(:), b(:)
67+
// integer, pointer :: ptr(:)
68+
// do i=...
69+
// a(ptr(i)) = b(ptr(i))
70+
// end do
71+
// end
72+
//
73+
// When callee is inlined, the dummy arguments 'a' and 'b' will
74+
// be rooted in TBAA tree corresponding to the `call callee` call site,
75+
// saying that the references to 'a' and 'b' cannot alias each other.
76+
// These tags will be created by AddAliasTagsPass, but it will not be able
77+
// to create any tags for 'ptr' references.
78+
// During the CodeGen, we create 'any data access' tags for the
79+
// 'ptr' acceses. If they are rooted within the root scope of `caller`,
80+
// they end up in a different TBAA tree with the 'a' and 'b' access
81+
// tags, so 'ptr', 'a' and 'b' references MayAlias. Moreover,
82+
// the box access of 'ptr' will also be in a different TBAA tree
83+
// with 'a' and 'b' tags, meaning they can also alias.
84+
// This will prevent LLVM vectorization even with memory conflict checks.
85+
// It seems that we'd better move all TBAA tags assignment to
86+
// AddAliasTagsPass, which can at least rely on the dummy arguments scopes.
5587
if (!enableTBAA)
5688
return;
5789
}

flang/lib/Optimizer/Transforms/AddAliasTags.cpp

Lines changed: 94 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@
1717
#include "flang/Optimizer/Dialect/FIRDialect.h"
1818
#include "flang/Optimizer/Dialect/FirAliasTagOpInterface.h"
1919
#include "flang/Optimizer/Transforms/Passes.h"
20+
#include "mlir/IR/Dominance.h"
2021
#include "mlir/Pass/Pass.h"
2122
#include "llvm/ADT/DenseMap.h"
2223
#include "llvm/ADT/StringRef.h"
24+
#include "llvm/ADT/Twine.h"
2325
#include "llvm/Support/CommandLine.h"
2426
#include "llvm/Support/Debug.h"
2527
#include "llvm/Support/raw_ostream.h"
@@ -54,24 +56,85 @@ namespace {
5456
/// Shared state per-module
5557
class PassState {
5658
public:
59+
PassState(mlir::DominanceInfo &domInfo) : domInfo(domInfo) {}
5760
/// memoised call to fir::AliasAnalysis::getSource
5861
inline const fir::AliasAnalysis::Source &getSource(mlir::Value value) {
5962
if (!analysisCache.contains(value))
60-
analysisCache.insert({value, analysis.getSource(value)});
63+
analysisCache.insert(
64+
{value, analysis.getSource(value, /*getInstantiationPoint=*/true)});
6165
return analysisCache[value];
6266
}
6367

6468
/// get the per-function TBAATree for this function
6569
inline const fir::TBAATree &getFuncTree(mlir::func::FuncOp func) {
6670
return forrest[func];
6771
}
72+
inline const fir::TBAATree &getFuncTreeWithScope(mlir::func::FuncOp func,
73+
fir::DummyScopeOp scope) {
74+
auto &scopeMap = scopeNames.at(func);
75+
return forrest.getFuncTreeWithScope(func, scopeMap.lookup(scope));
76+
}
77+
78+
void processFunctionScopes(mlir::func::FuncOp func);
79+
fir::DummyScopeOp getDeclarationScope(fir::DeclareOp declareOp);
6880

6981
private:
82+
mlir::DominanceInfo &domInfo;
7083
fir::AliasAnalysis analysis;
7184
llvm::DenseMap<mlir::Value, fir::AliasAnalysis::Source> analysisCache;
7285
fir::TBAAForrest forrest;
86+
// Unique names for fir.dummy_scope operations within
87+
// the given function.
88+
llvm::DenseMap<mlir::func::FuncOp,
89+
llvm::DenseMap<fir::DummyScopeOp, std::string>>
90+
scopeNames;
91+
// A map providing a vector of fir.dummy_scope operations
92+
// for the given function. The vectors are sorted according
93+
// to the dominance information.
94+
llvm::DenseMap<mlir::func::FuncOp, llvm::SmallVector<fir::DummyScopeOp, 16>>
95+
sortedScopeOperations;
7396
};
7497

98+
// Process fir.dummy_scope operations in the given func:
99+
// sort them according to the dominance information, and
100+
// associate a unique (within the current function) scope name
101+
// with each of them.
102+
void PassState::processFunctionScopes(mlir::func::FuncOp func) {
103+
if (scopeNames.contains(func))
104+
return;
105+
106+
auto &scopeMap = scopeNames.getOrInsertDefault(func);
107+
auto &scopeOps = sortedScopeOperations.getOrInsertDefault(func);
108+
func.walk([&](fir::DummyScopeOp op) { scopeOps.push_back(op); });
109+
llvm::stable_sort(scopeOps, [&](const fir::DummyScopeOp &op1,
110+
const fir::DummyScopeOp &op2) {
111+
return domInfo.properlyDominates(&*op1, &*op2);
112+
});
113+
unsigned scopeId = 0;
114+
for (auto scope : scopeOps) {
115+
if (scopeId != 0) {
116+
std::string name = (llvm::Twine("Scope ") + llvm::Twine(scopeId)).str();
117+
LLVM_DEBUG(llvm::dbgs() << "Creating scope '" << name << "':\n"
118+
<< scope << "\n");
119+
scopeMap.insert({scope, std::move(name)});
120+
}
121+
++scopeId;
122+
}
123+
}
124+
125+
// For the given fir.declare returns the dominating fir.dummy_scope
126+
// operation.
127+
fir::DummyScopeOp PassState::getDeclarationScope(fir::DeclareOp declareOp) {
128+
auto func = declareOp->getParentOfType<mlir::func::FuncOp>();
129+
assert(func && "fir.declare does not have parent func.func");
130+
auto &scopeOps = sortedScopeOperations.at(func);
131+
for (auto II = scopeOps.rbegin(), IE = scopeOps.rend(); II != IE; ++II) {
132+
if (domInfo.dominates(&**II, &*declareOp))
133+
return *II;
134+
}
135+
return nullptr;
136+
}
137+
75138
class AddAliasTagsPass : public fir::impl::AddAliasTagsBase<AddAliasTagsPass> {
76139
public:
77140
void runOnOperation() override;
@@ -85,6 +148,9 @@ class AddAliasTagsPass : public fir::impl::AddAliasTagsBase<AddAliasTagsPass> {
85148
} // namespace
86149

87150
static fir::DeclareOp getDeclareOp(mlir::Value arg) {
151+
if (auto declare =
152+
mlir::dyn_cast_or_null<fir::DeclareOp>(arg.getDefiningOp()))
153+
return declare;
88154
for (mlir::Operation *use : arg.getUsers())
89155
if (fir::DeclareOp declare = mlir::dyn_cast<fir::DeclareOp>(use))
90156
return declare;
@@ -94,7 +160,7 @@ static fir::DeclareOp getDeclareOp(mlir::Value arg) {
94160
/// Get the name of a function argument using the "fir.bindc_name" attribute,
95161
/// or ""
96162
static std::string getFuncArgName(mlir::Value arg) {
97-
// first try getting the name from the hlfir.declare
163+
// first try getting the name from the fir.declare
98164
if (fir::DeclareOp declare = getDeclareOp(arg))
99165
return declare.getUniqName().str();
100166

@@ -139,6 +205,23 @@ void AddAliasTagsPass::runOnAliasInterface(fir::FirAliasTagOpInterface op,
139205
return;
140206
}
141207

208+
// Process the scopes, if not processed yet.
209+
state.processFunctionScopes(func);
210+
211+
fir::DummyScopeOp scopeOp;
212+
if (auto declVal = source.origin.instantiationPoint) {
213+
// If the source is a dummy argument within some fir.dummy_scope,
214+
// then find the corresponding innermost scope to be used for finding
215+
// the right TBAA tree.
216+
auto declareOp =
217+
mlir::dyn_cast_or_null<fir::DeclareOp>(declVal.getDefiningOp());
218+
assert(declareOp && "Instantiation point must be fir.declare");
219+
if (auto dummyScope = declareOp.getDummyScope())
220+
scopeOp = mlir::cast<fir::DummyScopeOp>(dummyScope.getDefiningOp());
221+
if (!scopeOp)
222+
scopeOp = state.getDeclarationScope(declareOp);
223+
}
224+
142225
mlir::LLVM::TBAATagAttr tag;
143226
// TBAA for dummy arguments
144227
if (enableDummyArgs &&
@@ -147,7 +230,8 @@ void AddAliasTagsPass::runOnAliasInterface(fir::FirAliasTagOpInterface op,
147230
<< "Found reference to dummy argument at " << *op << "\n");
148231
std::string name = getFuncArgName(source.origin.u.get<mlir::Value>());
149232
if (!name.empty())
150-
tag = state.getFuncTree(func).dummyArgDataTree.getTag(name);
233+
tag = state.getFuncTreeWithScope(func, scopeOp)
234+
.dummyArgDataTree.getTag(name);
151235
else
152236
LLVM_DEBUG(llvm::dbgs().indent(2)
153237
<< "WARN: couldn't find a name for dummy argument " << *op
@@ -161,7 +245,7 @@ void AddAliasTagsPass::runOnAliasInterface(fir::FirAliasTagOpInterface op,
161245
const char *name = glbl.getRootReference().data();
162246
LLVM_DEBUG(llvm::dbgs().indent(2) << "Found reference to global " << name
163247
<< " at " << *op << "\n");
164-
tag = state.getFuncTree(func).globalDataTree.getTag(name);
248+
tag = state.getFuncTreeWithScope(func, scopeOp).globalDataTree.getTag(name);
165249

166250
// TBAA for SourceKind::Direct
167251
} else if (enableDirect &&
@@ -172,7 +256,8 @@ void AddAliasTagsPass::runOnAliasInterface(fir::FirAliasTagOpInterface op,
172256
const char *name = glbl.getRootReference().data();
173257
LLVM_DEBUG(llvm::dbgs().indent(2) << "Found reference to direct " << name
174258
<< " at " << *op << "\n");
175-
tag = state.getFuncTree(func).directDataTree.getTag(name);
259+
tag =
260+
state.getFuncTreeWithScope(func, scopeOp).directDataTree.getTag(name);
176261
} else {
177262
// SourceKind::Direct is likely to be extended to cases which are not a
178263
// SymbolRefAttr in the future
@@ -193,7 +278,8 @@ void AddAliasTagsPass::runOnAliasInterface(fir::FirAliasTagOpInterface op,
193278
if (name) {
194279
LLVM_DEBUG(llvm::dbgs().indent(2) << "Found reference to allocation "
195280
<< name << " at " << *op << "\n");
196-
tag = state.getFuncTree(func).allocatedDataTree.getTag(*name);
281+
tag = state.getFuncTreeWithScope(func, scopeOp)
282+
.allocatedDataTree.getTag(*name);
197283
} else {
198284
LLVM_DEBUG(llvm::dbgs().indent(2)
199285
<< "WARN: couldn't find a name for allocation " << *op
@@ -219,7 +305,8 @@ void AddAliasTagsPass::runOnOperation() {
219305
// Instead this pass stores state per mlir::ModuleOp (which is what MLIR
220306
// thinks the pass operates on), then the real work of the pass is done in
221307
// runOnAliasInterface
222-
PassState state;
308+
auto &domInfo = getAnalysis<mlir::DominanceInfo>();
309+
PassState state(domInfo);
223310

224311
mlir::ModuleOp mod = getOperation();
225312
mod.walk(

0 commit comments

Comments
 (0)