5889aac7创建于 2024年8月29日历史提交
/*
 * 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();
    }
}
}