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 #include "ecmascript/compiler/bytecode_circuit_builder.h"
16 #include "ecmascript/compiler/bytecodes.h"
17 #include "ecmascript/compiler/compiler_log.h"
18 #include "ecmascript/compiler/pass.h"
19 #include "ecmascript/compiler/ts_inline_lowering.h"
20 #include "ecmascript/ts_types/ts_manager.h"
21 #include "ecmascript/ts_types/ts_type.h"
22 #include "libpandabase/utils/utf.h"
23 #include "libpandafile/class_data_accessor-inl.h"
24
25 namespace panda::ecmascript::kungfu {
RunTSInlineLowering()26 void TSInlineLowering::RunTSInlineLowering()
27 {
28 std::vector<GateRef> gateList;
29 circuit_->GetAllGates(gateList);
30 for (const auto &gate : gateList) {
31 auto op = acc_.GetOpCode(gate);
32 if (op == OpCode::JS_BYTECODE) {
33 TryInline(gate);
34 }
35 }
36 }
37
TryInline(GateRef gate)38 void TSInlineLowering::TryInline(GateRef gate)
39 {
40 EcmaOpcode ecmaOpcode = acc_.GetByteCodeOpcode(gate);
41 switch (ecmaOpcode) {
42 case EcmaOpcode::CALLARG0_IMM8:
43 case EcmaOpcode::CALLARG1_IMM8_V8:
44 case EcmaOpcode::CALLARGS2_IMM8_V8_V8:
45 case EcmaOpcode::CALLARGS3_IMM8_V8_V8_V8:
46 case EcmaOpcode::CALLRANGE_IMM8_IMM8_V8:
47 case EcmaOpcode::WIDE_CALLRANGE_PREF_IMM16_V8:
48 TryInline(gate, false);
49 break;
50 case EcmaOpcode::CALLTHIS0_IMM8_V8:
51 case EcmaOpcode::CALLTHIS1_IMM8_V8_V8:
52 case EcmaOpcode::CALLTHIS2_IMM8_V8_V8_V8:
53 case EcmaOpcode::CALLTHIS3_IMM8_V8_V8_V8_V8:
54 case EcmaOpcode::CALLTHISRANGE_IMM8_IMM8_V8:
55 case EcmaOpcode::WIDE_CALLTHISRANGE_PREF_IMM16_V8:
56 TryInline(gate, true);
57 break;
58 default:
59 break;
60 }
61 }
62
TryInline(GateRef gate,bool isCallThis)63 void TSInlineLowering::TryInline(GateRef gate, bool isCallThis)
64 {
65 // first elem is function in old isa
66 size_t funcIndex = acc_.GetNumValueIn(gate) - 1;
67 auto funcType = acc_.GetGateType(acc_.GetValueIn(gate, funcIndex));
68 MethodLiteral* inlinedMethod = nullptr;
69 if (tsManager_->IsFunctionTypeKind(funcType)) {
70 GlobalTSTypeRef gt = funcType.GetGTRef();
71 auto methodOffset = tsManager_->GetFuncMethodOffset(gt);
72 if (methodOffset == 0 || info_->IsSkippedMethod(methodOffset)) {
73 return;
74 }
75 inlinedMethod = info_->GetJSPandaFile()->FindMethodLiteral(methodOffset);
76 if (!CheckParameter(gate, isCallThis, inlinedMethod)) {
77 return;
78 }
79 auto &bytecodeInfo = info_->GetBytecodeInfo();
80 auto &methodInfo = bytecodeInfo.GetMethodList().at(methodOffset);
81 auto &methodPcInfos = bytecodeInfo.GetMethodPcInfos();
82 auto &methodPcInfo = methodPcInfos[methodInfo.GetMethodPcInfoIndex()];
83 if (methodPcInfo.pcOffsets.size() <= MAX_INLINE_BYTECODE_COUNT &&
84 inlinedCall_ <= MAX_INLINE_CALL_ALLOWED) {
85 auto success = FilterInlinedMethod(inlinedMethod, methodPcInfo.pcOffsets);
86 if (success) {
87 CircuitRootScope scope(circuit_);
88 InlineCall(methodInfo, methodPcInfo, inlinedMethod);
89 ReplaceCallInput(gate, isCallThis);
90 inlinedCall_++;
91 }
92 }
93 }
94
95 if ((inlinedMethod != nullptr) && IsLogEnabled()) {
96 auto jsPandaFile = info_->GetJSPandaFile();
97 const std::string methodName(
98 MethodLiteral::GetMethodName(jsPandaFile, inlinedMethod->GetMethodId()));
99 std::string fileName = jsPandaFile->GetFileName();
100 std::string fullName = methodName + "@" + fileName;
101 LOG_COMPILER(INFO) << "";
102 LOG_COMPILER(INFO) << "\033[34m"
103 << "===================="
104 << " After inlining "
105 << "[" << fullName << "]"
106 << "===================="
107 << "\033[0m";
108 circuit_->PrintAllGatesWithBytecode();
109 LOG_COMPILER(INFO) << "\033[34m" << "========================= End ==========================" << "\033[0m";
110 }
111 }
112
FilterInlinedMethod(MethodLiteral * method,std::vector<const uint8_t * > pcOffsets)113 bool TSInlineLowering::FilterInlinedMethod(MethodLiteral* method, std::vector<const uint8_t*> pcOffsets)
114 {
115 const JSPandaFile *jsPandaFile = info_->GetJSPandaFile();
116 const panda_file::File *pf = jsPandaFile->GetPandaFile();
117 panda_file::MethodDataAccessor mda(*pf, method->GetMethodId());
118 panda_file::CodeDataAccessor cda(*pf, mda.GetCodeId().value());
119 if (cda.GetTriesSize() != 0) {
120 return false;
121 }
122 for (size_t i = 0; i < pcOffsets.size(); i++) {
123 auto pc = pcOffsets[i];
124 auto ecmaOpcode = info_->GetByteCodes()->GetOpcode(pc);
125 switch (ecmaOpcode) {
126 case EcmaOpcode::GETUNMAPPEDARGS:
127 case EcmaOpcode::SUSPENDGENERATOR_V8:
128 case EcmaOpcode::RESUMEGENERATOR:
129 case EcmaOpcode::COPYRESTARGS_IMM8:
130 case EcmaOpcode::WIDE_COPYRESTARGS_PREF_IMM16:
131 case EcmaOpcode::CREATEASYNCGENERATOROBJ_V8:
132 return false;
133 default:
134 break;
135 }
136 }
137 return true;
138 }
139
GetRecordName(const JSPandaFile * jsPandaFile,panda_file::File::EntityId methodIndex)140 CString TSInlineLowering::GetRecordName(const JSPandaFile *jsPandaFile,
141 panda_file::File::EntityId methodIndex)
142 {
143 const panda_file::File *pf = jsPandaFile->GetPandaFile();
144 panda_file::MethodDataAccessor patchMda(*pf, methodIndex);
145 panda_file::ClassDataAccessor patchCda(*pf, patchMda.GetClassId());
146 CString desc = utf::Mutf8AsCString(patchCda.GetDescriptor());
147 return jsPandaFile->ParseEntryPoint(desc);
148 }
149
InlineCall(MethodInfo & methodInfo,MethodPcInfo & methodPCInfo,MethodLiteral * method)150 void TSInlineLowering::InlineCall(MethodInfo &methodInfo, MethodPcInfo &methodPCInfo, MethodLiteral* method)
151 {
152 const JSPandaFile *jsPandaFile = info_->GetJSPandaFile();
153 TSManager *tsManager = info_->GetTSManager();
154 CompilerLog *log = info_->GetCompilerLog();
155 CString recordName = GetRecordName(jsPandaFile, method->GetMethodId());
156 bool hasTyps = jsPandaFile->HasTSTypes(recordName);
157 if (!hasTyps) {
158 return;
159 }
160 const std::string methodName(MethodLiteral::GetMethodName(jsPandaFile, method->GetMethodId()));
161 std::string fileName = jsPandaFile->GetFileName();
162 std::string fullName = methodName + "@" + fileName;
163
164 circuit_->InitRoot();
165 BytecodeCircuitBuilder builder(jsPandaFile, method, methodPCInfo,
166 tsManager, circuit_,
167 info_->GetByteCodes(), true, IsLogEnabled(),
168 hasTyps, fullName, recordName);
169 {
170 TimeScope timeScope("BytecodeToCircuit", methodName, method->GetMethodId().GetOffset(), log);
171 builder.BytecodeToCircuit();
172 }
173
174 PassData data(&builder, circuit_, info_, log, fullName,
175 methodInfo.GetMethodInfoIndex(), hasTyps, recordName,
176 method, method->GetMethodId().GetOffset());
177 PassRunner<PassData> pipeline(&data);
178 pipeline.RunPass<TypeInferPass>();
179 pipeline.RunPass<AsyncFunctionLoweringPass>();
180 }
181
CheckParameter(GateRef gate,bool isCallThis,MethodLiteral * method)182 bool TSInlineLowering::CheckParameter(GateRef gate, bool isCallThis, MethodLiteral* method)
183 {
184 size_t numIns = acc_.GetNumValueIn(gate);
185 size_t fixedInputsNum = isCallThis ? 1 : 0;
186
187 uint32_t declaredNumArgs = method->GetNumArgsWithCallField();
188 return declaredNumArgs == (numIns - fixedInputsNum);
189 }
190
ReplaceCallInput(GateRef gate,bool isCallThis)191 void TSInlineLowering::ReplaceCallInput(GateRef gate, bool isCallThis)
192 {
193 std::vector<GateRef> vec;
194 GateRef glue = acc_.GetGlueFromArgList();
195 size_t numIns = acc_.GetNumValueIn(gate);
196 // 1: last one elem is function
197 GateRef callTarget = acc_.GetValueIn(gate, numIns - 1);
198 GateRef thisObj = Circuit::NullGate();
199 size_t fixedInputsNum = 0;
200 if (isCallThis) {
201 fixedInputsNum = 1;
202 thisObj = acc_.GetValueIn(gate, 0);
203 } else {
204 thisObj = builder_.Undefined();
205 }
206 // -1: callTarget
207 size_t actualArgc = numIns + NUM_MANDATORY_JSFUNC_ARGS - fixedInputsNum;
208 vec.emplace_back(glue); // glue
209 vec.emplace_back(builder_.Undefined()); // env
210 vec.emplace_back(builder_.Int64(actualArgc)); // argc
211 vec.emplace_back(callTarget);
212 vec.emplace_back(builder_.Undefined()); // newTarget
213 vec.emplace_back(thisObj);
214 // -1: call Target
215 for (size_t i = fixedInputsNum; i < numIns - 1; i++) {
216 vec.emplace_back(acc_.GetValueIn(gate, i));
217 }
218 LowerToInlineCall(gate, vec);
219 }
220
MergeAllReturn(const std::vector<GateRef> & returnVector,GateRef & state,GateRef & depend,size_t numOfIns)221 GateRef TSInlineLowering::MergeAllReturn(const std::vector<GateRef> &returnVector,
222 GateRef &state, GateRef &depend, size_t numOfIns)
223 {
224 auto stateList = std::vector<GateRef>(numOfIns, Circuit::NullGate());
225 auto dependList = std::vector<GateRef>(numOfIns + 1, Circuit::NullGate());
226 auto vaueList = std::vector<GateRef>(numOfIns + 1, Circuit::NullGate());
227
228 dependList[0] = state;
229 vaueList[0] = state;
230 for (size_t i = 0; i < returnVector.size(); i++) {
231 GateRef returnGate = returnVector.at(i);
232 ASSERT(acc_.GetOpCode(acc_.GetState(returnGate)) != OpCode::IF_EXCEPTION);
233 stateList[i] = acc_.GetState(returnGate);
234 dependList[i + 1] = acc_.GetDep(returnGate);
235 vaueList[i + 1] = acc_.GetValueIn(returnGate, 0);
236 acc_.DeleteGate(returnGate);
237 }
238
239 state = circuit_->NewGate(circuit_->Merge(numOfIns), stateList);
240 depend = circuit_->NewGate(circuit_->DependSelector(numOfIns), dependList);
241 return circuit_->NewGate(circuit_->ValueSelector(numOfIns), MachineType::I64, numOfIns,
242 vaueList.data(), GateType::AnyType());
243 }
244
ReplaceEntryGate(GateRef callGate)245 void TSInlineLowering::ReplaceEntryGate(GateRef callGate)
246 {
247 auto stateEntry = acc_.GetStateRoot();
248 auto dependEntry = acc_.GetDependRoot();
249
250 GateRef callState = acc_.GetState(callGate);
251 GateRef callDepend = acc_.GetDep(callGate);
252 auto stateUse = acc_.Uses(stateEntry).begin();
253 acc_.ReplaceIn(stateUse, callState);
254 auto dependUse = acc_.Uses(dependEntry).begin();
255 acc_.ReplaceIn(dependUse, callDepend);
256 }
257
ReplaceReturnGate(GateRef callGate)258 void TSInlineLowering::ReplaceReturnGate(GateRef callGate)
259 {
260 std::vector<GateRef> returnVector;
261 acc_.GetReturnOuts(returnVector);
262 size_t successReturn = 0;
263
264 GateRef value = Circuit::NullGate();
265 GateRef state = Circuit::NullGate();
266 GateRef depend = Circuit::NullGate();
267 // 2: if success and if exception
268 for (size_t i = 0; i < returnVector.size(); i++) {
269 GateRef returnGate = returnVector.at(i);
270 GateRef returnState = acc_.GetState(returnGate);
271 if (acc_.GetOpCode(returnState) == OpCode::IF_EXCEPTION) {
272 continue;
273 }
274 successReturn++;
275 ASSERT(acc_.GetOpCode(returnState) == OpCode::IF_SUCCESS ||
276 acc_.GetOpCode(returnState) == OpCode::MERGE);
277 depend = acc_.GetDep(returnGate);
278 state = returnState;
279 value = acc_.GetValueIn(returnGate, 0);
280 acc_.DeleteGate(returnGate);
281 break;
282 }
283 if (successReturn > 1) {
284 value = MergeAllReturn(returnVector, state, depend, successReturn);
285 }
286 ReplaceHirAndDeleteState(callGate, state, depend, value);
287 }
288
ReplaceHirAndDeleteState(GateRef gate,GateRef state,GateRef depend,GateRef value)289 void TSInlineLowering::ReplaceHirAndDeleteState(GateRef gate, GateRef state, GateRef depend, GateRef value)
290 {
291 auto uses = acc_.Uses(gate);
292 for (auto useIt = uses.begin(); useIt != uses.end();) {
293 const OpCode op = acc_.GetOpCode(*useIt);
294 if (op == OpCode::IF_SUCCESS) {
295 auto firstUse = acc_.Uses(*useIt).begin();
296 acc_.ReplaceIn(*firstUse, firstUse.GetIndex(), state);
297 useIt = acc_.DeleteGate(useIt);
298 } else if (op == OpCode::IF_EXCEPTION) {
299 auto exceptionUseIt = acc_.Uses(*useIt).begin();
300 ASSERT(acc_.GetOpCode(*exceptionUseIt) == OpCode::RETURN);
301 acc_.DeleteGate(exceptionUseIt);
302 useIt = acc_.DeleteGate(useIt);
303 } else if (acc_.IsDependIn(useIt)) {
304 useIt = acc_.ReplaceIn(useIt, depend);
305 } else if (acc_.IsValueIn(useIt)) {
306 useIt = acc_.ReplaceIn(useIt, value);
307 } else {
308 UNREACHABLE();
309 }
310 }
311 acc_.DeleteGate(gate);
312 }
313
LowerToInlineCall(GateRef callGate,const std::vector<GateRef> & args)314 void TSInlineLowering::LowerToInlineCall(GateRef callGate, const std::vector<GateRef> &args)
315 {
316 // replace in value/args
317 ArgumentAccessor argAcc(circuit_);
318 ASSERT(argAcc.ArgsCount() == args.size());
319 for (size_t i = 0; i < argAcc.ArgsCount(); i++) {
320 GateRef arg = argAcc.ArgsAt(i);
321 acc_.UpdateAllUses(arg, args.at(i));
322 acc_.DeleteGate(arg);
323 }
324 // replace in depend and state
325 ReplaceEntryGate(callGate);
326 // replace use gate
327 ReplaceReturnGate(callGate);
328 }
329 } // namespace panda::ecmascript
330