Skip to content

Commit 7323533

Browse files
authored
[flang] AliasAnalysis: Fix pointer component logic (#94242)
This PR applies the changes discussed in [[RFC] Rationale for Flang AliasAnalysis pointer component logic](https://discourse.llvm.org/t/rfc-rationale-for-flang-aliasanalysis-pointer-component-logic/79252). In summary, this PR replaces the existing pointer component logic in Flang's AliasAnalysis implementation. That logic focuses on aliasing between pointers and non-pointer, non-target composites that have pointer components. However, it is more conservative than necessary, and some existing tests expect its current results when less conservative results seem reasonable. This PR splits the logic into two cases: 1. Source values are the same: Return MayAlias when one value is the address of a composite, and the other value is statically the address of a pointer component of that composite. 2. Source values are different: Return MayAlias when one value is the address of a composite (actual argument), and the other value is the address of a pointer (dummy arg) that might dynamically be a component of that composite. In both cases, the actual implementation is still more conservative than described above, but it can be improved further later. Details appear in the comments. Additionally, this PR revises the logic that reports MayAlias for a pointer/target vs. another pointer/target. It constrains the existing logic to handle only isData cases, and it adds less conservative handling of !isData cases elsewhere. First, it extends case 2 listed above to cover the case where the actual argument is the address of a pointer rather than a composite. Second, it adds a third case: where target attributes enable aliasing with a dummy argument.
1 parent b75f9f7 commit 7323533

File tree

4 files changed

+911
-28
lines changed

4 files changed

+911
-28
lines changed

flang/include/flang/Optimizer/Analysis/AliasAnalysis.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,26 @@ struct AliasAnalysis {
157157
bool isData() const;
158158
bool isBoxData() const;
159159

160+
/// @name Dummy Argument Aliasing
161+
///
162+
/// Check conditions related to dummy argument aliasing.
163+
///
164+
/// For all uses, a result of false can prevent MayAlias from being
165+
/// reported, so the list of cases where false is returned is conservative.
166+
167+
///@{
168+
/// The address of a (possibly host associated) dummy argument of the
169+
/// current function?
170+
bool mayBeDummyArgOrHostAssoc() const;
171+
/// \c mayBeDummyArgOrHostAssoc and the address of a pointer?
172+
bool mayBePtrDummyArgOrHostAssoc() const;
173+
/// The address of an actual argument of the current function?
174+
bool mayBeActualArg() const;
175+
/// \c mayBeActualArg and the address of either a pointer or a composite
176+
/// with a pointer component?
177+
bool mayBeActualArgWithPtr(const mlir::Value *val) const;
178+
///@}
179+
160180
mlir::Type getType() const;
161181
};
162182

flang/lib/Optimizer/Analysis/AliasAnalysis.cpp

Lines changed: 148 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,39 @@ bool AliasAnalysis::Source::isBoxData() const {
9696
origin.isData;
9797
}
9898

99+
bool AliasAnalysis::Source::mayBeDummyArgOrHostAssoc() const {
100+
return kind != SourceKind::Allocate && kind != SourceKind::Global;
101+
}
102+
103+
bool AliasAnalysis::Source::mayBePtrDummyArgOrHostAssoc() const {
104+
// Must alias like dummy arg (or HostAssoc).
105+
if (!mayBeDummyArgOrHostAssoc())
106+
return false;
107+
// Must be address of the dummy arg not of a dummy arg component.
108+
if (isRecordWithPointerComponent(valueType))
109+
return false;
110+
// Must be address *of* (not *in*) a pointer.
111+
return attributes.test(Attribute::Pointer) && !isData();
112+
}
113+
114+
bool AliasAnalysis::Source::mayBeActualArg() const {
115+
return kind != SourceKind::Allocate;
116+
}
117+
118+
bool AliasAnalysis::Source::mayBeActualArgWithPtr(
119+
const mlir::Value *val) const {
120+
// Must not be local.
121+
if (!mayBeActualArg())
122+
return false;
123+
// Can be address *of* (not *in*) a pointer.
124+
if (attributes.test(Attribute::Pointer) && !isData())
125+
return true;
126+
// Can be address of a composite with a pointer component.
127+
if (isRecordWithPointerComponent(val->getType()))
128+
return true;
129+
return false;
130+
}
131+
99132
AliasResult AliasAnalysis::alias(mlir::Value lhs, mlir::Value rhs) {
100133
// TODO: alias() has to be aware of the function scopes.
101134
// After MLIR inlining, the current implementation may
@@ -118,13 +151,42 @@ AliasResult AliasAnalysis::alias(mlir::Value lhs, mlir::Value rhs) {
118151
}
119152

120153
if (lhsSrc.kind == rhsSrc.kind) {
154+
// If the kinds and origins are the same, then lhs and rhs must alias unless
155+
// either source is approximate. Approximate sources are for parts of the
156+
// origin, but we don't have info here on which parts and whether they
157+
// overlap, so we normally return MayAlias in that case.
121158
if (lhsSrc.origin == rhsSrc.origin) {
122159
LLVM_DEBUG(llvm::dbgs()
123160
<< " aliasing because same source kind and origin\n");
124161
if (approximateSource)
125162
return AliasResult::MayAlias;
126163
return AliasResult::MustAlias;
127164
}
165+
// If one value is the address of a composite, and if the other value is the
166+
// address of a pointer/allocatable component of that composite, their
167+
// origins compare unequal because the latter has !isData(). As for the
168+
// address of any component vs. the address of the composite, a store to one
169+
// can affect a load from the other, so the result should be MayAlias. To
170+
// catch this case, we conservatively return MayAlias when one value is the
171+
// address of a composite, the other value is non-data, and they have the
172+
// same origin value.
173+
//
174+
// TODO: That logic does not check that the latter is actually a component
175+
// of the former, so it can return MayAlias when unnecessary. For example,
176+
// they might both be addresses of components of a larger composite.
177+
//
178+
// FIXME: Actually, we should generalize from isRecordWithPointerComponent
179+
// to any composite because a component with !isData() is not always a
180+
// pointer. However, Source::isRecordWithPointerComponent currently doesn't
181+
// actually check for pointer components, so it's fine for now.
182+
if (lhsSrc.origin.u == rhsSrc.origin.u &&
183+
((isRecordWithPointerComponent(lhs.getType()) && !rhsSrc.isData()) ||
184+
(isRecordWithPointerComponent(rhs.getType()) && !lhsSrc.isData()))) {
185+
LLVM_DEBUG(llvm::dbgs()
186+
<< " aliasing between composite and non-data component with "
187+
<< "same source kind and origin value\n");
188+
return AliasResult::MayAlias;
189+
}
128190

129191
// Two host associated accesses may overlap due to an equivalence.
130192
if (lhsSrc.kind == SourceKind::HostAssoc) {
@@ -134,12 +196,17 @@ AliasResult AliasAnalysis::alias(mlir::Value lhs, mlir::Value rhs) {
134196
}
135197

136198
Source *src1, *src2;
199+
mlir::Value *val1, *val2;
137200
if (lhsSrc.kind < rhsSrc.kind) {
138201
src1 = &lhsSrc;
139202
src2 = &rhsSrc;
203+
val1 = &lhs;
204+
val2 = &rhs;
140205
} else {
141206
src1 = &rhsSrc;
142207
src2 = &lhsSrc;
208+
val1 = &rhs;
209+
val2 = &lhs;
143210
}
144211

145212
if (src1->kind == SourceKind::Argument &&
@@ -162,23 +229,88 @@ AliasResult AliasAnalysis::alias(mlir::Value lhs, mlir::Value rhs) {
162229
src2->attributes.set(Attribute::Target);
163230
}
164231

165-
// Dummy TARGET/POINTER argument may alias with a global TARGET/POINTER.
232+
// Two TARGET/POINTERs may alias. The logic here focuses on data. Handling
233+
// of non-data is included below.
166234
if (src1->isTargetOrPointer() && src2->isTargetOrPointer() &&
167-
src1->isData() == src2->isData()) {
235+
src1->isData() && src2->isData()) {
168236
LLVM_DEBUG(llvm::dbgs() << " aliasing because of target or pointer\n");
169237
return AliasResult::MayAlias;
170238
}
171239

172-
// Box for POINTER component inside an object of a derived type
173-
// may alias box of a POINTER object, as well as boxes for POINTER
174-
// components inside two objects of derived types may alias.
175-
if ((isRecordWithPointerComponent(src1->valueType) &&
176-
src2->isTargetOrPointer()) ||
177-
(isRecordWithPointerComponent(src2->valueType) &&
178-
src1->isTargetOrPointer()) ||
179-
(isRecordWithPointerComponent(src1->valueType) &&
180-
isRecordWithPointerComponent(src2->valueType))) {
181-
LLVM_DEBUG(llvm::dbgs() << " aliasing because of pointer components\n");
240+
// Aliasing for dummy arg with target attribute.
241+
//
242+
// The address of a dummy arg (or HostAssoc) may alias the address of a
243+
// non-local (global or another dummy arg) when both have target attributes.
244+
// If either is a composite, addresses of components may alias as well.
245+
//
246+
// The previous "if" calling isTargetOrPointer casts a very wide net and so
247+
// reports MayAlias for many such cases that would otherwise be reported here.
248+
// It specifically skips such cases where one or both values have !isData()
249+
// (e.g., address *of* pointer/allocatable component vs. address of
250+
// composite), so this "if" catches those cases.
251+
if (src1->attributes.test(Attribute::Target) &&
252+
src2->attributes.test(Attribute::Target) &&
253+
((src1->mayBeDummyArgOrHostAssoc() && src2->mayBeActualArg()) ||
254+
(src2->mayBeDummyArgOrHostAssoc() && src1->mayBeActualArg()))) {
255+
LLVM_DEBUG(llvm::dbgs()
256+
<< " aliasing between targets where one is a dummy arg\n");
257+
return AliasResult::MayAlias;
258+
}
259+
260+
// Aliasing for dummy arg that is a pointer.
261+
//
262+
// The address of a pointer dummy arg (but not a pointer component of a dummy
263+
// arg) may alias the address of either (1) a non-local pointer or (2) thus a
264+
// non-local composite with a pointer component. A non-local might be a
265+
// global or another dummy arg. The following is an example of the global
266+
// composite case:
267+
//
268+
// module m
269+
// type t
270+
// real, pointer :: p
271+
// end type
272+
// type(t) :: a
273+
// type(t) :: b
274+
// contains
275+
// subroutine test(p)
276+
// real, pointer :: p
277+
// p = 42
278+
// a = b
279+
// print *, p
280+
// end subroutine
281+
// end module
282+
// program main
283+
// use m
284+
// real, target :: x1 = 1
285+
// real, target :: x2 = 2
286+
// a%p => x1
287+
// b%p => x2
288+
// call test(a%p)
289+
// end
290+
//
291+
// The dummy argument p is an alias for a%p, even for the purposes of pointer
292+
// association during the assignment a = b. Thus, the program should print 2.
293+
//
294+
// The same is true when p is HostAssoc. For example, we might replace the
295+
// test subroutine above with:
296+
//
297+
// subroutine test(p)
298+
// real, pointer :: p
299+
// call internal()
300+
// contains
301+
// subroutine internal()
302+
// p = 42
303+
// a = b
304+
// print *, p
305+
// end subroutine
306+
// end subroutine
307+
if ((src1->mayBePtrDummyArgOrHostAssoc() &&
308+
src2->mayBeActualArgWithPtr(val2)) ||
309+
(src2->mayBePtrDummyArgOrHostAssoc() &&
310+
src1->mayBeActualArgWithPtr(val1))) {
311+
LLVM_DEBUG(llvm::dbgs()
312+
<< " aliasing between pointer dummy arg and either pointer or "
313+
<< "composite with pointer component\n");
182314
return AliasResult::MayAlias;
183315
}
184316

@@ -278,6 +410,8 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v,
278410
followBoxData = true;
279411
})
280412
.Case<fir::ArrayCoorOp, fir::CoordinateOp>([&](auto op) {
413+
if (isPointerReference(ty))
414+
attributes.set(Attribute::Pointer);
281415
v = op->getOperand(0);
282416
defOp = v.getDefiningOp();
283417
if (mlir::isa<fir::BaseBoxType>(v.getType()))
@@ -396,6 +530,8 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v,
396530
defOp = v.getDefiningOp();
397531
})
398532
.Case<hlfir::DesignateOp>([&](auto op) {
533+
auto varIf = llvm::cast<fir::FortranVariableOpInterface>(defOp);
534+
attributes |= getAttrsFromVariable(varIf);
399535
// Track further through the memory indexed into
400536
// => if the source arrays/structures don't alias then nor do the
401537
// results of hlfir.designate

flang/test/Analysis/AliasAnalysis/alias-analysis-3.fir

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
// CHECK-LABEL: Testing : "_QMmPtest
2121
// CHECK: a#0 <-> func.region0#0: MayAlias
2222

23-
// FIXME: a's box cannot alias with raw reference to f32 (x), so MayAlias below must be NoAlias:
24-
// CHECK: a#0 <-> func.region0#1: MayAlias
23+
// a's box cannot alias with raw reference to f32 (x)
24+
// CHECK: a#0 <-> func.region0#1: NoAlias
2525

2626
// pointer_dummy's box cannot alias with raw reference to f32 (x)
2727
// CHECK: func.region0#0 <-> func.region0#1: NoAlias
@@ -46,7 +46,7 @@ func.func private @_QPtest2(!fir.ref<f32>)
4646

4747
// -----
4848

49-
// A composite with a pointer component may alias with a dummy
49+
// A composite with a pointer component does not alias with a dummy
5050
// argument of composite type with a pointer component:
5151
// module m
5252
// type t
@@ -63,7 +63,7 @@ func.func private @_QPtest2(!fir.ref<f32>)
6363
// end module m
6464

6565
// CHECK-LABEL: Testing : "_QMmPtest"
66-
// CHECK: a#0 <-> func.region0#0: MayAlias
66+
// CHECK: a#0 <-> func.region0#0: NoAlias
6767

6868
fir.global @_QMmEa : !fir.type<_QMmTt{pointer_component:!fir.box<!fir.ptr<f32>>}> {
6969
%0 = fir.undefined !fir.type<_QMmTt{pointer_component:!fir.box<!fir.ptr<f32>>}>
@@ -88,7 +88,7 @@ func.func private @_QPtest2(!fir.ref<f32>)
8888
// -----
8989

9090
// Two dummy arguments of composite type with a pointer component
91-
// may alias each other:
91+
// do not alias each other:
9292
// module m
9393
// type t
9494
// real, pointer :: pointer_component
@@ -103,7 +103,7 @@ func.func private @_QPtest2(!fir.ref<f32>)
103103
// end module m
104104

105105
// CHECK-LABEL: Testing : "_QMmPtest"
106-
// CHECK: func.region0#0 <-> func.region0#1: MayAlias
106+
// CHECK: func.region0#0 <-> func.region0#1: NoAlias
107107

108108
func.func @_QMmPtest(%arg0: !fir.ref<!fir.type<_QMmTt{pointer_component:!fir.box<!fir.ptr<f32>>}>> {fir.bindc_name = "a"}, %arg1: !fir.ref<!fir.type<_QMmTt{pointer_component:!fir.box<!fir.ptr<f32>>}>> {fir.bindc_name = "b"}, %arg2: !fir.ref<f32> {fir.bindc_name = "x", fir.target}) attributes {test.ptr = "func"} {
109109
%0 = fir.field_index pointer_component, !fir.type<_QMmTt{pointer_component:!fir.box<!fir.ptr<f32>>}>
@@ -137,8 +137,7 @@ func.func private @_QPtest2(!fir.ref<f32>)
137137
// end module m
138138

139139
// CHECK-LABEL: Testing : "_QMmPtest"
140-
// FIXME: MayAlias must be NoAlias
141-
// CHECK: func.region0#0 <-> func.region0#1: MayAlias
140+
// CHECK: func.region0#0 <-> func.region0#1: NoAlias
142141

143142
func.func @_QMmPtest(%arg0: !fir.ref<!fir.type<_QMmTt{allocatable_component:!fir.box<!fir.heap<f32>>}>> {fir.bindc_name = "a"}, %arg1: !fir.ref<!fir.type<_QMmTt{allocatable_component:!fir.box<!fir.heap<f32>>}>> {fir.bindc_name = "b"}) attributes {test.ptr = "func"} {
144143
%0 = fir.field_index allocatable_component, !fir.type<_QMmTt{allocatable_component:!fir.box<!fir.heap<f32>>}>

0 commit comments

Comments
 (0)