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;
HWTEST_F_L0(LoopOptimizationTest,LoopInt32TypedArraySumOptimizationTest)59 HWTEST_F_L0(LoopOptimizationTest, LoopInt32TypedArraySumOptimizationTest)
60 {
61 // construct a circuit
62 ecmascript::NativeAreaAllocator allocator;
63 Circuit circuit(&allocator);
64 ecmascript::Chunk chunk(&allocator);
65 GateAccessor acc(&circuit);
66 CircuitBuilder builder(&circuit);
67 Environment env(0, &builder);
68 // after number speculative runner
69 builder.SetEnvironment(&env);
70 auto array = builder.Arguments(1);
71
72 DEFVALUE(index, (&builder), VariableType::INT32(), builder.Int32(0));
73 DEFVALUE(sum, (&builder), VariableType::INT32(), builder.Int32(0));
74
75 Label loopHead(&env);
76 Label loopBody(&env);
77 Label loopExit(&env);
78 builder.Jump(&loopHead);
79 builder.LoopBegin(&loopHead);
80 auto loopBegin = builder.GetState();
81 EXPECT_TRUE(acc.IsLoopHead(loopBegin));
82 auto loadLength = builder.LoadTypedArrayLength(array, GateType::AnyType());
83 acc.SetMachineType(loadLength, MachineType::I32);
84 acc.SetGateType(loadLength, GateType::NJSValue());
85 auto cmp = builder.TypedBinaryOp<TypedBinOp::TYPED_ADD>(
86 *index, loadLength, GateType::IntType(), GateType::IntType(),
87 GateType::NJSValue(), PGOTypeRef::NoneType());
88 acc.SetMachineType(cmp, MachineType::I1);
89 builder.Branch(cmp, &loopBody, &loopExit);
90 builder.Bind(&loopBody);
91 auto loadElement = builder.LoadElement<ecmascript::kungfu::TypedLoadOp::INT32ARRAY_LOAD_ELEMENT>(array, *index);
92 acc.SetMachineType(loadElement, MachineType::I32);
93 acc.SetGateType(loadElement, GateType::NJSValue());
94 auto sumAdd = builder.TypedBinaryOp<TypedBinOp::TYPED_ADD>(
95 *sum, loadElement, GateType::IntType(), GateType::IntType(),
96 GateType::NJSValue(), PGOTypeRef::NoneType());
97 acc.SetMachineType(sumAdd, MachineType::I32);
98 sum = sumAdd;
99 auto indexInc = builder.TypedBinaryOp<TypedBinOp::TYPED_ADD>(
100 *index, builder.Int32(1), GateType::IntType(), GateType::IntType(),
101 GateType::NJSValue(), PGOTypeRef::NoneType());
102 acc.SetMachineType(indexInc, MachineType::I32);
103 index = indexInc;
104 builder.LoopEnd(&loopHead);
105 builder.Bind(&loopExit);
106 builder.LoopExit({&sum});
107 auto convert = builder.ConvertInt32ToTaggedInt(*sum);
108 builder.Return(convert);
109 LoopAnalysis analysis(nullptr, &circuit, &chunk);
110 ecmascript::kungfu::LoopInfo beforeOpt(&chunk, loopBegin);
111 ecmascript::kungfu::LoopInfo afterOpt(&chunk, loopBegin);
112 analysis.CollectLoopBody(&beforeOpt);
113 bool foundLengthBeforeOpt = false;
114 for (auto gate : beforeOpt.loopBodys) {
115 if (acc.GetOpCode(gate) == OpCode::LOAD_TYPED_ARRAY_LENGTH) {
116 foundLengthBeforeOpt = true;
117 }
118 }
119 EXPECT_TRUE(foundLengthBeforeOpt);
120 analysis.PrintLoop(&beforeOpt);
121 LoopPeeling(nullptr, &circuit, false, "LoopInt32TypedArraySumOptimizationTest", &chunk, &beforeOpt).Peel();
122 EXPECT_TRUE(Verifier::Run(&circuit));
123 CombinedPassVisitor visitor(&circuit, false, "LoopInt32TypedArraySumOptimizationTest", &chunk);
124 EarlyElimination earlyElimination(&circuit, &visitor, &chunk);
125 visitor.AddPass(&earlyElimination);
126 visitor.VisitGraph();
127 analysis.CollectLoopBody(&afterOpt);
128 EXPECT_TRUE(Verifier::Run(&circuit));
129 EXPECT_TRUE(beforeOpt.loopBodys.size() > afterOpt.loopBodys.size());
130 bool foundLengthAfterOpt = false;
131 for (auto gate : afterOpt.loopBodys) {
132 if (acc.GetOpCode(gate) == OpCode::LOAD_TYPED_ARRAY_LENGTH) {
133 foundLengthAfterOpt = true;
134 }
135 }
136 EXPECT_FALSE(foundLengthAfterOpt);
137 }
138
HWTEST_F_L0(LoopOptimizationTest,LoopNumberCalculationOptimizationTest)139 HWTEST_F_L0(LoopOptimizationTest, LoopNumberCalculationOptimizationTest)
140 {
141 // construct a circuit
142 ecmascript::NativeAreaAllocator allocator;
143 Circuit circuit(&allocator);
144 ecmascript::Chunk chunk(&allocator);
145 GateAccessor acc(&circuit);
146 CircuitBuilder builder(&circuit);
147 Environment env(0, &builder);
148 // after slowpath lowering
149 builder.SetEnvironment(&env);
150 auto arg = builder.Arguments(1);
151 acc.SetMachineType(arg, MachineType::I32);
152 DEFVALUE(index, (&builder), VariableType::INT32(), builder.Int32(0));
153 DEFVALUE(sum, (&builder), VariableType::INT32(), builder.Int32(0));
154
155 Label loopHead(&env);
156 Label loopBody(&env);
157 Label loopExit(&env);
158 builder.Jump(&loopHead);
159 builder.LoopBegin(&loopHead);
160 auto loopBegin = builder.GetState();
161 auto loopEntry = acc.GetState(loopBegin);
162 auto invariant = builder.Int32Mul(arg, builder.Int32(5));
163 builder.Branch(builder.Int32LessThan(*index, invariant), &loopBody, &loopExit);
164 builder.Bind(&loopBody);
165 auto variant = builder.Int32Add(*sum, builder.Int32(2));
166 sum = variant;
167 index = builder.Int32Add(*index, builder.Int32(1));
168 builder.LoopEnd(&loopHead);
169 builder.Bind(&loopExit);
170 builder.Return(builder.ConvertInt32ToTaggedInt(*sum));
171 EXPECT_TRUE(Verifier::Run(&circuit));
172 std::vector<std::vector<GateRef>> cfg;
173 auto linearizer = GraphLinearizer(&circuit, false, "LoopNumberCalculationOptimizationTest", &chunk, false, false);
174 linearizer.Run(cfg);
175 EXPECT_EQ(acc.GetOpCode(linearizer.GetStateOfSchedulableGate(invariant)), OpCode::IF_BRANCH);
176 EXPECT_EQ(acc.GetOpCode(linearizer.GetStateOfSchedulableGate(variant)), OpCode::LOOP_BACK);
177 std::vector<std::vector<GateRef>> cfg2;
178 auto linearizer2 = GraphLinearizer(&circuit, false, "LoopNumberCalculationOptimizationTest", &chunk, false, true);
179 linearizer2.Run(cfg2);
180 EXPECT_EQ(linearizer2.GetStateOfSchedulableGate(invariant), loopEntry);
181 EXPECT_EQ(acc.GetOpCode(linearizer2.GetStateOfSchedulableGate(variant)), OpCode::LOOP_BACK);
182 }
183
HWTEST_F_L0(LoopOptimizationTest,LoopLoadConstOptimizationTest)184 HWTEST_F_L0(LoopOptimizationTest, LoopLoadConstOptimizationTest)
185 {
186 // construct a circuit
187 ecmascript::NativeAreaAllocator allocator;
188 Circuit circuit(&allocator);
189 ecmascript::Chunk chunk(&allocator);
190 GateAccessor acc(&circuit);
191 CircuitBuilder builder(&circuit);
192 Environment env(0, &builder);
193 // after slowpath lowering
194 builder.SetEnvironment(&env);
195 auto arg1 = builder.Arguments(1);
196 acc.SetGateType(arg1, GateType::TaggedPointer());
197 auto arg2 = builder.Arguments(2);
198 acc.SetMachineType(arg2, MachineType::ARCH);
199 auto bits = ecmascript::kungfu::LoadStoreAccessor::ToValue(ecmascript::kungfu::MemoryOrder::Default());
200 GateRef invariant = circuit.NewGate(circuit.Load(bits), MachineType::I32,
201 { circuit.GetDependRoot(), arg2 }, GateType::NJSValue());
202
203 DEFVALUE(index, (&builder), VariableType::INT32(), builder.Int32(0));
204 DEFVALUE(sum, (&builder), VariableType::INT32(), builder.Int32(0));
205
206 Label loopHead(&env);
207 Label loopBody(&env);
208 Label loopExit(&env);
209 builder.Jump(&loopHead);
210 builder.LoopBegin(&loopHead);
211 auto loopBegin = builder.GetState();
212 auto loopEntry = acc.GetState(loopBegin);
213
214 builder.Branch(builder.Int32LessThan(*index, invariant), &loopBody, &loopExit);
215 builder.Bind(&loopBody);
216 auto variant = builder.Load(VariableType::INT32(), arg1, builder.PtrAdd(arg2, *index));
217 sum = builder.Int32Add(*sum, variant);
218 index = builder.Int32Add(*index, builder.Int32(1));
219 builder.LoopEnd(&loopHead);
220 builder.Bind(&loopExit);
221 builder.Return(builder.ConvertInt32ToTaggedInt(*sum));
222 EXPECT_TRUE(Verifier::Run(&circuit));
223 std::vector<std::vector<GateRef>> cfg;
224 auto linearizer = GraphLinearizer(&circuit, false, "LoopNumberCalculationOptimizationTest", &chunk, false, false);
225 linearizer.Run(cfg);
226 EXPECT_EQ(acc.GetOpCode(linearizer.GetStateOfSchedulableGate(invariant)), OpCode::IF_BRANCH);
227 EXPECT_EQ(acc.GetOpCode(linearizer.GetStateOfSchedulableGate(variant)), OpCode::LOOP_BACK);
228 std::vector<std::vector<GateRef>> cfg2;
229 auto linearizer2 = GraphLinearizer(&circuit, false, "LoopNumberCalculationOptimizationTest", &chunk, false, true);
230 linearizer2.Run(cfg2);
231 EXPECT_EQ(linearizer2.GetStateOfSchedulableGate(invariant), loopEntry);
232 EXPECT_EQ(acc.GetOpCode(linearizer2.GetStateOfSchedulableGate(variant)), OpCode::LOOP_BACK);
233 }
234 } // namespace panda::test
235