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