• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 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 #include "ecmascript/compiler/array_bounds_check_elimination.h"
17 
18 namespace panda::ecmascript::kungfu {
Run()19 void ArrayBoundsCheckElimination::Run()
20 {
21     bounds_.resize(circuit_->GetMaxGateId() + 1, nullptr); // 1: +1 for size
22     indexCheckInfo_.resize(circuit_->GetMaxGateId() + 1, nullptr);
23     graphLinearizer_.SetScheduleJSOpcode();
24     graphLinearizer_.LinearizeGraph();
25     CalcBounds(graphLinearizer_.GetEntryRegion(), nullptr);
26 }
27 
28 /*
29     i_lower + c_lower <= x <= i_upper + c_upper
30     Initially, when nothing about the bounds is known yet, every instrution has the bounds:
31         MIN <= x <= MAX
32 */
Bound()33 ArrayBoundsCheckElimination::Bound::Bound()
34 {
35     lower_ = INT_MIN;
36     upper_ = INT_MAX;
37     lowerGate_ = Circuit::NullGate();
38     upperGate_ = Circuit::NullGate();
39 }
40 
Bound(int lower,GateRef lowerGate,int upper,GateRef upperGate)41 ArrayBoundsCheckElimination::Bound::Bound(int lower, GateRef lowerGate, int upper, GateRef upperGate)
42 {
43     lower_ = lower;
44     upper_ = upper;
45     lowerGate_ = lowerGate;
46     upperGate_ = upperGate;
47 }
48 
Bound(TypedBinOp op,GateRef gate,int constant)49 ArrayBoundsCheckElimination::Bound::Bound(TypedBinOp op, GateRef gate, int constant)
50 {
51     switch (op) {
52         case TypedBinOp::TYPED_EQ: {
53             lower_ = constant;
54             lowerGate_ = gate;
55             upper_ = constant;
56             upperGate_ = gate;
57             break;
58         }
59         case TypedBinOp::TYPED_NOTEQ: {
60             lower_ = INT_MIN;
61             lowerGate_ = Circuit::NullGate();
62             upper_ = INT_MAX;
63             upperGate_ = Circuit::NullGate();
64             if (gate == Circuit::NullGate()) {
65                 if (constant == INT_MIN) {
66                     lower_++;
67                 }
68                 if (constant == INT_MAX) {
69                     upper_--;
70                 }
71             }
72             break;
73         }
74         case TypedBinOp::TYPED_GREATEREQ: {
75             lower_ = constant;
76             lowerGate_ = gate;
77             upper_ = INT_MAX;
78             upperGate_ = Circuit::NullGate();
79             break;
80         }
81         case TypedBinOp::TYPED_LESSEQ: {
82             lower_ = INT_MIN;
83             lowerGate_ = Circuit::NullGate();
84             upper_ = constant;
85             upperGate_ = gate;
86             break;
87         }
88         default: {
89             LOG_ECMA(FATAL) << "unknown binary op";
90             UNREACHABLE();
91         }
92     }
93 }
94 
AndOp(Bound * bound,Bound * b)95 ArrayBoundsCheckElimination::Bound *ArrayBoundsCheckElimination::AndOp(Bound *bound, Bound *b)
96 {
97     // Update lower bound
98     if (bound->lowerGate_ == b->lowerGate_) {
99         bound->lower_ = std::max(bound->lower_, b->lower_);
100     }
101     if (b->HasLower()) {
102         bool set = true;
103         if (bound->lowerGate_ != Circuit::NullGate() && b->lowerGate_ != Circuit::NullGate()) {
104             auto boundLowerGateRegion = graphLinearizer_.GateToRegion(bound->lowerGate_);
105             auto bLowerGateRegion = graphLinearizer_.GateToRegion(b->lowerGate_);
106             int32_t boundLowerDominatorDepth = -1;
107             if (boundLowerGateRegion) {
108                 boundLowerDominatorDepth = boundLowerGateRegion->GetDepth();
109             }
110             int32_t bLowerDominatorDepth = -1;
111             if (bLowerGateRegion) {
112                 bLowerDominatorDepth = bLowerGateRegion->GetDepth();
113             }
114             set = (boundLowerDominatorDepth < bLowerDominatorDepth);
115         }
116         if (set) {
117             bound->lower_ = b->lower_;
118             bound->lowerGate_ = b->lowerGate_;
119         }
120     }
121 
122     // Update upper bound
123     if (bound->upperGate_ == b->upperGate_) {
124         bound->upper_ = std::min(bound->upper_, b->upper_);
125     }
126     if (b->HasUpper()) {
127         bool set = true;
128         if (bound->upperGate_ != Circuit::NullGate() && b->upperGate_ != Circuit::NullGate()) {
129             auto boundUpperGateRegion = graphLinearizer_.GateToRegion(bound->upperGate_);
130             auto bUpperGateRegion = graphLinearizer_.GateToRegion(b->upperGate_);
131             int32_t boundUpperDominatorDepth = -1;
132             if (boundUpperGateRegion) {
133                 boundUpperDominatorDepth = boundUpperGateRegion->GetDepth();
134             }
135             int32_t bUpperDominatorDepth = -1;
136             if (bUpperGateRegion) {
137                 bUpperDominatorDepth = bUpperGateRegion->GetDepth();
138             }
139             set = (boundUpperDominatorDepth < bUpperDominatorDepth);
140         }
141         if (set) {
142             bound->upper_ = b->upper_;
143             bound->upperGate_ = b->upperGate_;
144         }
145     }
146 
147     return bound;
148 }
149 
OrOp(Bound * bound,Bound * b)150 ArrayBoundsCheckElimination::Bound *ArrayBoundsCheckElimination::OrOp(Bound *bound, Bound *b)
151 {
152     // Update lower bound
153     if (bound->lowerGate_ != b->lowerGate_) {
154         bound->lowerGate_ = Circuit::NullGate();
155         bound->lower_ = INT_MIN;
156     } else {
157         bound->lower_ = std::min(bound->lower_, b->lower_);
158     }
159     // Update upper bound
160     if (bound->upperGate_ != b->upperGate_) {
161         bound->upperGate_ = Circuit::NullGate();
162         bound->upper_ = INT_MAX;
163     } else {
164         bound->upper_ = std::max(bound->upper_, b->upper_);
165     }
166 
167     return bound;
168 }
169 
DoConstant(GateRef gate)170 ArrayBoundsCheckElimination::Bound *ArrayBoundsCheckElimination::DoConstant(GateRef gate)
171 {
172     int constValue = static_cast<int>(acc_.GetConstantValue(gate));
173     return chunk_->New<Bound>(constValue, Circuit::NullGate(), constValue, Circuit::NullGate());
174 }
175 
DoBinaryArithmeticOp(GateRef gate)176 ArrayBoundsCheckElimination::Bound *ArrayBoundsCheckElimination::DoBinaryArithmeticOp(GateRef gate)
177 {
178     auto op = acc_.GetTypedBinaryOp(gate);
179     auto x = FindBoundGate(acc_.GetValueIn(gate, 0));
180     auto y = FindBoundGate(acc_.GetValueIn(gate, 1));
181     if (!acc_.IsConstant(x) || !acc_.IsConstant(y)) { // One of the operands must be non-constant!
182         if (op == TypedBinOp::TYPED_AND && (acc_.IsConstant(x) || acc_.IsConstant(y))) {
183             int constValue = 0;
184             if (acc_.IsConstant(x)) {
185                 constValue = static_cast<int>(acc_.GetConstantValue(x));
186             } else {
187                 constValue = static_cast<int>(acc_.GetConstantValue(y));
188             }
189             if (constValue >= 0) {
190                 return chunk_->New<Bound>(0, Circuit::NullGate(), constValue, Circuit::NullGate());
191             }
192         } else if (op == TypedBinOp::TYPED_MOD) {
193             Bound *xBound = GetBound(x);
194             if (xBound->Lower() >= 0 && xBound->LowerGate() == Circuit::NullGate() && IsArrayLength(y)) {
195                 return chunk_->New<Bound>(0, Circuit::NullGate(), -1, y);
196             } else if (xBound->HasLower() && xBound->Lower() >= 0 && acc_.IsConstant(y)
197                         && acc_.GetConstantValue(y) != 0) {
198                 int constValue = static_cast<int>(acc_.GetConstantValue(y));
199                 if (constValue != INT_MIN) {
200                     return chunk_->New<Bound>(0, Circuit::NullGate(), abs(constValue) - 1, Circuit::NullGate());
201                 } else {
202                     return chunk_->New<Bound>();
203                 }
204             } else {
205                 return chunk_->New<Bound>();
206             }
207         } else if (((acc_.IsConstant(x) || acc_.IsConstant(y)) && op == TypedBinOp::TYPED_ADD) ||
208             (acc_.IsConstant(y) && op == TypedBinOp::TYPED_SUB)) {
209             // x is constant, y is variable.
210             if (acc_.IsConstant(y)) {
211                 std::swap(x, y);
212             }
213 
214             // Add, Constant now in x
215             int constValue = static_cast<int>(acc_.GetConstantValue(x));
216             if (op == TypedBinOp::TYPED_SUB) {
217                 constValue = -constValue;
218             }
219 
220             Bound *bound = GetBound(y);
221             if (bound == nullptr) {
222                 LOG_ECMA(FATAL) << "ArrayBoundsCheckElimination::DoBinaryArithmeticOp:bound is nullptr";
223                 UNREACHABLE();
224             }
225             if (!bound->HasUpper() || !bound->HasLower()) {
226                 return chunk_->New<Bound>();
227             }
228 
229             int lower = bound->Lower();
230             int upper = bound->Upper();
231             int newLower = lower + constValue;
232             int newUpper = upper + constValue;
233             bool overflow = ((constValue < 0 && (newLower > lower)) ||
234                                 (constValue > 0 && (newUpper < upper)));
235             if (overflow) {
236                 return chunk_->New<Bound>();
237             } else {
238                 return chunk_->New<Bound>(newLower, bound->LowerGate(), newUpper, bound->UpperGate());
239             }
240         } else if (op == TypedBinOp::TYPED_SUB) {
241             Bound *bound = GetBound(x);
242             if (bound == nullptr) {
243                 LOG_ECMA(FATAL) << "ArrayBoundsCheckElimination::DoBinaryArithmeticOp:bound is nullptr";
244                 UNREACHABLE();
245             }
246             if (bound->LowerGate() == y) {
247                 return chunk_->New<Bound>(TypedBinOp::TYPED_GREATEREQ, Circuit::NullGate(), bound->Lower());
248             } else {
249                 return chunk_->New<Bound>();
250             }
251         } else {
252             return chunk_->New<Bound>();
253         }
254     }
255     return nullptr;
256 }
257 
DoUnaryArithmeticOp(GateRef gate)258 ArrayBoundsCheckElimination::Bound *ArrayBoundsCheckElimination::DoUnaryArithmeticOp(GateRef gate)
259 {
260     auto op = acc_.GetTypedUnAccessor(gate).GetTypedUnOp();
261     auto x = FindBoundGate(acc_.GetValueIn(gate, 0));
262     int constValue = 0;
263     if (op == TypedUnOp::TYPED_INC) {
264         constValue = 1;
265     } else if (op == TypedUnOp::TYPED_DEC) {
266         constValue = -1;
267     } else {
268         return chunk_->New<Bound>();
269     }
270     Bound *bound = GetBound(x);
271     // only support which bounds has been calculated
272     if (!bound->HasUpper() || !bound->HasLower()) {
273         return chunk_->New<Bound>();
274     }
275 
276     int lower = bound->Lower();
277     int upper = bound->Upper();
278     int newLower = lower + constValue;
279     int newUpper = upper + constValue;
280     bool overflow = ((constValue < 0 && (newLower > lower)) ||
281                         (constValue > 0 && (newUpper < upper)));
282     if (overflow) {
283         return chunk_->New<Bound>();
284     } else {
285         return chunk_->New<Bound>(newLower, bound->LowerGate(), newUpper, bound->UpperGate());
286     }
287 }
288 
InLoop(GateRef loopHeader,GateRef gate)289 bool ArrayBoundsCheckElimination::InLoop(GateRef loopHeader, GateRef gate)
290 {
291     while (gate != acc_.GetStateRoot()) {
292         if (gate == loopHeader) {
293             return true;
294         } else {
295             gate = acc_.GetState(gate, 0);
296         }
297     }
298     return false;
299 }
300 
DoPhi(GateRef gate)301 ArrayBoundsCheckElimination::Bound *ArrayBoundsCheckElimination::DoPhi(GateRef gate)
302 {
303     Bound *bound = nullptr;
304     size_t valueSize = acc_.GetInValueCount(gate);
305     GateRef stateIn = acc_.GetState(gate);
306     bool isLoopHead = acc_.IsLoopHead(stateIn);
307     bool hasUpper = true;
308     bool hasLower = true;
309     for (size_t i = 0; i < valueSize; i++) {
310         GateRef value = FindBoundGate(acc_.GetValueIn(gate, i));
311         // Check if instruction is connected with phi itself
312         if (isLoopHead && acc_.GetOpCode(value) == OpCode::TYPED_UNARY_OP
313             && InLoop(stateIn, value)) {
314             auto unOp = acc_.GetTypedUnAccessor(value).GetTypedUnOp();
315             switch (unOp) {
316                 case TypedUnOp::TYPED_INC: {
317                     hasUpper = false;
318                     break;
319                 }
320                 case TypedUnOp::TYPED_DEC: {
321                     hasLower = false;
322                     break;
323                 }
324                 default:
325                     break;
326             }
327             continue;
328         }
329 
330         Bound *vBound = GetBound(value);
331         if (vBound == nullptr) {
332             LOG_ECMA(FATAL) << "ArrayBoundsCheckElimination::DoPhi:vBound is nullptr";
333             UNREACHABLE();
334         }
335         Bound *curBound;
336         GateRef curGate;
337         int curConstant;
338         GetInstrAndConstValueFromOp(value, curGate, curConstant);
339         if (!vBound->HasUpper() || !vBound->HasLower()) {
340             curBound = chunk_->New<Bound>(curConstant, curGate, curConstant, curGate);
341         } else {
342             curBound = vBound;
343         }
344 
345         if (curBound) {
346             if (!bound) {
347                 bound = curBound->Copy(chunk_);
348             } else {
349                 bound = OrOp(bound, curBound);
350             }
351         } else {
352             bound = chunk_->New<Bound>();
353             break;
354         }
355     }
356 
357     if (!hasUpper) {
358         bound->RemoveUpper();
359     }
360     if (!hasLower) {
361         bound->RemoveLower();
362     }
363     return bound;
364 }
365 
VisitGate(GateRef gate)366 ArrayBoundsCheckElimination::Bound *ArrayBoundsCheckElimination::VisitGate(GateRef gate)
367 {
368     OpCode op = acc_.GetOpCode(gate);
369     switch (op) {
370         case OpCode::CONSTANT:
371             return DoConstant(gate);
372         case OpCode::TYPED_BINARY_OP:
373             return DoBinaryArithmeticOp(gate);
374         case OpCode::TYPED_UNARY_OP:
375             return DoUnaryArithmeticOp(gate);
376         case OpCode::VALUE_SELECTOR:
377             return DoPhi(gate);
378         default:
379             return nullptr;
380     }
381     return nullptr;
382 }
383 
GetInstrAndConstValueFromBinaryOp(GateRef gate,GateRef & other,int & value)384 bool ArrayBoundsCheckElimination::GetInstrAndConstValueFromBinaryOp(GateRef gate, GateRef& other, int& value)
385 {
386     ASSERT(acc_.GetOpCode(gate) == OpCode::TYPED_BINARY_OP);
387     auto op = acc_.GetTypedBinaryOp(gate);
388     auto x = FindBoundGate(acc_.GetValueIn(gate, 0));
389     auto y = FindBoundGate(acc_.GetValueIn(gate, 1));
390     other = x;
391     if ((op == TypedBinOp::TYPED_ADD && (acc_.IsConstant(x) || acc_.IsConstant(y)))
392         || (op == TypedBinOp::TYPED_SUB && acc_.IsConstant(y))) {
393         if (acc_.IsConstant(x)) {
394             value = static_cast<int>(acc_.GetConstantValue(x));
395             other = y;
396         } else {
397             value = static_cast<int>(acc_.GetConstantValue(y));
398             other = x;
399         }
400         if (op == TypedBinOp::TYPED_SUB) {
401             value = -value;
402         }
403     } else {
404         return false;
405     }
406     return true;
407 }
408 
GetInstrAndConstValueFromUnaryOp(GateRef gate,GateRef & other,int & value)409 bool ArrayBoundsCheckElimination::GetInstrAndConstValueFromUnaryOp(GateRef gate, GateRef& other, int& value)
410 {
411     ASSERT(acc_.GetOpCode(gate) == OpCode::TYPED_UNARY_OP);
412     auto op = acc_.GetTypedUnAccessor(gate).GetTypedUnOp();
413     auto x = FindBoundGate(acc_.GetValueIn(gate, 0));
414     if (op == TypedUnOp::TYPED_INC) {
415         value = 1;
416         other = x;
417     } else if (op == TypedUnOp::TYPED_DEC) {
418         value = -1;
419         other = x;
420     } else {
421         return false;
422     }
423     return true;
424 }
425 
GetInstrAndConstValueFromOp(GateRef gate,GateRef & instrValue,int & constValue)426 void ArrayBoundsCheckElimination::GetInstrAndConstValueFromOp(GateRef gate, GateRef& instrValue, int& constValue)
427 {
428     int base = 0;
429     constValue = 0;
430     instrValue = gate;
431     if (acc_.IsConstant(gate)) {
432         constValue = static_cast<int>(acc_.GetConstantValue(gate));
433         instrValue = Circuit::NullGate();
434         return ;
435     }
436 
437     while (acc_.GetOpCode(gate) == OpCode::TYPED_BINARY_OP ||
438            acc_.GetOpCode(gate) == OpCode::TYPED_UNARY_OP) {
439         bool flag = false;
440         int value = 0;
441         GateRef other = Circuit::NullGate();
442         if (acc_.GetOpCode(gate) == OpCode::TYPED_BINARY_OP) {
443             flag = GetInstrAndConstValueFromBinaryOp(gate, other, value);
444         } else if (acc_.GetOpCode(gate) == OpCode::TYPED_UNARY_OP) {
445             flag = GetInstrAndConstValueFromUnaryOp(gate, other, value);
446         }
447         if (!flag) {
448             break;
449         }
450         if (acc_.IsConstant(other)) {
451             base += value + static_cast<int>(acc_.GetConstantValue(other));
452             constValue = base;
453             instrValue = Circuit::NullGate();
454             break ;
455         } else {
456             base += value;
457             constValue = base;
458             instrValue = other;
459             gate = other;
460         }
461     }
462 }
463 
GetBound(GateRef gate)464 ArrayBoundsCheckElimination::Bound *ArrayBoundsCheckElimination::GetBound(GateRef gate)
465 {
466     if (gate == Circuit::NullGate()) {
467         return nullptr;
468     }
469     if (!bounds_[acc_.GetId(gate)]) {
470         bounds_[acc_.GetId(gate)] = chunk_->New<BoundStack>(chunk_);
471         Bound *bound = VisitGate(gate);
472         if (bound) {
473             bounds_[acc_.GetId(gate)]->push_back(bound);
474         }
475         if (bounds_[acc_.GetId(gate)]->size() == 0) {
476             bounds_[acc_.GetId(gate)]->push_back(chunk_->New<Bound>());
477         }
478     } else if (bounds_[acc_.GetId(gate)]->size() == 0) {
479         return chunk_->New<Bound>();
480     }
481     return bounds_[acc_.GetId(gate)]->back();
482 }
483 
UpdateBound(IntegerStack & pushed,GateRef gate,Bound * bound)484 void ArrayBoundsCheckElimination::UpdateBound(IntegerStack &pushed, GateRef gate, Bound *bound)
485 {
486     if (acc_.IsConstant(gate)) {
487         // No bound update for constants
488         return;
489     }
490     if (!bounds_[acc_.GetId(gate)]) {
491         GetBound(gate);
492     }
493     Bound* top = nullptr;
494     if (bounds_[acc_.GetId(gate)]->size() > 0) {
495         top = bounds_[acc_.GetId(gate)]->back();
496     }
497     if (top) {
498         bound = AndOp(bound, top);
499     }
500     bounds_[acc_.GetId(gate)]->push_back(bound);
501     pushed.push_back(acc_.GetId(gate));
502 }
503 
504 /*
505 x op y + constValue
506 for example:
507     x >= Circuit::NullGate() + 0
508     x < Length + 0
509 */
UpdateBound(IntegerStack & pushed,GateRef x,TypedBinOp op,GateRef instrValue,int constValue)510 void ArrayBoundsCheckElimination::UpdateBound(IntegerStack &pushed, GateRef x, TypedBinOp op,
511                                               GateRef instrValue, int constValue)
512 {
513     if (op == TypedBinOp::TYPED_GREATER) { // x < 3 -> x <= 4
514         op = TypedBinOp::TYPED_GREATEREQ;
515         // Cannot Represent c > INT_MAX, do not update bounds
516         if (constValue == INT_MAX && instrValue == Circuit::NullGate()) {
517             return;
518         }
519         constValue++;
520     } else if (op == TypedBinOp::TYPED_LESS) { // x > 3 -> x >= 2
521         op = TypedBinOp::TYPED_LESSEQ;
522         // Cannot Represent c < INT_MIN, do not update bounds
523         if (constValue == INT_MIN && instrValue == Circuit::NullGate()) {
524             return;
525         }
526         constValue--;
527     }
528     Bound *bound = chunk_->New<Bound>(op, instrValue, constValue);
529     UpdateBound(pushed, x, bound);
530 }
531 
532 // Add if condition when x is a variable, x op y
AddIfCondition(IntegerStack & pushed,GateRef x,GateRef y,TypedBinOp op)533 void ArrayBoundsCheckElimination::AddIfCondition(IntegerStack &pushed, GateRef x, GateRef y, TypedBinOp op)
534 {
535     if (acc_.IsConstant(x)) { // x must be non-constant!
536         return;
537     }
538     int constValue;
539     GateRef instrValue;
540     GetInstrAndConstValueFromOp(y, instrValue, constValue);
541     UpdateBound(pushed, x, op, instrValue, constValue);
542 }
543 
IsArrayLength(GateRef gate)544 bool ArrayBoundsCheckElimination::IsArrayLength(GateRef gate)
545 {
546     if (gate == Circuit::NullGate()) {
547         return false;
548     }
549     OpCode op = acc_.GetOpCode(gate);
550     switch (op) {
551         case OpCode::LOAD_ARRAY_LENGTH:
552         case OpCode::LOAD_TYPED_ARRAY_LENGTH:
553             return true;
554         default:
555             return false;
556     }
557     UNREACHABLE();
558     return false;
559 }
560 
FindBoundGate(GateRef gate)561 GateRef ArrayBoundsCheckElimination::FindBoundGate(GateRef gate)
562 {
563     OpCode op = acc_.GetOpCode(gate);
564     switch (op) {
565         // skip gate
566         case OpCode::RANGE_GUARD:
567             gate = FindBoundGate(acc_.GetValueIn(gate, 0));
568             break;
569         case OpCode::INDEX_CHECK: // get index
570             gate = FindBoundGate(acc_.GetValueIn(gate, 1));
571             break;
572         default:
573             break;
574     }
575     return gate;
576 }
577 
InArrayBound(Bound * bound,GateRef length,GateRef array)578 bool ArrayBoundsCheckElimination::InArrayBound(Bound *bound, GateRef length, GateRef array)
579 {
580     if (!bound || array == Circuit::NullGate()) {
581         return false;
582     }
583 
584     if (bound->Lower() >= 0 && bound->LowerGate() == Circuit::NullGate() &&
585         bound->Upper() < 0 && bound->UpperGate() != Circuit::NullGate()) {
586         if (length != Circuit::NullGate() && bound->UpperGate() == length) {
587             return true;
588         }
589     }
590 
591     return false;
592 }
593 
RemoveIndexCheck(GateRef gate)594 void ArrayBoundsCheckElimination::RemoveIndexCheck(GateRef gate)
595 {
596     ASSERT(acc_.GetDependCount(gate) == 1);
597     ASSERT(acc_.GetStateCount(gate) == 1);
598     ASSERT(acc_.GetInValueCount(gate) == 2); // 2: ValueCount
599 
600     GateRef depend = acc_.GetDep(gate);
601     GateRef state = acc_.GetState(gate);
602     GateRef value = acc_.GetValueIn(gate, 1); // Index
603 
604     acc_.ReplaceGate(gate, state, depend, value);
605 }
606 
CheckLoop(GateRef array,GateRef lowerGate,int lower,GateRef upperGate,int upper)607 bool ArrayBoundsCheckElimination::CheckLoop(GateRef array, GateRef lowerGate, int lower, GateRef upperGate, int upper)
608 {
609     if (IsArrayLength(upperGate) && FindBoundGate(acc_.GetValueIn(upperGate, 0)) == array) {
610         if (upper >= 0) {
611             return false;
612         }
613     }
614     if (IsArrayLength(lowerGate) && FindBoundGate(acc_.GetValueIn(lowerGate, 0)) == array) {
615         if (lower >= 0) {
616             return false;
617         }
618     }
619     return true;
620 }
621 
LoopInvariant(GateRegion * loopHeader,GateRef gate)622 bool ArrayBoundsCheckElimination::LoopInvariant(GateRegion *loopHeader, GateRef gate)
623 {
624     if (gate == Circuit::NullGate()) {
625         return true;
626     }
627     auto gateRegion = graphLinearizer_.GateToRegion(gate);
628     GateRegion* g = loopHeader->GetDominator();
629     while (g != nullptr) {
630         if (g == gateRegion) {
631             return true;
632         }
633         if (g == g->GetDominator()) { // entry
634             break ;
635         }
636         g = g->GetDominator();
637     }
638     return false;
639 }
640 
Predicate(GateRef left,TypedBinOp cond,GateRef right)641 GateRef ArrayBoundsCheckElimination::Predicate(GateRef left, TypedBinOp cond, GateRef right)
642 {
643     return builder_.InsertRangeCheckPredicate(left, cond, right);
644 }
645 
PredicateCmpWithConst(GateRef left,TypedBinOp cond,int32_t right)646 GateRef ArrayBoundsCheckElimination::PredicateCmpWithConst(GateRef left, TypedBinOp cond, int32_t right)
647 {
648     GateRef constGate = builder_.Int32(right);
649     return Predicate(left, cond, constGate);
650 }
651 
PredicateAdd(GateRef left,int32_t leftConst,TypedBinOp cond,GateRef right)652 GateRef ArrayBoundsCheckElimination::PredicateAdd(GateRef left, int32_t leftConst, TypedBinOp cond, GateRef right)
653 {
654     GateRef constGate = builder_.Int32(leftConst);
655     GateRef binaryOpGate = builder_.InsertTypedBinaryop(left, constGate, TypedBinOp::TYPED_ADD);
656     return Predicate(binaryOpGate, cond, right);
657 }
658 
PredicateAddCmpWithConst(GateRef left,int32_t leftConst,TypedBinOp cond,int32_t right)659 GateRef ArrayBoundsCheckElimination::PredicateAddCmpWithConst(GateRef left, int32_t leftConst,
660                                                               TypedBinOp cond, int32_t right)
661 {
662     GateRef constGate = builder_.Int32(right);
663     return PredicateAdd(left, leftConst, cond, constGate);
664 }
665 
LoopInvariantMotionForIndexCheck(GateRef array,GateRef length,GateRef lengthMetaData,GateRef lowerGate,int lower,GateRef upperGate,int upper,bool isTypedArray)666 void ArrayBoundsCheckElimination::LoopInvariantMotionForIndexCheck(GateRef array, GateRef length,
667     GateRef lengthMetaData, GateRef lowerGate, int lower, GateRef upperGate, int upper, bool isTypedArray)
668 {
669     // lower > 0
670     if (lowerGate != Circuit::NullGate()) {
671         if (lower == 0) {
672             // lowerGate >= 0
673             PredicateCmpWithConst(lowerGate, TypedBinOp::TYPED_GREATEREQ, 0);
674         } else if (lower > 0) {
675             // lowerGate + lower >= 0
676             PredicateAddCmpWithConst(lowerGate, lower, TypedBinOp::TYPED_GREATEREQ, 0);
677         } else {
678             // lowerGate + lower < 0
679             // lower < 0
680             // lowerGate < -lower
681             lower++;
682             lower = -lower;
683             PredicateCmpWithConst(lowerGate, TypedBinOp::TYPED_GREATER, lower);
684         }
685     }
686 
687     // LOAD LENGTH if necessary
688     if (length == Circuit::NullGate()) {
689         length = builder_.InsertLoadArrayLength(array, lengthMetaData, isTypedArray);
690     }
691 
692     if (upperGate == Circuit::NullGate()) {
693         ASSERT(upper >= 0);
694         PredicateCmpWithConst(length, TypedBinOp::TYPED_GREATER, upper);
695     } else {
696         if (upper == 0) {
697             Predicate(upperGate, TypedBinOp::TYPED_LESS, length);
698         } else if (upper > 0) {
699             // upperGate + upper < length
700             PredicateAdd(upperGate, upper, TypedBinOp::TYPED_LESS, length);
701         } else {
702             // upperGate + upper < length
703             // upper < 0
704             // upperGate < length + (-upper)
705             PredicateAdd(length, -upper, TypedBinOp::TYPED_GREATER, upperGate);
706         }
707     }
708 }
709 
ProcessIndexCheck(GateRegion * loopHeader,GateRef gate)710 void ArrayBoundsCheckElimination::ProcessIndexCheck(GateRegion *loopHeader, GateRef gate)
711 {
712     auto length = FindBoundGate(acc_.GetValueIn(gate, 0));
713     auto array = FindBoundGate(acc_.GetValueIn(length, 0));
714     auto index = FindBoundGate(acc_.GetValueIn(gate, 1));
715     Bound *indexBound = GetBound(index);
716     if (!indexBound->HasLower() || !indexBound->HasUpper()) {
717         return;
718     }
719 
720     if (InArrayBound(indexBound, length, array)) {
721         RemoveIndexCheck(gate);
722     } else if (loopHeader) {
723         if (!LoopInvariant(loopHeader, array)
724             || !LoopInvariant(loopHeader, indexBound->LowerGate())
725             || !LoopInvariant(loopHeader, indexBound->UpperGate())
726             || (indexBound->LowerGate() == Circuit::NullGate() && indexBound->Lower() < 0)
727             || (indexBound->UpperGate() == Circuit::NullGate() && indexBound->Upper() < 0)) {
728             return;
729         }
730 
731         ASSERT(length != Circuit::NullGate());
732         GateRef lengthMetaData = length;
733         bool isTypedArray = false;
734         if (acc_.GetOpCode(length) == OpCode::LOAD_TYPED_ARRAY_LENGTH) {
735             isTypedArray = true;
736         }
737 
738         // Length instrution
739         if (!LoopInvariant(loopHeader, length)) {
740             // Generate length instruction yourself
741             length = Circuit::NullGate();
742         }
743 
744         // Insert Before loopHeader State, and if find IF_TRUE and IF_FALSE, insert after the DEPEND_RELAY
745         // if find MERGE, insert after DEPEND_SELECTOR
746         GateRef insertAfter = acc_.GetState(loopHeader->GetState(), 0); // after end
747         GateRef stateIn = insertAfter;
748         GateRef dependIn = insertAfter;
749         acc_.GetStateInAndDependIn(insertAfter, stateIn, dependIn);
750 
751         if (!CheckLoop(array, indexBound->LowerGate(), indexBound->Lower(),
752                        indexBound->UpperGate(), indexBound->Upper())) {
753             return;
754         }
755 
756         Environment env(stateIn, dependIn, {}, circuit_, &builder_);
757         LoopInvariantMotionForIndexCheck(array, length, lengthMetaData, indexBound->LowerGate(),
758             indexBound->Lower(), indexBound->UpperGate(), indexBound->Upper(), isTypedArray);
759         RemoveIndexCheck(gate);
760     }
761 }
762 
ProcessIf(IntegerStack & pushed,GateRegion * parent,OpCode cond)763 void ArrayBoundsCheckElimination::ProcessIf(IntegerStack &pushed, GateRegion *parent, OpCode cond)
764 {
765     auto& gateLists = parent->GetGates();
766     for (int i = static_cast<int>(gateLists.size()) - 1; i >= 0; i--) { // Found the last BinaryOp
767         GateRef gate = gateLists[i];
768         if (gate == Circuit::NullGate()) continue;
769         OpCode opGate = acc_.GetOpCode(gate);
770         if (opGate != OpCode::TYPED_BINARY_OP) {
771             continue ;
772         }
773 
774         TypedBinOp op = acc_.GetTypedBinaryOp(gate);
775         GateRef x = FindBoundGate(acc_.GetValueIn(gate, 0));
776         GateRef y = FindBoundGate(acc_.GetValueIn(gate, 1));
777 
778         switch (op) {
779             case TypedBinOp::TYPED_LESS:
780             case TypedBinOp::TYPED_LESSEQ:
781             case TypedBinOp::TYPED_GREATER:
782             case TypedBinOp::TYPED_GREATEREQ:
783             case TypedBinOp::TYPED_EQ:
784             case TypedBinOp::TYPED_NOTEQ: {
785                 if (cond == OpCode::IF_TRUE) {
786                     op = acc_.GetRevCompareOpForTypedBinOp(op);
787                 }
788                 AddIfCondition(pushed, x, y, op);
789                 AddIfCondition(pushed, y, x, acc_.GetSwapCompareOpForTypedBinOp(op));
790                 break;
791             }
792             default:
793                 break;
794         }
795         break;
796     }
797 }
798 
Contain(GateLists & gateLists,GateRef gate)799 bool ArrayBoundsCheckElimination::Contain(GateLists &gateLists, GateRef gate)
800 {
801     for (size_t i = 0; i < gateLists.size(); i++) {
802         if (gateLists[i] == gate) {
803             return true;
804         }
805     }
806     return false;
807 }
808 
AddAccessIndexedInfo(GateLists & indices,GateRef gate,int idx,GateRef indexCheck)809 void ArrayBoundsCheckElimination::AddAccessIndexedInfo(GateLists &indices, GateRef gate, int idx, GateRef indexCheck)
810 {
811     IndexCheckInfo *indexCheckInfo = indexCheckInfo_[acc_.GetId(gate)];
812     if (indexCheckInfo == nullptr) {
813         indexCheckInfo = chunk_->New<IndexCheckInfo>(chunk_);
814         indexCheckInfo_[acc_.GetId(gate)] = indexCheckInfo;
815         indices.push_back(gate);
816         indexCheckInfo->min_ = idx;
817         indexCheckInfo->max_ = idx;
818     } else if (idx >= indexCheckInfo->min_ && idx <= indexCheckInfo->max_) {
819         RemoveIndexCheck(indexCheck);
820         return;
821     }
822     indexCheckInfo->min_ = std::min(indexCheckInfo->min_, idx);
823     indexCheckInfo->max_ = std::max(indexCheckInfo->max_, idx);
824     indexCheckInfo->list_.push_back(indexCheck);
825 }
826 
InBlockMotion(GateLists & indexChecked,GateLists & arrays)827 void ArrayBoundsCheckElimination::InBlockMotion(GateLists &indexChecked, GateLists &arrays)
828 {
829     GateLists indices(chunk_);
830     for (size_t i = 0; i < arrays.size(); i++) {
831         int maxConstant = -1;
832         GateLists listConstant(chunk_);
833         GateRef arrayGate = arrays[i];
834         for (size_t j = 0; j < indexChecked.size(); j++) {
835             GateRef indexCheck = indexChecked[j];
836             // INDEX_CHECK may be dead
837             if (acc_.GetOpCode(indexCheck) != OpCode::INDEX_CHECK) {
838                 continue;
839             }
840             GateRef length = FindBoundGate(acc_.GetValueIn(indexCheck, 0));
841             GateRef index = FindBoundGate(acc_.GetValueIn(indexCheck, 1));
842             GateRef array = FindBoundGate(acc_.GetValueIn(length, 0));
843             if (array != arrayGate) {
844                 continue;
845             }
846             if (acc_.IsConstant(index)) {
847                 int constValue = static_cast<int>(acc_.GetConstantValue(index));
848                 if (constValue >= 0 && constValue <= maxConstant) {
849                     RemoveIndexCheck(indexCheck);
850                 } else if (constValue >= 0 && constValue > maxConstant) {
851                     maxConstant = constValue;
852                     listConstant.push_back(indexCheck);
853                 }
854             } else {
855                 int lastInteger;
856                 GateRef lastGate;
857                 GetInstrAndConstValueFromOp(index, lastGate, lastInteger);
858                 if (lastInteger >= 0 && lastGate == Circuit::NullGate()) { // IsConstant
859                     if (lastInteger <= maxConstant) {
860                         RemoveIndexCheck(indexCheck);
861                     } else {
862                         maxConstant = lastInteger;
863                         listConstant.push_back(indexCheck);
864                     }
865                 } else if (lastGate != Circuit::NullGate()) {
866                     AddAccessIndexedInfo(indices, lastGate, lastInteger, indexCheck);
867                 } // when lastInteger < 0, dont remove IndexCheck
868             }
869         }
870 
871         // Iterate over all different indices
872         for (size_t j = 0; j < indices.size(); j++) {
873             GateRef index = indices[j];
874 
875             IndexCheckInfo *info = indexCheckInfo_[acc_.GetId(index)];
876             ASSERT(info != nullptr);
877 
878             // maybe index < 0, max > 0
879             // max + index in [0, a.length)
880             // min + index overflow !!!, min + index > 0
881             // so, min + index >= INT_MIN, min >= INT_MIN - index
882             // max in [-index, a.length - index)
883             // min >= INT_MIN + max
884             bool rangeCond = (info->max_ < 0 || info->max_ + INT_MIN <= info->min_);
885             if (info->list_.size() > 2 && rangeCond) { // 2: size
886                 GateRef insertAfter = info->list_.front();
887                 GateRef length = FindBoundGate(acc_.GetValueIn(insertAfter, 0));
888                 ASSERT(length != Circuit::NullGate());
889 
890                 Environment env(insertAfter, circuit_, &builder_);
891 
892                 // Calculate lower bound
893                 GateRef lowerCompare = index;
894                 if (info->min_ > 0) {
895                     GateRef minGate = builder_.Int32(info->min_);
896                     lowerCompare = builder_.InsertTypedBinaryop(lowerCompare, minGate, TypedBinOp::TYPED_ADD);
897                 } else if (info->min_ < 0) {
898                     GateRef minGate = builder_.Int32(-info->min_);
899                     lowerCompare = builder_.InsertTypedBinaryop(lowerCompare, minGate, TypedBinOp::TYPED_SUB);
900                 }
901 
902                 PredicateCmpWithConst(lowerCompare, TypedBinOp::TYPED_GREATEREQ, 0);
903 
904                 // Calculate upper bound
905                 GateRef upperCompare = index;
906                 if (info->max_ != 0) {
907                     if (info->max_ > 0) {
908                         GateRef maxGate = builder_.Int32(info->max_);
909                         upperCompare = builder_.InsertTypedBinaryop(upperCompare, maxGate, TypedBinOp::TYPED_ADD);
910                     } else if (info->max_ < 0) {
911                         GateRef maxGate = builder_.Int32(-info->max_);
912                         upperCompare = builder_.InsertTypedBinaryop(upperCompare, maxGate, TypedBinOp::TYPED_SUB);
913                     }
914                 }
915 
916                 Predicate(upperCompare, TypedBinOp::TYPED_LESS, length);
917                 for (auto& indexCheck: (info->list_)) {
918                     RemoveIndexCheck(indexCheck);
919                 }
920             }
921         }
922 
923         // index only constant
924         if (listConstant.size() > 1) {
925             GateRef firIndexCheckGate = listConstant.front();
926             Environment env(firIndexCheckGate, circuit_, &builder_);
927             GateRef length = FindBoundGate(acc_.GetValueIn(firIndexCheckGate, 0));
928             ASSERT(length != Circuit::NullGate());
929             ASSERT(maxConstant >= 0);
930             PredicateCmpWithConst(length, TypedBinOp::TYPED_GREATER, maxConstant); // length > index
931             for (size_t j = 0; j < listConstant.size(); j++) {
932                 GateRef indexCheck = listConstant[j];
933                 RemoveIndexCheck(indexCheck);
934             }
935         }
936 
937         for (size_t j = 0; j < indices.size(); j++) {
938             indexCheckInfo_[acc_.GetId(indices[j])] = nullptr;
939         }
940         indices.clear();
941     }
942 }
943 
CalcBounds(GateRegion * block,GateRegion * loopHeader)944 void ArrayBoundsCheckElimination::CalcBounds(GateRegion *block, GateRegion *loopHeader)
945 {
946     // Pushed stack for condition
947     IntegerStack pushed(chunk_);
948 
949     // Process If
950     GateRegion *parent = block->GetDominator();
951     if (parent != nullptr) {
952         auto gate = block->GetGates().front();
953         auto op = acc_.GetOpCode(gate);
954         if (op == OpCode::IF_TRUE || op == OpCode::IF_FALSE) { // Recognize If (including the condition in forloop)
955             ProcessIf(pushed, parent, op);
956         }
957     }
958 
959     GateLists indexChecked(chunk_);
960     GateLists arrays(chunk_);
961 
962     auto& gateList_ = block->GetGates();
963     for (size_t i = 0; i < gateList_.size(); i++) { // Visit GateUnion
964         GateRef gate = gateList_[i];
965         auto op = acc_.GetOpCode(gate);
966         if (op == OpCode::INDEX_CHECK) {
967             auto length = FindBoundGate(acc_.GetValueIn(gate, 0));
968             auto index = FindBoundGate(acc_.GetValueIn(gate, 1));
969             auto array = FindBoundGate(acc_.GetValueIn(length, 0));
970 
971             ProcessIndexCheck(loopHeader, gate);
972             indexChecked.push_back(gate);
973 
974             if (!Contain(arrays, array)) {
975                 arrays.push_back(array);
976             }
977 
978             // Give IndexCheck a bound [0, Length - 1]
979             Bound *b = GetBound(index);
980             if (b->LowerGate() == Circuit::NullGate()) { // LowerBound is the Constant !!!
981                 UpdateBound(pushed, index, TypedBinOp::TYPED_GREATEREQ, Circuit::NullGate(), 0);
982             }
983             if (!b->HasUpper() && length != Circuit::NullGate()) { // default dont know the Length
984                 UpdateBound(pushed, index, TypedBinOp::TYPED_LESS, length, 0);
985             }
986         }
987     }
988 
989     InBlockMotion(indexChecked, arrays);
990 
991     auto& dominatedRegions_ = block->GetDominatedRegions();
992     for (size_t i = 0; i < dominatedRegions_.size(); i++) {
993         GateRegion *nex = dominatedRegions_[i];
994         if (block->IsLoopHead() && (block->GetLoopIndex() == nex->GetLoopIndex()
995             || nex->GetLoopDepth() > block->GetLoopDepth())) {
996             CalcBounds(nex, block);
997         } else {
998             CalcBounds(nex, loopHeader);
999         }
1000     }
1001 
1002     for (size_t i = 0; i < pushed.size(); i++) {
1003         bounds_[pushed[i]]->pop_back();
1004     }
1005 }
1006 }
1007