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/bytecodes.h"
16#include "ecmascript/compiler/circuit_builder.h"
17#include "ecmascript/compiler/early_elimination.h"
18#include "ecmascript/compiler/gate_accessor.h"
19#include "ecmascript/compiler/graph_editor.h"
20#include "ecmascript/compiler/loop_analysis.h"
21#include "ecmascript/compiler/loop_peeling.h"
22#include "ecmascript/compiler/pass.h"
23#include "ecmascript/compiler/stub_builder.h"
24#include "ecmascript/compiler/type.h"
25#include "ecmascript/compiler/variable_type.h"
26#include "ecmascript/compiler/verifier.h"
27#include "ecmascript/compiler/typed_bytecode_lowering.h"
28#include "ecmascript/compiler/typed_hcr_lowering.h"
29#include "ecmascript/pgo_profiler/types/pgo_profiler_type.h"
30#include "ecmascript/mem/chunk.h"
31#include "ecmascript/mem/native_area_allocator.h"
32#include "ecmascript/tests/test_helper.h"
33#include "gtest/gtest-death-test.h"
34#include "gtest/gtest.h"
35
36namespace panda::test {
37class LoopOptimizationTest : public testing::Test {
38};
39using ecmascript::kungfu::Circuit;
40using ecmascript::kungfu::GateAccessor;
41using ecmascript::kungfu::GateType;
42using ecmascript::kungfu::MachineType;
43using ecmascript::kungfu::CircuitBuilder;
44using ecmascript::kungfu::Label;
45using ecmascript::kungfu::OpCode;
46using ecmascript::kungfu::GateRef;
47using ecmascript::kungfu::Variable;
48using ecmascript::kungfu::VariableType;
49using ecmascript::kungfu::Verifier;
50using ecmascript::kungfu::LoopAnalysis;
51using ecmascript::kungfu::Environment;
52using ecmascript::kungfu::LoopPeeling;
53using ecmascript::kungfu::EarlyElimination;
54using ecmascript::kungfu::CombinedPassVisitor;
55using ecmascript::kungfu::TypedBinOp;
56using ecmascript::kungfu::PGOTypeRef;
57using ecmascript::kungfu::PGOSampleType;
58using ecmascript::kungfu::GraphLinearizer;
59using ecmascript::kungfu::ParamType;
60HWTEST_F_L0(LoopOptimizationTest, LoopInt32TypedArraySumOptimizationTest)
61{
62    // construct a circuit
63    ecmascript::NativeAreaAllocator allocator;
64    Circuit circuit(&allocator);
65    ecmascript::Chunk chunk(&allocator);
66    GateAccessor acc(&circuit);
67    CircuitBuilder builder(&circuit);
68    Environment env(0, &builder);
69    // after number speculative runner
70    builder.SetEnvironment(&env);
71    auto array = builder.Arguments(1);
72
73    DEFVALUE(index, (&builder), VariableType::INT32(), builder.Int32(0));
74    DEFVALUE(sum, (&builder), VariableType::INT32(), builder.Int32(0));
75
76    Label loopHead(&env);
77    Label loopBody(&env);
78    Label loopExit(&env);
79    builder.Jump(&loopHead);
80    builder.LoopBegin(&loopHead);
81    auto loopBegin = builder.GetState();
82    EXPECT_TRUE(acc.IsLoopHead(loopBegin));
83    auto loadLength = builder.LoadTypedArrayLength(array, ParamType::AnyType());
84    acc.SetMachineType(loadLength, MachineType::I32);
85    acc.SetGateType(loadLength, GateType::NJSValue());
86    auto cmp = builder.TypedBinaryOp<TypedBinOp::TYPED_ADD>(*index, loadLength, ParamType::IntType());
87    acc.SetMachineType(cmp, MachineType::I1);
88    builder.Branch(cmp, &loopBody, &loopExit);
89    builder.Bind(&loopBody);
90    auto loadElement = builder.LoadElement<ecmascript::kungfu::TypedLoadOp::INT32ARRAY_LOAD_ELEMENT>(array, *index);
91    acc.SetMachineType(loadElement, MachineType::I32);
92    acc.SetGateType(loadElement, GateType::NJSValue());
93    auto sumAdd = builder.TypedBinaryOp<TypedBinOp::TYPED_ADD>(*sum, loadElement, ParamType::IntType());
94    acc.SetMachineType(sumAdd, MachineType::I32);
95    sum = sumAdd;
96    auto indexInc = builder.TypedBinaryOp<TypedBinOp::TYPED_ADD>(*index, builder.Int32(1), ParamType::IntType());
97    acc.SetMachineType(indexInc, MachineType::I32);
98    index = indexInc;
99    builder.LoopEnd(&loopHead);
100    builder.Bind(&loopExit);
101    builder.LoopExit({&sum});
102    auto convert = builder.ConvertInt32ToTaggedInt(*sum);
103    builder.Return(convert);
104    LoopAnalysis analysis(nullptr, &circuit, &chunk);
105    ecmascript::kungfu::LoopInfo beforeOpt(&chunk, loopBegin);
106    ecmascript::kungfu::LoopInfo afterOpt(&chunk, loopBegin);
107    analysis.CollectLoopBody(&beforeOpt);
108    bool foundLengthBeforeOpt = false;
109    for (auto gate : beforeOpt.loopBodys) {
110        if (acc.GetOpCode(gate) == OpCode::LOAD_TYPED_ARRAY_LENGTH) {
111            foundLengthBeforeOpt = true;
112        }
113    }
114    EXPECT_TRUE(foundLengthBeforeOpt);
115    analysis.PrintLoop(&beforeOpt);
116    LoopPeeling(nullptr, &circuit, false, "LoopInt32TypedArraySumOptimizationTest", &chunk, &beforeOpt).Peel();
117    EXPECT_TRUE(Verifier::Run(&circuit));
118    CombinedPassVisitor visitor(&circuit, false, "LoopInt32TypedArraySumOptimizationTest", &chunk);
119    EarlyElimination earlyElimination(&circuit, &visitor, &chunk, true, true);
120    visitor.AddPass(&earlyElimination);
121    visitor.VisitGraph();
122    analysis.CollectLoopBody(&afterOpt);
123    EXPECT_TRUE(Verifier::Run(&circuit));
124    EXPECT_TRUE(beforeOpt.loopBodys.size() > afterOpt.loopBodys.size());
125    bool foundLengthAfterOpt = false;
126    for (auto gate : afterOpt.loopBodys) {
127        if (acc.GetOpCode(gate) == OpCode::LOAD_TYPED_ARRAY_LENGTH) {
128            foundLengthAfterOpt = true;
129        }
130    }
131    EXPECT_FALSE(foundLengthAfterOpt);
132}
133
134HWTEST_F_L0(LoopOptimizationTest, LoopNumberCalculationOptimizationTest)
135{
136    // construct a circuit
137    ecmascript::NativeAreaAllocator allocator;
138    Circuit circuit(&allocator);
139    ecmascript::Chunk chunk(&allocator);
140    GateAccessor acc(&circuit);
141    CircuitBuilder builder(&circuit);
142    Environment env(0, &builder);
143    // after slowpath lowering
144    builder.SetEnvironment(&env);
145    auto arg = builder.Arguments(1);
146    acc.SetMachineType(arg, MachineType::I32);
147    DEFVALUE(index, (&builder), VariableType::INT32(), builder.Int32(0));
148    DEFVALUE(sum, (&builder), VariableType::INT32(), builder.Int32(0));
149
150    Label loopHead(&env);
151    Label loopBody(&env);
152    Label loopExit(&env);
153    builder.Jump(&loopHead);
154    builder.LoopBegin(&loopHead);
155    auto loopBegin = builder.GetState();
156    auto loopEntry = acc.GetState(loopBegin);
157    auto invariant = builder.Int32Mul(arg, builder.Int32(5));
158    builder.Branch(builder.Int32LessThan(*index, invariant), &loopBody, &loopExit);
159    builder.Bind(&loopBody);
160    auto variant = builder.Int32Add(*sum, builder.Int32(2));
161    sum = variant;
162    index = builder.Int32Add(*index, builder.Int32(1));
163    builder.LoopEnd(&loopHead);
164    builder.Bind(&loopExit);
165    builder.Return(builder.ConvertInt32ToTaggedInt(*sum));
166    EXPECT_TRUE(Verifier::Run(&circuit));
167    std::vector<std::vector<GateRef>> cfg;
168    auto linearizer = GraphLinearizer(&circuit, false, "LoopNumberCalculationOptimizationTest", &chunk, false, false);
169    linearizer.Run(cfg);
170    EXPECT_EQ(acc.GetOpCode(linearizer.GetStateOfSchedulableGate(invariant)), OpCode::IF_BRANCH);
171    EXPECT_EQ(acc.GetOpCode(linearizer.GetStateOfSchedulableGate(variant)), OpCode::LOOP_BACK);
172    std::vector<std::vector<GateRef>> cfg2;
173    auto linearizer2 = GraphLinearizer(&circuit, false, "LoopNumberCalculationOptimizationTest", &chunk, false, true);
174    linearizer2.Run(cfg2);
175    EXPECT_EQ(linearizer2.GetStateOfSchedulableGate(invariant), loopEntry);
176    EXPECT_EQ(acc.GetOpCode(linearizer2.GetStateOfSchedulableGate(variant)), OpCode::LOOP_BACK);
177}
178
179HWTEST_F_L0(LoopOptimizationTest, LoopLoadConstOptimizationTest)
180{
181    // construct a circuit
182    ecmascript::NativeAreaAllocator allocator;
183    Circuit circuit(&allocator);
184    ecmascript::Chunk chunk(&allocator);
185    GateAccessor acc(&circuit);
186    CircuitBuilder builder(&circuit);
187    Environment env(0, &builder);
188    // after slowpath lowering
189    builder.SetEnvironment(&env);
190    auto arg1 = builder.Arguments(1);
191    acc.SetGateType(arg1, GateType::TaggedPointer());
192    auto arg2 = builder.Arguments(2);
193    acc.SetMachineType(arg2, MachineType::ARCH);
194    auto bits = ecmascript::kungfu::LoadStoreAccessor::ToValue(ecmascript::kungfu::MemoryAttribute::Default());
195    GateRef invariant = circuit.NewGate(circuit.Load(bits), MachineType::I32,
196        { circuit.GetDependRoot(), arg2 }, GateType::NJSValue());
197
198    DEFVALUE(index, (&builder), VariableType::INT32(), builder.Int32(0));
199    DEFVALUE(sum, (&builder), VariableType::INT32(), builder.Int32(0));
200
201    Label loopHead(&env);
202    Label loopBody(&env);
203    Label loopExit(&env);
204    builder.Jump(&loopHead);
205    builder.LoopBegin(&loopHead);
206    auto loopBegin = builder.GetState();
207    auto loopEntry = acc.GetState(loopBegin);
208
209    builder.Branch(builder.Int32LessThan(*index, invariant), &loopBody, &loopExit);
210    builder.Bind(&loopBody);
211    auto variant = builder.Load(VariableType::INT32(), arg1, builder.PtrAdd(arg2, *index));
212    sum = builder.Int32Add(*sum, variant);
213    index = builder.Int32Add(*index, builder.Int32(1));
214    builder.LoopEnd(&loopHead);
215    builder.Bind(&loopExit);
216    builder.Return(builder.ConvertInt32ToTaggedInt(*sum));
217    EXPECT_TRUE(Verifier::Run(&circuit));
218    std::vector<std::vector<GateRef>> cfg;
219    auto linearizer = GraphLinearizer(&circuit, false, "LoopNumberCalculationOptimizationTest", &chunk, false, false);
220    linearizer.Run(cfg);
221    EXPECT_EQ(acc.GetOpCode(linearizer.GetStateOfSchedulableGate(invariant)), OpCode::IF_BRANCH);
222    EXPECT_EQ(acc.GetOpCode(linearizer.GetStateOfSchedulableGate(variant)), OpCode::LOOP_BACK);
223    std::vector<std::vector<GateRef>> cfg2;
224    auto linearizer2 = GraphLinearizer(&circuit, false, "LoopNumberCalculationOptimizationTest", &chunk, false, true);
225    linearizer2.Run(cfg2);
226    EXPECT_EQ(linearizer2.GetStateOfSchedulableGate(invariant), loopEntry);
227    EXPECT_EQ(acc.GetOpCode(linearizer2.GetStateOfSchedulableGate(variant)), OpCode::LOOP_BACK);
228}
229} // namespace panda::test
230