• 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 #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