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 
36 namespace panda::test {
37 class LoopOptimizationTest : public testing::Test {
38 };
39 using ecmascript::kungfu::Circuit;
40 using ecmascript::kungfu::GateAccessor;
41 using ecmascript::kungfu::GateType;
42 using ecmascript::kungfu::MachineType;
43 using ecmascript::kungfu::CircuitBuilder;
44 using ecmascript::kungfu::Label;
45 using ecmascript::kungfu::OpCode;
46 using ecmascript::kungfu::GateRef;
47 using ecmascript::kungfu::Variable;
48 using ecmascript::kungfu::VariableType;
49 using ecmascript::kungfu::Verifier;
50 using ecmascript::kungfu::LoopAnalysis;
51 using ecmascript::kungfu::Environment;
52 using ecmascript::kungfu::LoopPeeling;
53 using ecmascript::kungfu::EarlyElimination;
54 using ecmascript::kungfu::CombinedPassVisitor;
55 using ecmascript::kungfu::TypedBinOp;
56 using ecmascript::kungfu::PGOTypeRef;
57 using ecmascript::kungfu::PGOSampleType;
58 using ecmascript::kungfu::GraphLinearizer;
59 using ecmascript::kungfu::ParamType;
HWTEST_F_L0(LoopOptimizationTest, LoopInt32TypedArraySumOptimizationTest)60 HWTEST_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 
HWTEST_F_L0(LoopOptimizationTest, LoopNumberCalculationOptimizationTest)134 HWTEST_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 
HWTEST_F_L0(LoopOptimizationTest, LoopLoadConstOptimizationTest)179 HWTEST_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