1//===- AddAliasTags.cpp ---------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9//===----------------------------------------------------------------------===//
10/// \file
11/// Adds TBAA alias tags to fir loads and stores, based on information from
12/// fir::AliasAnalysis. More are added later in CodeGen - see fir::TBAABuilder
13//===----------------------------------------------------------------------===//
14
15#include "flang/Optimizer/Analysis/AliasAnalysis.h"
16#include "flang/Optimizer/Analysis/TBAAForest.h"
17#include "flang/Optimizer/Dialect/FIRDialect.h"
18#include "flang/Optimizer/Dialect/FirAliasTagOpInterface.h"
19#include "flang/Optimizer/Transforms/Passes.h"
20#include "mlir/Pass/Pass.h"
21#include "llvm/ADT/DenseMap.h"
22#include "llvm/ADT/StringRef.h"
23#include "llvm/Support/CommandLine.h"
24#include "llvm/Support/Debug.h"
25#include "llvm/Support/raw_ostream.h"
26#include <optional>
27
28namespace fir {
29#define GEN_PASS_DEF_ADDALIASTAGS
30#include "flang/Optimizer/Transforms/Passes.h.inc"
31} // namespace fir
32
33#define DEBUG_TYPE "fir-add-alias-tags"
34
35static llvm::cl::opt<bool>
36 enableDummyArgs("dummy-arg-tbaa", llvm::cl::init(true), llvm::cl::Hidden,
37 llvm::cl::desc("Add TBAA tags to dummy arguments"));
38static llvm::cl::opt<bool>
39 enableGlobals("globals-tbaa", llvm::cl::init(Val: true), llvm::cl::Hidden,
40 llvm::cl::desc("Add TBAA tags to global variables"));
41static llvm::cl::opt<bool>
42 enableDirect("direct-tbaa", llvm::cl::init(Val: true), llvm::cl::Hidden,
43 llvm::cl::desc("Add TBAA tags to direct variables"));
44// This is **known unsafe** (misscompare in spec2017/wrf_r). It should
45// not be enabled by default.
46// The code is kept so that these may be tried with new benchmarks to see if
47// this is worth fixing in the future.
48static llvm::cl::opt<bool> enableLocalAllocs(
49 "local-alloc-tbaa", llvm::cl::init(Val: false), llvm::cl::Hidden,
50 llvm::cl::desc("Add TBAA tags to local allocations. UNSAFE."));
51
52namespace {
53
54/// Shared state per-module
55class PassState {
56public:
57 /// memoised call to fir::AliasAnalysis::getSource
58 inline const fir::AliasAnalysis::Source &getSource(mlir::Value value) {
59 if (!analysisCache.contains(value))
60 analysisCache.insert({value, analysis.getSource(value)});
61 return analysisCache[value];
62 }
63
64 /// get the per-function TBAATree for this function
65 inline const fir::TBAATree &getFuncTree(mlir::func::FuncOp func) {
66 return forrest[func];
67 }
68
69private:
70 fir::AliasAnalysis analysis;
71 llvm::DenseMap<mlir::Value, fir::AliasAnalysis::Source> analysisCache;
72 fir::TBAAForrest forrest;
73};
74
75class AddAliasTagsPass : public fir::impl::AddAliasTagsBase<AddAliasTagsPass> {
76public:
77 void runOnOperation() override;
78
79private:
80 /// The real workhorse of the pass. This is a runOnOperation() which
81 /// operates on fir::FirAliasTagOpInterface, using some extra state
82 void runOnAliasInterface(fir::FirAliasTagOpInterface op, PassState &state);
83};
84
85} // namespace
86
87static fir::DeclareOp getDeclareOp(mlir::Value arg) {
88 for (mlir::Operation *use : arg.getUsers())
89 if (fir::DeclareOp declare = mlir::dyn_cast<fir::DeclareOp>(use))
90 return declare;
91 return nullptr;
92}
93
94/// Get the name of a function argument using the "fir.bindc_name" attribute,
95/// or ""
96static std::string getFuncArgName(mlir::Value arg) {
97 // first try getting the name from the hlfir.declare
98 if (fir::DeclareOp declare = getDeclareOp(arg))
99 return declare.getUniqName().str();
100
101 // get from attribute on function argument
102 // always succeeds because arg is a function argument
103 mlir::BlockArgument blockArg = mlir::cast<mlir::BlockArgument>(arg);
104 assert(blockArg.getOwner() && blockArg.getOwner()->isEntryBlock() &&
105 "arg is a function argument");
106 mlir::FunctionOpInterface func = mlir::dyn_cast<mlir::FunctionOpInterface>(
107 blockArg.getOwner()->getParentOp());
108 mlir::StringAttr attr = func.getArgAttrOfType<mlir::StringAttr>(
109 blockArg.getArgNumber(), "fir.bindc_name");
110 if (!attr)
111 return "";
112 return attr.str();
113}
114
115void AddAliasTagsPass::runOnAliasInterface(fir::FirAliasTagOpInterface op,
116 PassState &state) {
117 mlir::func::FuncOp func = op->getParentOfType<mlir::func::FuncOp>();
118 if (!func)
119 return;
120
121 llvm::SmallVector<mlir::Value> accessedOperands = op.getAccessedOperands();
122 assert(accessedOperands.size() == 1 &&
123 "load and store only access one address");
124 mlir::Value memref = accessedOperands.front();
125
126 // skip boxes. These get an "any descriptor access" tag in TBAABuilder
127 // (CodeGen). I didn't see any speedup from giving each box a separate TBAA
128 // type.
129 if (mlir::isa<fir::BaseBoxType>(fir::unwrapRefType(memref.getType())))
130 return;
131 LLVM_DEBUG(llvm::dbgs() << "Analysing " << op << "\n");
132
133 const fir::AliasAnalysis::Source &source = state.getSource(memref);
134 if (source.isTargetOrPointer()) {
135 LLVM_DEBUG(llvm::dbgs().indent(2) << "Skipping TARGET/POINTER\n");
136 // These will get an "any data access" tag in TBAABuilder (CodeGen): causing
137 // them to "MayAlias" with all non-box accesses
138 return;
139 }
140
141 mlir::LLVM::TBAATagAttr tag;
142 // TBAA for dummy arguments
143 if (enableDummyArgs &&
144 source.kind == fir::AliasAnalysis::SourceKind::Argument) {
145 LLVM_DEBUG(llvm::dbgs().indent(2)
146 << "Found reference to dummy argument at " << *op << "\n");
147 std::string name = getFuncArgName(source.u.get<mlir::Value>());
148 if (!name.empty())
149 tag = state.getFuncTree(func).dummyArgDataTree.getTag(name);
150 else
151 LLVM_DEBUG(llvm::dbgs().indent(2)
152 << "WARN: couldn't find a name for dummy argument " << *op
153 << "\n");
154
155 // TBAA for global variables
156 } else if (enableGlobals &&
157 source.kind == fir::AliasAnalysis::SourceKind::Global) {
158 mlir::SymbolRefAttr glbl = source.u.get<mlir::SymbolRefAttr>();
159 const char *name = glbl.getRootReference().data();
160 LLVM_DEBUG(llvm::dbgs().indent(2) << "Found reference to global " << name
161 << " at " << *op << "\n");
162 tag = state.getFuncTree(func).globalDataTree.getTag(name);
163
164 // TBAA for SourceKind::Direct
165 } else if (enableDirect &&
166 source.kind == fir::AliasAnalysis::SourceKind::Direct) {
167 if (source.u.is<mlir::SymbolRefAttr>()) {
168 mlir::SymbolRefAttr glbl = source.u.get<mlir::SymbolRefAttr>();
169 const char *name = glbl.getRootReference().data();
170 LLVM_DEBUG(llvm::dbgs().indent(2) << "Found reference to direct " << name
171 << " at " << *op << "\n");
172 tag = state.getFuncTree(func).directDataTree.getTag(name);
173 } else {
174 // SourceKind::Direct is likely to be extended to cases which are not a
175 // SymbolRefAttr in the future
176 LLVM_DEBUG(llvm::dbgs().indent(2) << "Can't get name for direct "
177 << source << " at " << *op << "\n");
178 }
179
180 // TBAA for local allocations
181 } else if (enableLocalAllocs &&
182 source.kind == fir::AliasAnalysis::SourceKind::Allocate) {
183 std::optional<llvm::StringRef> name;
184 mlir::Operation *sourceOp = source.u.get<mlir::Value>().getDefiningOp();
185 if (auto alloc = mlir::dyn_cast_or_null<fir::AllocaOp>(sourceOp))
186 name = alloc.getUniqName();
187 else if (auto alloc = mlir::dyn_cast_or_null<fir::AllocMemOp>(sourceOp))
188 name = alloc.getUniqName();
189 if (name) {
190 LLVM_DEBUG(llvm::dbgs().indent(2) << "Found reference to allocation "
191 << name << " at " << *op << "\n");
192 tag = state.getFuncTree(func).allocatedDataTree.getTag(*name);
193 } else {
194 LLVM_DEBUG(llvm::dbgs().indent(2)
195 << "WARN: couldn't find a name for allocation " << *op
196 << "\n");
197 }
198 } else {
199 if (source.kind != fir::AliasAnalysis::SourceKind::Argument &&
200 source.kind != fir::AliasAnalysis::SourceKind::Allocate &&
201 source.kind != fir::AliasAnalysis::SourceKind::Global)
202 LLVM_DEBUG(llvm::dbgs().indent(2)
203 << "WARN: unsupported value: " << source << "\n");
204 }
205
206 if (tag)
207 op.setTBAATags(mlir::ArrayAttr::get(&getContext(), tag));
208}
209
210void AddAliasTagsPass::runOnOperation() {
211 LLVM_DEBUG(llvm::dbgs() << "=== Begin " DEBUG_TYPE " ===\n");
212
213 // MLIR forbids storing state in a pass because different instances might be
214 // used in different threads.
215 // Instead this pass stores state per mlir::ModuleOp (which is what MLIR
216 // thinks the pass operates on), then the real work of the pass is done in
217 // runOnAliasInterface
218 PassState state;
219
220 mlir::ModuleOp mod = getOperation();
221 mod.walk(
222 [&](fir::FirAliasTagOpInterface op) { runOnAliasInterface(op, state); });
223
224 LLVM_DEBUG(llvm::dbgs() << "=== End " DEBUG_TYPE " ===\n");
225}
226
227std::unique_ptr<mlir::Pass> fir::createAliasTagsPass() {
228 return std::make_unique<AddAliasTagsPass>();
229}
230

source code of flang/lib/Optimizer/Transforms/AddAliasTags.cpp