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
21 namespace panda::ecmascript::kungfu {
ProcessAll()22 void 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
ProcessJumpTable()39 void 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
RebuildGeneratorCfg(GateRef resumeGate,GateRef restoreOffsetGate,GateRef ifFalseCondition,GateRef newTarget,GateRef & firstState)72 void 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
UpdateValueSelector(GateRef prevLoopBeginGate,GateRef controlStateGate,GateRef prevBcOffsetPhiGate,bool genNewValuePhiGate)174 void 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
IsAsyncRelated() const229 bool AsyncFunctionLowering::IsAsyncRelated() const
230 {
231 return bcBuilder_->GetAsyncRelatedGates().size() > 0;
232 }
233
ModifyStateInput(GateRef stateInGate,GateRef ifBranch,GateRef ifFalse)234 void 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
CheckResumeInLoopBody(GateRef stateInGate,bool & resumeInLoopBody)249 void 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
GetDependPhiFromLoopBegin(GateRef gate) const284 GateRef 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
GetEntryBBStateOut() const296 GateRef 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
GetEntryBBDependOut() const308 GateRef 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