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 | |
28 | namespace 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 | |
35 | static 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" )); |
38 | static 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" )); |
41 | static 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. |
48 | static 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 | |
52 | namespace { |
53 | |
54 | /// Shared state per-module |
55 | class PassState { |
56 | public: |
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 | |
69 | private: |
70 | fir::AliasAnalysis analysis; |
71 | llvm::DenseMap<mlir::Value, fir::AliasAnalysis::Source> analysisCache; |
72 | fir::TBAAForrest forrest; |
73 | }; |
74 | |
75 | class AddAliasTagsPass : public fir::impl::AddAliasTagsBase<AddAliasTagsPass> { |
76 | public: |
77 | void runOnOperation() override; |
78 | |
79 | private: |
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 | |
87 | static 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 "" |
96 | static 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 | |
115 | void 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 | |
210 | void 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 | |
227 | std::unique_ptr<mlir::Pass> fir::createAliasTagsPass() { |
228 | return std::make_unique<AddAliasTagsPass>(); |
229 | } |
230 | |