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