1/*
2 * Copyright (c) 2022-2024 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
16#include "ecmascript/compiler/async_function_lowering.h"
17
18#include "ecmascript/js_generator_object.h"
19#include "ecmascript/compiler/circuit_builder-inl.h"
20
21namespace panda::ecmascript::kungfu {
22void AsyncFunctionLowering::ProcessAll()
23{
24    ProcessJumpTable();
25
26    if (IsLogEnabled()) {
27        LOG_COMPILER(INFO) << "";
28        LOG_COMPILER(INFO) << "\033[34m"
29                           << "===================="
30                           << " After async function lowering "
31                           << "[" << GetMethodName() << "]"
32                           << "===================="
33                           << "\033[0m";
34        circuit_->PrintAllGatesWithBytecode();
35        LOG_COMPILER(INFO) << "\033[34m" << "========================= End ==========================" << "\033[0m";
36    }
37}
38
39void AsyncFunctionLowering::ProcessJumpTable()
40{
41    GateRef newTarget = argAccessor_.GetCommonArgGate(CommonArgIdx::NEW_TARGET);
42    GateRef isEqual = builder_.Equal(newTarget, builder_.Undefined());
43    auto firstUse = accessor_.ConstUses(stateEntry_).begin();
44    GateRef ifBranchCondition = builder_.Branch(stateEntry_, isEqual, 1, 1, "checkNewTarget");
45    GateRef ifTrueCondition = builder_.IfTrue(ifBranchCondition);
46    GateRef ifFalseCondition = builder_.IfFalse(ifBranchCondition);
47    while (accessor_.GetOpCode(*firstUse) == OpCode::STATE_SPLIT) {
48        firstUse++;
49    }
50    accessor_.ReplaceStateIn(*firstUse, ifTrueCondition);
51
52    GateRef contextOffset = builder_.IntPtr(JSGeneratorObject::GENERATOR_CONTEXT_OFFSET);
53    GateRef val = builder_.PtrAdd(newTarget, contextOffset);
54    GateRef dependStart = builder_.DependRelay(ifFalseCondition, dependEntry_);
55    auto bit = LoadStoreAccessor::ToValue(MemoryAttribute::Default());
56    GateRef contextGate = circuit_->NewGate(circuit_->Load(bit), MachineType::I64, {dependStart, val},
57                                            GateType::TaggedPointer());
58    GateRef bcOffset = builder_.IntPtr(GeneratorContext::GENERATOR_BC_OFFSET_OFFSET);
59    val = builder_.PtrAdd(contextGate, bcOffset);
60    GateRef restoreOffsetGate = circuit_->NewGate(circuit_->Load(bit), MachineType::I32, {contextGate, val},
61                                                  GateType::NJSValue());
62    GateRef firstState = Circuit::NullGate();
63    const auto &suspendAndResumeGates = bcBuilder_->GetAsyncRelatedGates();
64    for (const auto &gate : suspendAndResumeGates) {
65        EcmaOpcode ecmaOpcode = accessor_.GetByteCodeOpcode(gate);
66        if (ecmaOpcode == EcmaOpcode::RESUMEGENERATOR) {
67            RebuildGeneratorCfg(gate, restoreOffsetGate, ifFalseCondition, newTarget, firstState);
68        }
69    }
70}
71
72void AsyncFunctionLowering::RebuildGeneratorCfg(GateRef resumeGate, GateRef restoreOffsetGate, GateRef ifFalseCondition,
73                                                GateRef newTarget, GateRef &firstState)
74{
75    GateRef stateGate = accessor_.GetState(resumeGate);
76    GateRef suspendGate = stateGate;
77    if (accessor_.GetOpCode(suspendGate) == OpCode::IF_SUCCESS) {
78        suspendGate = accessor_.GetState(suspendGate);
79    }
80    GateRef offsetConstantGate = accessor_.GetValueIn(suspendGate);
81    offsetConstantGate = builder_.TruncInt64ToInt32(offsetConstantGate);
82    auto stateInGate = accessor_.GetState(resumeGate);
83    bool flag = true;
84    GateRef prevLoopBeginGate = Circuit::NullGate();
85    GateRef loopBeginStateIn = Circuit::NullGate();
86    GateRef prevBcOffsetPhiGate = Circuit::NullGate();
87    while (true) {
88        if (stateInGate == GetEntryBBStateOut()) {  // from state entry
89            GateRef condition = builder_.Equal(offsetConstantGate, restoreOffsetGate);
90            GateRef ifBranch = circuit_->NewGate(circuit_->IfBranch(0), { ifFalseCondition, condition });
91            GateRef ifTrue = circuit_->NewGate(circuit_->IfTrue(), {ifBranch});
92            GateRef ifFalse = circuit_->NewGate(circuit_->IfFalse(), {ifBranch});
93            GateRef ifTrueDepend = builder_.DependRelay(ifTrue, restoreOffsetGate);
94            GateRef ifFalseDepend = builder_.DependRelay(ifFalse, restoreOffsetGate);
95            if (flag) {
96                accessor_.ReplaceStateIn(resumeGate, ifTrue);
97                accessor_.ReplaceValueIn(resumeGate, newTarget);
98                accessor_.ReplaceDependIn(resumeGate, ifTrueDepend);
99                circuit_->NewGate(circuit_->Return(), MachineType::NOVALUE,
100                    { stateGate, suspendGate, suspendGate, circuit_->GetReturnRoot() },
101                    GateType::AnyType());
102            } else {
103                loopBeginStateIn = ifTrue;
104            }
105            accessor_.ReplaceStateIn(ifBranch, ifFalseCondition);
106            if (firstState != Circuit::NullGate()) {
107                accessor_.ReplaceStateIn(firstState, ifFalse);
108            } else {
109                auto constant = builder_.UndefineConstant();
110                circuit_->NewGate(circuit_->Return(), MachineType::NOVALUE,
111                    { ifFalse, ifFalseDepend, constant, circuit_->GetReturnRoot() },
112                    GateType::AnyType());
113            }
114            firstState = ifBranch;
115        }
116        auto opcode = accessor_.GetOpCode(stateInGate);
117        if (opcode == OpCode::LOOP_BEGIN) {
118            bool resumeInLoopBody = false;
119            CheckResumeInLoopBody(stateInGate, resumeInLoopBody);
120            if (resumeInLoopBody) {
121                // This constant gate must be created by the NewGate method to distinguish whether the while
122                // loop needs to modify the phi node or not.
123                GateRef emptyOffsetGate = circuit_->NewGate(circuit_->GetMetaBuilder()->Constant(-1),
124                                                            MachineType::I32, GateType::NJSValue());
125
126                auto numIn = accessor_.GetNumIns(stateInGate);
127                std::vector<GateRef> inList(numIn + 1, emptyOffsetGate);
128                inList[0] = stateInGate; // 0 : state in
129                inList[1] = restoreOffsetGate; // 1 : outloop value in
130                GateRef bcOffsetPhiGate = circuit_->NewGate(circuit_->ValueSelector(numIn), MachineType::I32,
131                                                            inList, GateType::NJSValue());
132
133                GateRef condition = builder_.Equal(offsetConstantGate, bcOffsetPhiGate);
134                GateRef ifBranch = circuit_->NewGate(circuit_->IfBranch(0), {stateInGate, condition});
135                GateRef ifTrue = circuit_->NewGate(circuit_->IfTrue(), {ifBranch});
136                GateRef ifFalse = circuit_->NewGate(circuit_->IfFalse(), {ifBranch});
137
138                GateRef resumeStateGate = accessor_.GetState(resumeGate);
139                if (accessor_.GetOpCode(resumeStateGate) != OpCode::IF_TRUE) {
140                    accessor_.ReplaceStateIn(resumeGate, ifTrue);
141                    accessor_.ReplaceValueIn(resumeGate, newTarget);
142                    accessor_.ReplaceDependIn(resumeGate, GetDependPhiFromLoopBegin(stateInGate));
143                    circuit_->NewGate(circuit_->Return(), MachineType::NOVALUE,
144                        { stateGate, suspendGate, suspendGate, circuit_->GetReturnRoot() },
145                        GateType::AnyType());
146                } else {
147                    // Handling multi-layer for loops
148                    // When in a multi-layer loop, the value-selector node of the prev-loop
149                    // should be used directly instead of generating a new node
150                    UpdateValueSelector(prevLoopBeginGate, ifTrue, prevBcOffsetPhiGate, false);
151                    accessor_.ReplaceValueIn(prevBcOffsetPhiGate, bcOffsetPhiGate);
152                }
153                accessor_.ReplaceStateIn(ifBranch, stateInGate);
154                ModifyStateInput(stateInGate, ifBranch, ifFalse);
155
156                prevLoopBeginGate = stateInGate;
157                prevBcOffsetPhiGate = bcOffsetPhiGate;
158                stateInGate = accessor_.GetState(stateInGate);
159                flag = false;
160                continue;
161            }
162        }
163        if (loopBeginStateIn != Circuit::NullGate()) {
164            UpdateValueSelector(prevLoopBeginGate, loopBeginStateIn, prevBcOffsetPhiGate);
165            break;
166        }
167        if (stateInGate == GetEntryBBStateOut()) {
168            break;
169        }
170        stateInGate = accessor_.GetState(stateInGate);
171    }
172}
173
174void AsyncFunctionLowering::UpdateValueSelector(GateRef prevLoopBeginGate,
175                                                GateRef controlStateGate,
176                                                GateRef prevBcOffsetPhiGate,
177                                                bool genNewValuePhiGate)
178{
179    GateRef loopBeginFirstState = accessor_.GetState(prevLoopBeginGate);
180    // 2: statesIn
181    GateRef newGate = circuit_->NewGate(circuit_->Merge(2),
182                                        {controlStateGate, loopBeginFirstState});
183
184    if (genNewValuePhiGate) {
185        GateRef emptyOffsetGate =
186            circuit_->NewGate(circuit_->GetMetaBuilder()->Constant(-1), // -1: distinguish bcoffset
187            MachineType::I32, GateType::NJSValue());
188        GateRef restoreOffset = accessor_.GetValueIn(prevBcOffsetPhiGate);
189        // this value selector is compatible with await in the loop body
190        GateRef valueSelector = circuit_->NewGate(circuit_->ValueSelector(2), MachineType::I32, // 2: num of valueIn
191                                                  {newGate, restoreOffset, emptyOffsetGate},
192                                                  GateType::NJSValue());
193        accessor_.ReplaceValueIn(prevBcOffsetPhiGate, valueSelector);
194    }
195    accessor_.ReplaceStateIn(prevLoopBeginGate, newGate);
196    auto loopBeginUses = accessor_.Uses(prevLoopBeginGate);
197    for (auto use : loopBeginUses) {
198        if (accessor_.GetOpCode(use) == OpCode::VALUE_SELECTOR && use != prevBcOffsetPhiGate) {
199            auto machineType = accessor_.GetMachineType(use);
200            auto gateType = accessor_.GetGateType(use);
201            GateRef undefinedGate = Circuit::NullGate();
202            if (gateType.IsNumberType()) {
203                undefinedGate =
204                circuit_->NewGate(circuit_->GetMetaBuilder()->Constant(JSTaggedValue::VALUE_ZERO),
205                                  machineType, GateType::IntType());
206            } else {
207                undefinedGate =
208                circuit_->NewGate(circuit_->GetMetaBuilder()->Constant(JSTaggedValue::VALUE_UNDEFINED),
209                                  machineType, gateType);
210            }
211            auto firstValueGate = accessor_.GetValueIn(use, 0);
212            auto newValueSelector = circuit_->NewGate(circuit_->ValueSelector(2), machineType, // 2: valuesIn
213                                                      {newGate, undefinedGate, firstValueGate},
214                                                      gateType);
215            accessor_.ReplaceValueIn(use, newValueSelector);
216        } else if (accessor_.GetOpCode(use) == OpCode::DEPEND_SELECTOR) {
217            // if there is a dependSelector in the use node of the loop-begin, a new dependSelector node needs
218            // to be generated. This node is bound to the merge node (newGate) before the loop-begin, and its
219            // input corresponds to the 'dependEntry' (not the frist time enter the function) and
220            // 'dependGate' (the first time enter the function) nodes.
221            auto dependGate = accessor_.GetDep(use);
222            auto newDependSelector = circuit_->NewGate(circuit_->DependSelector(2), // 2: num of dependIn
223                                                       {newGate, circuit_->GetDependRoot(), dependGate});
224            accessor_.ReplaceDependIn(use, newDependSelector);
225        }
226    }
227}
228
229bool AsyncFunctionLowering::IsAsyncRelated() const
230{
231    return bcBuilder_->GetAsyncRelatedGates().size() > 0;
232}
233
234void AsyncFunctionLowering::ModifyStateInput(GateRef stateInGate, GateRef ifBranch, GateRef ifFalse)
235{
236    // Find the node with LOOP_BEGIN as State input and modify its
237    // state input to the newly created IF_FALSE node.
238    auto uses = accessor_.Uses(stateInGate);
239    for (auto useIt = uses.begin(); useIt != uses.end();) {
240        GateRef use = *useIt;
241        if (accessor_.IsState(use) && use != ifBranch) {
242            useIt = accessor_.ReplaceIn(useIt, ifFalse);
243        } else {
244            useIt++;
245        }
246    }
247}
248
249void AsyncFunctionLowering::CheckResumeInLoopBody(GateRef stateInGate, bool &resumeInLoopBody)
250{
251    ASSERT(accessor_.GetOpCode(stateInGate) == OpCode::LOOP_BEGIN);
252    ChunkQueue<GateRef> resumeList(circuit_->chunk());
253    ChunkVector<VisitState> visited(circuit_->GetMaxGateId() + 1, VisitState::UNVISITED, circuit_->chunk());
254    for (size_t i = 0; i < accessor_.GetNumIns(stateInGate); i++) {
255        GateRef inGate = accessor_.GetIn(stateInGate, i);
256        if (accessor_.GetOpCode(inGate) == OpCode::LOOP_BACK) {
257            resumeList.push(inGate);
258            visited[accessor_.GetId(inGate)] = VisitState::VISITED;
259        }
260    }
261    auto loopBeginId = accessor_.GetId(stateInGate);
262    visited[loopBeginId] = VisitState::VISITED;
263    while (!resumeList.empty()) {
264        GateRef curGate = resumeList.front();
265        if (accessor_.GetOpCode(curGate) == OpCode::JS_BYTECODE &&
266            accessor_.GetByteCodeOpcode(curGate) == EcmaOpcode::RESUMEGENERATOR) {
267            resumeInLoopBody = true;
268            break;
269        }
270        resumeList.pop();
271        size_t stateStart = 0;
272        size_t stateEnd = accessor_.GetStateCount(curGate);
273        for (size_t idx = stateStart; idx < stateEnd; idx++) {
274            GateRef gate = accessor_.GetState(curGate, idx);
275            auto id = accessor_.GetId(gate);
276            if (visited[id] == VisitState::UNVISITED) {
277                visited[id] = VisitState::VISITED;
278                resumeList.push(gate);
279            }
280        }
281    }
282}
283
284GateRef AsyncFunctionLowering::GetDependPhiFromLoopBegin(GateRef gate) const
285{
286    auto loopBeginUses = accessor_.ConstUses(gate);
287    for (auto use : loopBeginUses) {
288        if (accessor_.GetOpCode(use) == OpCode::DEPEND_SELECTOR) {
289            return use;
290        }
291    }
292    LOG_COMPILER(FATAL) << "Can not find depend-selector from loopbegin";
293    return Circuit::NullGate();
294}
295
296GateRef AsyncFunctionLowering::GetEntryBBStateOut() const
297{
298    auto& bb = bcBuilder_->GetBasicBlockById(0);   // 0 : Entry Block Id
299    // state may CheckSafePointAndStackOver
300    auto state = bb.dependCache;
301    if (state == Circuit::NullGate()) {
302        return circuit_->GetStateRoot();
303    } else {
304        return state;
305    }
306}
307
308GateRef AsyncFunctionLowering::GetEntryBBDependOut() const
309{
310    auto& bb = bcBuilder_->GetBasicBlockById(0);   // 0 : Entry Block Id
311    auto depend = bb.dependCache;
312    if (depend == Circuit::NullGate()) {
313        return circuit_->GetDependRoot();
314    } else {
315        return depend;
316    }
317}
318}  // panda::ecmascript::kungfu
319
320