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