xref: /llvm-project/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h (revision a871124f8709f6b5e837c6044ce7df056f52292a)
1 //===-- AliasAnalysis.h - Alias Analysis in FIR -----------------*- C++ -*-===//
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 #ifndef FORTRAN_OPTIMIZER_ANALYSIS_ALIASANALYSIS_H
10 #define FORTRAN_OPTIMIZER_ANALYSIS_ALIASANALYSIS_H
11 
12 #include "flang/Common/enum-class.h"
13 #include "flang/Common/enum-set.h"
14 #include "mlir/Analysis/AliasAnalysis.h"
15 #include "mlir/IR/Value.h"
16 #include "llvm/ADT/PointerUnion.h"
17 
18 namespace fir {
19 
20 //===----------------------------------------------------------------------===//
21 // AliasAnalysis
22 //===----------------------------------------------------------------------===//
23 struct AliasAnalysis {
24   // Structures to describe the memory source of a value.
25 
26   /// Kind of the memory source referenced by a value.
27   ENUM_CLASS(SourceKind,
28              /// Unique memory allocated by an operation, e.g.
29              /// by fir::AllocaOp or fir::AllocMemOp.
30              Allocate,
31              /// A global object allocated statically on the module level.
32              Global,
33              /// Memory allocated outside of a function and passed
34              /// to the function as a by-ref argument.
35              Argument,
36              /// Represents memory allocated outside of a function
37              /// and passed to the function via host association tuple.
38              HostAssoc,
39              /// Represents memory allocated by unknown means and
40              /// with the memory address defined by a memory reading
41              /// operation (e.g. fir::LoadOp).
42              Indirect,
43              /// Starting point to the analysis whereby nothing is known about
44              /// the source
45              Unknown);
46 
47   /// Attributes of the memory source object.
48   ENUM_CLASS(Attribute, Target, Pointer, IntentIn);
49 
50   // See
51   // https://discourse.llvm.org/t/rfc-distinguish-between-data-and-non-data-in-fir-alias-analysis/78759/1
52   //
53   // It is possible, while following the source of a memory reference through
54   // the use-def chain, to arrive at the same origin, even though the starting
55   // points were known to not alias.
56   //
57   // clang-format off
58   // Example:
59   //  ------------------- test.f90 --------------------
60   //  module top
61   //    real, pointer :: a(:)
62   //  end module
63   //
64   //  subroutine test()
65   //    use top
66   //    a(1) = 1
67   //  end subroutine
68   //  -------------------------------------------------
69   //
70   //  flang -fc1 -emit-fir test.f90 -o test.fir
71   //
72   //  ------------------- test.fir --------------------
73   //  fir.global @_QMtopEa : !fir.box<!fir.ptr<!fir.array<?xf32>>>
74   //
75   //  func.func @_QPtest() {
76   //    %c1 = arith.constant 1 : index
77   //    %cst = arith.constant 1.000000e+00 : f32
78   //    %0 = fir.address_of(@_QMtopEa) : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>
79   //    %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>>>>
80   //    %2 = fir.load %1 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>
81   //    ...
82   //    %5 = fir.array_coor %2 %c1 : (!fir.box<!fir.ptr<!fir.array<?xf32>>>, !fir.shift<1>, index) -> !fir.ref<f32>
83   //    fir.store %cst to %5 : !fir.ref<f32>
84   //    return
85   //  }
86   //  -------------------------------------------------
87   //
88   // With high level operations, such as fir.array_coor, it is possible to
89   // reach into the data wrapped by the box (the descriptor). Therefore when
90   // asking about the memory source of %5, we are really asking about the
91   // source of the data of box %2.
92   //
93   // When asking about the source of %0 which is the address of the box, we
94   // reach the same source as in the first case: the global @_QMtopEa. Yet one
95   // source refers to the data while the other refers to the address of the box
96   // itself.
97   //
98   // To distinguish between the two, the isData flag has been added, whereby
99   // data is defined as any memory reference that is not a box reference.
100   // Additionally, because it is relied on in HLFIR lowering, we allow querying
101   // on a box SSA value, which is interpreted as querying on its data.
102   //
103   // So in the above example, !fir.ref<f32> and !fir.box<!fir.ptr<!fir.array<?xf32>>> is data,
104   // while !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>> is not data.
105 
106   // This also applies to function arguments. In the example below, %arg0
107   // is data, %arg1 is not data but a load of %arg1 is.
108   //
109   // func.func @_QFPtest2(%arg0: !fir.ref<f32>, %arg1: !fir.ref<!fir.box<!fir.ptr<f32>>> )  {
110   //    %0 = fir.load %arg1 : !fir.ref<!fir.box<!fir.ptr<f32>>>
111   //    ... }
112   //
113   // clang-format on
114 
115   struct Source {
116     using SourceUnion = llvm::PointerUnion<mlir::SymbolRefAttr, mlir::Value>;
117     using Attributes = Fortran::common::EnumSet<Attribute, Attribute_enumSize>;
118 
119     struct SourceOrigin {
120       /// Source definition of a value.
121       SourceUnion u;
122 
123       /// A value definition denoting the place where the corresponding
124       /// source variable was instantiated by the front-end.
125       /// Currently, it is the result of [hl]fir.declare of the source,
126       /// if we can reach it.
127       /// It helps to identify the scope where the corresponding variable
128       /// was defined in the original Fortran source, e.g. when MLIR
129       /// inlining happens an inlined fir.declare of the callee's
130       /// dummy argument identifies the scope where the source
131       /// may be treated as a dummy argument.
132       mlir::Operation *instantiationPoint;
133 
134       /// Whether the source was reached following data or box reference
135       bool isData{false};
136     };
137 
138     SourceOrigin origin;
139 
140     /// Kind of the memory source.
141     SourceKind kind;
142     /// Value type of the source definition.
143     mlir::Type valueType;
144     /// Attributes of the memory source object, e.g. Target.
145     Attributes attributes;
146     /// Have we lost precision following the source such that
147     /// even an exact match cannot be MustAlias?
148     bool approximateSource;
149     /// Source object is used in an internal procedure via host association.
150     bool isCapturedInInternalProcedure{false};
151 
152     /// Print information about the memory source to `os`.
153     void print(llvm::raw_ostream &os) const;
154 
155     /// Return true, if Target or Pointer attribute is set.
156     bool isTargetOrPointer() const;
157 
158     bool isDummyArgument() const;
159     bool isData() const;
160     bool isBoxData() const;
161 
162     /// Is this source a variable from the Fortran source?
163     bool isFortranUserVariable() const;
164 
165     /// @name Dummy Argument Aliasing
166     ///
167     /// Check conditions related to dummy argument aliasing.
168     ///
169     /// For all uses, a result of false can prevent MayAlias from being
170     /// reported, so the list of cases where false is returned is conservative.
171 
172     ///@{
173     /// The address of a (possibly host associated) dummy argument of the
174     /// current function?
175     bool mayBeDummyArgOrHostAssoc() const;
176     /// \c mayBeDummyArgOrHostAssoc and the address of a pointer?
177     bool mayBePtrDummyArgOrHostAssoc() const;
178     /// The address of an actual argument of the current function?
179     bool mayBeActualArg() const;
180     /// \c mayBeActualArg and the address of either a pointer or a composite
181     /// with a pointer component?
182     bool mayBeActualArgWithPtr(const mlir::Value *val) const;
183     ///@}
184 
185     mlir::Type getType() const;
186   };
187 
188   friend llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
189                                        const AliasAnalysis::Source &op);
190 
191   /// Given the values and their sources, return their aliasing behavior.
192   mlir::AliasResult alias(Source lhsSrc, Source rhsSrc, mlir::Value lhs,
193                           mlir::Value rhs);
194 
195   /// Given two values, return their aliasing behavior.
196   mlir::AliasResult alias(mlir::Value lhs, mlir::Value rhs);
197 
198   /// Return the modify-reference behavior of `op` on `location`.
199   mlir::ModRefResult getModRef(mlir::Operation *op, mlir::Value location);
200 
201   /// Return the modify-reference behavior of operations inside `region` on
202   /// `location`. Contrary to getModRef(operation, location), this will visit
203   /// nested regions recursively according to the HasRecursiveMemoryEffects
204   /// trait.
205   mlir::ModRefResult getModRef(mlir::Region &region, mlir::Value location);
206 
207   /// Return the memory source of a value.
208   /// If getLastInstantiationPoint is true, the search for the source
209   /// will stop at [hl]fir.declare if it represents a dummy
210   /// argument declaration (i.e. it has the dummy_scope operand).
211   fir::AliasAnalysis::Source getSource(mlir::Value,
212                                        bool getLastInstantiationPoint = false);
213 
214 private:
215   /// Return true, if `ty` is a reference type to an object of derived type
216   /// that contains a component with POINTER attribute.
217   static bool isRecordWithPointerComponent(mlir::Type ty);
218 
219   /// Return true, if `ty` is a reference type to a boxed
220   /// POINTER object or a raw fir::PointerType.
221   static bool isPointerReference(mlir::Type ty);
222 };
223 
224 inline bool operator==(const AliasAnalysis::Source::SourceOrigin &lhs,
225                        const AliasAnalysis::Source::SourceOrigin &rhs) {
226   return lhs.u == rhs.u && lhs.isData == rhs.isData;
227 }
228 inline bool operator!=(const AliasAnalysis::Source::SourceOrigin &lhs,
229                        const AliasAnalysis::Source::SourceOrigin &rhs) {
230   return !(lhs == rhs);
231 }
232 
233 inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
234                                      const AliasAnalysis::Source &op) {
235   op.print(os);
236   return os;
237 }
238 
239 } // namespace fir
240 
241 #endif // FORTRAN_OPTIMIZER_ANALYSIS_ALIASANALYSIS_H
242