Skip to content

Commit

Permalink
[flang] AliasAnalysis: More formally define and distinguish between d…
Browse files Browse the repository at this point in the history
…ata and non-data (#91020)

This PR is an implementation for changes proposed in
https://discourse.llvm.org/t/rfc-distinguish-between-data-and-non-data-in-fir-alias-analysis/78759

Test updates were made when the query was on the wrong reference. So, it
is my hope that this will clear ambiguity on the nature of the queries
from here on.
There are also some TODOs that were addressed. 

It also partly implements what
#87723 is attempting to
accomplish. At least, on a point-to-point query between references, the
distinction is made. To apply it to TBAA, would be another PR.

Note that, the changes were minimal in the TBAA code to retain the
current results.
  • Loading branch information
Renaud-K authored May 16, 2024
1 parent 5ac3435 commit ee407e1
Show file tree
Hide file tree
Showing 6 changed files with 248 additions and 121 deletions.
95 changes: 90 additions & 5 deletions flang/include/flang/Optimizer/Analysis/AliasAnalysis.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,6 @@ struct AliasAnalysis {
/// Represents memory allocated outside of a function
/// and passed to the function via host association tuple.
HostAssoc,
/// Represents direct memory access whose source cannot be further
/// determined
Direct,
/// Represents memory allocated by unknown means and
/// with the memory address defined by a memory reading
/// operation (e.g. fir::LoadOp).
Expand All @@ -50,12 +47,85 @@ struct AliasAnalysis {
/// Attributes of the memory source object.
ENUM_CLASS(Attribute, Target, Pointer, IntentIn);

// See
// https://discourse.llvm.org/t/rfc-distinguish-between-data-and-non-data-in-fir-alias-analysis/78759/1
//
// It is possible, while following the source of a memory reference through
// the use-def chain, to arrive at the same origin, even though the starting
// points were known to not alias.
//
// clang-format off
// Example:
// ------------------- test.f90 --------------------
// module top
// real, pointer :: a(:)
// end module
//
// subroutine test()
// use top
// a(1) = 1
// end subroutine
// -------------------------------------------------
//
// flang-new -fc1 -emit-fir test.f90 -o test.fir
//
// ------------------- test.fir --------------------
// fir.global @_QMtopEa : !fir.box<!fir.ptr<!fir.array<?xf32>>>
//
// func.func @_QPtest() {
// %c1 = arith.constant 1 : index
// %cst = arith.constant 1.000000e+00 : f32
// %0 = fir.address_of(@_QMtopEa) : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>
// %1 = fir.declare %0 {fortran_attrs = #fir.var_attrs<pointer>, uniq_name = "_QMtopEa"} : (!fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>) -> !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>
// %2 = fir.load %1 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>
// ...
// %5 = fir.array_coor %2 %c1 : (!fir.box<!fir.ptr<!fir.array<?xf32>>>, !fir.shift<1>, index) -> !fir.ref<f32>
// fir.store %cst to %5 : !fir.ref<f32>
// return
// }
// -------------------------------------------------
//
// With high level operations, such as fir.array_coor, it is possible to
// reach into the data wrapped by the box (the descriptor). Therefore when
// asking about the memory source of %5, we are really asking about the
// source of the data of box %2.
//
// When asking about the source of %0 which is the address of the box, we
// reach the same source as in the first case: the global @_QMtopEa. Yet one
// source refers to the data while the other refers to the address of the box
// itself.
//
// To distinguish between the two, the isData flag has been added, whereby
// data is defined as any memory reference that is not a box reference.
// Additionally, because it is relied on in HLFIR lowering, we allow querying
// on a box SSA value, which is interpreted as querying on its data.
//
// So in the above example, !fir.ref<f32> and !fir.box<!fir.ptr<!fir.array<?xf32>>> is data,
// while !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>> is not data.

// This also applies to function arguments. In the example below, %arg0
// is data, %arg1 is not data but a load of %arg1 is.
//
// func.func @_QFPtest2(%arg0: !fir.ref<f32>, %arg1: !fir.ref<!fir.box<!fir.ptr<f32>>> ) {
// %0 = fir.load %arg1 : !fir.ref<!fir.box<!fir.ptr<f32>>>
// ... }
//
// clang-format on

struct Source {
using SourceUnion = llvm::PointerUnion<mlir::SymbolRefAttr, mlir::Value>;
using Attributes = Fortran::common::EnumSet<Attribute, Attribute_enumSize>;

/// Source definition of a value.
SourceUnion u;
struct SourceOrigin {
/// Source definition of a value.
SourceUnion u;

/// Whether the source was reached following data or box reference
bool isData{false};
};

SourceOrigin origin;

/// Kind of the memory source.
SourceKind kind;
/// Value type of the source definition.
Expand All @@ -77,6 +147,12 @@ struct AliasAnalysis {
/// attribute.
bool isRecordWithPointerComponent() const;

bool isDummyArgument() const;
bool isData() const;
bool isBoxData() const;

mlir::Type getType() const;

/// Return true, if `ty` is a reference type to a boxed
/// POINTER object or a raw fir::PointerType.
static bool isPointerReference(mlir::Type ty);
Expand All @@ -95,6 +171,15 @@ struct AliasAnalysis {
Source getSource(mlir::Value);
};

inline bool operator==(const AliasAnalysis::Source::SourceOrigin &lhs,
const AliasAnalysis::Source::SourceOrigin &rhs) {
return lhs.u == rhs.u && lhs.isData == rhs.isData;
}
inline bool operator!=(const AliasAnalysis::Source::SourceOrigin &lhs,
const AliasAnalysis::Source::SourceOrigin &rhs) {
return !(lhs == rhs);
}

inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
const AliasAnalysis::Source &op) {
op.print(os);
Expand Down
Loading

0 comments on commit ee407e1

Please sign in to comment.