arkcompiler_ets_runtime/ecmascript/compiler/array_bounds_check_elimination.cpp
2024-08-29 21:30:22 +08:00

1007 lines
35 KiB
C++

/*
* Copyright (c) 2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ecmascript/compiler/array_bounds_check_elimination.h"
namespace panda::ecmascript::kungfu {
void ArrayBoundsCheckElimination::Run()
{
bounds_.resize(circuit_->GetMaxGateId() + 1, nullptr); // 1: +1 for size
indexCheckInfo_.resize(circuit_->GetMaxGateId() + 1, nullptr);
graphLinearizer_.SetScheduleJSOpcode();
graphLinearizer_.LinearizeGraph();
CalcBounds(graphLinearizer_.GetEntryRegion(), nullptr);
}
/*
i_lower + c_lower <= x <= i_upper + c_upper
Initially, when nothing about the bounds is known yet, every instrution has the bounds:
MIN <= x <= MAX
*/
ArrayBoundsCheckElimination::Bound::Bound()
{
lower_ = INT_MIN;
upper_ = INT_MAX;
lowerGate_ = Circuit::NullGate();
upperGate_ = Circuit::NullGate();
}
ArrayBoundsCheckElimination::Bound::Bound(int lower, GateRef lowerGate, int upper, GateRef upperGate)
{
lower_ = lower;
upper_ = upper;
lowerGate_ = lowerGate;
upperGate_ = upperGate;
}
ArrayBoundsCheckElimination::Bound::Bound(TypedBinOp op, GateRef gate, int constant)
{
switch (op) {
case TypedBinOp::TYPED_EQ: {
lower_ = constant;
lowerGate_ = gate;
upper_ = constant;
upperGate_ = gate;
break;
}
case TypedBinOp::TYPED_NOTEQ: {
lower_ = INT_MIN;
lowerGate_ = Circuit::NullGate();
upper_ = INT_MAX;
upperGate_ = Circuit::NullGate();
if (gate == Circuit::NullGate()) {
if (constant == INT_MIN) {
lower_++;
}
if (constant == INT_MAX) {
upper_--;
}
}
break;
}
case TypedBinOp::TYPED_GREATEREQ: {
lower_ = constant;
lowerGate_ = gate;
upper_ = INT_MAX;
upperGate_ = Circuit::NullGate();
break;
}
case TypedBinOp::TYPED_LESSEQ: {
lower_ = INT_MIN;
lowerGate_ = Circuit::NullGate();
upper_ = constant;
upperGate_ = gate;
break;
}
default: {
LOG_ECMA(FATAL) << "unknown binary op";
UNREACHABLE();
}
}
}
ArrayBoundsCheckElimination::Bound *ArrayBoundsCheckElimination::AndOp(Bound *bound, Bound *b)
{
// Update lower bound
if (bound->lowerGate_ == b->lowerGate_) {
bound->lower_ = std::max(bound->lower_, b->lower_);
}
if (b->HasLower()) {
bool set = true;
if (bound->lowerGate_ != Circuit::NullGate() && b->lowerGate_ != Circuit::NullGate()) {
auto boundLowerGateRegion = graphLinearizer_.GateToRegion(bound->lowerGate_);
auto bLowerGateRegion = graphLinearizer_.GateToRegion(b->lowerGate_);
int32_t boundLowerDominatorDepth = -1;
if (boundLowerGateRegion) {
boundLowerDominatorDepth = boundLowerGateRegion->GetDepth();
}
int32_t bLowerDominatorDepth = -1;
if (bLowerGateRegion) {
bLowerDominatorDepth = bLowerGateRegion->GetDepth();
}
set = (boundLowerDominatorDepth < bLowerDominatorDepth);
}
if (set) {
bound->lower_ = b->lower_;
bound->lowerGate_ = b->lowerGate_;
}
}
// Update upper bound
if (bound->upperGate_ == b->upperGate_) {
bound->upper_ = std::min(bound->upper_, b->upper_);
}
if (b->HasUpper()) {
bool set = true;
if (bound->upperGate_ != Circuit::NullGate() && b->upperGate_ != Circuit::NullGate()) {
auto boundUpperGateRegion = graphLinearizer_.GateToRegion(bound->upperGate_);
auto bUpperGateRegion = graphLinearizer_.GateToRegion(b->upperGate_);
int32_t boundUpperDominatorDepth = -1;
if (boundUpperGateRegion) {
boundUpperDominatorDepth = boundUpperGateRegion->GetDepth();
}
int32_t bUpperDominatorDepth = -1;
if (bUpperGateRegion) {
bUpperDominatorDepth = bUpperGateRegion->GetDepth();
}
set = (boundUpperDominatorDepth < bUpperDominatorDepth);
}
if (set) {
bound->upper_ = b->upper_;
bound->upperGate_ = b->upperGate_;
}
}
return bound;
}
ArrayBoundsCheckElimination::Bound *ArrayBoundsCheckElimination::OrOp(Bound *bound, Bound *b)
{
// Update lower bound
if (bound->lowerGate_ != b->lowerGate_) {
bound->lowerGate_ = Circuit::NullGate();
bound->lower_ = INT_MIN;
} else {
bound->lower_ = std::min(bound->lower_, b->lower_);
}
// Update upper bound
if (bound->upperGate_ != b->upperGate_) {
bound->upperGate_ = Circuit::NullGate();
bound->upper_ = INT_MAX;
} else {
bound->upper_ = std::max(bound->upper_, b->upper_);
}
return bound;
}
ArrayBoundsCheckElimination::Bound *ArrayBoundsCheckElimination::DoConstant(GateRef gate)
{
int constValue = static_cast<int>(acc_.GetConstantValue(gate));
return chunk_->New<Bound>(constValue, Circuit::NullGate(), constValue, Circuit::NullGate());
}
ArrayBoundsCheckElimination::Bound *ArrayBoundsCheckElimination::DoBinaryArithmeticOp(GateRef gate)
{
auto op = acc_.GetTypedBinaryOp(gate);
auto x = FindBoundGate(acc_.GetValueIn(gate, 0));
auto y = FindBoundGate(acc_.GetValueIn(gate, 1));
if (!acc_.IsConstant(x) || !acc_.IsConstant(y)) { // One of the operands must be non-constant!
if (op == TypedBinOp::TYPED_AND && (acc_.IsConstant(x) || acc_.IsConstant(y))) {
int constValue = 0;
if (acc_.IsConstant(x)) {
constValue = static_cast<int>(acc_.GetConstantValue(x));
} else {
constValue = static_cast<int>(acc_.GetConstantValue(y));
}
if (constValue >= 0) {
return chunk_->New<Bound>(0, Circuit::NullGate(), constValue, Circuit::NullGate());
}
} else if (op == TypedBinOp::TYPED_MOD) {
Bound *xBound = GetBound(x);
if (xBound->Lower() >= 0 && xBound->LowerGate() == Circuit::NullGate() && IsArrayLength(y)) {
return chunk_->New<Bound>(0, Circuit::NullGate(), -1, y);
} else if (xBound->HasLower() && xBound->Lower() >= 0 && acc_.IsConstant(y)
&& acc_.GetConstantValue(y) != 0) {
int constValue = static_cast<int>(acc_.GetConstantValue(y));
if (constValue != INT_MIN) {
return chunk_->New<Bound>(0, Circuit::NullGate(), abs(constValue) - 1, Circuit::NullGate());
} else {
return chunk_->New<Bound>();
}
} else {
return chunk_->New<Bound>();
}
} else if (((acc_.IsConstant(x) || acc_.IsConstant(y)) && op == TypedBinOp::TYPED_ADD) ||
(acc_.IsConstant(y) && op == TypedBinOp::TYPED_SUB)) {
// x is constant, y is variable.
if (acc_.IsConstant(y)) {
std::swap(x, y);
}
// Add, Constant now in x
int constValue = static_cast<int>(acc_.GetConstantValue(x));
if (op == TypedBinOp::TYPED_SUB) {
constValue = -constValue;
}
Bound *bound = GetBound(y);
if (bound == nullptr) {
LOG_ECMA(FATAL) << "ArrayBoundsCheckElimination::DoBinaryArithmeticOp:bound is nullptr";
UNREACHABLE();
}
if (!bound->HasUpper() || !bound->HasLower()) {
return chunk_->New<Bound>();
}
int lower = bound->Lower();
int upper = bound->Upper();
int newLower = lower + constValue;
int newUpper = upper + constValue;
bool overflow = ((constValue < 0 && (newLower > lower)) ||
(constValue > 0 && (newUpper < upper)));
if (overflow) {
return chunk_->New<Bound>();
} else {
return chunk_->New<Bound>(newLower, bound->LowerGate(), newUpper, bound->UpperGate());
}
} else if (op == TypedBinOp::TYPED_SUB) {
Bound *bound = GetBound(x);
if (bound == nullptr) {
LOG_ECMA(FATAL) << "ArrayBoundsCheckElimination::DoBinaryArithmeticOp:bound is nullptr";
UNREACHABLE();
}
if (bound->LowerGate() == y) {
return chunk_->New<Bound>(TypedBinOp::TYPED_GREATEREQ, Circuit::NullGate(), bound->Lower());
} else {
return chunk_->New<Bound>();
}
} else {
return chunk_->New<Bound>();
}
}
return nullptr;
}
ArrayBoundsCheckElimination::Bound *ArrayBoundsCheckElimination::DoUnaryArithmeticOp(GateRef gate)
{
auto op = acc_.GetTypedUnAccessor(gate).GetTypedUnOp();
auto x = FindBoundGate(acc_.GetValueIn(gate, 0));
int constValue = 0;
if (op == TypedUnOp::TYPED_INC) {
constValue = 1;
} else if (op == TypedUnOp::TYPED_DEC) {
constValue = -1;
} else {
return chunk_->New<Bound>();
}
Bound *bound = GetBound(x);
// only support which bounds has been calculated
if (!bound->HasUpper() || !bound->HasLower()) {
return chunk_->New<Bound>();
}
int lower = bound->Lower();
int upper = bound->Upper();
int newLower = lower + constValue;
int newUpper = upper + constValue;
bool overflow = ((constValue < 0 && (newLower > lower)) ||
(constValue > 0 && (newUpper < upper)));
if (overflow) {
return chunk_->New<Bound>();
} else {
return chunk_->New<Bound>(newLower, bound->LowerGate(), newUpper, bound->UpperGate());
}
}
bool ArrayBoundsCheckElimination::InLoop(GateRef loopHeader, GateRef gate)
{
while (gate != acc_.GetStateRoot()) {
if (gate == loopHeader) {
return true;
} else {
gate = acc_.GetState(gate, 0);
}
}
return false;
}
ArrayBoundsCheckElimination::Bound *ArrayBoundsCheckElimination::DoPhi(GateRef gate)
{
Bound *bound = nullptr;
size_t valueSize = acc_.GetInValueCount(gate);
GateRef stateIn = acc_.GetState(gate);
bool isLoopHead = acc_.IsLoopHead(stateIn);
bool hasUpper = true;
bool hasLower = true;
for (size_t i = 0; i < valueSize; i++) {
GateRef value = FindBoundGate(acc_.GetValueIn(gate, i));
// Check if instruction is connected with phi itself
if (isLoopHead && acc_.GetOpCode(value) == OpCode::TYPED_UNARY_OP
&& InLoop(stateIn, value)) {
auto unOp = acc_.GetTypedUnAccessor(value).GetTypedUnOp();
switch (unOp) {
case TypedUnOp::TYPED_INC: {
hasUpper = false;
break;
}
case TypedUnOp::TYPED_DEC: {
hasLower = false;
break;
}
default:
break;
}
continue;
}
Bound *vBound = GetBound(value);
if (vBound == nullptr) {
LOG_ECMA(FATAL) << "ArrayBoundsCheckElimination::DoPhi:vBound is nullptr";
UNREACHABLE();
}
Bound *curBound;
GateRef curGate;
int curConstant;
GetInstrAndConstValueFromOp(value, curGate, curConstant);
if (!vBound->HasUpper() || !vBound->HasLower()) {
curBound = chunk_->New<Bound>(curConstant, curGate, curConstant, curGate);
} else {
curBound = vBound;
}
if (curBound) {
if (!bound) {
bound = curBound->Copy(chunk_);
} else {
bound = OrOp(bound, curBound);
}
} else {
bound = chunk_->New<Bound>();
break;
}
}
if (!hasUpper) {
bound->RemoveUpper();
}
if (!hasLower) {
bound->RemoveLower();
}
return bound;
}
ArrayBoundsCheckElimination::Bound *ArrayBoundsCheckElimination::VisitGate(GateRef gate)
{
OpCode op = acc_.GetOpCode(gate);
switch (op) {
case OpCode::CONSTANT:
return DoConstant(gate);
case OpCode::TYPED_BINARY_OP:
return DoBinaryArithmeticOp(gate);
case OpCode::TYPED_UNARY_OP:
return DoUnaryArithmeticOp(gate);
case OpCode::VALUE_SELECTOR:
return DoPhi(gate);
default:
return nullptr;
}
return nullptr;
}
bool ArrayBoundsCheckElimination::GetInstrAndConstValueFromBinaryOp(GateRef gate, GateRef& other, int& value)
{
ASSERT(acc_.GetOpCode(gate) == OpCode::TYPED_BINARY_OP);
auto op = acc_.GetTypedBinaryOp(gate);
auto x = FindBoundGate(acc_.GetValueIn(gate, 0));
auto y = FindBoundGate(acc_.GetValueIn(gate, 1));
other = x;
if ((op == TypedBinOp::TYPED_ADD && (acc_.IsConstant(x) || acc_.IsConstant(y)))
|| (op == TypedBinOp::TYPED_SUB && acc_.IsConstant(y))) {
if (acc_.IsConstant(x)) {
value = static_cast<int>(acc_.GetConstantValue(x));
other = y;
} else {
value = static_cast<int>(acc_.GetConstantValue(y));
other = x;
}
if (op == TypedBinOp::TYPED_SUB) {
value = -value;
}
} else {
return false;
}
return true;
}
bool ArrayBoundsCheckElimination::GetInstrAndConstValueFromUnaryOp(GateRef gate, GateRef& other, int& value)
{
ASSERT(acc_.GetOpCode(gate) == OpCode::TYPED_UNARY_OP);
auto op = acc_.GetTypedUnAccessor(gate).GetTypedUnOp();
auto x = FindBoundGate(acc_.GetValueIn(gate, 0));
if (op == TypedUnOp::TYPED_INC) {
value = 1;
other = x;
} else if (op == TypedUnOp::TYPED_DEC) {
value = -1;
other = x;
} else {
return false;
}
return true;
}
void ArrayBoundsCheckElimination::GetInstrAndConstValueFromOp(GateRef gate, GateRef& instrValue, int& constValue)
{
int base = 0;
constValue = 0;
instrValue = gate;
if (acc_.IsConstant(gate)) {
constValue = static_cast<int>(acc_.GetConstantValue(gate));
instrValue = Circuit::NullGate();
return ;
}
while (acc_.GetOpCode(gate) == OpCode::TYPED_BINARY_OP ||
acc_.GetOpCode(gate) == OpCode::TYPED_UNARY_OP) {
bool flag = false;
int value = 0;
GateRef other = Circuit::NullGate();
if (acc_.GetOpCode(gate) == OpCode::TYPED_BINARY_OP) {
flag = GetInstrAndConstValueFromBinaryOp(gate, other, value);
} else if (acc_.GetOpCode(gate) == OpCode::TYPED_UNARY_OP) {
flag = GetInstrAndConstValueFromUnaryOp(gate, other, value);
}
if (!flag) {
break;
}
if (acc_.IsConstant(other)) {
base += value + static_cast<int>(acc_.GetConstantValue(other));
constValue = base;
instrValue = Circuit::NullGate();
break ;
} else {
base += value;
constValue = base;
instrValue = other;
gate = other;
}
}
}
ArrayBoundsCheckElimination::Bound *ArrayBoundsCheckElimination::GetBound(GateRef gate)
{
if (gate == Circuit::NullGate()) {
return nullptr;
}
if (!bounds_[acc_.GetId(gate)]) {
bounds_[acc_.GetId(gate)] = chunk_->New<BoundStack>(chunk_);
Bound *bound = VisitGate(gate);
if (bound) {
bounds_[acc_.GetId(gate)]->push_back(bound);
}
if (bounds_[acc_.GetId(gate)]->size() == 0) {
bounds_[acc_.GetId(gate)]->push_back(chunk_->New<Bound>());
}
} else if (bounds_[acc_.GetId(gate)]->size() == 0) {
return chunk_->New<Bound>();
}
return bounds_[acc_.GetId(gate)]->back();
}
void ArrayBoundsCheckElimination::UpdateBound(IntegerStack &pushed, GateRef gate, Bound *bound)
{
if (acc_.IsConstant(gate)) {
// No bound update for constants
return;
}
if (!bounds_[acc_.GetId(gate)]) {
GetBound(gate);
}
Bound* top = nullptr;
if (bounds_[acc_.GetId(gate)]->size() > 0) {
top = bounds_[acc_.GetId(gate)]->back();
}
if (top) {
bound = AndOp(bound, top);
}
bounds_[acc_.GetId(gate)]->push_back(bound);
pushed.push_back(acc_.GetId(gate));
}
/*
x op y + constValue
for example:
x >= Circuit::NullGate() + 0
x < Length + 0
*/
void ArrayBoundsCheckElimination::UpdateBound(IntegerStack &pushed, GateRef x, TypedBinOp op,
GateRef instrValue, int constValue)
{
if (op == TypedBinOp::TYPED_GREATER) { // x < 3 -> x <= 4
op = TypedBinOp::TYPED_GREATEREQ;
// Cannot Represent c > INT_MAX, do not update bounds
if (constValue == INT_MAX && instrValue == Circuit::NullGate()) {
return;
}
constValue++;
} else if (op == TypedBinOp::TYPED_LESS) { // x > 3 -> x >= 2
op = TypedBinOp::TYPED_LESSEQ;
// Cannot Represent c < INT_MIN, do not update bounds
if (constValue == INT_MIN && instrValue == Circuit::NullGate()) {
return;
}
constValue--;
}
Bound *bound = chunk_->New<Bound>(op, instrValue, constValue);
UpdateBound(pushed, x, bound);
}
// Add if condition when x is a variable, x op y
void ArrayBoundsCheckElimination::AddIfCondition(IntegerStack &pushed, GateRef x, GateRef y, TypedBinOp op)
{
if (acc_.IsConstant(x)) { // x must be non-constant!
return;
}
int constValue;
GateRef instrValue;
GetInstrAndConstValueFromOp(y, instrValue, constValue);
UpdateBound(pushed, x, op, instrValue, constValue);
}
bool ArrayBoundsCheckElimination::IsArrayLength(GateRef gate)
{
if (gate == Circuit::NullGate()) {
return false;
}
OpCode op = acc_.GetOpCode(gate);
switch (op) {
case OpCode::LOAD_ARRAY_LENGTH:
case OpCode::LOAD_TYPED_ARRAY_LENGTH:
return true;
default:
return false;
}
UNREACHABLE();
return false;
}
GateRef ArrayBoundsCheckElimination::FindBoundGate(GateRef gate)
{
OpCode op = acc_.GetOpCode(gate);
switch (op) {
// skip gate
case OpCode::RANGE_GUARD:
gate = FindBoundGate(acc_.GetValueIn(gate, 0));
break;
case OpCode::INDEX_CHECK: // get index
gate = FindBoundGate(acc_.GetValueIn(gate, 1));
break;
default:
break;
}
return gate;
}
bool ArrayBoundsCheckElimination::InArrayBound(Bound *bound, GateRef length, GateRef array)
{
if (!bound || array == Circuit::NullGate()) {
return false;
}
if (bound->Lower() >= 0 && bound->LowerGate() == Circuit::NullGate() &&
bound->Upper() < 0 && bound->UpperGate() != Circuit::NullGate()) {
if (length != Circuit::NullGate() && bound->UpperGate() == length) {
return true;
}
}
return false;
}
void ArrayBoundsCheckElimination::RemoveIndexCheck(GateRef gate)
{
ASSERT(acc_.GetDependCount(gate) == 1);
ASSERT(acc_.GetStateCount(gate) == 1);
ASSERT(acc_.GetInValueCount(gate) == 2); // 2: ValueCount
GateRef depend = acc_.GetDep(gate);
GateRef state = acc_.GetState(gate);
GateRef value = acc_.GetValueIn(gate, 1); // Index
acc_.ReplaceGate(gate, state, depend, value);
}
bool ArrayBoundsCheckElimination::CheckLoop(GateRef array, GateRef lowerGate, int lower, GateRef upperGate, int upper)
{
if (IsArrayLength(upperGate) && FindBoundGate(acc_.GetValueIn(upperGate, 0)) == array) {
if (upper >= 0) {
return false;
}
}
if (IsArrayLength(lowerGate) && FindBoundGate(acc_.GetValueIn(lowerGate, 0)) == array) {
if (lower >= 0) {
return false;
}
}
return true;
}
bool ArrayBoundsCheckElimination::LoopInvariant(GateRegion *loopHeader, GateRef gate)
{
if (gate == Circuit::NullGate()) {
return true;
}
auto gateRegion = graphLinearizer_.GateToRegion(gate);
GateRegion* g = loopHeader->GetDominator();
while (g != nullptr) {
if (g == gateRegion) {
return true;
}
if (g == g->GetDominator()) { // entry
break ;
}
g = g->GetDominator();
}
return false;
}
GateRef ArrayBoundsCheckElimination::Predicate(GateRef left, TypedBinOp cond, GateRef right)
{
return builder_.InsertRangeCheckPredicate(left, cond, right);
}
GateRef ArrayBoundsCheckElimination::PredicateCmpWithConst(GateRef left, TypedBinOp cond, int32_t right)
{
GateRef constGate = builder_.Int32(right);
return Predicate(left, cond, constGate);
}
GateRef ArrayBoundsCheckElimination::PredicateAdd(GateRef left, int32_t leftConst, TypedBinOp cond, GateRef right)
{
GateRef constGate = builder_.Int32(leftConst);
GateRef binaryOpGate = builder_.InsertTypedBinaryop(left, constGate, TypedBinOp::TYPED_ADD);
return Predicate(binaryOpGate, cond, right);
}
GateRef ArrayBoundsCheckElimination::PredicateAddCmpWithConst(GateRef left, int32_t leftConst,
TypedBinOp cond, int32_t right)
{
GateRef constGate = builder_.Int32(right);
return PredicateAdd(left, leftConst, cond, constGate);
}
void ArrayBoundsCheckElimination::LoopInvariantMotionForIndexCheck(GateRef array, GateRef length,
GateRef lengthMetaData, GateRef lowerGate, int lower, GateRef upperGate, int upper, bool isTypedArray)
{
// lower > 0
if (lowerGate != Circuit::NullGate()) {
if (lower == 0) {
// lowerGate >= 0
PredicateCmpWithConst(lowerGate, TypedBinOp::TYPED_GREATEREQ, 0);
} else if (lower > 0) {
// lowerGate + lower >= 0
PredicateAddCmpWithConst(lowerGate, lower, TypedBinOp::TYPED_GREATEREQ, 0);
} else {
// lowerGate + lower < 0
// lower < 0
// lowerGate < -lower
lower++;
lower = -lower;
PredicateCmpWithConst(lowerGate, TypedBinOp::TYPED_GREATER, lower);
}
}
// LOAD LENGTH if necessary
if (length == Circuit::NullGate()) {
length = builder_.InsertLoadArrayLength(array, lengthMetaData, isTypedArray);
}
if (upperGate == Circuit::NullGate()) {
ASSERT(upper >= 0);
PredicateCmpWithConst(length, TypedBinOp::TYPED_GREATER, upper);
} else {
if (upper == 0) {
Predicate(upperGate, TypedBinOp::TYPED_LESS, length);
} else if (upper > 0) {
// upperGate + upper < length
PredicateAdd(upperGate, upper, TypedBinOp::TYPED_LESS, length);
} else {
// upperGate + upper < length
// upper < 0
// upperGate < length + (-upper)
PredicateAdd(length, -upper, TypedBinOp::TYPED_GREATER, upperGate);
}
}
}
void ArrayBoundsCheckElimination::ProcessIndexCheck(GateRegion *loopHeader, GateRef gate)
{
auto length = FindBoundGate(acc_.GetValueIn(gate, 0));
auto array = FindBoundGate(acc_.GetValueIn(length, 0));
auto index = FindBoundGate(acc_.GetValueIn(gate, 1));
Bound *indexBound = GetBound(index);
if (!indexBound->HasLower() || !indexBound->HasUpper()) {
return;
}
if (InArrayBound(indexBound, length, array)) {
RemoveIndexCheck(gate);
} else if (loopHeader) {
if (!LoopInvariant(loopHeader, array)
|| !LoopInvariant(loopHeader, indexBound->LowerGate())
|| !LoopInvariant(loopHeader, indexBound->UpperGate())
|| (indexBound->LowerGate() == Circuit::NullGate() && indexBound->Lower() < 0)
|| (indexBound->UpperGate() == Circuit::NullGate() && indexBound->Upper() < 0)) {
return;
}
ASSERT(length != Circuit::NullGate());
GateRef lengthMetaData = length;
bool isTypedArray = false;
if (acc_.GetOpCode(length) == OpCode::LOAD_TYPED_ARRAY_LENGTH) {
isTypedArray = true;
}
// Length instrution
if (!LoopInvariant(loopHeader, length)) {
// Generate length instruction yourself
length = Circuit::NullGate();
}
// Insert Before loopHeader State, and if find IF_TRUE and IF_FALSE, insert after the DEPEND_RELAY
// if find MERGE, insert after DEPEND_SELECTOR
GateRef insertAfter = acc_.GetState(loopHeader->GetState(), 0); // after end
GateRef stateIn = insertAfter;
GateRef dependIn = insertAfter;
acc_.GetStateInAndDependIn(insertAfter, stateIn, dependIn);
if (!CheckLoop(array, indexBound->LowerGate(), indexBound->Lower(),
indexBound->UpperGate(), indexBound->Upper())) {
return;
}
Environment env(stateIn, dependIn, {}, circuit_, &builder_);
LoopInvariantMotionForIndexCheck(array, length, lengthMetaData, indexBound->LowerGate(),
indexBound->Lower(), indexBound->UpperGate(), indexBound->Upper(), isTypedArray);
RemoveIndexCheck(gate);
}
}
void ArrayBoundsCheckElimination::ProcessIf(IntegerStack &pushed, GateRegion *parent, OpCode cond)
{
auto& gateLists = parent->GetGates();
for (int i = static_cast<int>(gateLists.size()) - 1; i >= 0; i--) { // Found the last BinaryOp
GateRef gate = gateLists[i];
if (gate == Circuit::NullGate()) continue;
OpCode opGate = acc_.GetOpCode(gate);
if (opGate != OpCode::TYPED_BINARY_OP) {
continue ;
}
TypedBinOp op = acc_.GetTypedBinaryOp(gate);
GateRef x = FindBoundGate(acc_.GetValueIn(gate, 0));
GateRef y = FindBoundGate(acc_.GetValueIn(gate, 1));
switch (op) {
case TypedBinOp::TYPED_LESS:
case TypedBinOp::TYPED_LESSEQ:
case TypedBinOp::TYPED_GREATER:
case TypedBinOp::TYPED_GREATEREQ:
case TypedBinOp::TYPED_EQ:
case TypedBinOp::TYPED_NOTEQ: {
if (cond == OpCode::IF_TRUE) {
op = acc_.GetRevCompareOpForTypedBinOp(op);
}
AddIfCondition(pushed, x, y, op);
AddIfCondition(pushed, y, x, acc_.GetSwapCompareOpForTypedBinOp(op));
break;
}
default:
break;
}
break;
}
}
bool ArrayBoundsCheckElimination::Contain(GateLists &gateLists, GateRef gate)
{
for (size_t i = 0; i < gateLists.size(); i++) {
if (gateLists[i] == gate) {
return true;
}
}
return false;
}
void ArrayBoundsCheckElimination::AddAccessIndexedInfo(GateLists &indices, GateRef gate, int idx, GateRef indexCheck)
{
IndexCheckInfo *indexCheckInfo = indexCheckInfo_[acc_.GetId(gate)];
if (indexCheckInfo == nullptr) {
indexCheckInfo = chunk_->New<IndexCheckInfo>(chunk_);
indexCheckInfo_[acc_.GetId(gate)] = indexCheckInfo;
indices.push_back(gate);
indexCheckInfo->min_ = idx;
indexCheckInfo->max_ = idx;
} else if (idx >= indexCheckInfo->min_ && idx <= indexCheckInfo->max_) {
RemoveIndexCheck(indexCheck);
return;
}
indexCheckInfo->min_ = std::min(indexCheckInfo->min_, idx);
indexCheckInfo->max_ = std::max(indexCheckInfo->max_, idx);
indexCheckInfo->list_.push_back(indexCheck);
}
void ArrayBoundsCheckElimination::InBlockMotion(GateLists &indexChecked, GateLists &arrays)
{
GateLists indices(chunk_);
for (size_t i = 0; i < arrays.size(); i++) {
int maxConstant = -1;
GateLists listConstant(chunk_);
GateRef arrayGate = arrays[i];
for (size_t j = 0; j < indexChecked.size(); j++) {
GateRef indexCheck = indexChecked[j];
// INDEX_CHECK may be dead
if (acc_.GetOpCode(indexCheck) != OpCode::INDEX_CHECK) {
continue;
}
GateRef length = FindBoundGate(acc_.GetValueIn(indexCheck, 0));
GateRef index = FindBoundGate(acc_.GetValueIn(indexCheck, 1));
GateRef array = FindBoundGate(acc_.GetValueIn(length, 0));
if (array != arrayGate) {
continue;
}
if (acc_.IsConstant(index)) {
int constValue = static_cast<int>(acc_.GetConstantValue(index));
if (constValue >= 0 && constValue <= maxConstant) {
RemoveIndexCheck(indexCheck);
} else if (constValue >= 0 && constValue > maxConstant) {
maxConstant = constValue;
listConstant.push_back(indexCheck);
}
} else {
int lastInteger;
GateRef lastGate;
GetInstrAndConstValueFromOp(index, lastGate, lastInteger);
if (lastInteger >= 0 && lastGate == Circuit::NullGate()) { // IsConstant
if (lastInteger <= maxConstant) {
RemoveIndexCheck(indexCheck);
} else {
maxConstant = lastInteger;
listConstant.push_back(indexCheck);
}
} else if (lastGate != Circuit::NullGate()) {
AddAccessIndexedInfo(indices, lastGate, lastInteger, indexCheck);
} // when lastInteger < 0, dont remove IndexCheck
}
}
// Iterate over all different indices
for (size_t j = 0; j < indices.size(); j++) {
GateRef index = indices[j];
IndexCheckInfo *info = indexCheckInfo_[acc_.GetId(index)];
ASSERT(info != nullptr);
// maybe index < 0, max > 0
// max + index in [0, a.length)
// min + index overflow !!!, min + index > 0
// so, min + index >= INT_MIN, min >= INT_MIN - index
// max in [-index, a.length - index)
// min >= INT_MIN + max
bool rangeCond = (info->max_ < 0 || info->max_ + INT_MIN <= info->min_);
if (info->list_.size() > 2 && rangeCond) { // 2: size
GateRef insertAfter = info->list_.front();
GateRef length = FindBoundGate(acc_.GetValueIn(insertAfter, 0));
ASSERT(length != Circuit::NullGate());
Environment env(insertAfter, circuit_, &builder_);
// Calculate lower bound
GateRef lowerCompare = index;
if (info->min_ > 0) {
GateRef minGate = builder_.Int32(info->min_);
lowerCompare = builder_.InsertTypedBinaryop(lowerCompare, minGate, TypedBinOp::TYPED_ADD);
} else if (info->min_ < 0) {
GateRef minGate = builder_.Int32(-info->min_);
lowerCompare = builder_.InsertTypedBinaryop(lowerCompare, minGate, TypedBinOp::TYPED_SUB);
}
PredicateCmpWithConst(lowerCompare, TypedBinOp::TYPED_GREATEREQ, 0);
// Calculate upper bound
GateRef upperCompare = index;
if (info->max_ != 0) {
if (info->max_ > 0) {
GateRef maxGate = builder_.Int32(info->max_);
upperCompare = builder_.InsertTypedBinaryop(upperCompare, maxGate, TypedBinOp::TYPED_ADD);
} else if (info->max_ < 0) {
GateRef maxGate = builder_.Int32(-info->max_);
upperCompare = builder_.InsertTypedBinaryop(upperCompare, maxGate, TypedBinOp::TYPED_SUB);
}
}
Predicate(upperCompare, TypedBinOp::TYPED_LESS, length);
for (auto& indexCheck: (info->list_)) {
RemoveIndexCheck(indexCheck);
}
}
}
// index only constant
if (listConstant.size() > 1) {
GateRef firIndexCheckGate = listConstant.front();
Environment env(firIndexCheckGate, circuit_, &builder_);
GateRef length = FindBoundGate(acc_.GetValueIn(firIndexCheckGate, 0));
ASSERT(length != Circuit::NullGate());
ASSERT(maxConstant >= 0);
PredicateCmpWithConst(length, TypedBinOp::TYPED_GREATER, maxConstant); // length > index
for (size_t j = 0; j < listConstant.size(); j++) {
GateRef indexCheck = listConstant[j];
RemoveIndexCheck(indexCheck);
}
}
for (size_t j = 0; j < indices.size(); j++) {
indexCheckInfo_[acc_.GetId(indices[j])] = nullptr;
}
indices.clear();
}
}
void ArrayBoundsCheckElimination::CalcBounds(GateRegion *block, GateRegion *loopHeader)
{
// Pushed stack for condition
IntegerStack pushed(chunk_);
// Process If
GateRegion *parent = block->GetDominator();
if (parent != nullptr) {
auto gate = block->GetGates().front();
auto op = acc_.GetOpCode(gate);
if (op == OpCode::IF_TRUE || op == OpCode::IF_FALSE) { // Recognize If (including the condition in forloop)
ProcessIf(pushed, parent, op);
}
}
GateLists indexChecked(chunk_);
GateLists arrays(chunk_);
auto& gateList_ = block->GetGates();
for (size_t i = 0; i < gateList_.size(); i++) { // Visit GateUnion
GateRef gate = gateList_[i];
auto op = acc_.GetOpCode(gate);
if (op == OpCode::INDEX_CHECK) {
auto length = FindBoundGate(acc_.GetValueIn(gate, 0));
auto index = FindBoundGate(acc_.GetValueIn(gate, 1));
auto array = FindBoundGate(acc_.GetValueIn(length, 0));
ProcessIndexCheck(loopHeader, gate);
indexChecked.push_back(gate);
if (!Contain(arrays, array)) {
arrays.push_back(array);
}
// Give IndexCheck a bound [0, Length - 1]
Bound *b = GetBound(index);
if (b->LowerGate() == Circuit::NullGate()) { // LowerBound is the Constant !!!
UpdateBound(pushed, index, TypedBinOp::TYPED_GREATEREQ, Circuit::NullGate(), 0);
}
if (!b->HasUpper() && length != Circuit::NullGate()) { // default dont know the Length
UpdateBound(pushed, index, TypedBinOp::TYPED_LESS, length, 0);
}
}
}
InBlockMotion(indexChecked, arrays);
auto& dominatedRegions_ = block->GetDominatedRegions();
for (size_t i = 0; i < dominatedRegions_.size(); i++) {
GateRegion *nex = dominatedRegions_[i];
if (block->IsLoopHead() && (block->GetLoopIndex() == nex->GetLoopIndex()
|| nex->GetLoopDepth() > block->GetLoopDepth())) {
CalcBounds(nex, block);
} else {
CalcBounds(nex, loopHeader);
}
}
for (size_t i = 0; i < pushed.size(); i++) {
bounds_[pushed[i]]->pop_back();
}
}
}