Skip to content

[flang] AliasAnalysis: Fix pointer component logic #94242

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 25 commits into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
484cb90
WIP: [flang] AliasAnalysis: Fix pointer component logic
jdenny-ornl May 28, 2024
9f6d2a7
WIP: Fix case of ptr dummy arg vs. ptr component
jdenny-ornl May 30, 2024
78fc160
Fix logic in various ways, and add some tests
jdenny-ornl Jun 5, 2024
4273b38
Merge commit 'b3b9f8dd4cad' into flang-aa-ptr-component
jdenny-ornl Jun 11, 2024
492efb3
Check x%next vs. y%next
jdenny-ornl Jun 11, 2024
9b4d39a
Merge branch 'main' into flang-aa-ptr-component
jdenny-ornl Jun 13, 2024
ad0438c
Drop accidental -debug from fir-opt in test
jdenny-ornl Jun 13, 2024
1e5fdd4
Relocate some functions as requested by reviewer
jdenny-ornl Jun 13, 2024
3e48239
Value -> mlir::Value
jdenny-ornl Jun 19, 2024
bec0399
Fix and test HostAssoc case
jdenny-ornl Jun 19, 2024
776a17d
Fix and test after convert-hlfir-to-fir
jdenny-ornl Jun 20, 2024
09e91c6
Merge branch 'main' into flang-aa-ptr-component
jdenny-ornl Jul 10, 2024
4a73682
Encapsulate repeated logic into functions
jdenny-ornl Jul 12, 2024
ee248f5
Fix todos for handling addresses of pointers
jdenny-ornl Jul 12, 2024
a61be3a
Fix handling of dummy arg with target attribute
jdenny-ornl Jul 12, 2024
ea40a25
Give test more meaningful name
jdenny-ornl Jul 12, 2024
ba8e749
Merge branch 'main' into flang-aa-ptr-component
jdenny-ornl Aug 13, 2024
b14ff32
Merge branch 'main' into flang-aa-ptr-component
jdenny-ornl Aug 23, 2024
b31f352
Merge branch 'main' into flang-aa-ptr-component
jdenny-ornl Aug 26, 2024
553cf74
Remove todo about merging tests
jdenny-ornl Sep 5, 2024
285c2ce
Merge branch 'main' into flang-aa-ptr-component
jdenny-ornl Sep 5, 2024
27f1d94
Merge branch 'main' into flang-aa-ptr-component
jdenny-ornl Sep 16, 2024
aa4bb45
Merge branch 'main' into flang-aa-ptr-component
jdenny-ornl Sep 26, 2024
0766eba
Merge branch 'main' into flang-aa-ptr-component
jdenny-ornl Oct 14, 2024
942a625
Improve some function names and comments
jdenny-ornl Oct 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions flang/include/flang/Optimizer/Analysis/AliasAnalysis.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,26 @@ struct AliasAnalysis {
bool isData() const;
bool isBoxData() const;

/// @name Dummy Argument Aliasing
///
/// Check conditions related to dummy argument aliasing.
///
/// For all uses, a result of false can prevent MayAlias from being
/// reported, so the list of cases where false is returned is conservative.

///@{
/// The address of a (possibly host associated) dummy argument of the
/// current function?
bool mayBeDummyArgOrHostAssoc() const;
/// \c mayBeDummyArgOrHostAssoc and the address of a pointer?
bool mayBePtrDummyArgOrHostAssoc() const;
/// The address of an actual argument of the current function?
bool mayBeActualArg() const;
/// \c mayBeActualArg and the address of either a pointer or a composite
/// with a pointer component?
bool mayBeActualArgWithPtr(const mlir::Value *val) const;
///@}

mlir::Type getType() const;
};

Expand Down
160 changes: 148 additions & 12 deletions flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,39 @@ bool AliasAnalysis::Source::isBoxData() const {
origin.isData;
}

bool AliasAnalysis::Source::mayBeDummyArgOrHostAssoc() const {
return kind != SourceKind::Allocate && kind != SourceKind::Global;
}

bool AliasAnalysis::Source::mayBePtrDummyArgOrHostAssoc() const {
// Must alias like dummy arg (or HostAssoc).
if (!mayBeDummyArgOrHostAssoc())
return false;
// Must be address of the dummy arg not of a dummy arg component.
if (isRecordWithPointerComponent(valueType))
return false;
// Must be address *of* (not *in*) a pointer.
return attributes.test(Attribute::Pointer) && !isData();
}

bool AliasAnalysis::Source::mayBeActualArg() const {
return kind != SourceKind::Allocate;
}

bool AliasAnalysis::Source::mayBeActualArgWithPtr(
const mlir::Value *val) const {
// Must not be local.
if (!mayBeActualArg())
return false;
// Can be address *of* (not *in*) a pointer.
if (attributes.test(Attribute::Pointer) && !isData())
return true;
// Can be address of a composite with a pointer component.
if (isRecordWithPointerComponent(val->getType()))
return true;
return false;
}

AliasResult AliasAnalysis::alias(mlir::Value lhs, mlir::Value rhs) {
// TODO: alias() has to be aware of the function scopes.
// After MLIR inlining, the current implementation may
Expand All @@ -118,13 +151,42 @@ AliasResult AliasAnalysis::alias(mlir::Value lhs, mlir::Value rhs) {
}

if (lhsSrc.kind == rhsSrc.kind) {
// If the kinds and origins are the same, then lhs and rhs must alias unless
// either source is approximate. Approximate sources are for parts of the
// origin, but we don't have info here on which parts and whether they
// overlap, so we normally return MayAlias in that case.
if (lhsSrc.origin == rhsSrc.origin) {
LLVM_DEBUG(llvm::dbgs()
<< " aliasing because same source kind and origin\n");
if (approximateSource)
return AliasResult::MayAlias;
return AliasResult::MustAlias;
}
// If one value is the address of a composite, and if the other value is the
// address of a pointer/allocatable component of that composite, their
// origins compare unequal because the latter has !isData(). As for the
// address of any component vs. the address of the composite, a store to one
// can affect a load from the other, so the result should be MayAlias. To
// catch this case, we conservatively return MayAlias when one value is the
// address of a composite, the other value is non-data, and they have the
// same origin value.
//
// TODO: That logic does not check that the latter is actually a component
// of the former, so it can return MayAlias when unnecessary. For example,
// they might both be addresses of components of a larger composite.
//
// FIXME: Actually, we should generalize from isRecordWithPointerComponent
// to any composite because a component with !isData() is not always a
// pointer. However, Source::isRecordWithPointerComponent currently doesn't
// actually check for pointer components, so it's fine for now.
if (lhsSrc.origin.u == rhsSrc.origin.u &&
((isRecordWithPointerComponent(lhs.getType()) && !rhsSrc.isData()) ||
(isRecordWithPointerComponent(rhs.getType()) && !lhsSrc.isData()))) {
LLVM_DEBUG(llvm::dbgs()
<< " aliasing between composite and non-data component with "
<< "same source kind and origin value\n");
return AliasResult::MayAlias;
}

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

Source *src1, *src2;
mlir::Value *val1, *val2;
if (lhsSrc.kind < rhsSrc.kind) {
src1 = &lhsSrc;
src2 = &rhsSrc;
val1 = &lhs;
val2 = &rhs;
} else {
src1 = &rhsSrc;
src2 = &lhsSrc;
val1 = &rhs;
val2 = &lhs;
}

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

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

// Box for POINTER component inside an object of a derived type
// may alias box of a POINTER object, as well as boxes for POINTER
// components inside two objects of derived types may alias.
if ((isRecordWithPointerComponent(src1->valueType) &&
src2->isTargetOrPointer()) ||
(isRecordWithPointerComponent(src2->valueType) &&
src1->isTargetOrPointer()) ||
(isRecordWithPointerComponent(src1->valueType) &&
isRecordWithPointerComponent(src2->valueType))) {
LLVM_DEBUG(llvm::dbgs() << " aliasing because of pointer components\n");
// Aliasing for dummy arg with target attribute.
//
// The address of a dummy arg (or HostAssoc) may alias the address of a
// non-local (global or another dummy arg) when both have target attributes.
// If either is a composite, addresses of components may alias as well.
//
// The previous "if" calling isTargetOrPointer casts a very wide net and so
// reports MayAlias for many such cases that would otherwise be reported here.
// It specifically skips such cases where one or both values have !isData()
// (e.g., address *of* pointer/allocatable component vs. address of
// composite), so this "if" catches those cases.
if (src1->attributes.test(Attribute::Target) &&
src2->attributes.test(Attribute::Target) &&
((src1->mayBeDummyArgOrHostAssoc() && src2->mayBeActualArg()) ||
(src2->mayBeDummyArgOrHostAssoc() && src1->mayBeActualArg()))) {
LLVM_DEBUG(llvm::dbgs()
<< " aliasing between targets where one is a dummy arg\n");
return AliasResult::MayAlias;
}

// Aliasing for dummy arg that is a pointer.
//
// The address of a pointer dummy arg (but not a pointer component of a dummy
// arg) may alias the address of either (1) a non-local pointer or (2) thus a
// non-local composite with a pointer component. A non-local might be a
// global or another dummy arg. The following is an example of the global
// composite case:
//
// module m
// type t
// real, pointer :: p
// end type
// type(t) :: a
// type(t) :: b
// contains
// subroutine test(p)
// real, pointer :: p
// p = 42
// a = b
// print *, p
// end subroutine
// end module
// program main
// use m
// real, target :: x1 = 1
// real, target :: x2 = 2
// a%p => x1
// b%p => x2
// call test(a%p)
// end
//
// The dummy argument p is an alias for a%p, even for the purposes of pointer
// association during the assignment a = b. Thus, the program should print 2.
//
// The same is true when p is HostAssoc. For example, we might replace the
// test subroutine above with:
//
// subroutine test(p)
// real, pointer :: p
// call internal()
// contains
// subroutine internal()
// p = 42
// a = b
// print *, p
// end subroutine
// end subroutine
if ((src1->mayBePtrDummyArgOrHostAssoc() &&
src2->mayBeActualArgWithPtr(val2)) ||
(src2->mayBePtrDummyArgOrHostAssoc() &&
src1->mayBeActualArgWithPtr(val1))) {
LLVM_DEBUG(llvm::dbgs()
<< " aliasing between pointer dummy arg and either pointer or "
<< "composite with pointer component\n");
return AliasResult::MayAlias;
}

Expand Down Expand Up @@ -274,6 +406,8 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v,
followBoxData = true;
})
.Case<fir::ArrayCoorOp, fir::CoordinateOp>([&](auto op) {
if (isPointerReference(ty))
attributes.set(Attribute::Pointer);
v = op->getOperand(0);
defOp = v.getDefiningOp();
if (mlir::isa<fir::BaseBoxType>(v.getType()))
Expand Down Expand Up @@ -392,6 +526,8 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v,
defOp = v.getDefiningOp();
})
.Case<hlfir::DesignateOp>([&](auto op) {
auto varIf = llvm::cast<fir::FortranVariableOpInterface>(defOp);
attributes |= getAttrsFromVariable(varIf);
// Track further through the memory indexed into
// => if the source arrays/structures don't alias then nor do the
// results of hlfir.designate
Expand Down
15 changes: 7 additions & 8 deletions flang/test/Analysis/AliasAnalysis/alias-analysis-3.fir
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
// CHECK-LABEL: Testing : "_QMmPtest
// CHECK: a#0 <-> func.region0#0: MayAlias

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

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

// -----

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

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

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

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

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

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"} {
%0 = fir.field_index pointer_component, !fir.type<_QMmTt{pointer_component:!fir.box<!fir.ptr<f32>>}>
Expand Down Expand Up @@ -137,8 +137,7 @@ func.func private @_QPtest2(!fir.ref<f32>)
// end module m

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

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"} {
%0 = fir.field_index allocatable_component, !fir.type<_QMmTt{allocatable_component:!fir.box<!fir.heap<f32>>}>
Expand Down
Loading