mirror of
https://gitee.com/openharmony/arkcompiler_ets_runtime
synced 2025-02-26 07:16:57 +00:00
Array Bounds Check Elimination - Part 2
(Loop-Invariant Checks + Grouping Checks) Issue: https://gitee.com/openharmony/arkcompiler_ets_runtime/issues/I7V12I Signed-off-by: liuzhijie <jay.lau2020.work@outlook.com> Change-Id: I584c54a9ed5c472f43f8a83d3fdab6c588db5898
This commit is contained in:
parent
d37c289fd8
commit
39e0d61227
@ -12,16 +12,18 @@
|
||||
* 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());
|
||||
CalcBounds(graphLinearizer_.GetEntryRegion(), nullptr);
|
||||
|
||||
if (IsLogEnabled()) {
|
||||
LOG_COMPILER(INFO) << "";
|
||||
@ -152,6 +154,26 @@ ArrayBoundsCheckElimination::Bound *ArrayBoundsCheckElimination::AndOp(Bound *bo
|
||||
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 = acc_.GetConstantValue(gate);
|
||||
@ -164,7 +186,32 @@ ArrayBoundsCheckElimination::Bound *ArrayBoundsCheckElimination::DoArithmeticOp(
|
||||
auto x = acc_.GetValueIn(gate, 0);
|
||||
auto y = acc_.GetValueIn(gate, 1);
|
||||
if (!acc_.IsConstant(x) || !acc_.IsConstant(y)) { // One of the operands must be non-constant!
|
||||
if (((acc_.IsConstant(x) || acc_.IsConstant(y)) && op == TypedBinOp::TYPED_ADD) ||
|
||||
if (op == TypedBinOp::TYPED_AND && (acc_.IsConstant(x) || acc_.IsConstant(y))) {
|
||||
int constValue = 0;
|
||||
if (acc_.IsConstant(x)) {
|
||||
constValue = acc_.GetConstantValue(x);
|
||||
} else {
|
||||
constValue = acc_.GetConstantValue(y);
|
||||
}
|
||||
if (constValue >= 0) {
|
||||
return 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 new Bound(0, Circuit::NullGate(), -1, y);
|
||||
} else if (xBound->HasLower() && xBound->Lower() >= 0 && acc_.IsConstant(y)
|
||||
&& acc_.GetConstantValue(y) != 0) {
|
||||
int constValue = acc_.GetConstantValue(y);
|
||||
if (constValue != INT_MIN) {
|
||||
return new Bound(0, Circuit::NullGate(), abs(constValue) - 1, Circuit::NullGate());
|
||||
} else {
|
||||
return new Bound();
|
||||
}
|
||||
} else {
|
||||
return 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)) {
|
||||
@ -193,18 +240,94 @@ ArrayBoundsCheckElimination::Bound *ArrayBoundsCheckElimination::DoArithmeticOp(
|
||||
} else {
|
||||
return new Bound(newLower, bound->LowerGate(), newUpper, bound->UpperGate());
|
||||
}
|
||||
} else {
|
||||
} else if (op == TypedBinOp::TYPED_SUB) {
|
||||
Bound *bound = GetBound(x);
|
||||
if (op == TypedBinOp::TYPED_SUB && bound->LowerGate() == y) {
|
||||
if (bound->LowerGate() == y) {
|
||||
return new Bound(TypedBinOp::TYPED_GREATEREQ, Circuit::NullGate(), bound->Lower());
|
||||
} else {
|
||||
return new Bound();
|
||||
}
|
||||
} else {
|
||||
return new Bound();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ArrayBoundsCheckElimination::InLoop(GateRef loopHeader, GateRef gate)
|
||||
{
|
||||
while (gate != acc_.GetStateRoot()) {
|
||||
if (gate == loopHeader) {
|
||||
return true;
|
||||
} else {
|
||||
gate = acc_.GetState(gate, 0);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
Do phi
|
||||
*/
|
||||
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 = 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);
|
||||
Bound *curBound;
|
||||
GateRef curGate;
|
||||
int curConstant;
|
||||
GetInstrAndConstValueFromOp(value, curGate, curConstant);
|
||||
if (!vBound->HasUpper() || !vBound->HasLower()) {
|
||||
curBound = new Bound(curConstant, curGate, curConstant, curGate);
|
||||
} else {
|
||||
curBound = vBound;
|
||||
}
|
||||
|
||||
if (curBound) {
|
||||
if (!bound) {
|
||||
bound = curBound->Copy();
|
||||
} else {
|
||||
bound = OrOp(bound, curBound);
|
||||
}
|
||||
} else {
|
||||
bound = new Bound();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasUpper) {
|
||||
bound->RemoveUpper();
|
||||
}
|
||||
if (!hasLower) {
|
||||
bound->RemoveLower();
|
||||
}
|
||||
return bound;
|
||||
}
|
||||
|
||||
ArrayBoundsCheckElimination::Bound *ArrayBoundsCheckElimination::VisitGate(GateRef gate)
|
||||
{
|
||||
OpCode op = acc_.GetOpCode(gate);
|
||||
@ -213,12 +336,66 @@ ArrayBoundsCheckElimination::Bound *ArrayBoundsCheckElimination::VisitGate(GateR
|
||||
return DoConstant(gate);
|
||||
case OpCode::TYPED_BINARY_OP:
|
||||
return DoArithmeticOp(gate);
|
||||
case OpCode::VALUE_SELECTOR:
|
||||
return DoPhi(gate);
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// y = a + b - c .....
|
||||
void ArrayBoundsCheckElimination::GetInstrAndConstValueFromOp(GateRef gate, GateRef& instrValue, int& constValue)
|
||||
{
|
||||
int base = 0;
|
||||
constValue = 0;
|
||||
instrValue = gate;
|
||||
if (acc_.IsConstant(gate)) {
|
||||
constValue = acc_.GetConstantValue(gate);
|
||||
instrValue = Circuit::NullGate();
|
||||
} else {
|
||||
while (acc_.GetOpCode(gate) == OpCode::TYPED_BINARY_OP) {
|
||||
auto op = acc_.GetTypedBinaryOp(gate);
|
||||
auto x = acc_.GetValueIn(gate, 0);
|
||||
auto y = acc_.GetValueIn(gate, 1);
|
||||
GateRef other = x;
|
||||
if ((op == TypedBinOp::TYPED_ADD && (acc_.IsConstant(x) || acc_.IsConstant(y)))
|
||||
|| (op == TypedBinOp::TYPED_SUB && acc_.IsConstant(y))) {
|
||||
int value = 0;
|
||||
if (acc_.IsConstant(x)) {
|
||||
value = acc_.GetConstantValue(x);
|
||||
other = y;
|
||||
} else {
|
||||
value = acc_.GetConstantValue(y);
|
||||
other = x;
|
||||
}
|
||||
|
||||
while (acc_.GetOpCode(other) == OpCode::INDEX_CHECK) { // Get IndexCheck Index
|
||||
other = acc_.GetValueIn(other, 1);
|
||||
}
|
||||
|
||||
if (op == TypedBinOp::TYPED_SUB) {
|
||||
value = -value;
|
||||
}
|
||||
|
||||
if (acc_.IsConstant(other)) {
|
||||
base += value + acc_.GetConstantValue(other);
|
||||
constValue = base;
|
||||
instrValue = Circuit::NullGate();
|
||||
break ;
|
||||
} else {
|
||||
base += value;
|
||||
constValue = base;
|
||||
instrValue = other;
|
||||
gate = other;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArrayBoundsCheckElimination::Bound *ArrayBoundsCheckElimination::GetBound(GateRef gate)
|
||||
{
|
||||
if (gate == Circuit::NullGate()) {
|
||||
@ -295,42 +472,43 @@ void ArrayBoundsCheckElimination::AddIfCondition(IntegerStack &pushed, GateRef x
|
||||
if (acc_.IsConstant(x)) { // x must be non-constant!
|
||||
return;
|
||||
}
|
||||
int constValue = 0;
|
||||
GateRef instrValue = y;
|
||||
if (acc_.IsConstant(y)) {
|
||||
constValue = acc_.GetConstantValue(y);
|
||||
instrValue = Circuit::NullGate();
|
||||
} else if (acc_.GetOpCode(y) == OpCode::TYPED_BINARY_OP) {
|
||||
auto binaryOp = acc_.GetTypedBinaryOp(y);
|
||||
auto a = acc_.GetValueIn(y, 0);
|
||||
auto b = acc_.GetValueIn(y, 1);
|
||||
if (binaryOp == TypedBinOp::TYPED_ADD) {
|
||||
if (acc_.IsConstant(a)) {
|
||||
constValue = acc_.GetConstantValue(a);
|
||||
instrValue = b;
|
||||
} else if (acc_.IsConstant(b)) {
|
||||
constValue = acc_.GetConstantValue(b);
|
||||
instrValue = a;
|
||||
}
|
||||
} else if (binaryOp == TypedBinOp::TYPED_SUB && acc_.IsConstant(b)) { // x >= a op b
|
||||
constValue = -acc_.GetConstantValue(b);
|
||||
instrValue = a;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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 (bound->UpperGate() == array || (length != Circuit::NullGate() && bound->UpperGate() == length)) {
|
||||
if (length != Circuit::NullGate() && bound->UpperGate() == length) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Solve Multidimensional array. To fix a[i][j] case, maybe LOAD_ELEMENT are not the same one.
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -344,25 +522,120 @@ void ArrayBoundsCheckElimination::RemoveIndexCheck(GateRef gate)
|
||||
GateRef state = acc_.GetState(gate);
|
||||
GateRef value = acc_.GetValueIn(gate, 1); // Index
|
||||
|
||||
auto uses = acc_.Uses(gate);
|
||||
for (auto it = uses.begin(); it != uses.end();) {
|
||||
if (acc_.IsStateIn(it)) {
|
||||
ASSERT(state != Circuit::NullGate());
|
||||
it = acc_.ReplaceIn(it, state);
|
||||
} else if (acc_.IsDependIn(it)) {
|
||||
ASSERT(depend != Circuit::NullGate());
|
||||
it = acc_.ReplaceIn(it, depend);
|
||||
} else if (acc_.IsValueIn(it)) {
|
||||
ASSERT(value != Circuit::NullGate());
|
||||
it = acc_.ReplaceIn(it, value);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
acc_.DeleteGate(gate);
|
||||
acc_.ReplaceGate(gate, state, depend, value);
|
||||
}
|
||||
|
||||
void ArrayBoundsCheckElimination::ProcessIndexCheck(GateRef gate)
|
||||
bool ArrayBoundsCheckElimination::CheckLoop(GateRef array, GateRef lowerGate, int lower, GateRef upperGate, int upper)
|
||||
{
|
||||
if (IsArrayLength(upperGate) && acc_.GetValueIn(upperGate, 0) == array) {
|
||||
if (upper >= 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (IsArrayLength(lowerGate) && 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);
|
||||
if (!gateRegion) {
|
||||
return true;
|
||||
}
|
||||
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, GateType::NumberType(),
|
||||
GateType::NumberType(), GateType::AnyType(),
|
||||
PGOSampleType::NoneType(), 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 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, 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 = acc_.GetValueIn(gate, 0);
|
||||
auto array = acc_.GetValueIn(length, 0);
|
||||
@ -371,7 +644,45 @@ void ArrayBoundsCheckElimination::ProcessIndexCheck(GateRef gate)
|
||||
if (!indexBound->HasLower() || !indexBound->HasUpper()) {
|
||||
return;
|
||||
}
|
||||
if (InArrayBound(indexBound, array, length)) {
|
||||
|
||||
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());
|
||||
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, indexBound->LowerGate(), indexBound->Lower(),
|
||||
indexBound->UpperGate(), indexBound->Upper(), isTypedArray);
|
||||
RemoveIndexCheck(gate);
|
||||
}
|
||||
}
|
||||
@ -379,10 +690,13 @@ void ArrayBoundsCheckElimination::ProcessIndexCheck(GateRef gate)
|
||||
void ArrayBoundsCheckElimination::ProcessIf(IntegerStack &pushed, GateRegion *parent, OpCode cond)
|
||||
{
|
||||
auto& gateLists = parent->GetGates();
|
||||
for (size_t i = gateLists.size() - 1; i >= 0; i--) { // Found the last BinaryOp
|
||||
for (int i = 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;
|
||||
if (opGate != OpCode::TYPED_BINARY_OP) {
|
||||
continue ;
|
||||
}
|
||||
|
||||
TypedBinOp op = acc_.GetTypedBinaryOp(gate);
|
||||
GateRef x = acc_.GetValueIn(gate, 0);
|
||||
@ -408,7 +722,7 @@ void ArrayBoundsCheckElimination::ProcessIf(IntegerStack &pushed, GateRegion *pa
|
||||
}
|
||||
}
|
||||
|
||||
bool ArrayBoundsCheckElimination::Contain(ChunkVector<GateRef>& gateLists, GateRef gate)
|
||||
bool ArrayBoundsCheckElimination::Contain(GateLists &gateLists, GateRef gate)
|
||||
{
|
||||
for (size_t i = 0; i < gateLists.size(); i++) {
|
||||
if (gateLists[i] == gate) {
|
||||
@ -418,7 +732,154 @@ bool ArrayBoundsCheckElimination::Contain(ChunkVector<GateRef>& gateLists, GateR
|
||||
return false;
|
||||
}
|
||||
|
||||
void ArrayBoundsCheckElimination::CalcBounds(GateRegion *block)
|
||||
void ArrayBoundsCheckElimination::AddAccessIndexedInfo(GateLists &indices, GateRef gate, int idx, GateRef indexCheck)
|
||||
{
|
||||
IndexCheckInfo *indexCheckInfo = indexCheckInfo_[acc_.GetId(gate)];
|
||||
if (indexCheckInfo == nullptr) {
|
||||
indexCheckInfo = 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 = acc_.GetValueIn(indexCheck, 0);
|
||||
GateRef index = acc_.GetValueIn(indexCheck, 1);
|
||||
GateRef array = acc_.GetValueIn(length, 0);
|
||||
if (array != arrayGate) {
|
||||
continue;
|
||||
}
|
||||
if (acc_.IsConstant(index)) {
|
||||
int constValue = 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) {
|
||||
GateRef insertAfter = info->list_.front();
|
||||
GateRef length = 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,
|
||||
GateType::NumberType(), GateType::NumberType(),
|
||||
GateType::AnyType(), PGOSampleType::NoneType(),
|
||||
TypedBinOp::TYPED_ADD);
|
||||
} else if (info->min_ < 0) {
|
||||
GateRef minGate = builder_.Int32(-info->min_);
|
||||
lowerCompare = builder_.InsertTypedBinaryop(lowerCompare, minGate,
|
||||
GateType::NumberType(), GateType::NumberType(),
|
||||
GateType::AnyType(), PGOSampleType::NoneType(),
|
||||
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,
|
||||
GateType::NumberType(), GateType::NumberType(),
|
||||
GateType::AnyType(), PGOSampleType::NoneType(),
|
||||
TypedBinOp::TYPED_ADD);
|
||||
} else if (info->max_ < 0) {
|
||||
GateRef maxGate = builder_.Int32(-info->max_);
|
||||
upperCompare = builder_.InsertTypedBinaryop(upperCompare, maxGate,
|
||||
GateType::NumberType(), GateType::NumberType(),
|
||||
GateType::AnyType(), PGOSampleType::NoneType(),
|
||||
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 = 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_);
|
||||
@ -428,13 +889,13 @@ void ArrayBoundsCheckElimination::CalcBounds(GateRegion *block)
|
||||
if (parent != nullptr) {
|
||||
auto gate = block->GetGates().front();
|
||||
auto op = acc_.GetOpCode(gate);
|
||||
if (op == OpCode::IF_TRUE || op == OpCode::IF_FALSE) { // Recognize If
|
||||
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_);
|
||||
GateLists indexChecked(chunk_);
|
||||
GateLists arrays(chunk_);
|
||||
|
||||
auto& gateList_ = block->GetGates();
|
||||
for (size_t i = 0; i < gateList_.size(); i++) { // Visit GateUnion
|
||||
@ -445,11 +906,11 @@ void ArrayBoundsCheckElimination::CalcBounds(GateRegion *block)
|
||||
auto index = acc_.GetValueIn(gate, 1);
|
||||
auto array = acc_.GetValueIn(length, 0);
|
||||
|
||||
ProcessIndexCheck(gate);
|
||||
IndexChecked.push_back(gate);
|
||||
ProcessIndexCheck(loopHeader, gate);
|
||||
indexChecked.push_back(gate);
|
||||
|
||||
if (!Contain(Arrays, array)) {
|
||||
Arrays.push_back(array);
|
||||
if (!Contain(arrays, array)) {
|
||||
arrays.push_back(array);
|
||||
}
|
||||
|
||||
// Give IndexCheck a bound [0, Length - 1]
|
||||
@ -462,11 +923,18 @@ void ArrayBoundsCheckElimination::CalcBounds(GateRegion *block)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InBlockMotion(indexChecked, arrays);
|
||||
|
||||
auto& dominatedRegions_ = block->GetDominatedRegions();
|
||||
for (size_t i = 0; i < dominatedRegions_.size(); i++) {
|
||||
GateRegion *nex = dominatedRegions_[i];
|
||||
CalcBounds(nex);
|
||||
if (block->IsLoopHead() && (block->GetInnerLoopIndex() == nex->GetInnerLoopIndex()
|
||||
|| nex->GetLoopDepth() > block->GetLoopDepth())) {
|
||||
CalcBounds(nex, block);
|
||||
} else {
|
||||
CalcBounds(nex, loopHeader);
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < pushed.size(); i++) {
|
||||
|
@ -12,6 +12,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef ECMASCRIPT_COMPILER_ARRAY_BOUNDS_CHECK_ELIMINATION_H
|
||||
#define ECMASCRIPT_COMPILER_ARRAY_BOUNDS_CHECK_ELIMINATION_H
|
||||
|
||||
@ -26,8 +27,8 @@ namespace panda::ecmascript::kungfu {
|
||||
class ArrayBoundsCheckElimination {
|
||||
public:
|
||||
ArrayBoundsCheckElimination(Circuit *circuit, bool enableLog, const std::string& name, Chunk* chunk)
|
||||
: acc_(circuit), bounds_(chunk), circuit_(circuit), chunk_(chunk), enableLog_(enableLog),
|
||||
graphLinearizer_(circuit, enableLog, name, chunk, true), methodName_(name) {}
|
||||
: acc_(circuit), bounds_(chunk), circuit_(circuit), builder_(circuit), chunk_(chunk), enableLog_(enableLog),
|
||||
graphLinearizer_(circuit, enableLog, name, chunk, true), methodName_(name), indexCheckInfo_(chunk) {}
|
||||
|
||||
~ArrayBoundsCheckElimination() = default;
|
||||
void Run();
|
||||
@ -64,6 +65,16 @@ private:
|
||||
{
|
||||
return lowerGate_ != Circuit::NullGate() || lower_ > INT_MIN;
|
||||
}
|
||||
void RemoveUpper()
|
||||
{
|
||||
upperGate_ = Circuit::NullGate();
|
||||
upper_ = INT_MAX;
|
||||
}
|
||||
void RemoveLower()
|
||||
{
|
||||
lowerGate_ = Circuit::NullGate();
|
||||
lower_ = INT_MIN;
|
||||
}
|
||||
bool IsSmaller(Bound *b)
|
||||
{
|
||||
if (b->LowerGate() != upperGate_) {
|
||||
@ -71,6 +82,10 @@ private:
|
||||
}
|
||||
return upper_ < b->Lower();
|
||||
}
|
||||
Bound* Copy()
|
||||
{
|
||||
return new Bound(lower_, lowerGate_, upper_, upperGate_);
|
||||
}
|
||||
|
||||
private:
|
||||
int upper_;
|
||||
@ -93,33 +108,62 @@ private:
|
||||
|
||||
typedef ChunkVector<Bound*> BoundStack;
|
||||
typedef ChunkVector<BoundStack*> BoundMap;
|
||||
typedef ChunkVector<GateRef> IndexCheckList;
|
||||
typedef ChunkVector<GateRef> GateLists;
|
||||
typedef ChunkVector<int> IntegerStack;
|
||||
|
||||
typedef ChunkVector<GateRef> GateLists;
|
||||
|
||||
void AddAccessIndexedInfo(GateLists &indices, GateRef gate, int idx, GateRef indexCheck);
|
||||
void AddIfCondition(IntegerStack &pushed, GateRef x, GateRef y, TypedBinOp op);
|
||||
Bound *AndOp(Bound *bound, Bound *b);
|
||||
bool Contain(ChunkVector<GateRef>& gateLists, GateRef gate);
|
||||
void CalcBounds(GateRegion *block);
|
||||
Bound *OrOp(Bound *bound, Bound *b);
|
||||
bool Contain(GateLists& gateLists, GateRef gate);
|
||||
void CalcBounds(GateRegion *block, GateRegion *loopHeader);
|
||||
bool CheckLoop(GateRef array, GateRef lowerGate, int lower, GateRef upperGate, int upper);
|
||||
void InBlockMotion(GateLists &indexChecked, GateLists &arrays);
|
||||
bool InLoop(GateRef loopHeader, GateRef gate);
|
||||
bool IsArrayLength(GateRef gate);
|
||||
bool LoopInvariant(GateRegion *loopHeader, GateRef gate);
|
||||
void UpdateBound(IntegerStack &pushed, GateRef gate, Bound *bound);
|
||||
void UpdateBound(IntegerStack &pushed, GateRef x, TypedBinOp op, GateRef y, int constValue);
|
||||
void ProcessIndexCheck(GateRef gate);
|
||||
void ProcessIndexCheck(GateRegion *loopHeader, GateRef gate);
|
||||
void RemoveIndexCheck(GateRef gate);
|
||||
void CopyStateInAndDependIn(GateRef &stateIn, GateRef &dependIn, GateRef insertAfter);
|
||||
void LoopInvariantMotionForIndexCheck(GateRef array, GateRef length, GateRef lowerGate, int lower,
|
||||
GateRef upperGate, int upper, bool isTypedArray);
|
||||
void GetInstrAndConstValueFromOp(GateRef gate, GateRef &instrValue, int& constValue);
|
||||
Bound *GetBound(GateRef gate);
|
||||
Bound *DoConstant(GateRef gate);
|
||||
Bound *DoArithmeticOp(GateRef gate);
|
||||
Bound *DoPhi(GateRef gate);
|
||||
void SetBound(GateRef gate, Bound *bound);
|
||||
void ProcessIf(IntegerStack &pushed, GateRegion *parent, OpCode cond);
|
||||
bool InArrayBound(Bound *bound, GateRef Length, GateRef Array);
|
||||
bool InArrayBound(Bound *bound, GateRef length, GateRef array);
|
||||
Bound *VisitGate(GateRef gate);
|
||||
|
||||
void ReplaceIn(GateRef stateIn, GateRef dependIn, GateRef newGate);
|
||||
|
||||
GateRef Predicate(GateRef left, TypedBinOp cond, GateRef right);
|
||||
GateRef PredicateCmpWithConst(GateRef left, TypedBinOp cond, int right);
|
||||
GateRef PredicateAdd(GateRef left, int leftConst, TypedBinOp cond, GateRef right);
|
||||
GateRef PredicateAddCmpWithConst(GateRef left, int leftConst, TypedBinOp cond, int right);
|
||||
|
||||
GateAccessor acc_;
|
||||
BoundMap bounds_;
|
||||
Circuit *circuit_ {nullptr};
|
||||
CircuitBuilder builder_;
|
||||
Chunk *chunk_ {nullptr};
|
||||
bool enableLog_ {false};
|
||||
GraphLinearizer graphLinearizer_;
|
||||
std::string methodName_;
|
||||
|
||||
class IndexCheckInfo {
|
||||
public:
|
||||
IndexCheckInfo(Chunk* chunk): list_(chunk) {}
|
||||
GateLists list_;
|
||||
int min_;
|
||||
int max_;
|
||||
};
|
||||
typedef ChunkVector<IndexCheckInfo*> IndexCheckInfoList;
|
||||
IndexCheckInfoList indexCheckInfo_;
|
||||
};
|
||||
}
|
||||
#endif
|
@ -2147,4 +2147,108 @@ void CircuitBuilder::ClearConstantCache(GateRef gate)
|
||||
auto gateType = acc_.GetGateType(gate);
|
||||
GetCircuit()->ClearConstantCache(machineType, value, gateType);
|
||||
}
|
||||
|
||||
GateRef CircuitBuilder::InsertTypedBinaryop(GateRef left, GateRef right, GateType leftType, GateType rightType,
|
||||
GateType gateType, PGOSampleType sampleType, TypedBinOp op)
|
||||
{
|
||||
auto currentLabel = env_->GetCurrentLabel();
|
||||
auto currentControl = currentLabel->GetControl();
|
||||
auto currentDepend = currentLabel->GetDepend();
|
||||
uint64_t operandTypes = GatePairTypeAccessor::ToValue(leftType, rightType);
|
||||
auto ret = GetCircuit()->NewGate(circuit_->TypedBinaryOp(operandTypes, op, sampleType),
|
||||
MachineType::I64,
|
||||
{currentControl, currentDepend, left, right},
|
||||
gateType);
|
||||
acc_.ReplaceInAfterInsert(currentControl, currentDepend, ret);
|
||||
currentLabel->SetControl(ret);
|
||||
currentLabel->SetDepend(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
GateRef CircuitBuilder::InsertRangeCheckPredicate(GateRef left, TypedBinOp cond, GateRef right)
|
||||
{
|
||||
auto currentLabel = env_->GetCurrentLabel();
|
||||
auto currentControl = currentLabel->GetControl();
|
||||
auto currentDepend = currentLabel->GetDepend();
|
||||
auto frameState = acc_.FindNearestFrameState(currentDepend);
|
||||
TypedBinaryAccessor accessor(GateType::IntType(), cond);
|
||||
auto ret = GetCircuit()->NewGate(circuit_->RangeCheckPredicate(accessor.ToValue()),
|
||||
MachineType::I32,
|
||||
{currentControl, currentDepend, left, right, frameState},
|
||||
GateType::IntType());
|
||||
acc_.ReplaceInAfterInsert(currentControl, currentDepend, ret);
|
||||
currentLabel->SetControl(ret);
|
||||
currentLabel->SetDepend(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
GateRef CircuitBuilder::InsertStableArrayCheck(GateRef array)
|
||||
{
|
||||
auto currentLabel = env_->GetCurrentLabel();
|
||||
auto currentControl = currentLabel->GetControl();
|
||||
auto currentDepend = currentLabel->GetDepend();
|
||||
GateRef frameState = acc_.FindNearestFrameState(currentDepend);
|
||||
ElementsKind kind = acc_.TryGetElementsKind(array);
|
||||
ArrayMetaDataAccessor::Mode mode = ArrayMetaDataAccessor::Mode::LOAD_LENGTH;
|
||||
ArrayMetaDataAccessor accessor(kind, mode);
|
||||
auto ret = GetCircuit()->NewGate(circuit_->StableArrayCheck(accessor.ToValue()),
|
||||
MachineType::I1,
|
||||
{currentControl, currentDepend, array, frameState},
|
||||
GateType::NJSValue());
|
||||
acc_.ReplaceInAfterInsert(currentControl, currentDepend, ret);
|
||||
currentLabel->SetControl(ret);
|
||||
currentLabel->SetDepend(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
GateRef CircuitBuilder::InsertTypedArrayCheck(GateType type, GateRef array)
|
||||
{
|
||||
auto currentLabel = env_->GetCurrentLabel();
|
||||
auto currentControl = currentLabel->GetControl();
|
||||
auto currentDepend = currentLabel->GetDepend();
|
||||
GateRef frameState = acc_.FindNearestFrameState(currentDepend);
|
||||
auto ret = GetCircuit()->NewGate(circuit_->TypedArrayCheck(static_cast<size_t>(type.Value())),
|
||||
MachineType::I1,
|
||||
{currentControl, currentDepend, array, frameState},
|
||||
GateType::NJSValue());
|
||||
acc_.ReplaceInAfterInsert(currentControl, currentDepend, ret);
|
||||
currentLabel->SetControl(ret);
|
||||
currentLabel->SetDepend(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
GateRef CircuitBuilder::InsertLoadArrayLength(GateRef array, bool isTypedArray)
|
||||
{
|
||||
auto currentLabel = env_->GetCurrentLabel();
|
||||
auto currentControl = currentLabel->GetControl();
|
||||
auto currentDepend = currentLabel->GetDepend();
|
||||
GateType arrayType = acc_.GetGateType(array);
|
||||
if (isTypedArray) {
|
||||
InsertTypedArrayCheck(arrayType, array);
|
||||
currentControl = currentLabel->GetControl();
|
||||
currentDepend = currentLabel->GetDepend();
|
||||
auto ret = GetCircuit()->NewGate(circuit_->LoadTypedArrayLength(static_cast<size_t>(arrayType.Value())),
|
||||
MachineType::I64,
|
||||
{ currentControl, currentDepend, array },
|
||||
GateType::IntType());
|
||||
acc_.ReplaceInAfterInsert(currentControl, currentDepend, ret);
|
||||
currentLabel->SetControl(ret);
|
||||
currentLabel->SetDepend(ret);
|
||||
return ret;
|
||||
} else {
|
||||
InsertStableArrayCheck(array);
|
||||
currentControl = currentLabel->GetControl();
|
||||
currentDepend = currentLabel->GetDepend();
|
||||
auto ret = GetCircuit()->NewGate(circuit_->LoadArrayLength(),
|
||||
MachineType::I64,
|
||||
{ currentControl, currentDepend, array },
|
||||
GateType::IntType());
|
||||
acc_.ReplaceInAfterInsert(currentControl, currentDepend, ret);
|
||||
currentLabel->SetControl(ret);
|
||||
currentLabel->SetDepend(ret);
|
||||
return ret;
|
||||
}
|
||||
UNREACHABLE();
|
||||
return Circuit::NullGate();
|
||||
}
|
||||
} // namespace panda::ecmascript::kungfu
|
||||
|
@ -318,6 +318,12 @@ public:
|
||||
GateRef CheckTaggedNumberAndConvertToFloat64(GateRef gate);
|
||||
GateRef CheckTaggedNumberAndConvertToBool(GateRef gate);
|
||||
GateRef CheckTaggedBooleanAndConvertToBool(GateRef gate);
|
||||
GateRef InsertStableArrayCheck(GateRef array);
|
||||
GateRef InsertLoadArrayLength(GateRef array, bool isTypedArray);
|
||||
GateRef InsertTypedArrayCheck(GateType type, GateRef array);
|
||||
GateRef InsertTypedBinaryop(GateRef left, GateRef right, GateType leftType, GateType rightType,
|
||||
GateType gateType, PGOSampleType sampleType, TypedBinOp op);
|
||||
GateRef InsertRangeCheckPredicate(GateRef left, TypedBinOp cond, GateRef right);
|
||||
GateRef TypedConditionJump(MachineType type, TypedJumpOp jumpOp, BranchKind branchKind, GateType typeVal,
|
||||
const std::vector<GateRef>& inList);
|
||||
GateRef TypedNewAllocateThis(GateRef ctor, GateRef hclassIndex, GateRef frameState);
|
||||
|
@ -156,6 +156,12 @@ TypedUnaryAccessor GateAccessor::GetTypedUnAccessor(GateRef gate) const
|
||||
return TypedUnaryAccessor(gatePtr->GetOneParameterMetaData()->GetValue());
|
||||
}
|
||||
|
||||
TypedBinaryAccessor GateAccessor::GetTypedBinaryAccessor(GateRef gate) const
|
||||
{
|
||||
Gate *gatePtr = circuit_->LoadGatePtr(gate);
|
||||
return TypedBinaryAccessor(gatePtr->GetOneParameterMetaData()->GetValue());
|
||||
}
|
||||
|
||||
TypedJumpAccessor GateAccessor::GetTypedJumpAccessor(GateRef gate) const
|
||||
{
|
||||
ASSERT(GetOpCode(gate) == OpCode::TYPED_CONDITION_JUMP);
|
||||
@ -1199,6 +1205,96 @@ void GateAccessor::ReplaceGate(GateRef gate, GateRef state, GateRef depend, Gate
|
||||
DeleteGate(gate);
|
||||
}
|
||||
|
||||
// When Insert newGate, all the stateIn from state and dependIn from depend can be replaced to newGate
|
||||
void GateAccessor::ReplaceInAfterInsert(GateRef state, GateRef depend, GateRef newGate)
|
||||
{
|
||||
auto uses = Uses(state);
|
||||
for (auto useIt = uses.begin(); useIt != uses.end();) {
|
||||
if (IsStateIn(useIt) && (*useIt != newGate)) {
|
||||
ASSERT(newGate != Circuit::NullGate());
|
||||
// Exception, for example, IF_TRUE / IF_FALSE -> DEPEND_RELAY,
|
||||
// or LOOP_BEGIN / MERGE -> DEPEND_SELECTOR cannot be replaced
|
||||
if (!IsState(*useIt)) {
|
||||
useIt++;
|
||||
continue;
|
||||
}
|
||||
useIt = ReplaceIn(useIt, newGate);
|
||||
} else {
|
||||
useIt++;
|
||||
}
|
||||
}
|
||||
|
||||
uses = Uses(depend);
|
||||
for (auto useIt = uses.begin(); useIt != uses.end();) {
|
||||
if (IsDependIn(useIt) && (*useIt != newGate)) {
|
||||
ASSERT(newGate != Circuit::NullGate());
|
||||
if (!IsState(*useIt)) {
|
||||
useIt++;
|
||||
continue;
|
||||
}
|
||||
useIt = ReplaceIn(useIt, newGate);
|
||||
} else {
|
||||
useIt++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When loopExit, find stateSplit after DEPEND_SELECTOR
|
||||
void GateAccessor::GetFrameStateDependIn(GateRef gate, GateRef &dependIn)
|
||||
{
|
||||
auto uses = Uses(gate);
|
||||
size_t stateSplitCount = 0;
|
||||
GateRef stateSplit = Circuit::NullGate();
|
||||
for (auto it = uses.begin(); it != uses.end();) {
|
||||
if (GetOpCode(*it) == OpCode::STATE_SPLIT) {
|
||||
ASSERT(stateSplitCount < 1); // only one state Split;
|
||||
stateSplitCount++;
|
||||
stateSplit = *it;
|
||||
break;
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT(stateSplitCount <= 1);
|
||||
if (stateSplitCount == 1 && stateSplit != Circuit::NullGate()) {
|
||||
dependIn = stateSplit;
|
||||
}
|
||||
}
|
||||
|
||||
// When ifOp or loopExit, insertAfter
|
||||
// stateIn: IF_TRUE / IF_FALSE / MERGE
|
||||
// dependIn: DEPEND_RELAY / DEPEND_SELECTOR, if stateSplit follow closely, after the stateSplit.
|
||||
|
||||
void GateAccessor::GetStateInAndDependIn(GateRef insertAfter, GateRef &stateIn, GateRef &dependIn)
|
||||
{
|
||||
if (GetOpCode(insertAfter) == OpCode::IF_TRUE || GetOpCode(insertAfter) == OpCode::IF_FALSE) {
|
||||
auto uses = Uses(insertAfter);
|
||||
for (auto it = uses.begin(); it != uses.end();) {
|
||||
if (GetOpCode(*it) == OpCode::DEPEND_RELAY) {
|
||||
stateIn = insertAfter;
|
||||
dependIn = (*it);
|
||||
break;
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
} else if (GetOpCode(insertAfter) == OpCode::MERGE) {
|
||||
auto uses = Uses(insertAfter);
|
||||
for (auto it = uses.begin(); it != uses.end();) {
|
||||
if (GetOpCode(*it) == OpCode::DEPEND_SELECTOR) {
|
||||
stateIn = insertAfter;
|
||||
dependIn = (*it);
|
||||
GetFrameStateDependIn(*it, dependIn);
|
||||
break;
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
ASSERT(GetDependCount(dependIn) > 0);
|
||||
}
|
||||
|
||||
GateRef GateAccessor::GetFrameState(GateRef gate) const
|
||||
{
|
||||
ASSERT(HasFrameState(gate));
|
||||
|
@ -399,6 +399,7 @@ public:
|
||||
GlobalTSTypeRef GetFuncGT(GateRef gate) const;
|
||||
GateType GetParamGateType(GateRef gate) const;
|
||||
TypedUnaryAccessor GetTypedUnAccessor(GateRef gate) const;
|
||||
TypedBinaryAccessor GetTypedBinaryAccessor(GateRef gate) const;
|
||||
TypedJumpAccessor GetTypedJumpAccessor(GateRef gate) const;
|
||||
ArrayMetaDataAccessor GetArrayMetaDataAccessor(GateRef gate) const;
|
||||
ObjectTypeAccessor GetObjectTypeAccessor(GateRef gate) const;
|
||||
@ -442,6 +443,9 @@ public:
|
||||
size_t GetInValueCount(GateRef gate) const;
|
||||
size_t GetInValueStarts(GateRef gate) const;
|
||||
void UpdateAllUses(GateRef gate, GateRef replaceValueIn);
|
||||
void ReplaceInAfterInsert(GateRef state, GateRef depend, GateRef newGate);
|
||||
void GetFrameStateDependIn(GateRef gate, GateRef &dependIn);
|
||||
void GetStateInAndDependIn(GateRef insertAfter, GateRef &stateIn, GateRef &dependIn);
|
||||
void ReplaceIn(GateRef gate, size_t index, GateRef in);
|
||||
void ReplaceStateIn(GateRef gate, GateRef in, size_t index = 0);
|
||||
void ReplaceDependIn(GateRef gate, GateRef in, size_t index = 0);
|
||||
|
@ -396,7 +396,8 @@ std::string MachineTypeToStr(MachineType machineType);
|
||||
V(StableArrayCheck, STABLE_ARRAY_CHECK, GateFlags::CHECKABLE, 1, 1, 1) \
|
||||
V(HClassStableArrayCheck, HCLASS_STABLE_ARRAY_CHECK, GateFlags::CHECKABLE, 1, 1, 1) \
|
||||
V(ObjectTypeCheck, OBJECT_TYPE_CHECK, GateFlags::CHECKABLE, 1, 1, 2) \
|
||||
V(ObjectTypeCompare, OBJECT_TYPE_COMPARE, GateFlags::CHECKABLE, 1, 1, 2)
|
||||
V(ObjectTypeCompare, OBJECT_TYPE_COMPARE, GateFlags::CHECKABLE, 1, 1, 2) \
|
||||
V(RangeCheckPredicate, RANGE_CHECK_PREDICATE, GateFlags::CHECKABLE, 1, 1, 2)
|
||||
|
||||
#define GATE_META_DATA_LIST_WITH_ONE_PARAMETER(V) \
|
||||
V(Arg, ARG, GateFlags::HAS_ROOT, 0, 0, 0) \
|
||||
@ -1017,6 +1018,38 @@ private:
|
||||
uint64_t bitField_;
|
||||
};
|
||||
|
||||
class TypedBinaryAccessor {
|
||||
public:
|
||||
// type bits shift
|
||||
static constexpr int OPRAND_TYPE_BITS = 32;
|
||||
explicit TypedBinaryAccessor(uint64_t value) : bitField_(value) {}
|
||||
explicit TypedBinaryAccessor(GateType gate, TypedBinOp binOp)
|
||||
{
|
||||
bitField_ = TypedValueBits::Encode(gate.Value()) | TypedBinOpBits::Encode(binOp);
|
||||
}
|
||||
|
||||
GateType GetTypeValue() const
|
||||
{
|
||||
return GateType(TypedValueBits::Get(bitField_));
|
||||
}
|
||||
|
||||
TypedBinOp GetTypedBinOp() const
|
||||
{
|
||||
return TypedBinOpBits::Get(bitField_);
|
||||
}
|
||||
|
||||
uint64_t ToValue() const
|
||||
{
|
||||
return bitField_;
|
||||
}
|
||||
|
||||
private:
|
||||
using TypedValueBits = panda::BitField<uint32_t, 0, OPRAND_TYPE_BITS>;
|
||||
using TypedBinOpBits = TypedValueBits::NextField<TypedBinOp, OPRAND_TYPE_BITS>;
|
||||
|
||||
uint64_t bitField_;
|
||||
};
|
||||
|
||||
class TypedJumpAccessor {
|
||||
public:
|
||||
// type bits shift
|
||||
|
@ -340,6 +340,7 @@ public:
|
||||
ComputeLoopInfo();
|
||||
ComputeLoopExit();
|
||||
ComputeLoopHeader();
|
||||
ComputeLoopDepth();
|
||||
if (linearizer_->IsLogEnabled()) {
|
||||
for (size_t i = 0; i < numLoops_; i++) {
|
||||
auto& loopInfo = loops_[i];
|
||||
@ -521,6 +522,25 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void ComputeLoopDepth()
|
||||
{
|
||||
auto size = linearizer_->regionList_.size();
|
||||
for (size_t cur = 0; cur < size; cur++) {
|
||||
GateRegion* region = linearizer_->regionList_[cur];
|
||||
int loopDepth = 0;
|
||||
int innerLoopIndex = -1;
|
||||
for (int i = numLoops_ - 1; i >= 0; i--) {
|
||||
auto& loopInfo = loops_[i];
|
||||
if (loopInfo.loopBodys->TestBit(cur)) {
|
||||
loopDepth++;
|
||||
innerLoopIndex = i;
|
||||
}
|
||||
}
|
||||
region->SetLoopDepth(loopDepth);
|
||||
region->SetInnerLoopIndex(innerLoopIndex);
|
||||
}
|
||||
}
|
||||
|
||||
bool CheckRegionDomLoopExist(GateRegion* region, LoopInfo& loopInfo)
|
||||
{
|
||||
if (loopInfo.loopExits == nullptr) {
|
||||
@ -808,12 +828,12 @@ void GraphLinearizer::LinearizeGraph()
|
||||
builder.Run();
|
||||
ImmediateDominatorsGenerator generator(this, chunk_, regionList_.size());
|
||||
generator.Run();
|
||||
if (!IsSchedueLIR() && loopNumber_ > 0) {
|
||||
scheduleUpperBound_ = true;
|
||||
LoopInfoBuilder loopInfoBuilder(this, chunk_);
|
||||
loopInfoBuilder.Run();
|
||||
}
|
||||
if (!onlyBB_) {
|
||||
if (!IsSchedueLIR() && loopNumber_ > 0) {
|
||||
scheduleUpperBound_ = true;
|
||||
LoopInfoBuilder loopInfoBuilder(this, chunk_);
|
||||
loopInfoBuilder.Run();
|
||||
}
|
||||
GateScheduler scheduler(this);
|
||||
scheduler.Prepare();
|
||||
scheduler.ScheduleUpperBound();
|
||||
|
@ -195,6 +195,26 @@ public:
|
||||
return depth_;
|
||||
}
|
||||
|
||||
void SetLoopDepth(size_t loopDepth)
|
||||
{
|
||||
loopDepth_ = loopDepth;
|
||||
}
|
||||
|
||||
size_t GetLoopDepth()
|
||||
{
|
||||
return loopDepth_;
|
||||
}
|
||||
|
||||
void SetInnerLoopIndex(size_t innerLoopIndex)
|
||||
{
|
||||
innerLoopIndex_ = innerLoopIndex;
|
||||
}
|
||||
|
||||
int GetInnerLoopIndex()
|
||||
{
|
||||
return innerLoopIndex_;
|
||||
}
|
||||
|
||||
private:
|
||||
enum StateKind {
|
||||
BRANCH,
|
||||
@ -205,6 +225,8 @@ private:
|
||||
};
|
||||
static constexpr int32_t INVALID_DEPTH = -1;
|
||||
size_t id_ {0};
|
||||
size_t loopDepth_ {0}; // the loop nesting level of this block
|
||||
int innerLoopIndex_ {-1}; // number of the innermost loop of this block
|
||||
int32_t depth_ {INVALID_DEPTH};
|
||||
GateRegion* iDominator_ {nullptr};
|
||||
GateRegion* loopHead_ {nullptr};
|
||||
|
@ -84,6 +84,10 @@ void NumberSpeculativeLowering::VisitGate(GateRef gate)
|
||||
VisitIndexCheck(gate);
|
||||
break;
|
||||
}
|
||||
case OpCode::RANGE_CHECK_PREDICATE: {
|
||||
VisitRangeCheckPredicate(gate);
|
||||
break;
|
||||
}
|
||||
case OpCode::LOAD_ARRAY_LENGTH:
|
||||
case OpCode::LOAD_TYPED_ARRAY_LENGTH: {
|
||||
VisitLoadArrayLength(gate);
|
||||
@ -523,6 +527,12 @@ void NumberSpeculativeLowering::VisitPhi(GateRef gate)
|
||||
}
|
||||
}
|
||||
|
||||
void NumberSpeculativeLowering::VisitRangeCheckPredicate(GateRef gate)
|
||||
{
|
||||
acc_.SetGateType(gate, GateType::NJSValue());
|
||||
acc_.SetMachineType(gate, MachineType::I32);
|
||||
}
|
||||
|
||||
void NumberSpeculativeLowering::VisitIndexCheck(GateRef gate)
|
||||
{
|
||||
acc_.SetGateType(gate, GateType::NJSValue());
|
||||
|
@ -45,6 +45,7 @@ private:
|
||||
void VisitUndefinedStrictEq(GateRef gate);
|
||||
void VisitCallBuiltins(GateRef gate);
|
||||
void VisitRangeGuard(GateRef gate);
|
||||
void VisitRangeCheckPredicate(GateRef gate);
|
||||
void VisitIndexCheck(GateRef gate);
|
||||
void VisitLoadArrayLength(GateRef gate);
|
||||
void VisitLoadStringLength(GateRef gate);
|
||||
|
@ -87,6 +87,8 @@ GateRef NumberSpeculativeRetype::VisitGate(GateRef gate)
|
||||
return VisitTypedUnaryOp(gate);
|
||||
case OpCode::TYPED_CONDITION_JUMP:
|
||||
return VisitTypedConditionJump(gate);
|
||||
case OpCode::RANGE_CHECK_PREDICATE:
|
||||
return VisitRangeCheckPredicate(gate);
|
||||
case OpCode::INDEX_CHECK:
|
||||
return VisitIndexCheck(gate);
|
||||
case OpCode::LOAD_ARRAY_LENGTH:
|
||||
@ -895,6 +897,28 @@ GateRef NumberSpeculativeRetype::ConvertToTagged(GateRef gate)
|
||||
}
|
||||
}
|
||||
|
||||
GateRef NumberSpeculativeRetype::VisitRangeCheckPredicate(GateRef gate)
|
||||
{
|
||||
if (IsRetype()) {
|
||||
return SetOutputType(gate, GateType::IntType());
|
||||
}
|
||||
|
||||
if (IsConvert()) {
|
||||
Environment env(gate, circuit_, &builder_);
|
||||
GateRef value0 = acc_.GetValueIn(gate, 0);
|
||||
GateRef value1 = acc_.GetValueIn(gate, 1);
|
||||
GateType value0Type = acc_.GetGateType(value0);
|
||||
GateType value1Type = acc_.GetGateType(value1);
|
||||
acc_.ReplaceValueIn(gate, CheckAndConvertToInt32(value0, value0Type), 0);
|
||||
acc_.ReplaceValueIn(gate, CheckAndConvertToInt32(value1, value1Type), 1);
|
||||
|
||||
acc_.ReplaceStateIn(gate, builder_.GetState());
|
||||
acc_.ReplaceDependIn(gate, builder_.GetDepend());
|
||||
}
|
||||
|
||||
return Circuit::NullGate();
|
||||
}
|
||||
|
||||
GateRef NumberSpeculativeRetype::VisitIndexCheck(GateRef gate)
|
||||
{
|
||||
if (IsRetype()) {
|
||||
|
@ -78,6 +78,7 @@ private:
|
||||
GateRef VisitNumberShiftAndLogical(GateRef gate);
|
||||
GateRef VisitNumberMod(GateRef gate);
|
||||
GateRef VisitBooleanJump(GateRef gate);
|
||||
GateRef VisitRangeCheckPredicate(GateRef gate);
|
||||
GateRef VisitIndexCheck(GateRef gate);
|
||||
GateRef VisitLoadArrayLength(GateRef gate);
|
||||
GateRef VisitLoadStringLength(GateRef gate);
|
||||
|
@ -57,6 +57,9 @@ GateRef TypeMCRLowering::VisitGate(GateRef gate)
|
||||
case OpCode::OBJECT_TYPE_COMPARE:
|
||||
LowerObjectTypeCompare(gate);
|
||||
break;
|
||||
case OpCode::RANGE_CHECK_PREDICATE:
|
||||
LowerRangeCheckPredicate(gate);
|
||||
break;
|
||||
case OpCode::INDEX_CHECK:
|
||||
LowerIndexCheck(gate);
|
||||
break;
|
||||
@ -492,6 +495,38 @@ GateRef TypeMCRLowering::BuildCompareHClass(GateRef gate, GateRef frameState)
|
||||
return builder_.Equal(aotHCGate, receiverHClass);
|
||||
}
|
||||
|
||||
void TypeMCRLowering::LowerRangeCheckPredicate(GateRef gate)
|
||||
{
|
||||
Environment env(gate, circuit_, &builder_);
|
||||
auto deoptType = DeoptType::NOTARRAY;
|
||||
GateRef frameState = GetFrameState(gate);
|
||||
GateRef x = acc_.GetValueIn(gate, 0);
|
||||
GateRef y = acc_.GetValueIn(gate, 1);
|
||||
TypedBinaryAccessor accessor = acc_.GetTypedBinaryAccessor(gate);
|
||||
TypedBinOp cond = accessor.GetTypedBinOp();
|
||||
GateRef check = Circuit::NullGate();
|
||||
// check the condition
|
||||
switch (cond) {
|
||||
case TypedBinOp::TYPED_GREATER:
|
||||
check = builder_.Int32GreaterThan(x, y);
|
||||
break;
|
||||
case TypedBinOp::TYPED_GREATEREQ:
|
||||
check = builder_.Int32GreaterThanOrEqual(x, y);
|
||||
break;
|
||||
case TypedBinOp::TYPED_LESS:
|
||||
check = builder_.Int32LessThan(x, y);
|
||||
break;
|
||||
case TypedBinOp::TYPED_LESSEQ:
|
||||
check = builder_.Int32LessThanOrEqual(x, y);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
builder_.DeoptCheck(check, frameState, deoptType);
|
||||
acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), Circuit::NullGate());
|
||||
}
|
||||
|
||||
void TypeMCRLowering::LowerIndexCheck(GateRef gate)
|
||||
{
|
||||
Environment env(gate, circuit_, &builder_);
|
||||
|
@ -146,6 +146,7 @@ private:
|
||||
void LowerLoadElement(GateRef gate);
|
||||
void LowerLoadFromTaggedArray(GateRef gate);
|
||||
void LowerStoreToTaggedArray(GateRef gate, GateRef glue);
|
||||
void LowerRangeCheckPredicate(GateRef gate);
|
||||
|
||||
enum class ArrayState : uint8_t {
|
||||
PACKED = 0,
|
||||
|
18
test/aottest/array_bounds_check_elimination/BUILD.gn
Normal file
18
test/aottest/array_bounds_check_elimination/BUILD.gn
Normal file
@ -0,0 +1,18 @@
|
||||
# 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.
|
||||
|
||||
import("//arkcompiler/ets_runtime/test/test_helper.gni")
|
||||
|
||||
host_aot_test_action("array_bounds_check_elimination") {
|
||||
deps = []
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
function get(a: number[], p: number): number {
|
||||
if (p <= a.length && p > 0) {
|
||||
return a[p - 1];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function get2(a: number[], p: number): number {
|
||||
if (p <= a.length - 1 && p >= 0) {
|
||||
return a[p];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function clear1(a: number[], x: number) {
|
||||
for (let i = 0; i < x; i++) {
|
||||
a[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
function clear2(b: Int32Array[], x: number) {
|
||||
for (let i = 0; i < x; i++) {
|
||||
b[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
function triple(a: number[], i: number) {
|
||||
a[i + 3] = 0;
|
||||
a[i + 1] = 0;
|
||||
a[i + 2] = 0;
|
||||
}
|
||||
|
||||
function triple2(a: number[]) {
|
||||
a[0] = 0;
|
||||
a[1] = 0;
|
||||
a[2] = 0;
|
||||
}
|
||||
|
||||
let a: number[] = [1, 2, 3];
|
||||
let b: Int32Array[] = [1, 2, 3];
|
||||
let x = 3;
|
||||
print(get(a, x));
|
||||
print(get2(a, x));
|
||||
|
||||
clear1(a, 3);
|
||||
clear2(b, 3);
|
||||
|
||||
clear1(a, 4);
|
||||
clear2(b, 4);
|
||||
|
||||
let c: number[] = [1, 2, 3, 4];
|
||||
triple(c, 0);
|
||||
triple2(a);
|
||||
print(a[2]);
|
||||
print(c[2]);
|
@ -0,0 +1,17 @@
|
||||
# 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.
|
||||
|
||||
3
|
||||
0
|
||||
0
|
||||
0
|
@ -1,18 +0,0 @@
|
||||
function get(a: number[], p: number) {
|
||||
if(p <= a.length && p > 0) {
|
||||
return a[p - 1];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function get2(a: number[], p: number) {
|
||||
if(p <= a.length - 1 && p >= 0) {
|
||||
return a[p];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
let a: number[] = [1, 2, 3];
|
||||
let b = 2;
|
||||
get(a, b);
|
||||
get2(a, b);
|
Loading…
x
Reference in New Issue
Block a user