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