• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifndef COMPILER_OPTIMIZER_ANALYSIS_BOUNDSRANGE_ANALYSIS_H
17 #define COMPILER_OPTIMIZER_ANALYSIS_BOUNDSRANGE_ANALYSIS_H
18 
19 #include "optimizer/analysis/countable_loop_parser.h"
20 #include "optimizer/ir/graph_visitor.h"
21 #include "optimizer/ir/datatype.h"
22 #include "optimizer/ir/inst.h"
23 #include "optimizer/pass.h"
24 #include "utils/arena_containers.h"
25 
26 namespace ark::compiler {
27 /**
28  * Represents a range of values that a variable might have.
29  *
30  * It is used to represent variables of integral types according to their size
31  * and sign.
32  * It is used for REFERENCE type as well but only for reasoning whether a
33  * variable is NULL or not.
34  */
35 class BoundsRange {
36 public:
37     using RangePair = std::pair<BoundsRange, BoundsRange>;
38 
left_(GetMin (type))39     explicit BoundsRange(DataType::Type type = DataType::INT64) : left_(GetMin(type)), right_(GetMax(type)) {};
40 
41     explicit BoundsRange(int64_t left, int64_t right, const Inst *inst = nullptr,
42                          DataType::Type type = DataType::INT64);
43 
44     explicit BoundsRange(int64_t val, DataType::Type type = DataType::INT64);
45 
46     DEFAULT_COPY_SEMANTIC(BoundsRange);
47     DEFAULT_MOVE_SEMANTIC(BoundsRange);
48     ~BoundsRange() = default;
49 
50     void SetLenArray(const Inst *inst);
51 
GetLenArray()52     const Inst *GetLenArray()
53     {
54         return lenArray_;
55     }
56     int64_t GetLeft() const;
57 
58     int64_t GetRight() const;
59 
60     BoundsRange FitInType(DataType::Type type) const;
61 
62     BoundsRange Neg() const;
63 
64     BoundsRange Abs() const;
65 
66     BoundsRange Add(const BoundsRange &range) const;
67 
68     BoundsRange Sub(const BoundsRange &range) const;
69 
70     BoundsRange Mul(const BoundsRange &range) const;
71 
72     BoundsRange Div(const BoundsRange &range) const;
73 
74     BoundsRange Mod(const BoundsRange &range);
75 
76     BoundsRange And(const BoundsRange &range);
77 
78     BoundsRange Shr(const BoundsRange &range, DataType::Type type = DataType::INT64);
79 
80     BoundsRange AShr(const BoundsRange &range, DataType::Type type = DataType::INT64);
81 
82     BoundsRange Shl(const BoundsRange &range, DataType::Type type = DataType::INT64);
83 
84     BoundsRange Diff(const BoundsRange &range) const;
85 
86     bool IsConst() const;
87 
88     bool IsMaxRange(DataType::Type type = DataType::INT64) const;
89 
90     bool IsEqual(const BoundsRange &range) const;
91 
92     bool IsLess(const BoundsRange &range) const;
93 
94     bool IsLess(const Inst *inst) const;
95 
96     bool IsMore(const BoundsRange &range) const;
97 
98     bool IsMoreOrEqual(const BoundsRange &range) const;
99 
100     bool IsWithin(const BoundsRange &range) const;
101 
102     bool IsNotNegative() const;
103 
104     bool IsNegative() const;
105 
106     bool IsPositive() const;
107 
108     bool IsNotPositive() const;
109 
110     bool CanOverflow(DataType::Type type = DataType::INT64) const;
111 
112     bool CanOverflowNeg(DataType::Type type = DataType::INT64) const;
113 
114     static int64_t GetMin(DataType::Type type);
115 
116     static int64_t GetMax(DataType::Type type);
117 
118     static BoundsRange Union(const ArenaVector<BoundsRange> &ranges);
119 
120     static RangePair NarrowBoundsByNE(RangePair const &ranges);
121     static RangePair NarrowBoundsCase1(ConditionCode cc, RangePair const &ranges);
122     static RangePair NarrowBoundsCase2(ConditionCode cc, RangePair const &ranges);
123     static RangePair NarrowBoundsCase3(ConditionCode cc, RangePair const &ranges);
124     static RangePair NarrowBoundsCase4(ConditionCode cc, RangePair const &ranges);
125     static RangePair NarrowBoundsCase5(ConditionCode cc, RangePair const &ranges);
126     static RangePair NarrowBoundsCase6(ConditionCode cc, RangePair const &ranges);
127 
128     static RangePair TryNarrowBoundsByCC(ConditionCode cc, RangePair const &ranges);
129 
130     static std::optional<int64_t> AddWithOverflowCheck(int64_t left, int64_t right);
131     static std::optional<uint64_t> AddWithOverflowCheck(uint64_t left, uint64_t right);
132 
133     static std::optional<int64_t> MulWithOverflowCheck(int64_t left, int64_t right);
134     static std::optional<uint64_t> MulWithOverflowCheck(uint64_t left, uint64_t right);
135 
136     static std::optional<int64_t> DivWithOverflowCheck(int64_t left, int64_t right);
137 
138     static constexpr int64_t MAX_RANGE_VALUE = INT64_MAX;
139     static constexpr int64_t MIN_RANGE_VALUE = INT64_MIN;
140 
141     bool operator==(const BoundsRange &rhs) const
142     {
143         return left_ == rhs.left_ && right_ == rhs.right_ && lenArray_ == rhs.lenArray_;
144     }
145 
146     void Dump(std::ostream &out = std::cerr) const
147     {
148         out << "Range = [" << left_ << ", ";
149         out << right_ << "]";
150         if (lenArray_ != nullptr) {
151             out << ", len_array = " << lenArray_->GetId();
152         }
153         out << "\n";
154     }
155 
156 private:
157     int64_t left_ = MIN_RANGE_VALUE;
158     int64_t right_ = MAX_RANGE_VALUE;
159     const Inst *lenArray_ {nullptr};
160 };
161 
162 class BoundsRangeInfo {
163 public:
BoundsRangeInfo(ArenaAllocator * aa)164     explicit BoundsRangeInfo(ArenaAllocator *aa) : aa_(*aa), boundsRangeInfo_(aa->Adapter()) {}
165     NO_COPY_SEMANTIC(BoundsRangeInfo);
166     NO_MOVE_SEMANTIC(BoundsRangeInfo);
167     ~BoundsRangeInfo() = default;
168 
169     BoundsRange FindBoundsRange(const BasicBlock *block, const Inst *inst) const;
170 
171     void SetBoundsRange(const BasicBlock *block, const Inst *inst, BoundsRange range);
172 
Clear()173     void Clear()
174     {
175         boundsRangeInfo_.clear();
176     }
177 
178 private:
179     ArenaAllocator &aa_;
180     ArenaDoubleUnorderedMap<const BasicBlock *, const Inst *, BoundsRange> boundsRangeInfo_;
181 };
182 
183 // index phi, iterations
184 using LoopIterationsInfo = std::pair<CountableLoopInfo, uint64_t>;
185 
186 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
187 class BoundsAnalysis : public Analysis, public GraphVisitor {
188 public:
189     explicit BoundsAnalysis(Graph *graph);
190     NO_MOVE_SEMANTIC(BoundsAnalysis);
191     NO_COPY_SEMANTIC(BoundsAnalysis);
192     ~BoundsAnalysis() override = default;
193 
194     const ArenaVector<BasicBlock *> &GetBlocksToVisit() const override;
195 
196     bool RunImpl() override;
197 
GetPassName()198     const char *GetPassName() const override
199     {
200         return "BoundsAnalysis";
201     }
202 
GetBoundsRangeInfo()203     BoundsRangeInfo *GetBoundsRangeInfo()
204     {
205         return &boundsRangeInfo_;
206     }
207 
GetBoundsRangeInfo()208     const BoundsRangeInfo *GetBoundsRangeInfo() const
209     {
210         return &boundsRangeInfo_;
211     }
212 
213     static bool IsInstNotNull(const Inst *inst, BasicBlock *block);
214 
215     static void VisitNeg(GraphVisitor *v, Inst *inst);
216     static void VisitNegOverflowAndZeroCheck(GraphVisitor *v, Inst *inst);
217     static void VisitAbs(GraphVisitor *v, Inst *inst);
218     static void VisitAdd(GraphVisitor *v, Inst *inst);
219     static void VisitAddOverflowCheck(GraphVisitor *v, Inst *inst);
220     static void VisitSub(GraphVisitor *v, Inst *inst);
221     static void VisitSubOverflowCheck(GraphVisitor *v, Inst *inst);
222     static void VisitMod(GraphVisitor *v, Inst *inst);
223     static void VisitDiv(GraphVisitor *v, Inst *inst);
224     static void VisitMul(GraphVisitor *v, Inst *inst);
225     static void VisitAnd(GraphVisitor *v, Inst *inst);
226     static void VisitShr(GraphVisitor *v, Inst *inst);
227     static void VisitAShr(GraphVisitor *v, Inst *inst);
228     static void VisitShl(GraphVisitor *v, Inst *inst);
229     static void VisitIfImm(GraphVisitor *v, Inst *inst);
230     static void VisitPhi(GraphVisitor *v, Inst *inst);
231     static void VisitNullCheck(GraphVisitor *v, Inst *inst);
232 
233 #include "optimizer/ir/visitor.inc"
234 private:
235     static bool CheckTriangleCase(const BasicBlock *block, const BasicBlock *tgtBlock);
236     static void ProcessNullCheck(GraphVisitor *v, const Inst *checkInst, const Inst *refInput);
237 
238     static BoundsRange UpdateLenArray(BoundsRange range, const Inst *lenArray, const Inst *upper);
239     static void CalcNewBoundsRangeForIsInstanceInput(GraphVisitor *v, IsInstanceInst *isInstance, IfImmInst *ifImm);
240     static void CalcNewBoundsRangeForCompare(GraphVisitor *v, BasicBlock *block, ConditionCode cc, Inst *left,
241                                              Inst *right, BasicBlock *tgtBlock);
242     template <Opcode OPC>
243     static void CalcNewBoundsRangeUnary(GraphVisitor *v, const Inst *inst);
244     template <Opcode OPC>
245     static void CalcNewBoundsRangeBinary(GraphVisitor *v, const Inst *inst);
246     static BoundsRange CalcNewBoundsRangeAdd(const BoundsRangeInfo *bri, const Inst *inst);
247     static BoundsRange CalcNewBoundsRangeSub(const BoundsRangeInfo *bri, const Inst *inst);
248     static BoundsRange CalcNewBoundsRangeMod(const BoundsRangeInfo *bri, const Inst *inst);
249     static BoundsRange CalcNewBoundsRangeMul(const BoundsRangeInfo *bri, const Inst *inst);
250     static BoundsRange CalcNewBoundsRangeDiv(const BoundsRangeInfo *bri, const Inst *inst);
251     static BoundsRange CalcNewBoundsRangeShr(const BoundsRangeInfo *bri, const Inst *inst);
252     static BoundsRange CalcNewBoundsRangeAShr(const BoundsRangeInfo *bri, const Inst *inst);
253     static BoundsRange CalcNewBoundsRangeShl(const BoundsRangeInfo *bri, const Inst *inst);
254     static BoundsRange CalcNewBoundsRangeAnd(const BoundsRangeInfo *bri, const Inst *inst);
255     static bool CheckBoundsRange(const BoundsRangeInfo *bri, const Inst *inst);
256     template <bool CHECK_TYPE = false>
257     static bool MergePhiPredecessors(PhiInst *phi, BoundsRangeInfo *bri);
258     bool ProcessCountableLoop(PhiInst *phi, BoundsRangeInfo *bri);
259     std::optional<uint64_t> GetNestedLoopIterations(Loop *loop, CountableLoopInfo &loopInfo);
260     std::optional<LoopIterationsInfo> GetSimpleLoopIterationsInfo(Loop *loop);
261     std::optional<LoopIterationsInfo> GetNestedLoopIterationsInfo(Loop *loop);
262     Inst *TryFindCorrespondingInitPhi(PhiInst *updatePhi, BasicBlock *header);
263     bool ProcessIndexPhi(Loop *loop, BoundsRangeInfo *bri, CountableLoopInfo &loopInfo);
264     bool ProcessInitPhi(PhiInst *initPhi, BoundsRangeInfo *bri);
265     bool ProcessUpdatePhi(PhiInst *updatePhi, BoundsRangeInfo *bri, uint64_t iterations);
266     void VisitLoop(BasicBlock *header, BasicBlock *updatePhiBlock);
267 
268 private:
269     bool loopsRevisiting_ {false};
270     Marker visited_ {UNDEF_MARKER};
271     BoundsRangeInfo boundsRangeInfo_;
272     ArenaUnorderedMap<Loop *, std::optional<LoopIterationsInfo>> loopsInfoTable_;
273 };
274 }  // namespace ark::compiler
275 
276 #endif  // COMPILER_OPTIMIZER_ANALYSIS_BOUNDS_ANALYSIS_H
277