clang 24.0.0git
CIRBasicAliasAnalysis.cpp
Go to the documentation of this file.
1//===- CIRBasicAliasAnalysis.cpp - Basic CIR Alias Analysis ---------------===//
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
10#include "mlir/Interfaces/SideEffectInterfaces.h"
13#include "llvm/Support/DebugLog.h"
14
15#define DEBUG_TYPE "cir-basic-alias-analysis"
16
17using namespace llvm;
18using namespace cir;
19
20//===----------------------------------------------------------------------===//
21// Helpers
22//===----------------------------------------------------------------------===//
23
24static constexpr unsigned MaxLookupDepth = 6;
25
26mlir::Value CIRBasicAliasAnalysis::getUnderlyingObject(mlir::Value val) {
27 LDBG() << "Getting underlying object for: " << val;
28
29 for (unsigned depth = 0; depth < MaxLookupDepth; ++depth) {
30 mlir::Operation *defOp = val.getDefiningOp();
31 if (!defOp) {
32 LDBG() << "No defining operation, stopping";
33 break; // Block argument (e.g. function parameter) — stop here.
34 }
35
36 // Bitcast and address-space casts don't change the underlying object.
37 // array_to_ptrdecay produces an element pointer to the same storage as
38 // the array pointer, so strip through it too.
39 if (auto castOp = mlir::dyn_cast<cir::CastOp>(defOp)) {
40 if (castOp.isAllocaPreservingCast() ||
41 castOp.getKind() == cir::CastKind::array_to_ptrdecay) {
42 LDBG() << "Walking past cast operation";
43 val = castOp.getSrc();
44 continue;
45 }
46 LDBG() << "Opaque cast operation, stopping";
47 break;
48 }
49
50 // Pointer stride: only strip through when we can prove the access stays
51 // within the bounds of the underlying allocation.
52 if (auto strideOp = mlir::dyn_cast<cir::PtrStrideOp>(defOp)) {
53 auto constOp = strideOp.getStride().getDefiningOp<cir::ConstantOp>();
54 if (constOp) {
55 if (auto intAttr = mlir::dyn_cast<cir::IntAttr>(constOp.getValue())) {
56 APInt stride = intAttr.getValue();
57
58 // Zero stride is trivially in-bounds.
59 if (stride.isZero()) {
60 LDBG() << "Walking past zero-strided PtrStrideOp";
61 val = strideOp.getBase();
62 continue;
63 }
64 }
65 }
66 // Dynamic stride or unverifiable bounds — stop here conservatively.
67 LDBG() << "Non-zero or dynamic PtrStrideOp, stopping";
68 break;
69 }
70
71 // Handle special cases for zero-offset sub-object accesses.
72 if (auto op = mlir::dyn_cast<cir::GetMemberOp>(defOp)) {
73 if (op.getIndex() == 0) {
74 LDBG() << "GetMemberOp[0], following to underlying object";
75 val = op.getAddr();
76 continue;
77 } else {
78 LDBG() << "GetMemberOp, non-zero index, stopping";
79 break;
80 }
81 }
82 if (auto op = mlir::dyn_cast<cir::GetElementOp>(defOp)) {
83 cir::IntAttr index;
84 if (auto constOp = op.getIndex().getDefiningOp<cir::ConstantOp>())
85 index = mlir::dyn_cast<cir::IntAttr>(constOp.getValue());
86 if (index && index.getValue().isZero()) {
87 LDBG() << "GetElementOp[0], following to underlying object";
88 val = op.getBase();
89 continue;
90 }
91 LDBG() << "GetElementOp, non-zero or dynamic index, stopping";
92 break;
93 }
94 if (auto op = mlir::dyn_cast<cir::BaseClassAddrOp>(defOp)) {
95 // A zero byte offset means the base subobject starts at the same address
96 // as the derived object.
97 if (op.getOffset().isZero()) {
98 LDBG() << "BaseClassAddrOp[0], following to underlying object";
99 val = op.getDerivedAddr();
100 continue;
101 }
102 LDBG() << "BaseClassAddrOp, non-zero offset, stopping";
103 break;
104 }
105 if (auto op = mlir::dyn_cast<cir::DerivedClassAddrOp>(defOp)) {
106 // The offset is stored unsigned but applied as a negative adjustment. A
107 // zero offset means the derived object starts at the same address as the
108 // base subobject.
109 if (op.getOffset().isZero()) {
110 LDBG() << "DerivedClassAddrOp[0], following to underlying object";
111 val = op.getBaseAddr();
112 continue;
113 }
114 LDBG() << "DerivedClassAddrOp, non-zero offset, stopping";
115 break;
116 }
117 if (auto op = mlir::dyn_cast<cir::ComplexRealPtrOp>(defOp)) {
118 LDBG() << "Getting input pointer for ComplexRealPtrOp";
119 val = op.getOperand();
120 continue;
121 }
122 if (auto op = mlir::dyn_cast<cir::ComplexImagPtrOp>(defOp)) {
123 LDBG() << "ComplexImagPtrOp, stopping";
124 break;
125 }
126
127 LDBG() << "Unhandled operation, stopping";
128 break; // Unknown op — stop here conservatively.
129 }
130 return val;
131}
132
133CIRBasicAliasAnalysis::ObjectRelation
134CIRBasicAliasAnalysis::classifyObjects(mlir::Value lhs, mlir::Value rhs) {
135 LDBG() << "Checking if " << lhs << " and " << rhs << " are distinct objects";
136
137 // Two values are distinct allocations if they originate from different
138 // cir.alloca operations (or other allocation ops) in the same function.
139 // TODO: Extend to cover global addresses, function arguments with noalias,
140 // and heap allocations.
141 mlir::Value lhsObj = getUnderlyingObject(lhs);
142 mlir::Value rhsObj = getUnderlyingObject(rhs);
143
144 if (lhsObj == rhsObj) {
145 LDBG() << "Identical values, not distinct";
146 return ObjectRelation::Identical;
147 }
148
149 // Different cir.alloca ops in the same function cannot alias.
150 if (mlir::isa_and_nonnull<cir::AllocaOp>(lhsObj.getDefiningOp()) &&
151 mlir::isa_and_nonnull<cir::AllocaOp>(rhsObj.getDefiningOp())) {
152 LDBG() << "Different cir.alloca ops in the same function, distinct";
153 return ObjectRelation::Distinct;
154 }
155
156 LDBG() << "Conservative fallback, not distinct";
157 return ObjectRelation::Unknown;
158}
159
160//===----------------------------------------------------------------------===//
161// CIRBasicAliasAnalysis
162//===----------------------------------------------------------------------===//
163
164mlir::AliasResult CIRBasicAliasAnalysis::alias(mlir::Value lhs,
165 mlir::Value rhs) {
166 LDBG() << "Checking alias between: " << lhs << " and " << rhs;
167
168 if (lhs == rhs) {
169 LDBG() << "Trivial alias between identical values";
170 return mlir::AliasResult::MustAlias;
171 }
172
173 ObjectRelation relation = classifyObjects(lhs, rhs);
174 switch (relation) {
175 case ObjectRelation::Distinct:
176 LDBG() << "No alias between distinct objects";
177 return mlir::AliasResult::NoAlias;
178 case ObjectRelation::Identical:
179 LDBG() << "Must alias between identical objects";
180 return mlir::AliasResult::MustAlias;
181 case ObjectRelation::Unknown:
182 // Conservative fallback — the aggregate will try other implementations.
183 LDBG() << "Conservative fallback, may alias";
184 return mlir::AliasResult::MayAlias;
185 }
186 llvm_unreachable("Unhandled ObjectRelation");
187}
188
189mlir::ModRefResult CIRBasicAliasAnalysis::getModRef(mlir::Operation *op,
190 mlir::Value location) {
191 LDBG() << "getModRef: "
192 << mlir::OpWithFlags(op, mlir::OpPrintingFlags().skipRegions())
193 << " on location " << location;
194
195 auto effects = mlir::dyn_cast<mlir::MemoryEffectOpInterface>(op);
196 if (!effects) {
197 LDBG() << "No memory effect interface, returning ModAndRef";
198 return mlir::ModRefResult::getModAndRef();
199 }
200
202 effects.getEffects(effectList);
203
204 auto classifyEffect = [location, this](
205 const mlir::MemoryEffects::EffectInstance &effect) {
206 if (mlir::isa<mlir::MemoryEffects::Allocate>(effect.getEffect())) {
207 LDBG() << "Skipping allocate effect";
208 return mlir::ModRefResult::getNoModRef();
209 }
210
211 mlir::AliasResult aliasResult = mlir::AliasResult::MayAlias;
212 if (mlir::Value affectedLocation = effect.getValue()) {
213 LDBG() << " Checking alias between affected location "
214 << affectedLocation << " and query location " << location;
215 aliasResult = alias(affectedLocation, location);
216 LDBG() << " Alias result: "
217 << (aliasResult.isMust() ? "MustAlias"
218 : aliasResult.isNo() ? "NoAlias"
219 : "MayAlias");
220 } else {
221 // An effect on a non-addressable resource cannot affect a
222 // pointer-based location.
223 if (!effect.getResource()->isAddressable()) {
224 LDBG() << " Effect on non-addressable resource '"
225 << effect.getResource()->getName() << "', skipping (NoAlias)";
226 aliasResult = mlir::AliasResult::NoAlias;
227 } else {
228 LDBG() << " No effect value, assuming MayAlias";
229 }
230 }
231
232 // If the affected location doesn't alias with the query location,
233 // ignore this effect.
234 if (aliasResult.isNo()) {
235 LDBG() << "No alias with affected location";
236 return mlir::ModRefResult::getNoModRef();
237 }
238
239 // TODO: Consider whether Free should be NoModRef.
240 if (mlir::isa<mlir::MemoryEffects::Free>(effect.getEffect())) {
241 LDBG() << "Skipping free effect";
242 return mlir::ModRefResult::getModAndRef();
243 }
244
245 if (mlir::isa<mlir::MemoryEffects::Write>(effect.getEffect())) {
246 LDBG() << "Write effect, adding Mod";
247 return mlir::ModRefResult::getMod();
248 }
249
250 if (mlir::isa<mlir::MemoryEffects::Read>(effect.getEffect())) {
251 LDBG() << "Read effect, adding Ref";
252 return mlir::ModRefResult::getRef();
253 }
254
255 LDBG() << "Unexpected memory effect: " << effect.getEffect();
256 return mlir::ModRefResult::getNoModRef();
257 };
258
259 return llvm::accumulate(llvm::map_range(effectList, classifyEffect),
260 mlir::ModRefResult::getNoModRef(),
261 [](mlir::ModRefResult lhs, mlir::ModRefResult rhs) {
262 return lhs.merge(rhs);
263 });
264}
static constexpr unsigned MaxLookupDepth
mlir::ModRefResult getModRef(mlir::Operation *op, mlir::Value location)
Return the modify-reference behavior of op on location.
mlir::AliasResult alias(mlir::Value lhs, mlir::Value rhs)
Return the aliasing behavior between two values.
Diagnostic wrappers for TextAPI types for error reporting.
Definition Dominators.h:30