1/*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15#include "ecmascript/compiler/range_guard.h"
16
17namespace panda::ecmascript::kungfu {
18
19void RangeGuard::Initialize()
20{
21    dependChains_.resize(circuit_->GetMaxGateId() + 1, nullptr); // 1: +1 for size
22    GateRef entry = acc_.GetDependRoot();
23    VisitDependEntry(entry);
24}
25
26GateRef RangeGuard::VisitGate(GateRef gate)
27{
28    auto op = acc_.GetOpCode(gate);
29    switch (op) {
30        case OpCode::VALUE_SELECTOR:
31        case OpCode::TYPED_BINARY_OP:
32        case OpCode::TYPED_UNARY_OP:
33        case OpCode::INDEX_CHECK: {
34            return TryApplyRangeGuardGate(gate);
35        }
36        case OpCode::DEPEND_SELECTOR: {
37            return TraverseDependSelector(gate);
38        }
39        default: {
40            if (acc_.GetDependCount(gate) == 1) { // 1: depend in is 1
41                return TraverseOthers(gate);
42            }
43            break;
44        }
45    }
46    return Circuit::NullGate();
47}
48
49GateRef RangeGuard::TraverseOthers(GateRef gate)
50{
51    ASSERT(acc_.GetDependCount(gate) >= 1);
52    auto depIn = acc_.GetDep(gate);
53    auto dependChain = GetDependChain(depIn);
54    if (dependChain == nullptr) {
55        return Circuit::NullGate();
56    }
57
58    return UpdateDependChain(gate, dependChain);
59}
60
61GateRef RangeGuard::TraverseDependSelector(GateRef gate)
62{
63    auto state = acc_.GetState(gate);
64    if (acc_.IsLoopHead(state)) {
65        return TraverseOthers(gate);
66    }
67
68    auto dependCount = acc_.GetDependCount(gate);
69    for (size_t i = 0; i < dependCount; ++i) {
70        auto depend = acc_.GetDep(gate, i);
71        auto dependChain = GetDependChain(depend);
72        if (dependChain == nullptr) {
73            return Circuit::NullGate();
74        }
75    }
76
77    // all depend done.
78    auto depend = acc_.GetDep(gate);
79    auto dependChain = GetDependChain(depend);
80    DependChains* copy = new (chunk_) DependChains(chunk_);
81    copy->CopyFrom(dependChain);
82    for (size_t i = 1; i < dependCount; ++i) { // 1: second in
83        auto dependIn = acc_.GetDep(gate, i);
84        auto tempChain = GetDependChain(dependIn);
85        copy->Merge(tempChain);
86    }
87    return UpdateDependChain(gate, copy);
88}
89
90GateRef RangeGuard::TryApplyRangeGuardForLength(DependChains* dependChain, GateRef gate, GateRef input)
91{
92    ASSERT(dependChain != nullptr);
93    uint32_t length = FoundIndexCheckedForLength(dependChain, input);
94    if (length) { // when length not equal to 0, then Found the IndexCheck Success
95        Environment env(gate, circuit_, &builder_);
96        // If the IndexCheck before the ArrayLength used, the ArrayLength must start by 1.
97        auto rangeGuardGate = builder_.RangeGuard(input, 1, length);
98        return rangeGuardGate;
99    }
100    return Circuit::NullGate();
101}
102
103GateRef RangeGuard::TryApplyRangeGuardForIndex(DependChains* dependChain, GateRef gate, GateRef input)
104{
105    ASSERT(dependChain != nullptr);
106    uint32_t length = FoundIndexCheckedForIndex(dependChain, input);
107    if (length) { // when length not equal to 0, then Found the IndexCheck Success
108        Environment env(gate, circuit_, &builder_);
109        // If the IndexCheck used in the Array, the index must in the Array range.
110        auto rangeGuardGate = builder_.RangeGuard(input, 0, length);
111        return rangeGuardGate;
112    }
113    return Circuit::NullGate();
114}
115
116GateRef RangeGuard::TryApplyRangeGuardGate(GateRef gate)
117{
118    if (acc_.GetDependCount(gate) < 1) {
119        return Circuit::NullGate();
120    }
121
122    auto depIn = acc_.GetDep(gate);
123    auto dependChain = GetDependChain(depIn);
124    // dependChain is null
125    if (dependChain == nullptr) {
126        return Circuit::NullGate();
127    }
128
129    auto numIns = acc_.GetInValueCount(gate);
130    for (size_t i = 0; i < numIns; ++i) {
131        auto originalInput = acc_.GetValueIn(gate, i);
132        auto originalInputOpcode = acc_.GetOpCode(originalInput);
133        auto rangeGuardGate = Circuit::NullGate();
134        if (originalInputOpcode == OpCode::LOAD_TYPED_ARRAY_LENGTH ||
135            originalInputOpcode == OpCode::LOAD_ARRAY_LENGTH) {
136            rangeGuardGate = TryApplyRangeGuardForLength(dependChain, gate, originalInput);
137        } else if (originalInputOpcode != OpCode::CONSTANT && rangeGuardGate == Circuit::NullGate()) {
138            rangeGuardGate = TryApplyRangeGuardForIndex(dependChain, gate, originalInput);
139        }
140        if (rangeGuardGate != Circuit::NullGate()) {
141            acc_.ReplaceValueIn(gate, rangeGuardGate, i);
142        }
143    }
144    dependChain = dependChain->UpdateNode(gate);
145    return UpdateDependChain(gate, dependChain);
146}
147
148GateRef RangeGuard::VisitDependEntry(GateRef gate)
149{
150    auto empty = new (chunk_) DependChains(chunk_);
151    return UpdateDependChain(gate, empty);
152}
153
154GateRef RangeGuard::UpdateDependChain(GateRef gate, DependChains* dependChain)
155{
156    ASSERT(dependChain != nullptr);
157    auto oldDependChain = GetDependChain(gate);
158    if (dependChain->Equals(oldDependChain)) {
159        return Circuit::NullGate();
160    }
161    dependChains_[acc_.GetId(gate)] = dependChain;
162    return gate;
163}
164
165uint32_t RangeGuard::CheckIndexCheckLengthInput(GateRef lhs, GateRef rhs) const
166{
167    auto lhsOpcode = acc_.GetOpCode(lhs);
168    if (lhsOpcode == OpCode::INDEX_CHECK) {
169        auto indexCheckLengthInput = acc_.GetValueIn(lhs, 0); // length
170        auto indexCheckLengthInputOpcode = acc_.GetOpCode(indexCheckLengthInput);
171        if (indexCheckLengthInput == rhs && indexCheckLengthInputOpcode == OpCode::LOAD_TYPED_ARRAY_LENGTH) {
172            TypedArrayMetaDataAccessor accessor = acc_.GetTypedArrayMetaDataAccessor(indexCheckLengthInput);
173            OnHeapMode onHeap = accessor.GetOnHeapMode();
174            int32_t max = onHeap == OnHeapMode::ON_HEAP ? RangeInfo::TYPED_ARRAY_ONHEAP_MAX : INT32_MAX;
175            return max;
176        } else if (indexCheckLengthInput == rhs && indexCheckLengthInputOpcode == OpCode::LOAD_ARRAY_LENGTH) {
177            return INT32_MAX;
178        }
179    }
180    return 0;
181}
182
183uint32_t RangeGuard::CheckIndexCheckIndexInput(GateRef lhs, GateRef rhs) const
184{
185    auto lhsOpcode = acc_.GetOpCode(lhs);
186    if (lhsOpcode == OpCode::INDEX_CHECK) {
187        auto indexCheckLengthInput = acc_.GetValueIn(lhs, 0); // length
188        auto indexCheckIndexInput = acc_.GetValueIn(lhs, 1); // index
189        auto indexCheckLengthInputOpcode = acc_.GetOpCode(indexCheckLengthInput);
190        // TYPED_ARRAY
191        if (indexCheckIndexInput == rhs && indexCheckLengthInputOpcode == OpCode::LOAD_TYPED_ARRAY_LENGTH) {
192            TypedArrayMetaDataAccessor accessor = acc_.GetTypedArrayMetaDataAccessor(indexCheckLengthInput);
193            OnHeapMode onHeap = accessor.GetOnHeapMode();
194            int32_t max = onHeap == OnHeapMode::ON_HEAP ? RangeInfo::TYPED_ARRAY_ONHEAP_MAX : INT32_MAX;
195            return max;
196        } else if (indexCheckIndexInput == rhs && indexCheckLengthInputOpcode == OpCode::LOAD_ARRAY_LENGTH) { // ARRAY
197            return INT32_MAX;
198        }
199    }
200    return 0;
201}
202}  // namespace panda::ecmascript::kungfu
203