Skip to content

Commit 5b15ca4

Browse files
committed
[flang] Create TBAA subtree for COMMON block variables.
In order to help LLVM disambiguate accesses to the COMMON block variables, this patch creates a TBAA sub-tree for each COMMON block, and the places all variables belonging to this COMMON block into this sub-tree. The structure looks like this: ``` common /blk/ a, b, c "global data" | |- "blk_" | |- "blk_/a" |- "blk_/b" |- "blk_/c" ``` The TBAA tag for "a" is created in "blk_/a" root, etc. If, for some reason, we cannot identify a variable's name, but we know that it belongs to COMMON "blk", the TBAA tag will be created in "blk_" root - this tag indicates that this access can overlap with any accesses of a/b/c. I measured 10% speed-up on 434.zeusmp and 20% speed-up on 200.sixtrack on Zen4. I expect around the same speed-ups on ARM.
1 parent 25285b3 commit 5b15ca4

File tree

6 files changed

+355
-31
lines changed

6 files changed

+355
-31
lines changed

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

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ struct TBAATree {
4646

4747
mlir::LLVM::TBAATypeDescriptorAttr getRoot() const { return parent; }
4848

49+
/// For the given name, get or create a subtree in the current
50+
/// subtree. For example, this is used for creating subtrees
51+
/// inside the "global data" subtree for the COMMON block variables
52+
/// belonging to the same COMMON block.
53+
SubtreeState &getOrCreateNamedSubtree(mlir::StringAttr name);
54+
4955
private:
5056
SubtreeState(mlir::MLIRContext *ctx, std::string name,
5157
mlir::LLVM::TBAANodeAttr grandParent)
@@ -57,6 +63,9 @@ struct TBAATree {
5763
const std::string parentId;
5864
mlir::MLIRContext *const context;
5965
mlir::LLVM::TBAATypeDescriptorAttr parent;
66+
// A map of named sub-trees, e.g. sub-trees of the COMMON blocks
67+
// placed under the "global data" root.
68+
llvm::DenseMap<mlir::StringAttr, SubtreeState> namedSubtrees;
6069
};
6170

6271
/// A subtree for POINTER/TARGET variables data.
@@ -131,22 +140,29 @@ class TBAAForrest {
131140
// responsibility to provide unique name for the scope.
132141
// If the scope string is empty, returns the TBAA tree for the
133142
// "root" scope of the given function.
134-
inline const TBAATree &getFuncTreeWithScope(mlir::func::FuncOp func,
135-
llvm::StringRef scope) {
143+
inline TBAATree &getMutableFuncTreeWithScope(mlir::func::FuncOp func,
144+
llvm::StringRef scope) {
136145
mlir::StringAttr name = func.getSymNameAttr();
137146
if (!scope.empty())
138147
name = mlir::StringAttr::get(name.getContext(),
139148
llvm::Twine(name) + " - " + scope);
140149
return getFuncTree(name);
141150
}
142151

152+
inline const TBAATree &getFuncTreeWithScope(mlir::func::FuncOp func,
153+
llvm::StringRef scope) {
154+
return getMutableFuncTreeWithScope(func, scope);
155+
}
156+
143157
private:
144-
const TBAATree &getFuncTree(mlir::StringAttr symName) {
158+
TBAATree &getFuncTree(mlir::StringAttr symName) {
145159
if (!separatePerFunction)
146160
symName = mlir::StringAttr::get(symName.getContext(), "");
147161
if (!trees.contains(symName))
148162
trees.insert({symName, TBAATree::buildTree(symName)});
149-
return trees.at(symName);
163+
auto it = trees.find(symName);
164+
assert(it != trees.end());
165+
return it->second;
150166
}
151167

152168
// Should each function use a different tree?

flang/include/flang/Optimizer/Builder/FIRBuilder.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,12 @@ class FirOpBuilder : public mlir::OpBuilder, public mlir::OpBuilder::Listener {
365365
// Linkage helpers (inline). The default linkage is external.
366366
//===--------------------------------------------------------------------===//
367367

368-
mlir::StringAttr createCommonLinkage() { return getStringAttr("common"); }
368+
static mlir::StringAttr createCommonLinkage(mlir::MLIRContext *context) {
369+
return mlir::StringAttr::get(context, "common");
370+
}
371+
mlir::StringAttr createCommonLinkage() {
372+
return createCommonLinkage(getContext());
373+
}
369374

370375
mlir::StringAttr createInternalLinkage() { return getStringAttr("internal"); }
371376

flang/lib/Optimizer/Analysis/TBAAForest.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,21 @@
1111

1212
mlir::LLVM::TBAATagAttr
1313
fir::TBAATree::SubtreeState::getTag(llvm::StringRef uniqueName) const {
14-
std::string id = (parentId + "/" + uniqueName).str();
14+
std::string id = (parentId + '/' + uniqueName).str();
1515
mlir::LLVM::TBAATypeDescriptorAttr type =
1616
mlir::LLVM::TBAATypeDescriptorAttr::get(
1717
context, id, mlir::LLVM::TBAAMemberAttr::get(parent, 0));
1818
return mlir::LLVM::TBAATagAttr::get(type, type, 0);
19-
// return tag;
19+
}
20+
21+
fir::TBAATree::SubtreeState &
22+
fir::TBAATree::SubtreeState::getOrCreateNamedSubtree(mlir::StringAttr name) {
23+
if (!namedSubtrees.contains(name))
24+
namedSubtrees.insert(
25+
{name, SubtreeState(context, parentId + '/' + name.str(), parent)});
26+
auto it = namedSubtrees.find(name);
27+
assert(it != namedSubtrees.end());
28+
return it->second;
2029
}
2130

2231
mlir::LLVM::TBAATagAttr fir::TBAATree::SubtreeState::getTag() const {

flang/lib/Optimizer/Transforms/AddAliasTags.cpp

Lines changed: 71 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#include "flang/Optimizer/Analysis/AliasAnalysis.h"
1616
#include "flang/Optimizer/Analysis/TBAAForest.h"
17+
#include "flang/Optimizer/Builder/FIRBuilder.h"
1718
#include "flang/Optimizer/Dialect/FIRDialect.h"
1819
#include "flang/Optimizer/Dialect/FirAliasTagOpInterface.h"
1920
#include "flang/Optimizer/Transforms/Passes.h"
@@ -61,8 +62,10 @@ namespace {
6162
class PassState {
6263
public:
6364
PassState(mlir::DominanceInfo &domInfo,
64-
std::optional<unsigned> localAllocsThreshold)
65-
: domInfo(domInfo), localAllocsThreshold(localAllocsThreshold) {}
65+
std::optional<unsigned> localAllocsThreshold,
66+
const mlir::SymbolTable &symTab)
67+
: domInfo(domInfo), localAllocsThreshold(localAllocsThreshold),
68+
symTab(symTab) {}
6669
/// memoised call to fir::AliasAnalysis::getSource
6770
inline const fir::AliasAnalysis::Source &getSource(mlir::Value value) {
6871
if (!analysisCache.contains(value))
@@ -72,13 +75,14 @@ class PassState {
7275
}
7376

7477
/// get the per-function TBAATree for this function
75-
inline const fir::TBAATree &getFuncTree(mlir::func::FuncOp func) {
76-
return forrest[func];
78+
inline fir::TBAATree &getMutableFuncTreeWithScope(mlir::func::FuncOp func,
79+
fir::DummyScopeOp scope) {
80+
auto &scopeMap = scopeNames.at(func);
81+
return forrest.getMutableFuncTreeWithScope(func, scopeMap.lookup(scope));
7782
}
7883
inline const fir::TBAATree &getFuncTreeWithScope(mlir::func::FuncOp func,
7984
fir::DummyScopeOp scope) {
80-
auto &scopeMap = scopeNames.at(func);
81-
return forrest.getFuncTreeWithScope(func, scopeMap.lookup(scope));
85+
return getMutableFuncTreeWithScope(func, scope);
8286
}
8387

8488
void processFunctionScopes(mlir::func::FuncOp func);
@@ -98,8 +102,14 @@ class PassState {
98102
// attachment.
99103
bool attachLocalAllocTag();
100104

105+
fir::GlobalOp getGlobalDefiningOp(mlir::StringAttr name) const {
106+
return symTab.lookup<fir::GlobalOp>(name);
107+
}
108+
101109
private:
102110
mlir::DominanceInfo &domInfo;
111+
std::optional<unsigned> localAllocsThreshold;
112+
const mlir::SymbolTable &symTab;
103113
fir::AliasAnalysis analysis;
104114
llvm::DenseMap<mlir::Value, fir::AliasAnalysis::Source> analysisCache;
105115
fir::TBAAForrest forrest;
@@ -117,8 +127,6 @@ class PassState {
117127
// Local pass cache for derived types that contain descriptor
118128
// member(s), to avoid the cost of isRecordWithDescriptorMember().
119129
llvm::DenseSet<mlir::Type> typesContainingDescriptors;
120-
121-
std::optional<unsigned> localAllocsThreshold;
122130
};
123131

124132
// Process fir.dummy_scope operations in the given func:
@@ -310,14 +318,55 @@ void AddAliasTagsPass::runOnAliasInterface(fir::FirAliasTagOpInterface op,
310318
source.kind == fir::AliasAnalysis::SourceKind::Global &&
311319
!source.isBoxData()) {
312320
mlir::SymbolRefAttr glbl = llvm::cast<mlir::SymbolRefAttr>(source.origin.u);
313-
const char *name = glbl.getRootReference().data();
314-
LLVM_DEBUG(llvm::dbgs().indent(2) << "Found reference to global " << name
315-
<< " at " << *op << "\n");
316-
if (source.isPointer())
321+
mlir::StringAttr name = glbl.getRootReference();
322+
LLVM_DEBUG(llvm::dbgs().indent(2) << "Found reference to global "
323+
<< name.str() << " at " << *op << "\n");
324+
if (source.isPointer()) {
317325
tag = state.getFuncTreeWithScope(func, scopeOp).targetDataTree.getTag();
318-
else
319-
tag =
320-
state.getFuncTreeWithScope(func, scopeOp).globalDataTree.getTag(name);
326+
} else {
327+
// In general, place the tags under the "global data" root.
328+
fir::TBAATree::SubtreeState *subTree =
329+
&state.getMutableFuncTreeWithScope(func, scopeOp).globalDataTree;
330+
331+
// The COMMON blocks have their own sub-tree root under the "global data"
332+
// root, which is named after the name of the COMMON block.
333+
// If we can identify the name of the member variable, then
334+
// we create a sub-tree under the root of the COMMON block
335+
// and place the tag there. If we cannot identify the name
336+
// of the member variable (e.g. for whatever reason there is no
337+
// fir.declare for it), then we place the tag under the root
338+
// of the COMMON block.
339+
auto globalOp = state.getGlobalDefiningOp(name);
340+
// TODO: this is a subtle identification of the fact that
341+
// the variable belongs to a COMMON block.
342+
// Should we have an attribute on [hl]fir.declare
343+
// that specifies the name of the COMMON block the variable
344+
// belongs to?
345+
if (globalOp &&
346+
globalOp.getLinkName() ==
347+
fir::FirOpBuilder::createCommonLinkage(globalOp->getContext())) {
348+
// Get or create a sub-tree for the COMMON block.
349+
subTree = &subTree->getOrCreateNamedSubtree(name);
350+
351+
auto declOp = mlir::dyn_cast_or_null<fir::DeclareOp>(
352+
source.origin.instantiationPoint);
353+
mlir::StringAttr varName;
354+
if (declOp) {
355+
// The tag for the variable will be placed under its own
356+
// root in the COMMON sub-tree.
357+
varName = declOp.getUniqName();
358+
tag = subTree->getTag(varName.str());
359+
} else {
360+
tag = subTree->getTag();
361+
}
362+
LLVM_DEBUG(llvm::dbgs().indent(2)
363+
<< "Variable named '"
364+
<< (varName ? varName.str() : "<unknown>")
365+
<< "' is from COMMON block '" << name.str() << "'\n");
366+
} else {
367+
tag = subTree->getTag(name.str());
368+
}
369+
}
321370

322371
// TBAA for global variables with descriptors
323372
} else if (enableDirect &&
@@ -401,11 +450,14 @@ void AddAliasTagsPass::runOnOperation() {
401450
// thinks the pass operates on), then the real work of the pass is done in
402451
// runOnAliasInterface
403452
auto &domInfo = getAnalysis<mlir::DominanceInfo>();
404-
PassState state(domInfo, localAllocsThreshold.getPosition()
405-
? std::optional<unsigned>(localAllocsThreshold)
406-
: std::nullopt);
407-
408453
mlir::ModuleOp mod = getOperation();
454+
mlir::SymbolTable symTab(mod);
455+
PassState state(domInfo,
456+
localAllocsThreshold.getPosition()
457+
? std::optional<unsigned>(localAllocsThreshold)
458+
: std::nullopt,
459+
symTab);
460+
409461
mod.walk(
410462
[&](fir::FirAliasTagOpInterface op) { runOnAliasInterface(op, state); });
411463

0 commit comments

Comments
 (0)