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}