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