14514f5e3Sopenharmony_ci/* 24514f5e3Sopenharmony_ci * Copyright (c) 2024 Huawei Device Co., Ltd. 34514f5e3Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License"); 44514f5e3Sopenharmony_ci * you may not use this file except in compliance with the License. 54514f5e3Sopenharmony_ci * You may obtain a copy of the License at 64514f5e3Sopenharmony_ci * 74514f5e3Sopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0 84514f5e3Sopenharmony_ci * 94514f5e3Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software 104514f5e3Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS, 114514f5e3Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 124514f5e3Sopenharmony_ci * See the License for the specific language governing permissions and 134514f5e3Sopenharmony_ci * limitations under the License. 144514f5e3Sopenharmony_ci */ 154514f5e3Sopenharmony_ci 164514f5e3Sopenharmony_ci#include "ecmascript/compiler/induction_variable_analysis.h" 174514f5e3Sopenharmony_ci 184514f5e3Sopenharmony_cinamespace panda::ecmascript::kungfu { 194514f5e3Sopenharmony_ci 204514f5e3Sopenharmony_cibool InductionVariableAnalysis::IsIntConstant(GateRef gate) const 214514f5e3Sopenharmony_ci{ 224514f5e3Sopenharmony_ci if (acc_.GetOpCode(gate) != OpCode::CONSTANT) { 234514f5e3Sopenharmony_ci return false; 244514f5e3Sopenharmony_ci } 254514f5e3Sopenharmony_ci JSTaggedValue value(acc_.GetConstantValue(gate)); 264514f5e3Sopenharmony_ci return value.IsInt(); 274514f5e3Sopenharmony_ci} 284514f5e3Sopenharmony_ci 294514f5e3Sopenharmony_cibool InductionVariableAnalysis::IsInductionVariable(GateRef gate) const 304514f5e3Sopenharmony_ci{ 314514f5e3Sopenharmony_ci if (acc_.GetOpCode(gate) != OpCode::VALUE_SELECTOR) { 324514f5e3Sopenharmony_ci return false; 334514f5e3Sopenharmony_ci } 344514f5e3Sopenharmony_ci size_t numValueIn = acc_.GetNumValueIn(gate); 354514f5e3Sopenharmony_ci GateRef startGate = acc_.GetValueIn(gate, 0); 364514f5e3Sopenharmony_ci GateRef valueGate = acc_.GetValueIn(gate, 1); 374514f5e3Sopenharmony_ci if (!IsIntConstant(startGate)) { 384514f5e3Sopenharmony_ci return false; 394514f5e3Sopenharmony_ci } 404514f5e3Sopenharmony_ci if (acc_.GetOpCode(valueGate) != OpCode::TYPED_BINARY_OP) { 414514f5e3Sopenharmony_ci return false; 424514f5e3Sopenharmony_ci } 434514f5e3Sopenharmony_ci TypedBinOp binOp = acc_.GetTypedBinaryOp(valueGate); 444514f5e3Sopenharmony_ci if (binOp != TypedBinOp::TYPED_ADD && binOp != TypedBinOp::TYPED_SUB) { 454514f5e3Sopenharmony_ci return false; 464514f5e3Sopenharmony_ci } 474514f5e3Sopenharmony_ci TypedBinaryAccessor accessor(acc_.TryGetValue(valueGate)); 484514f5e3Sopenharmony_ci const ParamType paramType = accessor.GetParamType(); 494514f5e3Sopenharmony_ci if (!paramType.IsIntType()) { 504514f5e3Sopenharmony_ci return false; 514514f5e3Sopenharmony_ci } 524514f5e3Sopenharmony_ci 534514f5e3Sopenharmony_ci for (size_t i = 2; i < numValueIn; i++) { // 2: skip startGate and valueGate 544514f5e3Sopenharmony_ci if (acc_.GetValueIn(gate, i) != valueGate) { 554514f5e3Sopenharmony_ci return false; 564514f5e3Sopenharmony_ci } 574514f5e3Sopenharmony_ci } 584514f5e3Sopenharmony_ci 594514f5e3Sopenharmony_ci // check if value satisfies a = a + x 604514f5e3Sopenharmony_ci if (acc_.GetValueIn(valueGate, 0) != gate && acc_.GetValueIn(valueGate, 1) != gate) { 614514f5e3Sopenharmony_ci return false; 624514f5e3Sopenharmony_ci } 634514f5e3Sopenharmony_ci GateRef stride = acc_.GetValueIn(valueGate, 1); 644514f5e3Sopenharmony_ci if (acc_.GetValueIn(valueGate, 0) != gate) { 654514f5e3Sopenharmony_ci stride = acc_.GetValueIn(valueGate, 0); 664514f5e3Sopenharmony_ci } 674514f5e3Sopenharmony_ci if (!IsIntConstant(stride)) { 684514f5e3Sopenharmony_ci return false; 694514f5e3Sopenharmony_ci } 704514f5e3Sopenharmony_ci return true; 714514f5e3Sopenharmony_ci} 724514f5e3Sopenharmony_ci 734514f5e3Sopenharmony_cistd::pair<int32_t, int32_t> InductionVariableAnalysis::GetStartAndStride(GateRef gate) const 744514f5e3Sopenharmony_ci{ 754514f5e3Sopenharmony_ci ASSERT(acc_.GetOpCode(gate) == OpCode::VALUE_SELECTOR); 764514f5e3Sopenharmony_ci GateRef startGate = acc_.GetValueIn(gate, 0); 774514f5e3Sopenharmony_ci ASSERT(IsIntConstant(startGate)); 784514f5e3Sopenharmony_ci auto start = GetIntFromTaggedConstant(startGate); 794514f5e3Sopenharmony_ci 804514f5e3Sopenharmony_ci GateRef valueGate = acc_.GetValueIn(gate, 1); 814514f5e3Sopenharmony_ci ASSERT(acc_.GetOpCode(valueGate) == OpCode::TYPED_BINARY_OP); 824514f5e3Sopenharmony_ci [[maybe_unused]]TypedBinOp binOp = acc_.GetTypedBinaryOp(valueGate); 834514f5e3Sopenharmony_ci ASSERT(binOp == TypedBinOp::TYPED_ADD || binOp == TypedBinOp::TYPED_SUB); 844514f5e3Sopenharmony_ci TypedBinaryAccessor accessor(acc_.TryGetValue(valueGate)); 854514f5e3Sopenharmony_ci [[maybe_unused]]const ParamType paramType = accessor.GetParamType(); 864514f5e3Sopenharmony_ci ASSERT(paramType.IsIntType()); 874514f5e3Sopenharmony_ci 884514f5e3Sopenharmony_ci GateRef strideGate = acc_.GetValueIn(valueGate, 1); 894514f5e3Sopenharmony_ci if (acc_.GetValueIn(valueGate, 0) != gate) { 904514f5e3Sopenharmony_ci strideGate = acc_.GetValueIn(valueGate, 0); 914514f5e3Sopenharmony_ci } 924514f5e3Sopenharmony_ci ASSERT(IsIntConstant(strideGate)); 934514f5e3Sopenharmony_ci auto stride = GetIntFromTaggedConstant(strideGate); 944514f5e3Sopenharmony_ci 954514f5e3Sopenharmony_ci // a - xb < c -> a + (-x)b < c 964514f5e3Sopenharmony_ci if (acc_.GetTypedBinaryOp(valueGate) == TypedBinOp::TYPED_SUB) { 974514f5e3Sopenharmony_ci stride = -stride; 984514f5e3Sopenharmony_ci } 994514f5e3Sopenharmony_ci 1004514f5e3Sopenharmony_ci return std::make_pair(start, stride); 1014514f5e3Sopenharmony_ci} 1024514f5e3Sopenharmony_ci 1034514f5e3Sopenharmony_ciint32_t InductionVariableAnalysis::GetIntFromTaggedConstant(GateRef gate) const 1044514f5e3Sopenharmony_ci{ 1054514f5e3Sopenharmony_ci ASSERT(acc_.GetOpCode(gate) == OpCode::CONSTANT); 1064514f5e3Sopenharmony_ci JSTaggedValue value(acc_.GetConstantValue(gate)); 1074514f5e3Sopenharmony_ci return value.GetInt(); 1084514f5e3Sopenharmony_ci} 1094514f5e3Sopenharmony_ci 1104514f5e3Sopenharmony_cibool InductionVariableAnalysis::IsLessOrGreaterCmp(GateRef gate) const 1114514f5e3Sopenharmony_ci{ 1124514f5e3Sopenharmony_ci return acc_.GetTypedBinaryOp(gate) == TypedBinOp::TYPED_GREATEREQ || 1134514f5e3Sopenharmony_ci acc_.GetTypedBinaryOp(gate) == TypedBinOp::TYPED_GREATER || 1144514f5e3Sopenharmony_ci acc_.GetTypedBinaryOp(gate) == TypedBinOp::TYPED_LESSEQ || 1154514f5e3Sopenharmony_ci acc_.GetTypedBinaryOp(gate) == TypedBinOp::TYPED_LESS; 1164514f5e3Sopenharmony_ci} 1174514f5e3Sopenharmony_ci 1184514f5e3Sopenharmony_cibool InductionVariableAnalysis::TryGetLoopTimes(const GraphLinearizer::LoopInfo& loop, int32_t& loopTimes) const 1194514f5e3Sopenharmony_ci{ 1204514f5e3Sopenharmony_ci if (loop.loopExits->size() > 1) { 1214514f5e3Sopenharmony_ci return false; 1224514f5e3Sopenharmony_ci } 1234514f5e3Sopenharmony_ci ASSERT(loop.loopExits->size() == 1); 1244514f5e3Sopenharmony_ci GateRef loopExit = loop.loopExits->at(0)->GetState(); 1254514f5e3Sopenharmony_ci ASSERT(acc_.GetOpCode(loopExit) == OpCode::IF_TRUE || acc_.GetOpCode(loopExit) == OpCode::IF_FALSE); 1264514f5e3Sopenharmony_ci GateRef conditionJump = acc_.GetState(loopExit); 1274514f5e3Sopenharmony_ci GateRef cmp = acc_.GetValueIn(conditionJump); 1284514f5e3Sopenharmony_ci if (acc_.GetOpCode(cmp) != OpCode::TYPED_BINARY_OP || !IsLessOrGreaterCmp(cmp)) { 1294514f5e3Sopenharmony_ci return false; 1304514f5e3Sopenharmony_ci } 1314514f5e3Sopenharmony_ci GateRef limitGate = acc_.GetValueIn(cmp, 1); 1324514f5e3Sopenharmony_ci if (!IsIntConstant(limitGate)) { 1334514f5e3Sopenharmony_ci return false; 1344514f5e3Sopenharmony_ci } 1354514f5e3Sopenharmony_ci int32_t limit = GetIntFromTaggedConstant(limitGate); 1364514f5e3Sopenharmony_ci 1374514f5e3Sopenharmony_ci GateRef selector = acc_.GetValueIn(cmp, 0); 1384514f5e3Sopenharmony_ci if (!IsInductionVariable(selector)) { 1394514f5e3Sopenharmony_ci return false; 1404514f5e3Sopenharmony_ci } 1414514f5e3Sopenharmony_ci 1424514f5e3Sopenharmony_ci auto [start, stride] = GetStartAndStride(selector); 1434514f5e3Sopenharmony_ci 1444514f5e3Sopenharmony_ci bool cmpFlag = (acc_.GetOpCode(loopExit) == OpCode::IF_TRUE) ^ 1454514f5e3Sopenharmony_ci (acc_.GetTypedJumpAccessor(conditionJump).GetTypedJumpOp() == TypedJumpOp::TYPED_JEQZ) ^ 1464514f5e3Sopenharmony_ci (acc_.GetTypedBinaryOp(cmp) == TypedBinOp::TYPED_LESSEQ || 1474514f5e3Sopenharmony_ci acc_.GetTypedBinaryOp(cmp) == TypedBinOp::TYPED_LESS); 1484514f5e3Sopenharmony_ci bool equalFlag = (acc_.GetOpCode(loopExit) == OpCode::IF_TRUE) ^ 1494514f5e3Sopenharmony_ci (acc_.GetTypedJumpAccessor(conditionJump).GetTypedJumpOp() == TypedJumpOp::TYPED_JEQZ) ^ 1504514f5e3Sopenharmony_ci (acc_.GetTypedBinaryOp(cmp) == TypedBinOp::TYPED_GREATEREQ || 1514514f5e3Sopenharmony_ci acc_.GetTypedBinaryOp(cmp) == TypedBinOp::TYPED_LESSEQ); 1524514f5e3Sopenharmony_ci 1534514f5e3Sopenharmony_ci // a + xb >= c -> c - xb <= a 1544514f5e3Sopenharmony_ci if (!cmpFlag) { 1554514f5e3Sopenharmony_ci std::swap(start, limit); 1564514f5e3Sopenharmony_ci stride = -stride; 1574514f5e3Sopenharmony_ci } 1584514f5e3Sopenharmony_ci // a + xb < c -> a + xb <= c - 1 1594514f5e3Sopenharmony_ci if (!equalFlag) { 1604514f5e3Sopenharmony_ci limit--; 1614514f5e3Sopenharmony_ci } 1624514f5e3Sopenharmony_ci if (start > limit) { 1634514f5e3Sopenharmony_ci loopTimes = 0; 1644514f5e3Sopenharmony_ci return true; 1654514f5e3Sopenharmony_ci } 1664514f5e3Sopenharmony_ci loopTimes = (limit - start) / stride + 1; 1674514f5e3Sopenharmony_ci if (IsLogEnabled() && IsTraced()) { 1684514f5e3Sopenharmony_ci LOG_COMPILER(INFO) << "loopTimes: "<< loopTimes << " start: " << start 1694514f5e3Sopenharmony_ci << " stride: " << stride << " limit: " << limit; 1704514f5e3Sopenharmony_ci } 1714514f5e3Sopenharmony_ci return true; 1724514f5e3Sopenharmony_ci} 1734514f5e3Sopenharmony_ci 1744514f5e3Sopenharmony_civoid InductionVariableAnalysis::CollectInductionSelector() 1754514f5e3Sopenharmony_ci{ 1764514f5e3Sopenharmony_ci for (const auto &loop : graphLinearizer_.loops_) { 1774514f5e3Sopenharmony_ci int32_t loopTimes = 0; 1784514f5e3Sopenharmony_ci 1794514f5e3Sopenharmony_ci if (TryGetLoopTimes(loop, loopTimes)) { 1804514f5e3Sopenharmony_ci ReplaceInductionVariable(loop, loopTimes); 1814514f5e3Sopenharmony_ci } 1824514f5e3Sopenharmony_ci } 1834514f5e3Sopenharmony_ci} 1844514f5e3Sopenharmony_ci 1854514f5e3Sopenharmony_civoid InductionVariableAnalysis::ReplaceInductionVariable(const GraphLinearizer::LoopInfo& loop, 1864514f5e3Sopenharmony_ci const int32_t loopTimes) 1874514f5e3Sopenharmony_ci{ 1884514f5e3Sopenharmony_ci GateRef loopBegin = loop.loopHead->GetState(); 1894514f5e3Sopenharmony_ci auto uses = acc_.Uses(loopBegin); 1904514f5e3Sopenharmony_ci for (auto it = uses.begin(); it != uses.end(); it++) { 1914514f5e3Sopenharmony_ci if (acc_.GetOpCode(*it) == OpCode::VALUE_SELECTOR) { 1924514f5e3Sopenharmony_ci ASSERT(acc_.GetState(*it) == loopBegin); 1934514f5e3Sopenharmony_ci if (!IsInductionVariable(*it)) { 1944514f5e3Sopenharmony_ci continue; 1954514f5e3Sopenharmony_ci } 1964514f5e3Sopenharmony_ci auto [start, stride] = GetStartAndStride(*it); 1974514f5e3Sopenharmony_ci int64_t result = start + static_cast<int64_t>(stride) * loopTimes; 1984514f5e3Sopenharmony_ci if (result > static_cast<int64_t>(INT_MAX) || result < static_cast<int64_t>(INT_MIN)) { 1994514f5e3Sopenharmony_ci return; 2004514f5e3Sopenharmony_ci } 2014514f5e3Sopenharmony_ci if (IsLogEnabled() && IsTraced()) { 2024514f5e3Sopenharmony_ci LOG_COMPILER(INFO) << "result = " << start << " + " << stride << " * " 2034514f5e3Sopenharmony_ci << loopTimes << " = " << result; 2044514f5e3Sopenharmony_ci } 2054514f5e3Sopenharmony_ci TryReplaceOutOfLoopUses(*it, loop, static_cast<int32_t>(result)); 2064514f5e3Sopenharmony_ci } 2074514f5e3Sopenharmony_ci } 2084514f5e3Sopenharmony_ci} 2094514f5e3Sopenharmony_ci 2104514f5e3Sopenharmony_civoid InductionVariableAnalysis::TryReplaceOutOfLoopUses(GateRef gate, 2114514f5e3Sopenharmony_ci const GraphLinearizer::LoopInfo& loop, 2124514f5e3Sopenharmony_ci const int32_t result) 2134514f5e3Sopenharmony_ci{ 2144514f5e3Sopenharmony_ci ASSERT(IsInductionVariable(gate)); 2154514f5e3Sopenharmony_ci auto uses = acc_.Uses(gate); 2164514f5e3Sopenharmony_ci for (auto it = uses.begin(); it != uses.end();) { 2174514f5e3Sopenharmony_ci auto region = graphLinearizer_.GateToRegion(*it); 2184514f5e3Sopenharmony_ci if (!loop.loopBodys->TestBit(region->GetId()) && loop.loopHead != region) { 2194514f5e3Sopenharmony_ci GateRef constantValue = builder_.Int32(result); 2204514f5e3Sopenharmony_ci it = acc_.ReplaceIn(it, constantValue); 2214514f5e3Sopenharmony_ci } else { 2224514f5e3Sopenharmony_ci it++; 2234514f5e3Sopenharmony_ci } 2244514f5e3Sopenharmony_ci } 2254514f5e3Sopenharmony_ci} 2264514f5e3Sopenharmony_ci 2274514f5e3Sopenharmony_civoid InductionVariableAnalysis::Run() 2284514f5e3Sopenharmony_ci{ 2294514f5e3Sopenharmony_ci graphLinearizer_.SetScheduleJSOpcode(); 2304514f5e3Sopenharmony_ci graphLinearizer_.LinearizeGraph(); 2314514f5e3Sopenharmony_ci CollectInductionSelector(); 2324514f5e3Sopenharmony_ci if (IsLogEnabled()) { 2334514f5e3Sopenharmony_ci LOG_COMPILER(INFO) << ""; 2344514f5e3Sopenharmony_ci LOG_COMPILER(INFO) << "\033[34m" 2354514f5e3Sopenharmony_ci << "====================" 2364514f5e3Sopenharmony_ci << " After Induction Variable Analysis " 2374514f5e3Sopenharmony_ci << "[" << GetMethodName() << "]" 2384514f5e3Sopenharmony_ci << "====================" 2394514f5e3Sopenharmony_ci << "\033[0m"; 2404514f5e3Sopenharmony_ci circuit_->PrintAllGatesWithBytecode(); 2414514f5e3Sopenharmony_ci LOG_COMPILER(INFO) << "\033[34m" << "========================= End ==========================" << "\033[0m"; 2424514f5e3Sopenharmony_ci } 2434514f5e3Sopenharmony_ci} 2444514f5e3Sopenharmony_ci 2454514f5e3Sopenharmony_ci}