Skip to content

Commit 6cd86d0

Browse files
authored
[flang] Use fir.declare/fir.dummy_scope for TBAA tags attachments. (#92472)
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 d881bac commit 6cd86d0

File tree

10 files changed

+469
-34
lines changed

10 files changed

+469
-34
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: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ bool AliasAnalysis::Source::isRecordWithPointerComponent() const {
9595
}
9696

9797
AliasResult AliasAnalysis::alias(Value lhs, Value rhs) {
98+
// TODO: alias() has to be aware of the function scopes.
99+
// After MLIR inlining, the current implementation may
100+
// not recognize non-aliasing entities.
98101
auto lhsSrc = getSource(lhs);
99102
auto rhsSrc = getSource(rhs);
100103
bool approximateSource = lhsSrc.approximateSource || rhsSrc.approximateSource;
@@ -232,7 +235,8 @@ getAttrsFromVariable(fir::FortranVariableOpInterface var) {
232235
return attrs;
233236
}
234237

235-
AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v) {
238+
AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v,
239+
bool getInstantiationPoint) {
236240
auto *defOp = v.getDefiningOp();
237241
SourceKind type{SourceKind::Unknown};
238242
mlir::Type ty;
@@ -244,6 +248,7 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v) {
244248
bool followingData = !isBoxRef;
245249
mlir::SymbolRefAttr global;
246250
Source::Attributes attributes;
251+
mlir::Value instantiationPoint;
247252
while (defOp && !breakFromLoop) {
248253
ty = defOp->getResultTypes()[0];
249254
llvm::TypeSwitch<Operation *>(defOp)
@@ -334,6 +339,21 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v) {
334339
breakFromLoop = true;
335340
return;
336341
}
342+
if (getInstantiationPoint) {
343+
// Fetch only the innermost instantiation point.
344+
if (!instantiationPoint)
345+
instantiationPoint = op->getResult(0);
346+
347+
if (op.getDummyScope()) {
348+
// Do not track past DeclareOp that has the dummy_scope
349+
// operand. This DeclareOp is known to represent
350+
// a dummy argument for some runtime instantiation
351+
// of a procedure.
352+
type = SourceKind::Argument;
353+
breakFromLoop = true;
354+
return;
355+
}
356+
}
337357
// TODO: Look for the fortran attributes present on the operation
338358
// Track further through the operand
339359
v = op.getMemref();
@@ -372,9 +392,17 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v) {
372392
}
373393

374394
if (type == SourceKind::Global) {
375-
return {{global, followingData}, type, ty, attributes, approximateSource};
395+
return {{global, instantiationPoint, followingData},
396+
type,
397+
ty,
398+
attributes,
399+
approximateSource};
376400
}
377-
return {{v, followingData}, type, ty, attributes, approximateSource};
401+
return {{v, instantiationPoint, followingData},
402+
type,
403+
ty,
404+
attributes,
405+
approximateSource};
378406
}
379407

380408
} // 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/Dialect/FIROps.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3910,8 +3910,15 @@ std::optional<std::int64_t> fir::getIntIfConstant(mlir::Value value) {
39103910

39113911
bool fir::isDummyArgument(mlir::Value v) {
39123912
auto blockArg{mlir::dyn_cast<mlir::BlockArgument>(v)};
3913-
if (!blockArg)
3913+
if (!blockArg) {
3914+
auto defOp = v.getDefiningOp();
3915+
if (defOp) {
3916+
if (auto declareOp = mlir::dyn_cast<fir::DeclareOp>(defOp))
3917+
if (declareOp.getDummyScope())
3918+
return true;
3919+
}
39143920
return false;
3921+
}
39153922

39163923
auto *owner{blockArg.getOwner()};
39173924
return owner->isEntryBlock() &&

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(

flang/lib/Optimizer/Transforms/AddDebugInfo.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,13 @@ void AddDebugInfoPass::handleDeclareOp(fir::cg::XDeclareOp declOp,
9292

9393
// FIXME: There may be cases where an argument is processed a bit before
9494
// DeclareOp is generated. In that case, DeclareOp may point to an
95-
// intermediate op and not to BlockArgument. We need to find those cases and
96-
// walk the chain to get to the actual argument.
95+
// intermediate op and not to BlockArgument.
96+
// Moreover, with MLIR inlining we cannot use the BlockArgument
97+
// position to identify the original number of the dummy argument.
98+
// If we want to keep running AddDebugInfoPass late, the dummy argument
99+
// position in the argument list has to be expressed in FIR (e.g. as a
100+
// constant attribute of [hl]fir.declare/fircg.ext_declare operation that has
101+
// a dummy_scope operand).
97102
unsigned argNo = 0;
98103
if (fir::isDummyArgument(declOp.getMemref())) {
99104
auto arg = llvm::cast<mlir::BlockArgument>(declOp.getMemref());

0 commit comments

Comments
 (0)