FLANG
AliasAnalysis.h
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/BuiltinAttributes.h"
16#include "mlir/IR/Dominance.h"
17#include "mlir/IR/SymbolTable.h"
18#include "mlir/IR/Value.h"
19#include "llvm/ADT/DenseMap.h"
20#include "llvm/ADT/PointerUnion.h"
21#include "llvm/ADT/SmallVector.h"
22#include <cstddef>
23#include <memory>
24#include <utility>
25
26namespace fir {
27
29
30//===----------------------------------------------------------------------===//
31// AliasAnalysis
32//===----------------------------------------------------------------------===//
33struct AliasAnalysis {
34 AliasAnalysis() = default;
35
37 explicit AliasAnalysis(AliasAnalysisRecursiveEffectsCache &cache);
38
39 AliasAnalysis(AliasAnalysis &&other) noexcept;
40
41 ~AliasAnalysis();
42
43 AliasAnalysis(const AliasAnalysis &) = delete;
44 AliasAnalysis &operator=(const AliasAnalysis &) = delete;
45 AliasAnalysis &operator=(AliasAnalysis &&) = delete;
46
47 // Structures to describe the memory source of a value.
48
54 ENUM_CLASS(SourceKind,
59 Null,
62 Allocate,
64 Global,
67 Argument,
70 HostAssoc,
74 Indirect,
77 Unknown);
78
80 ENUM_CLASS(Attribute, Target, Pointer, IntentIn, CrayPointer, CrayPointee);
81
82 // See
83 // https://discourse.llvm.org/t/rfc-distinguish-between-data-and-non-data-in-fir-alias-analysis/78759/1
84 //
85 // It is possible, while following the source of a memory reference through
86 // the use-def chain, to arrive at the same origin, even though the starting
87 // points were known to not alias.
88 //
89 // clang-format off
90 // Example:
91 // ------------------- test.f90 --------------------
92 // module top
93 // real, pointer :: a(:)
94 // end module
95 //
96 // subroutine test()
97 // use top
98 // a(1) = 1
99 // end subroutine
100 // -------------------------------------------------
101 //
102 // flang -fc1 -emit-fir test.f90 -o test.fir
103 //
104 // ------------------- test.fir --------------------
105 // fir.global @_QMtopEa : !fir.box<!fir.ptr<!fir.array<?xf32>>>
106 //
107 // func.func @_QPtest() {
108 // %c1 = arith.constant 1 : index
109 // %cst = arith.constant 1.000000e+00 : f32
110 // %0 = fir.address_of(@_QMtopEa) : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>
111 // %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>>>>
112 // %2 = fir.load %1 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>
113 // ...
114 // %5 = fir.array_coor %2 %c1 : (!fir.box<!fir.ptr<!fir.array<?xf32>>>, !fir.shift<1>, index) -> !fir.ref<f32>
115 // fir.store %cst to %5 : !fir.ref<f32>
116 // return
117 // }
118 // -------------------------------------------------
119 //
120 // With high level operations, such as fir.array_coor, it is possible to
121 // reach into the data wrapped by the box (the descriptor). Therefore when
122 // asking about the memory source of %5, we are really asking about the
123 // source of the data of box %2.
124 //
125 // When asking about the source of %0 which is the address of the box, we
126 // reach the same source as in the first case: the global @_QMtopEa. Yet one
127 // source refers to the data while the other refers to the address of the box
128 // itself.
129 //
130 // To distinguish between the two, the isData flag has been added, whereby
131 // data is defined as any memory reference that is not a box reference.
132 // Additionally, because it is relied on in HLFIR lowering, we allow querying
133 // on a box SSA value, which is interpreted as querying on its data.
134 //
135 // So in the above example, !fir.ref<f32> and !fir.box<!fir.ptr<!fir.array<?xf32>>> is data,
136 // while !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>> is not data.
137
138 // This also applies to function arguments. In the example below, %arg0
139 // is data, %arg1 is not data but a load of %arg1 is.
140 //
141 // func.func @_QFPtest2(%arg0: !fir.ref<f32>, %arg1: !fir.ref<!fir.box<!fir.ptr<f32>>> ) {
142 // %0 = fir.load %arg1 : !fir.ref<!fir.box<!fir.ptr<f32>>>
143 // ... }
144 //
145 // clang-format on
146
147 struct Source {
148 using SourceUnion = llvm::PointerUnion<mlir::SymbolRefAttr, mlir::Value>;
150
153 SourceUnion u;
154
164 mlir::Operation *instantiationPoint;
165
167 bool isData{false};
168 };
169
173 struct PathStep {
183 Kind kind;
187 mlir::StringAttr component;
190 mlir::Type recordType;
191
192 bool operator==(const PathStep &o) const {
193 return kind == o.kind && component == o.component &&
195 }
196 bool operator!=(const PathStep &o) const { return !(*this == o); }
197 };
198
210 struct AccessPath {
212
216 bool isApproximate{false};
217
222 mlir::Operation *storageDecl{nullptr};
223
225 bool hasPointerDeref() const {
226 return llvm::any_of(steps, [](const PathStep &s) {
227 return s.kind == PathStep::Kind::PointerDeref;
228 });
229 }
230
231 bool operator==(const AccessPath &o) const {
232 return isApproximate == o.isApproximate &&
233 storageDecl == o.storageDecl && steps == o.steps;
234 }
235 bool operator!=(const AccessPath &o) const { return !(*this == o); }
236
237 void print(llvm::raw_ostream &os) const;
238 };
239
275 mlir::Value scope;
277 mlir::Value declValue;
284 Attributes attributes;
286 bool approximateSource{false};
289 bool isData{false};
290 };
291
292 SourceOrigin origin;
293
295 SourceKind kind;
297 mlir::Type valueType;
299 Attributes attributes;
312
314 void print(llvm::raw_ostream &os) const;
315
317 bool isTargetOrPointer() const;
318
320 bool isTarget() const;
321
323 bool isPointer() const;
324
327 bool isDeclaredAllocatable() const;
328
330 bool isCrayPointer() const;
331
333 bool isCrayPointee() const;
334
336 bool isCrayPointerOrPointee() const;
337
338 bool isDummyArgument() const;
339 bool isData() const;
340 bool isBoxData() const;
341
343 bool isFortranUserVariable() const;
344
351
355 bool mayBeDummyArgOrHostAssoc() const;
357 bool mayBePtrDummyArgOrHostAssoc() const;
359 bool mayBeActualArg() const;
362 bool mayBeActualArgWithPtr(const mlir::Value *val) const;
364
365 mlir::Type getType() const;
366 };
367
368 friend llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
369 const AliasAnalysis::Source &op);
370
372 mlir::AliasResult alias(Source lhsSrc, Source rhsSrc, mlir::Value lhs,
373 mlir::Value rhs);
374
376 mlir::AliasResult alias(mlir::Value lhs, mlir::Value rhs);
377
379 mlir::ModRefResult getModRef(mlir::Operation *op, mlir::Value location);
380
385 mlir::ModRefResult getModRef(mlir::Region &region, mlir::Value location);
386
405 bool getLastInstantiationPoint = false,
406 bool collectScopedOrigins = true);
407
412 void enableSourceCache();
413
415 void disableSourceCache();
416
418 std::size_t getSourceCacheSizeForTesting() const {
419 return getSourceCache.size();
420 }
421
423 std::size_t getSourceCacheHitsForTesting() const { return sourceCacheHits; }
424 std::size_t getSourceCacheMissesForTesting() const {
425 return sourceCacheMisses;
426 }
427
430 static bool isPointerReference(mlir::Type ty);
431
440 bool functionHasMultipleScopes(mlir::Value v);
441
442private:
443 friend class AliasAnalysisRecursiveEffectsCache;
444
448 fir::AliasAnalysis::Source getSourceImpl(mlir::Value v,
449 bool getLastInstantiationPoint,
450 bool collectScopedOrigins);
451
453 void clearSourceCache() { getSourceCache.clear(); }
454
461 Source buildSourceAtDeclare(const Source::ScopedOrigin &so);
462
468 mlir::Value getDeclarationScope(mlir::Operation *declareOp);
469
472 static bool isRecordWithPointerComponent(mlir::Type ty);
473
477 const mlir::SymbolTable *getNearestSymbolTable(mlir::Operation *from);
478
482 bool symbolMayHaveTargetAttr(mlir::SymbolRefAttr symbol,
483 mlir::Operation *from);
484
487 bool isCallToFortranUserProcedure(mlir::Operation *op);
488
492 mlir::ModRefResult getCallModRef(mlir::Operation *op, mlir::Value var);
493
507 llvm::DenseMap<mlir::Operation *, mlir::SymbolTable> symTabMap;
508
522 llvm::DenseMap<mlir::Operation *, std::unique_ptr<mlir::DominanceInfo>>
523 domInfoCache;
524 llvm::DenseMap<mlir::Operation *, llvm::SmallVector<mlir::Operation *, 16>>
525 sortedScopeCache;
530 llvm::DenseMap<mlir::Operation *, bool> multiScopeCache;
531
537 llvm::DenseMap<std::pair<mlir::Value, unsigned>, Source> getSourceCache;
538
540 bool sourceCacheEnabled = false;
541
544 std::size_t sourceCacheHits = 0;
545 std::size_t sourceCacheMisses = 0;
546
548 AliasAnalysisRecursiveEffectsCache *cache = nullptr;
549};
550
553class AliasAnalysisRecursiveEffectsCache {
554public:
555 AliasAnalysisRecursiveEffectsCache() = default;
556 ~AliasAnalysisRecursiveEffectsCache() {
557 if (aa)
558 aa->cache = nullptr;
559 }
560
561 AliasAnalysisRecursiveEffectsCache(
562 const AliasAnalysisRecursiveEffectsCache &) = delete;
563 AliasAnalysisRecursiveEffectsCache &
564 operator=(const AliasAnalysisRecursiveEffectsCache &) = delete;
565 AliasAnalysisRecursiveEffectsCache(AliasAnalysisRecursiveEffectsCache &&) =
566 delete;
567 AliasAnalysisRecursiveEffectsCache &
568 operator=(AliasAnalysisRecursiveEffectsCache &&) = delete;
569
572 void clear() { summaries.clear(); }
573
575 std::size_t getSummaryCacheSizeForTesting() const { return summaries.size(); }
576
579 std::size_t getSummaryCacheHitsForTesting() const { return summaryHits; }
580 std::size_t getSummaryCacheMissesForTesting() const { return summaryMisses; }
581
582private:
583 friend struct AliasAnalysis;
584
585 struct CallInfo {
586 mlir::Operation *op;
591 bool isFortranUserProcedure;
592 };
593
594 struct Summary {
595 bool hasUnknownWrite = false;
596 bool hasUnknownRead = false;
597 llvm::SmallVector<mlir::Value, 16> writeLocations;
598 llvm::SmallVector<mlir::Value, 16> readLocations;
599 llvm::SmallVector<CallInfo, 4> calls;
600 };
601
605 void buildSummary(mlir::Operation *op, Summary &out);
606 void buildSummary(mlir::Region &region, Summary &out);
607
608 mlir::ModRefResult getModRefFromSummary(mlir::Operation *op,
609 mlir::Value location);
610
612 AliasAnalysis *aa = nullptr;
613 llvm::DenseMap<mlir::Operation *, Summary> summaries;
614
616 std::size_t summaryHits = 0;
617 std::size_t summaryMisses = 0;
618};
619
620inline bool operator==(const AliasAnalysis::Source::SourceOrigin &lhs,
622 return lhs.u == rhs.u && lhs.isData == rhs.isData;
623}
624inline bool operator!=(const AliasAnalysis::Source::SourceOrigin &lhs,
626 return !(lhs == rhs);
627}
628
629inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
630 const AliasAnalysis::Source &op) {
631 op.print(os);
632 return os;
633}
634
635} // namespace fir
636
637#endif // FORTRAN_OPTIMIZER_ANALYSIS_ALIASANALYSIS_H
Definition enum-set.h:28
Definition AliasAnalysis.h:553
std::size_t getSummaryCacheHitsForTesting() const
Definition AliasAnalysis.h:579
std::size_t getSummaryCacheSizeForTesting() const
Testing only: number of op summaries currently held.
Definition AliasAnalysis.h:575
void clear()
Definition AliasAnalysis.h:572
Definition OpenACC.h:20
Definition AbstractConverter.h:37
Definition AliasAnalysis.h:210
mlir::Operation * storageDecl
Definition AliasAnalysis.h:222
bool hasPointerDeref() const
Return true if any step is a PointerDeref.
Definition AliasAnalysis.h:225
bool isApproximate
Definition AliasAnalysis.h:216
Definition AliasAnalysis.h:173
mlir::StringAttr component
Definition AliasAnalysis.h:187
mlir::Type recordType
Definition AliasAnalysis.h:190
Kind
Definition AliasAnalysis.h:174
@ Component
Named component access, e.g. xfield.
Definition AliasAnalysis.h:176
@ AllocDeref
Loading an ALLOCATABLE box (fir.load of !fir.box<!fir.heap<...>>).
Definition AliasAnalysis.h:181
@ PointerDeref
Definition AliasAnalysis.h:179
Definition AliasAnalysis.h:271
mlir::Value scope
Definition AliasAnalysis.h:275
AccessPath accessPath
Definition AliasAnalysis.h:280
bool isData
Definition AliasAnalysis.h:289
mlir::Value declValue
Result SSA value of the [hl]fir.declare op.
Definition AliasAnalysis.h:277
bool approximateSource
Whether the path is approximate at the moment of the snapshot.
Definition AliasAnalysis.h:286
Attributes attributes
Definition AliasAnalysis.h:284
Definition AliasAnalysis.h:151
mlir::Operation * instantiationPoint
Definition AliasAnalysis.h:164
SourceUnion u
Source definition of a value.
Definition AliasAnalysis.h:153
bool isData
Whether the source was reached following data or box reference.
Definition AliasAnalysis.h:167
Definition AliasAnalysis.h:147
bool isCrayPointer() const
Return true, if CrayPointer attribute is set.
Definition AliasAnalysis.cpp:485
bool isCapturedInInternalProcedure
Source object is used in an internal procedure via host association.
Definition AliasAnalysis.h:306
bool isFortranUserVariable() const
Is this source a variable from the Fortran source?
Definition AliasAnalysis.cpp:506
Attributes attributes
Attributes of the memory source object, e.g. Target.
Definition AliasAnalysis.h:299
bool isPointer() const
Return true, if Pointer attribute is set.
Definition AliasAnalysis.cpp:466
llvm::SmallVector< ScopedOrigin, 4 > scopedOrigins
Definition AliasAnalysis.h:311
SourceKind kind
Kind of the memory source.
Definition AliasAnalysis.h:295
bool isCrayPointee() const
Return true, if CrayPointee attribute is set.
Definition AliasAnalysis.cpp:481
void print(llvm::raw_ostream &os) const
Print information about the memory source to os.
Definition AliasAnalysis.cpp:423
bool mayBePtrDummyArgOrHostAssoc() const
mayBeDummyArgOrHostAssoc and the address of a pointer?
Definition AliasAnalysis.cpp:522
bool isTargetOrPointer() const
Return true, if Target or Pointer attribute is set.
Definition AliasAnalysis.cpp:457
bool mayBeDummyArgOrHostAssoc() const
Definition AliasAnalysis.cpp:517
AccessPath accessPath
The structured access path from the root variable.
Definition AliasAnalysis.h:304
bool approximateSource
Definition AliasAnalysis.h:302
bool mayBeActualArgWithPtr(const mlir::Value *val) const
Definition AliasAnalysis.cpp:537
mlir::Type valueType
Value type of the source definition.
Definition AliasAnalysis.h:297
bool isCrayPointerOrPointee() const
Return true, if CrayPointer or CrayPointee attribute is set.
Definition AliasAnalysis.cpp:489
bool isDeclaredAllocatable() const
Definition AliasAnalysis.cpp:470
bool isTarget() const
Return true, if Target attribute is set.
Definition AliasAnalysis.cpp:462
bool mayBeActualArg() const
The address of an actual argument of the current function?
Definition AliasAnalysis.cpp:533
std::size_t getSourceCacheSizeForTesting() const
Testing only: number of entries currently held in the getSource() cache.
Definition AliasAnalysis.h:418
void enableSourceCache()
Definition AliasAnalysis.cpp:1609
fir::AliasAnalysis::Source getSource(mlir::Value, bool getLastInstantiationPoint=false, bool collectScopedOrigins=true)
Definition AliasAnalysis.cpp:1616
mlir::AliasResult alias(Source lhsSrc, Source rhsSrc, mlir::Value lhs, mlir::Value rhs)
Given the values and their sources, return their aliasing behavior.
Definition AliasAnalysis.cpp:789
void disableSourceCache()
Disable getSource() memoization and drop any cached entries.
Definition AliasAnalysis.cpp:1611
static bool isPointerReference(mlir::Type ty)
Definition AliasAnalysis.cpp:449
ENUM_CLASS(SourceKind, Null, Allocate, Global, Argument, HostAssoc, Indirect, Unknown)
std::size_t getSourceCacheHitsForTesting() const
Testing only: cumulative getSource() cache hits / misses on this instance.
Definition AliasAnalysis.h:423
ENUM_CLASS(Attribute, Target, Pointer, IntentIn, CrayPointer, CrayPointee)
Attributes of the memory source object.
bool functionHasMultipleScopes(mlir::Value v)
Definition AliasAnalysis.cpp:2369
mlir::ModRefResult getModRef(mlir::Operation *op, mlir::Value location)
Return the modify-reference behavior of op on location.