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