• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2022 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 #include "bounds_analysis.h"
16 #include "dominators_tree.h"
17 #include "optimizer/ir/graph.h"
18 #include "optimizer/ir/graph_visitor.h"
19 #include "optimizer/ir/basicblock.h"
20 #include "optimizer/ir/inst.h"
21 #include "compiler/optimizer/ir/analysis.h"
22 #include "optimizer/analysis/loop_analyzer.h"
23 
24 namespace panda::compiler {
BoundsRange(int64_t val,DataType::Type type)25 BoundsRange::BoundsRange(int64_t val, DataType::Type type) : BoundsRange(val, val, nullptr, type) {}
26 
BoundsRange(int64_t left,int64_t right,const Inst * inst,DataType::Type type)27 BoundsRange::BoundsRange(int64_t left, int64_t right, const Inst *inst, [[maybe_unused]] DataType::Type type)
28     : left_(left), right_(right), len_array_(inst)
29 {
30     ASSERT(inst == nullptr);
31     ASSERT(left <= right);
32     ASSERT(GetMin(type) <= left);
33     ASSERT(right <= GetMax(type));
34 }
35 
GetLeft() const36 int64_t BoundsRange::GetLeft() const
37 {
38     return left_;
39 }
40 
GetRight() const41 int64_t BoundsRange::GetRight() const
42 {
43     return right_;
44 }
45 
IsConst() const46 bool BoundsRange::IsConst() const
47 {
48     return left_ == right_;
49 }
50 
IsMaxRange(DataType::Type type) const51 bool BoundsRange::IsMaxRange(DataType::Type type) const
52 {
53     return left_ <= GetMin(type) && right_ >= GetMax(type);
54 }
55 
IsEqual(const BoundsRange & range) const56 bool BoundsRange::IsEqual(const BoundsRange &range) const
57 {
58     return left_ == range.GetLeft() && right_ == range.GetRight();
59 }
60 
IsLess(const BoundsRange & range) const61 bool BoundsRange::IsLess(const BoundsRange &range) const
62 {
63     return right_ < range.GetLeft();
64 }
65 
IsLess(const Inst * inst) const66 bool BoundsRange::IsLess(const Inst *inst) const
67 {
68     return false;
69 }
70 
IsMore(const BoundsRange & range) const71 bool BoundsRange::IsMore(const BoundsRange &range) const
72 {
73     return left_ > range.GetRight();
74 }
75 
IsMoreOrEqual(const BoundsRange & range) const76 bool BoundsRange::IsMoreOrEqual(const BoundsRange &range) const
77 {
78     return left_ >= range.GetRight();
79 }
80 
IsNotNegative() const81 bool BoundsRange::IsNotNegative() const
82 {
83     return left_ >= 0;
84 }
85 
IsNegative() const86 bool BoundsRange::IsNegative() const
87 {
88     return right_ < 0;
89 }
90 /**
91  * Return the minimal value for a type.
92  *
93  * We consider that REFERENCE type has only non-negative address values
94  */
GetMin(DataType::Type type)95 int64_t BoundsRange::GetMin(DataType::Type type)
96 {
97     ASSERT(!IsFloatType(type));
98     switch (type) {
99         case DataType::BOOL:
100         case DataType::UINT8:
101         case DataType::UINT16:
102         case DataType::UINT32:
103         case DataType::UINT64:
104         case DataType::REFERENCE:
105             return 0;
106         case DataType::INT8:
107             return INT8_MIN;
108         case DataType::INT16:
109             return INT16_MIN;
110         case DataType::INT32:
111             return INT32_MIN;
112         case DataType::INT64:
113             return INT64_MIN;
114         default:
115             UNREACHABLE();
116     }
117 }
118 
119 /**
120  * Return the maximal value for a type.
121  *
122  * For REFERENCE we are interested in whether it is NULL or not.  Set the
123  * maximum to INT64_MAX regardless the real architecture bitness.
124  */
GetMax(DataType::Type type)125 int64_t BoundsRange::GetMax(DataType::Type type)
126 {
127     ASSERT(!IsFloatType(type));
128     ASSERT(type != DataType::UINT64);
129     switch (type) {
130         case DataType::BOOL:
131             return 1;
132         case DataType::UINT8:
133             return UINT8_MAX;
134         case DataType::UINT16:
135             return UINT16_MAX;
136         case DataType::UINT32:
137             return UINT32_MAX;
138         case DataType::INT8:
139             return INT8_MAX;
140         case DataType::INT16:
141             return INT16_MAX;
142         case DataType::INT32:
143             return INT32_MAX;
144         // NOLINTNEXTLINE(bugprone-branch-clone)
145         case DataType::INT64:
146             return INT64_MAX;
147         case DataType::REFERENCE:
148             return INT64_MAX;
149         default:
150             UNREACHABLE();
151     }
152 }
153 
FitInType(DataType::Type type) const154 BoundsRange BoundsRange::FitInType(DataType::Type type) const
155 {
156     auto type_min = BoundsRange::GetMin(type);
157     auto type_max = BoundsRange::GetMax(type);
158     if (left_ < type_min || left_ > type_max || right_ < type_min || right_ > type_max) {
159         return BoundsRange(type_min, type_max);
160     }
161     return *this;
162 }
163 
Union(const ArenaVector<BoundsRange> & ranges)164 BoundsRange BoundsRange::Union(const ArenaVector<BoundsRange> &ranges)
165 {
166     int64_t min = MAX_RANGE_VALUE;
167     int64_t max = MIN_RANGE_VALUE;
168     for (const auto &range : ranges) {
169         if (range.GetLeft() < min) {
170             min = range.GetLeft();
171         }
172         if (range.GetRight() > max) {
173             max = range.GetRight();
174         }
175     }
176     return BoundsRange(min, max);
177 }
178 
NarrowBoundsByNE(BoundsRange::RangePair const & ranges)179 BoundsRange::RangePair BoundsRange::NarrowBoundsByNE(BoundsRange::RangePair const &ranges)
180 {
181     auto &[left_range, right_range] = ranges;
182     int64_t ll = left_range.GetLeft();
183     int64_t lr = left_range.GetRight();
184     int64_t rl = right_range.GetLeft();
185     int64_t rr = right_range.GetRight();
186     // We can narrow bounds of a range if another is a constant and matches one of the bounds
187     // Mostly needed for a reference comparison with null
188     if (left_range.IsConst() && !right_range.IsConst()) {
189         if (ll == rl) {
190             return {left_range, BoundsRange(rl + 1, rr)};
191         }
192         if (ll == rr) {
193             return {left_range, BoundsRange(rl, rr - 1)};
194         }
195     }
196     if (!left_range.IsConst() && right_range.IsConst()) {
197         if (rl == ll) {
198             return {BoundsRange(ll + 1, lr), right_range};
199         }
200         if (rl == lr) {
201             return {BoundsRange(ll, lr - 1), right_range};
202         }
203     }
204     return ranges;
205 }
206 
NarrowBoundsCase1(ConditionCode cc,BoundsRange::RangePair const & ranges)207 BoundsRange::RangePair BoundsRange::NarrowBoundsCase1(ConditionCode cc, BoundsRange::RangePair const &ranges)
208 {
209     auto &[left_range, right_range] = ranges;
210     int64_t lr = left_range.GetRight();
211     int64_t rl = right_range.GetLeft();
212     if (cc == ConditionCode::CC_GT || cc == ConditionCode::CC_A) {
213         // With equal rl and lr left_range cannot be greater than right_range
214         if (rl == lr) {
215             return {BoundsRange(), BoundsRange()};
216         }
217         return {BoundsRange(rl + 1, lr), BoundsRange(rl, lr - 1)};
218     }
219     if (cc == ConditionCode::CC_GE || cc == ConditionCode::CC_AE || cc == ConditionCode::CC_EQ) {
220         return {BoundsRange(rl, lr), BoundsRange(rl, lr)};
221     }
222     return ranges;
223 }
224 
NarrowBoundsCase2(ConditionCode cc,BoundsRange::RangePair const & ranges)225 BoundsRange::RangePair BoundsRange::NarrowBoundsCase2(ConditionCode cc, BoundsRange::RangePair const &ranges)
226 {
227     if (cc == ConditionCode::CC_GT || cc == ConditionCode::CC_GE || cc == ConditionCode::CC_EQ ||
228         cc == ConditionCode::CC_A || cc == ConditionCode::CC_AE) {
229         return {BoundsRange(), BoundsRange()};
230     }
231     return ranges;
232 }
233 
NarrowBoundsCase3(ConditionCode cc,BoundsRange::RangePair const & ranges)234 BoundsRange::RangePair BoundsRange::NarrowBoundsCase3(ConditionCode cc, BoundsRange::RangePair const &ranges)
235 {
236     auto &[left_range, right_range] = ranges;
237     int64_t ll = left_range.GetLeft();
238     int64_t lr = left_range.GetRight();
239     int64_t rl = right_range.GetLeft();
240     int64_t rr = right_range.GetRight();
241     if (cc == ConditionCode::CC_GT || cc == ConditionCode::CC_A) {
242         // rl == lr handled in case 1
243         return {BoundsRange(rl + 1, lr), right_range};
244     }
245     if (cc == ConditionCode::CC_GE || cc == ConditionCode::CC_AE) {
246         return {BoundsRange(rl, lr), right_range};
247     }
248     if (cc == ConditionCode::CC_LT || cc == ConditionCode::CC_B) {
249         // With equal ll and rr left_range cannot be less than right_range
250         if (ll == rr) {
251             return {BoundsRange(), BoundsRange()};
252         }
253         return {BoundsRange(ll, rr - 1), right_range};
254     }
255     if (cc == ConditionCode::CC_LE || cc == ConditionCode::CC_BE) {
256         return {BoundsRange(ll, rr), right_range};
257     }
258     if (cc == ConditionCode::CC_EQ) {
259         return {BoundsRange(rl, rr), right_range};
260     }
261     return ranges;
262 }
263 
NarrowBoundsCase4(ConditionCode cc,BoundsRange::RangePair const & ranges)264 BoundsRange::RangePair BoundsRange::NarrowBoundsCase4(ConditionCode cc, BoundsRange::RangePair const &ranges)
265 {
266     auto &[left_range, right_range] = ranges;
267     int64_t ll = left_range.GetLeft();
268     int64_t rr = right_range.GetRight();
269     if (cc == ConditionCode::CC_LT || cc == ConditionCode::CC_B) {
270         // With equal ll and rr left_range cannot be less than right_range
271         if (ll == rr) {
272             return {BoundsRange(), BoundsRange()};
273         }
274         return {BoundsRange(ll, rr - 1), BoundsRange(ll + 1, rr)};
275     }
276     if (cc == ConditionCode::CC_LE || cc == ConditionCode::CC_BE || cc == ConditionCode::CC_EQ) {
277         return {BoundsRange(ll, rr), BoundsRange(ll, rr)};
278     }
279     return ranges;
280 }
281 
NarrowBoundsCase5(ConditionCode cc,BoundsRange::RangePair const & ranges)282 BoundsRange::RangePair BoundsRange::NarrowBoundsCase5(ConditionCode cc, BoundsRange::RangePair const &ranges)
283 {
284     if (cc == ConditionCode::CC_LT || cc == ConditionCode::CC_LE || cc == ConditionCode::CC_EQ ||
285         cc == ConditionCode::CC_B || cc == ConditionCode::CC_BE) {
286         return {BoundsRange(), BoundsRange()};
287     }
288     return ranges;
289 }
290 
NarrowBoundsCase6(ConditionCode cc,BoundsRange::RangePair const & ranges)291 BoundsRange::RangePair BoundsRange::NarrowBoundsCase6(ConditionCode cc, BoundsRange::RangePair const &ranges)
292 {
293     auto &[left_range, right_range] = ranges;
294     int64_t ll = left_range.GetLeft();
295     int64_t lr = left_range.GetRight();
296     int64_t rl = right_range.GetLeft();
297     int64_t rr = right_range.GetRight();
298     if (cc == ConditionCode::CC_GT || cc == ConditionCode::CC_A) {
299         // rl == lr handled in case 1
300         return {left_range, BoundsRange(rl, lr - 1)};
301     }
302     if (cc == ConditionCode::CC_GE || cc == ConditionCode::CC_AE) {
303         return {left_range, BoundsRange(rl, lr)};
304     }
305     if (cc == ConditionCode::CC_LT || cc == ConditionCode::CC_B) {
306         // ll == rr handled in case 4
307         return {left_range, BoundsRange(ll + 1, rr)};
308     }
309     if (cc == ConditionCode::CC_LE || cc == ConditionCode::CC_BE) {
310         return {left_range, BoundsRange(ll, rr)};
311     }
312     if (cc == ConditionCode::CC_EQ) {
313         return {left_range, BoundsRange(ll, lr)};
314     }
315     return ranges;
316 }
317 
318 /**
319  * Try narrow bounds range for <if (left CC right)> situation
320  * Return a pair of narrowed left and right intervals
321  */
TryNarrowBoundsByCC(ConditionCode cc,BoundsRange::RangePair const & ranges)322 BoundsRange::RangePair BoundsRange::TryNarrowBoundsByCC(ConditionCode cc, BoundsRange::RangePair const &ranges)
323 {
324     if (cc == ConditionCode::CC_NE) {
325         return NarrowBoundsByNE(ranges);
326     }
327     auto &[left_range, right_range] = ranges;
328     int64_t ll = left_range.GetLeft();
329     int64_t lr = left_range.GetRight();
330     int64_t rl = right_range.GetLeft();
331     int64_t rr = right_range.GetRight();
332     // For further description () is for left_range bounds and [] is for right_range bounds
333     // case 1: ( [ ) ]
334     if (ll <= rl && rl <= lr && lr <= rr) {
335         return NarrowBoundsCase1(cc, ranges);
336     }
337     // case 2: ( ) [ ]
338     if (ll <= lr && lr < rl && rl <= rr) {
339         return NarrowBoundsCase2(cc, ranges);
340     }
341     // case 3: ( [ ] )
342     if (ll <= rl && rl <= rr && rr <= lr) {
343         return NarrowBoundsCase3(cc, ranges);
344     }
345     // case 4: [ ( ] )
346     if (rl <= ll && ll <= rr && rr <= lr) {
347         return NarrowBoundsCase4(cc, ranges);
348     }
349     // case 5: [ ] ( )
350     if (rl <= rr && rr < ll && ll <= lr) {
351         return NarrowBoundsCase5(cc, ranges);
352     }
353     // case 6: [ ( ) ]
354     if (rl <= ll && ll <= lr && lr <= rr) {
355         return NarrowBoundsCase6(cc, ranges);
356     }
357     return ranges;
358 }
359 
FindBoundsRange(const BasicBlock * block,Inst * inst) const360 BoundsRange BoundsRangeInfo::FindBoundsRange(const BasicBlock *block, Inst *inst) const
361 {
362     ASSERT(block != nullptr && inst != nullptr);
363     ASSERT(!IsFloatType(inst->GetType()));
364     ASSERT(inst->GetType() == DataType::REFERENCE || DataType::GetCommonType(inst->GetType()) == DataType::INT64);
365     if (inst->GetOpcode() == Opcode::NullPtr) {
366         ASSERT(inst->GetType() == DataType::REFERENCE);
367         return BoundsRange(0);
368     }
369     if (IsInstNotNull(inst)) {
370         ASSERT(inst->GetType() == DataType::REFERENCE);
371         return BoundsRange(1, BoundsRange::GetMax(DataType::REFERENCE));
372     }
373     while (block != nullptr) {
374         if (bounds_range_info_.find(block) != bounds_range_info_.end() &&
375             bounds_range_info_.at(block).find(inst) != bounds_range_info_.at(block).end()) {
376             return bounds_range_info_.at(block).at(inst);
377         }
378         block = block->GetDominator();
379     }
380     if (inst->IsConst()) {
381         ASSERT(inst->GetType() == DataType::INT64);
382         auto val = static_cast<int64_t>(inst->CastToConstant()->GetIntValue());
383         return BoundsRange(val);
384     }
385     // if we know nothing about inst return the complete range of type
386     return BoundsRange(inst->GetType());
387 }
388 
SetBoundsRange(const BasicBlock * block,const Inst * inst,BoundsRange range)389 void BoundsRangeInfo::SetBoundsRange(const BasicBlock *block, const Inst *inst, BoundsRange range)
390 {
391     if (inst->IsConst() && range.GetLenArray() == nullptr) {
392         return;
393     }
394     if (inst->IsConst()) {
395         auto val = static_cast<int64_t>(static_cast<const ConstantInst *>(inst)->GetIntValue());
396         range = BoundsRange(val, val, range.GetLenArray());
397     }
398     ASSERT(inst->GetType() == DataType::REFERENCE || DataType::GetCommonType(inst->GetType()) == DataType::INT64);
399     ASSERT(range.GetLeft() >= BoundsRange::GetMin(inst->GetType()));
400     ASSERT(range.GetRight() <= BoundsRange::GetMax(inst->GetType()));
401     if (!range.IsMaxRange() || range.GetLenArray() != nullptr) {
402         if (bounds_range_info_.find(block) == bounds_range_info_.end()) {
403             auto it1 = bounds_range_info_.emplace(block, aa_.Adapter());
404             ASSERT(it1.second);
405             it1.first->second.emplace(inst, range);
406         } else if (bounds_range_info_.at(block).find(inst) == bounds_range_info_.at(block).end()) {
407             bounds_range_info_.at(block).emplace(inst, range);
408         } else {
409             bounds_range_info_.at(block).at(inst) = range;
410         }
411     }
412 }
413 
BoundsAnalysis(Graph * graph)414 BoundsAnalysis::BoundsAnalysis(Graph *graph) : Analysis(graph), bounds_range_info_(graph->GetAllocator()) {}
415 
RunImpl()416 bool BoundsAnalysis::RunImpl()
417 {
418     GetGraph()->RunPass<DominatorsTree>();
419     GetGraph()->RunPass<LoopAnalyzer>();
420 
421     VisitGraph();
422 
423     return true;
424 }
425 
GetBlocksToVisit() const426 const ArenaVector<BasicBlock *> &BoundsAnalysis::GetBlocksToVisit() const
427 {
428     return GetGraph()->GetBlocksRPO();
429 }
430 
VisitIf(GraphVisitor * v,Inst * inst)431 void BoundsAnalysis::VisitIf([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
432 {
433     UNREACHABLE();
434 }
435 
VisitIfImm(GraphVisitor * v,Inst * inst)436 void BoundsAnalysis::VisitIfImm(GraphVisitor *v, Inst *inst)
437 {
438     auto if_inst = inst->CastToIfImm();
439     ASSERT(if_inst->GetOperandsType() == DataType::BOOL);
440     ASSERT(if_inst->GetCc() == ConditionCode::CC_NE || if_inst->GetCc() == ConditionCode::CC_EQ);
441     ASSERT(if_inst->GetImm() == 0);
442 
443     auto input = inst->GetInput(0).GetInst();
444     if (input->GetOpcode() != Opcode::Compare) {
445         return;
446     }
447     auto compare = input->CastToCompare();
448     if (compare->GetOperandsType() == DataType::UINT64) {
449         return;
450     }
451     auto op0 = compare->GetInput(0).GetInst();
452     auto op1 = compare->GetInput(1).GetInst();
453 
454     if ((DataType::GetCommonType(op0->GetType()) != DataType::INT64 && op0->GetType() != DataType::REFERENCE) ||
455         (DataType::GetCommonType(op1->GetType()) != DataType::INT64 && op1->GetType() != DataType::REFERENCE)) {
456         return;
457     }
458 
459     auto cc = compare->GetCc();
460     auto block = inst->GetBasicBlock();
461     BasicBlock *true_block;
462     BasicBlock *false_block;
463     if (if_inst->GetCc() == ConditionCode::CC_NE) {
464         // Corresponds to Compare result
465         true_block = block->GetTrueSuccessor();
466         false_block = block->GetFalseSuccessor();
467     } else if (if_inst->GetCc() == ConditionCode::CC_EQ) {
468         // Corresponds to inversion of Compare result
469         true_block = block->GetFalseSuccessor();
470         false_block = block->GetTrueSuccessor();
471     } else {
472         UNREACHABLE();
473     }
474     CalcNewBoundsRangeForCompare(v, block, cc, op0, op1, true_block);
475     CalcNewBoundsRangeForCompare(v, block, GetInverseConditionCode(cc), op0, op1, false_block);
476 }
477 
VisitPhi(GraphVisitor * v,Inst * inst)478 void BoundsAnalysis::VisitPhi(GraphVisitor *v, Inst *inst)
479 {
480     if (IsFloatType(inst->GetType()) || inst->GetType() == DataType::REFERENCE || inst->GetType() == DataType::UINT64) {
481         return;
482     }
483     auto bri = static_cast<BoundsAnalysis *>(v)->GetBoundsRangeInfo();
484     auto phi = inst->CastToPhi();
485     auto phi_block = inst->GetBasicBlock();
486     auto phi_type = phi->GetType();
487     ArenaVector<BoundsRange> ranges(phi_block->GetGraph()->GetLocalAllocator()->Adapter());
488     for (auto &block : phi_block->GetPredsBlocks()) {
489         ranges.emplace_back(bri->FindBoundsRange(block, phi->GetPhiInput(block)));
490     }
491     bri->SetBoundsRange(phi_block, phi, BoundsRange::Union(ranges).FitInType(phi_type));
492 }
493 
CheckTriangleCase(const BasicBlock * block,const BasicBlock * tgt_block)494 bool BoundsAnalysis::CheckTriangleCase(const BasicBlock *block, const BasicBlock *tgt_block)
495 {
496     auto &preds_blocks = tgt_block->GetPredsBlocks();
497     auto loop = tgt_block->GetLoop();
498     auto &back_edges = loop->GetBackEdges();
499     if (preds_blocks.size() == 1) {
500         return true;
501     }
502     if (!loop->IsRoot() && back_edges.size() == 1 && preds_blocks.size() == 2U) {
503         if (preds_blocks[0] == block && preds_blocks[1] == back_edges[0]) {
504             return true;
505         }
506         if (preds_blocks[1] == block && preds_blocks[0] == back_edges[0]) {
507             return true;
508         }
509         return false;
510     }
511     return false;
512 }
513 
CalcNewBoundsRangeForCompare(GraphVisitor * v,BasicBlock * block,ConditionCode cc,Inst * left,Inst * right,BasicBlock * tgt_block)514 void BoundsAnalysis::CalcNewBoundsRangeForCompare(GraphVisitor *v, BasicBlock *block, ConditionCode cc, Inst *left,
515                                                   Inst *right, BasicBlock *tgt_block)
516 {
517     auto bri = static_cast<BoundsAnalysis *>(v)->GetBoundsRangeInfo();
518     auto left_range = bri->FindBoundsRange(block, left);
519     auto right_range = bri->FindBoundsRange(block, right);
520     // try to skip triangle:
521     /* [block]
522      *    |  \
523      *    |   \
524      *    |   [BB]
525      *    |   /
526      *    |  /
527      * [tgt_block]
528      */
529     if (CheckTriangleCase(block, tgt_block)) {
530         auto ranges = BoundsRange::TryNarrowBoundsByCC(cc, {left_range, right_range});
531         ASSERT(left_range.GetLenArray() == nullptr);
532         ASSERT(right_range.GetLenArray() == nullptr);
533         bri->SetBoundsRange(tgt_block, left, ranges.first.FitInType(left->GetType()));
534         bri->SetBoundsRange(tgt_block, right, ranges.second.FitInType(right->GetType()));
535     }
536 }
537 }  // namespace panda::compiler
538