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