mirror of
https://gitee.com/openharmony/arkcompiler_ets_runtime
synced 2024-10-06 23:54:03 +00:00
57872af0db
Issue:https://gitee.com/openharmony/arkcompiler_ets_runtime/issues/IAGE77 Signed-off-by: daizihan <daizihan@huawei.com> Change-Id: I417867e6ecc69086f50fc339cddfa3042f44cb9f
766 lines
29 KiB
C++
766 lines
29 KiB
C++
/*
|
|
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
#include "ecmascript/compiler/ts_inline_lowering.h"
|
|
|
|
#include "ecmascript/compiler/bytecode_circuit_builder.h"
|
|
#include "ecmascript/compiler/bytecodes.h"
|
|
#include "ecmascript/compiler/compiler_log.h"
|
|
#include "ecmascript/compiler/pass.h"
|
|
#include "ecmascript/compiler/type_info_accessors.h"
|
|
|
|
namespace panda::ecmascript::kungfu {
|
|
void TSInlineLowering::RunTSInlineLowering()
|
|
{
|
|
circuit_->AdvanceTime();
|
|
ChunkQueue<InlineTypeInfoAccessor> workList(chunk_);
|
|
UpdateWorkList(workList);
|
|
|
|
while (!workList.empty()) {
|
|
InlineTypeInfoAccessor &info = workList.front();
|
|
workList.pop();
|
|
TryInline(info, workList);
|
|
}
|
|
CollectInlineInfo();
|
|
}
|
|
|
|
void TSInlineLowering::CollectInlineInfo()
|
|
{
|
|
std::vector<GateRef> gateList;
|
|
circuit_->GetAllGates(gateList);
|
|
for (const auto &gate : gateList) {
|
|
auto op = acc_.GetOpCode(gate);
|
|
if (op == OpCode::FRAME_ARGS) {
|
|
GetInlinedMethodId(gate);
|
|
}
|
|
}
|
|
}
|
|
|
|
void TSInlineLowering::GetInlinedMethodId(GateRef gate)
|
|
{
|
|
ASSERT(acc_.GetOpCode(gate) == OpCode::FRAME_ARGS);
|
|
uint32_t methodOffset = 0;
|
|
auto pgoType = acc_.TryGetPGOType(gate);
|
|
if (pgoType.IsValidCallMethodId()) {
|
|
methodOffset = pgoType.GetCallMethodId();
|
|
}
|
|
acc_.UpdateMethodOffset(gate, methodOffset);
|
|
}
|
|
|
|
void TSInlineLowering::CandidateInlineCall(GateRef gate, ChunkQueue<InlineTypeInfoAccessor> &workList)
|
|
{
|
|
EcmaOpcode ecmaOpcode = acc_.GetByteCodeOpcode(gate);
|
|
switch (ecmaOpcode) {
|
|
case EcmaOpcode::LDOBJBYNAME_IMM8_ID16:
|
|
case EcmaOpcode::LDOBJBYNAME_IMM16_ID16:
|
|
case EcmaOpcode::LDTHISBYNAME_IMM8_ID16:
|
|
case EcmaOpcode::LDTHISBYNAME_IMM16_ID16:
|
|
CandidateAccessor(gate, workList, CallKind::CALL_GETTER);
|
|
break;
|
|
case EcmaOpcode::STOBJBYNAME_IMM8_ID16_V8:
|
|
case EcmaOpcode::STOBJBYNAME_IMM16_ID16_V8:
|
|
case EcmaOpcode::DEFINEFIELDBYNAME_IMM8_ID16_V8:
|
|
case EcmaOpcode::DEFINEPROPERTYBYNAME_IMM8_ID16_V8:
|
|
case EcmaOpcode::STTHISBYNAME_IMM8_ID16:
|
|
case EcmaOpcode::STTHISBYNAME_IMM16_ID16:
|
|
CandidateAccessor(gate, workList, CallKind::CALL_SETTER);
|
|
break;
|
|
case EcmaOpcode::CALLTHIS0_IMM8_V8:
|
|
case EcmaOpcode::CALLTHIS1_IMM8_V8_V8:
|
|
case EcmaOpcode::CALLTHIS2_IMM8_V8_V8_V8:
|
|
case EcmaOpcode::CALLTHIS3_IMM8_V8_V8_V8_V8:
|
|
case EcmaOpcode::CALLTHISRANGE_IMM8_IMM8_V8:
|
|
case EcmaOpcode::WIDE_CALLTHISRANGE_PREF_IMM16_V8:
|
|
CandidateNormalCall(gate, workList, CallKind::CALL_THIS);
|
|
break;
|
|
case EcmaOpcode::CALLRUNTIME_CALLINIT_PREF_IMM8_V8:
|
|
CandidateNormalCall(gate, workList, CallKind::CALL_INIT);
|
|
break;
|
|
case EcmaOpcode::CALLARG0_IMM8:
|
|
case EcmaOpcode::CALLARG1_IMM8_V8:
|
|
case EcmaOpcode::CALLARGS2_IMM8_V8_V8:
|
|
case EcmaOpcode::CALLARGS3_IMM8_V8_V8_V8:
|
|
case EcmaOpcode::CALLRANGE_IMM8_IMM8_V8:
|
|
case EcmaOpcode::WIDE_CALLRANGE_PREF_IMM16_V8:
|
|
CandidateNormalCall(gate, workList, CallKind::CALL);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void TSInlineLowering::TryInline(InlineTypeInfoAccessor &info, ChunkQueue<InlineTypeInfoAccessor> &workList)
|
|
{
|
|
GateRef gate = info.GetCallGate();
|
|
// inline doesn't support try-catch
|
|
bool inTryCatch = FilterCallInTryCatch(gate);
|
|
if (inTryCatch) {
|
|
return;
|
|
}
|
|
|
|
MethodLiteral* inlinedMethod = nullptr;
|
|
uint32_t methodOffset = info.GetCallMethodId();
|
|
if (methodOffset == 0 || ctx_->IsSkippedMethod(methodOffset)) {
|
|
return;
|
|
}
|
|
if (IsRecursiveFunc(info, methodOffset)) {
|
|
return;
|
|
}
|
|
inlinedMethod = ctx_->GetJSPandaFile()->FindMethodLiteral(methodOffset);
|
|
if (!CheckParameter(gate, info, inlinedMethod)) {
|
|
return;
|
|
}
|
|
auto &bytecodeInfo = ctx_->GetBytecodeInfo();
|
|
if (compilationEnv_->IsJitCompiler()) {
|
|
ctx_->GetBytecodeInfoCollector()->ProcessMethod(inlinedMethod);
|
|
}
|
|
ASSERT(bytecodeInfo.GetMethodList().find(methodOffset) != bytecodeInfo.GetMethodList().end());
|
|
auto &methodInfo = bytecodeInfo.GetMethodList().at(methodOffset);
|
|
auto &methodPcInfos = bytecodeInfo.GetMethodPcInfos();
|
|
auto &methodPcInfo = methodPcInfos[methodInfo.GetMethodPcInfoIndex()];
|
|
if (info.IsNormalCall() && ctx_->FilterMethod(inlinedMethod, methodPcInfo)) {
|
|
return;
|
|
}
|
|
GateRef frameState = GetFrameState(info);
|
|
GateRef frameArgs = acc_.GetValueIn(frameState);
|
|
size_t inlineCallCounts = GetOrInitialInlineCounts(frameArgs);
|
|
if (IsSmallMethod(methodPcInfo.pcOffsets.size()) && !IsInlineCountsOverflow(inlineCallCounts)) {
|
|
inlineSuccess_ = FilterInlinedMethod(inlinedMethod, methodPcInfo.pcOffsets);
|
|
if (inlineSuccess_) {
|
|
SetInitCallTargetAndConstPoolId(info);
|
|
CircuitRootScope scope(circuit_);
|
|
if (!noCheck_ && !info.IsCallInit()) {
|
|
InlineCheck(info);
|
|
}
|
|
if (!CalleePFIProcess(methodOffset)) {
|
|
return;
|
|
}
|
|
UpdateCallMethodFlagMap(methodOffset, inlinedMethod);
|
|
InlineCall(methodInfo, methodPcInfo, inlinedMethod, info);
|
|
UpdateInlineCounts(frameArgs, inlineCallCounts);
|
|
if (info.IsNormalCall()) {
|
|
UpdateWorkList(workList);
|
|
} else {
|
|
lastCallId_ = circuit_->GetGateCount() - 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((inlinedMethod != nullptr) && IsLogEnabled() && inlineSuccess_) {
|
|
inlineSuccess_ = false;
|
|
auto jsPandaFile = ctx_->GetJSPandaFile();
|
|
const std::string methodName(
|
|
MethodLiteral::GetMethodName(jsPandaFile, inlinedMethod->GetMethodId()));
|
|
std::string fileName(jsPandaFile->GetJSPandaFileDesc());
|
|
const std::string recordName(MethodLiteral::GetRecordName(jsPandaFile, inlinedMethod->GetMethodId()));
|
|
std::string fullName = methodName + "@" + recordName + "@" + fileName;
|
|
LOG_COMPILER(INFO) << "";
|
|
LOG_COMPILER(INFO) << "\033[34m"
|
|
<< "===================="
|
|
<< " After inlining "
|
|
<< "[" << fullName << "]"
|
|
<< " Caller method "
|
|
<< "[" << methodName_ << "]"
|
|
<< "===================="
|
|
<< "\033[0m";
|
|
circuit_->PrintAllGatesWithBytecode();
|
|
LOG_COMPILER(INFO) << "\033[34m" << "========================= End ==========================" << "\033[0m";
|
|
}
|
|
}
|
|
|
|
bool TSInlineLowering::FilterInlinedMethod(MethodLiteral* method, std::vector<const uint8_t*> pcOffsets)
|
|
{
|
|
const JSPandaFile *jsPandaFile = ctx_->GetJSPandaFile();
|
|
const panda_file::File *pf = jsPandaFile->GetPandaFile();
|
|
panda_file::MethodDataAccessor mda(*pf, method->GetMethodId());
|
|
panda_file::CodeDataAccessor cda(*pf, mda.GetCodeId().value());
|
|
if (cda.GetTriesSize() != 0) {
|
|
return false;
|
|
}
|
|
for (size_t i = 0; i < pcOffsets.size(); i++) {
|
|
auto pc = pcOffsets[i];
|
|
auto ecmaOpcode = ctx_->GetByteCodes()->GetOpcode(pc);
|
|
switch (ecmaOpcode) {
|
|
case EcmaOpcode::GETUNMAPPEDARGS:
|
|
case EcmaOpcode::SUSPENDGENERATOR_V8:
|
|
case EcmaOpcode::RESUMEGENERATOR:
|
|
case EcmaOpcode::COPYRESTARGS_IMM8:
|
|
case EcmaOpcode::WIDE_COPYRESTARGS_PREF_IMM16:
|
|
case EcmaOpcode::CREATEASYNCGENERATOROBJ_V8:
|
|
return false;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void TSInlineLowering::InlineCall(MethodInfo &methodInfo, MethodPcInfo &methodPCInfo, MethodLiteral* method,
|
|
InlineTypeInfoAccessor &info)
|
|
{
|
|
const JSPandaFile *jsPandaFile = ctx_->GetJSPandaFile();
|
|
CompilerLog *log = ctx_->GetCompilerLog();
|
|
CString recordName = MethodLiteral::GetRecordName(jsPandaFile, method->GetMethodId());
|
|
const std::string methodName(MethodLiteral::GetMethodName(jsPandaFile, method->GetMethodId()));
|
|
std::string fileName(jsPandaFile->GetJSPandaFileDesc());
|
|
std::string fullName = methodName + "@" + std::string(recordName) + "@" + fileName;
|
|
|
|
circuit_->InitRoot();
|
|
JITProfiler *profiler = nullptr;
|
|
if (compilationEnv_->IsJitCompiler()) {
|
|
profiler = compilationEnv_->GetPGOProfiler()->GetJITProfile();
|
|
}
|
|
BytecodeCircuitBuilder builder(jsPandaFile, method, methodPCInfo,
|
|
circuit_, ctx_->GetByteCodes(), IsLogEnabled(),
|
|
enableTypeLowering_, fullName, recordName, ctx_->GetPfDecoder(), true, profiler);
|
|
{
|
|
if (enableTypeLowering_) {
|
|
BuildFrameStateChain(info, builder);
|
|
}
|
|
TimeScope timeScope("BytecodeToCircuit", methodName, method->GetMethodId().GetOffset(), log);
|
|
builder.BytecodeToCircuit();
|
|
}
|
|
|
|
ReplaceInput(info, glue_, method);
|
|
|
|
PassData data(&builder, circuit_, ctx_, log, fullName,
|
|
&methodInfo, recordName, method, method->GetMethodId().GetOffset(),
|
|
nullptr, CVector<AbcFileInfo>{},
|
|
nativeAreaAllocator_, ctx_->GetPfDecoder(), passOptions_);
|
|
PassRunner<PassData> pipeline(&data);
|
|
pipeline.RunPass<RedundantPhiEliminationPass>();
|
|
if (builder.EnableLoopOptimization()) {
|
|
pipeline.RunPass<LoopOptimizationPass>();
|
|
pipeline.RunPass<RedundantPhiEliminationPass>();
|
|
}
|
|
pipeline.RunPass<PGOTypeInferPass>();
|
|
}
|
|
|
|
bool TSInlineLowering::CheckParameter(GateRef gate, InlineTypeInfoAccessor &info, MethodLiteral* method)
|
|
{
|
|
if (method == nullptr) {
|
|
return false;
|
|
}
|
|
if (info.IsCallAccessor()) {
|
|
return true;
|
|
}
|
|
size_t numIns = acc_.GetNumValueIn(gate);
|
|
size_t fixedInputsNum = info.IsCallThis() ? 2 : 1; // 2: calltarget and this
|
|
|
|
uint32_t declaredNumArgs = method->GetNumArgsWithCallField();
|
|
ASSERT(numIns >= fixedInputsNum);
|
|
return declaredNumArgs == (numIns - fixedInputsNum);
|
|
}
|
|
|
|
void TSInlineLowering::ReplaceCallInput(InlineTypeInfoAccessor &info, GateRef glue, MethodLiteral *method)
|
|
{
|
|
GateRef gate = info.GetCallGate();
|
|
bool isCallThis = info.IsCallThis();
|
|
std::vector<GateRef> vec;
|
|
size_t numIns = acc_.GetNumValueIn(gate);
|
|
ASSERT(numIns > 0);
|
|
// 1: last one elem is function
|
|
GateRef callTarget = acc_.GetValueIn(gate, numIns - 1);
|
|
GateRef thisObj = Circuit::NullGate();
|
|
size_t fixedInputsNum = 0;
|
|
if (isCallThis) {
|
|
fixedInputsNum = 2; // 2: call target and this
|
|
thisObj = acc_.GetValueIn(gate, 0);
|
|
} else {
|
|
fixedInputsNum = 1; // 1: call target
|
|
thisObj = builder_.Undefined();
|
|
}
|
|
// -1: callTarget
|
|
size_t actualArgc = numIns + NUM_MANDATORY_JSFUNC_ARGS - fixedInputsNum;
|
|
vec.emplace_back(glue); // glue
|
|
if (!method->IsFastCall()) {
|
|
vec.emplace_back(builder_.Int64(actualArgc)); // argc
|
|
vec.emplace_back(builder_.IntPtr(0)); // argv
|
|
}
|
|
vec.emplace_back(callTarget);
|
|
if (!method->IsFastCall()) {
|
|
vec.emplace_back(builder_.Undefined()); // newTarget
|
|
}
|
|
vec.emplace_back(thisObj);
|
|
// -1: call Target
|
|
for (size_t i = fixedInputsNum - 1; i < numIns - 1; i++) {
|
|
vec.emplace_back(acc_.GetValueIn(gate, i));
|
|
}
|
|
LowerToInlineCall(info, vec, method);
|
|
}
|
|
|
|
void TSInlineLowering::ReplaceAccessorInput(InlineTypeInfoAccessor &info, GateRef glue, MethodLiteral *method)
|
|
{
|
|
GateRef gate = info.GetCallGate();
|
|
std::vector<GateRef> vec;
|
|
GateRef thisObj = GetAccessorReceiver(gate);
|
|
GateRef callTarget = Circuit::NullGate();
|
|
callTarget = BuildAccessor(info);
|
|
size_t actualArgc = 0;
|
|
if (info.IsCallGetter()) {
|
|
actualArgc = NUM_MANDATORY_JSFUNC_ARGS;
|
|
} else if (info.IsCallSetter()) {
|
|
actualArgc = NUM_MANDATORY_JSFUNC_ARGS + 1;
|
|
} else {
|
|
UNREACHABLE();
|
|
}
|
|
|
|
vec.emplace_back(glue); // glue
|
|
if (!method->IsFastCall()) {
|
|
vec.emplace_back(builder_.Int64(actualArgc)); // argc
|
|
vec.emplace_back(builder_.IntPtr(0)); // argv
|
|
}
|
|
vec.emplace_back(callTarget);
|
|
if (!method->IsFastCall()) {
|
|
vec.emplace_back(builder_.Undefined()); // newTarget
|
|
}
|
|
vec.emplace_back(thisObj);
|
|
|
|
if (info.IsCallSetter()) {
|
|
vec.emplace_back(GetCallSetterValue(gate));
|
|
}
|
|
LowerToInlineCall(info, vec, method);
|
|
}
|
|
|
|
GateRef TSInlineLowering::BuildAccessor(InlineTypeInfoAccessor &info)
|
|
{
|
|
GateRef gate = info.GetCallGate();
|
|
Environment env(gate, circuit_, &builder_);
|
|
GateRef receiver = GetAccessorReceiver(gate);
|
|
GateRef accessor = Circuit::NullGate();
|
|
uint32_t plrData = info.GetPlr().GetData();
|
|
|
|
const PGORWOpType *pgoTypes = acc_.TryGetPGOType(gate).GetPGORWOpType();
|
|
ASSERT(pgoTypes->GetCount() == 1);
|
|
auto pgoType = pgoTypes->GetObjectInfo(0);
|
|
PGOTypeManager *ptManager = compilationEnv_->GetPTManager();
|
|
int holderHCIndex = ptManager->GetHolderHIndexByPGOObjectInfoType(pgoType, compilationEnv_->IsAotCompiler());
|
|
ArgumentAccessor argAcc(circuit_);
|
|
GateRef unsharedConstPool = argAcc.GetFrameArgsIn(gate, FrameArgIdx::UNSHARED_CONST_POOL);
|
|
|
|
auto currentLabel = env.GetCurrentLabel();
|
|
auto state = currentLabel->GetControl();
|
|
auto depend = currentLabel->GetDepend();
|
|
GateRef frameState = acc_.GetFrameState(gate);
|
|
GateRef holder = circuit_->NewGate(circuit_->LookUpHolder(), MachineType::I64,
|
|
{ state, depend, receiver, builder_.Int32(holderHCIndex), unsharedConstPool, frameState }, GateType::AnyType());
|
|
|
|
if (info.IsCallGetter()) {
|
|
accessor = circuit_->NewGate(circuit_->LoadGetter(), MachineType::I64,
|
|
{ holder, holder, builder_.Int32(plrData) }, GateType::AnyType());
|
|
} else {
|
|
accessor = circuit_->NewGate(circuit_->LoadSetter(), MachineType::I64,
|
|
{ holder, holder, builder_.Int32(plrData) }, GateType::AnyType());
|
|
}
|
|
acc_.ReplaceDependIn(gate, holder);
|
|
acc_.ReplaceStateIn(gate, holder);
|
|
return accessor;
|
|
}
|
|
|
|
void TSInlineLowering::ReplaceInput(InlineTypeInfoAccessor &info, GateRef glue, MethodLiteral *method)
|
|
{
|
|
if (info.IsNormalCall()) {
|
|
ReplaceCallInput(info, glue, method);
|
|
} else {
|
|
ASSERT(info.IsCallAccessor());
|
|
ReplaceAccessorInput(info, glue, method);
|
|
}
|
|
}
|
|
|
|
GateRef TSInlineLowering::MergeAllReturn(const std::vector<GateRef> &returnVector, GateRef &state, GateRef &depend)
|
|
{
|
|
size_t numOfIns = returnVector.size();
|
|
auto stateList = std::vector<GateRef>(numOfIns, Circuit::NullGate());
|
|
auto dependList = std::vector<GateRef>(numOfIns + 1, Circuit::NullGate());
|
|
auto vaueList = std::vector<GateRef>(numOfIns + 1, Circuit::NullGate());
|
|
|
|
for (size_t i = 0; i < returnVector.size(); i++) {
|
|
GateRef returnGate = returnVector.at(i);
|
|
ASSERT(acc_.GetOpCode(acc_.GetState(returnGate)) != OpCode::IF_EXCEPTION);
|
|
stateList[i] = acc_.GetState(returnGate);
|
|
dependList[i + 1] = acc_.GetDep(returnGate);
|
|
vaueList[i + 1] = acc_.GetValueIn(returnGate, 0);
|
|
acc_.DeleteGate(returnGate);
|
|
}
|
|
|
|
state = circuit_->NewGate(circuit_->Merge(numOfIns), stateList);
|
|
dependList[0] = state;
|
|
vaueList[0] = state;
|
|
depend = circuit_->NewGate(circuit_->DependSelector(numOfIns), dependList);
|
|
return circuit_->NewGate(circuit_->ValueSelector(numOfIns), MachineType::I64, numOfIns + 1,
|
|
vaueList.data(), GateType::AnyType());
|
|
}
|
|
|
|
void TSInlineLowering::ReplaceEntryGate(GateRef callGate, GateRef callerFunc, GateRef inlineFunc, GateRef glue)
|
|
{
|
|
auto stateEntry = acc_.GetStateRoot();
|
|
auto dependEntry = acc_.GetDependRoot();
|
|
|
|
GateRef callState = acc_.GetState(callGate);
|
|
GateRef callDepend = acc_.GetDep(callGate);
|
|
auto stateUse = acc_.Uses(stateEntry);
|
|
|
|
// support inline trace
|
|
GateRef newDep = Circuit::NullGate();
|
|
if (traceInline_) {
|
|
std::vector<GateRef> args{callerFunc, inlineFunc};
|
|
newDep = TraceInlineFunction(glue, callDepend, args, callGate);
|
|
} else {
|
|
newDep = callDepend;
|
|
}
|
|
|
|
for (auto stateUseIt = stateUse.begin(); stateUseIt != stateUse.end();) {
|
|
stateUseIt = acc_.ReplaceIn(stateUseIt, callState);
|
|
}
|
|
|
|
auto dependUse = acc_.Uses(dependEntry);
|
|
for (auto dependUseIt = dependUse.begin(); dependUseIt != dependUse.end();) {
|
|
dependUseIt = acc_.ReplaceIn(dependUseIt, newDep);
|
|
}
|
|
}
|
|
|
|
GateRef TSInlineLowering::TraceInlineFunction(GateRef glue, GateRef depend, std::vector<GateRef> &args,
|
|
GateRef callGate)
|
|
{
|
|
size_t index = RTSTUB_ID(AotInlineTrace);
|
|
GateRef result = builder_.NoLabelCallRuntime(glue, depend, index, args, callGate);
|
|
return result;
|
|
}
|
|
|
|
void TSInlineLowering::ReplaceReturnGate(GateRef callGate)
|
|
{
|
|
std::vector<GateRef> returnVector;
|
|
acc_.GetReturnOuts(returnVector);
|
|
|
|
GateRef value = Circuit::NullGate();
|
|
GateRef state = Circuit::NullGate();
|
|
GateRef depend = Circuit::NullGate();
|
|
|
|
if (returnVector.size() == 1) {
|
|
GateRef returnGate = returnVector.at(0);
|
|
GateRef returnState = acc_.GetState(returnGate);
|
|
depend = acc_.GetDep(returnGate);
|
|
state = returnState;
|
|
value = acc_.GetValueIn(returnGate, 0);
|
|
acc_.DeleteGate(returnGate);
|
|
} else {
|
|
value = MergeAllReturn(returnVector, state, depend);
|
|
}
|
|
SupplementType(callGate, value);
|
|
ReplaceHirAndDeleteState(callGate, state, depend, value);
|
|
}
|
|
|
|
void TSInlineLowering::ReplaceHirAndDeleteState(GateRef gate, GateRef state, GateRef depend, GateRef value)
|
|
{
|
|
auto uses = acc_.Uses(gate);
|
|
for (auto useIt = uses.begin(); useIt != uses.end();) {
|
|
if (acc_.IsStateIn(useIt)) {
|
|
useIt = acc_.ReplaceIn(useIt, state);
|
|
} else if (acc_.IsDependIn(useIt)) {
|
|
useIt = acc_.ReplaceIn(useIt, depend);
|
|
} else if (acc_.IsValueIn(useIt)) {
|
|
useIt = acc_.ReplaceIn(useIt, value);
|
|
} else {
|
|
LOG_ECMA(FATAL) << "this branch is unreachable";
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
acc_.DeleteGate(gate);
|
|
}
|
|
|
|
void TSInlineLowering::LowerToInlineCall(
|
|
InlineTypeInfoAccessor &info, const std::vector<GateRef> &args, MethodLiteral *method)
|
|
{
|
|
GateRef callGate = info.GetCallGate();
|
|
// replace in value/args
|
|
ArgumentAccessor argAcc(circuit_);
|
|
ASSERT(argAcc.ArgsCount() == args.size());
|
|
for (size_t i = 0; i < argAcc.ArgsCount(); i++) {
|
|
GateRef arg = argAcc.ArgsAt(i);
|
|
acc_.UpdateAllUses(arg, args.at(i));
|
|
if (acc_.GetGateType(args.at(i)).IsAnyType()) {
|
|
acc_.SetGateType(args.at(i), acc_.GetGateType(arg));
|
|
}
|
|
acc_.DeleteGate(arg);
|
|
}
|
|
// replace in depend and state
|
|
GateRef glue = args.at(static_cast<size_t>(CommonArgIdx::GLUE));
|
|
GateRef inlineFunc;
|
|
if (method->IsFastCall()) {
|
|
inlineFunc = args.at(static_cast<size_t>(FastCallArgIdx::FUNC));
|
|
} else {
|
|
inlineFunc = args.at(static_cast<size_t>(CommonArgIdx::FUNC));
|
|
}
|
|
GateRef frameArgs = GetFrameArgs(info);
|
|
GateRef callerFunc = acc_.GetValueIn(frameArgs, 0);
|
|
ReplaceEntryGate(callGate, callerFunc, inlineFunc, glue);
|
|
// replace use gate
|
|
ReplaceReturnGate(callGate);
|
|
// remove Useless root gates
|
|
RemoveRoot();
|
|
}
|
|
|
|
void TSInlineLowering::InlineFuncCheck(const InlineTypeInfoAccessor &info)
|
|
{
|
|
GateRef gate = info.GetCallGate();
|
|
GateRef callState = acc_.GetState(gate);
|
|
GateRef callDepend = acc_.GetDep(gate);
|
|
ASSERT(acc_.HasFrameState(gate));
|
|
GateRef frameState = acc_.GetFrameState(gate);
|
|
ASSERT(acc_.GetNumValueIn(gate) > 0);
|
|
size_t funcIndex = acc_.GetNumValueIn(gate) - 1;
|
|
GateRef inlineFunc = acc_.GetValueIn(gate, funcIndex);
|
|
// Do not load from inlineFunc because type in inlineFunc could be modified by others
|
|
uint32_t methodOffset = info.GetCallMethodId();
|
|
GateRef ret = circuit_->NewGate(circuit_->JSInlineTargetTypeCheck(info.GetType()),
|
|
MachineType::I1, {callState, callDepend, inlineFunc, builder_.IntPtr(methodOffset), frameState},
|
|
GateType::NJSValue());
|
|
acc_.ReplaceStateIn(gate, ret);
|
|
acc_.ReplaceDependIn(gate, ret);
|
|
}
|
|
|
|
void TSInlineLowering::InlineAccessorCheck(const InlineTypeInfoAccessor &info)
|
|
{
|
|
ASSERT(info.IsCallAccessor());
|
|
GateRef gate = info.GetCallGate();
|
|
GateRef receiver = GetAccessorReceiver(gate);
|
|
Environment env(gate, circuit_, &builder_);
|
|
|
|
const PGORWOpType *pgoTypes = acc_.TryGetPGOType(gate).GetPGORWOpType();
|
|
ASSERT(pgoTypes->GetCount() == 1);
|
|
auto pgoType = pgoTypes->GetObjectInfo(0);
|
|
PGOTypeManager *ptManager = compilationEnv_->GetPTManager();
|
|
int receiverHCIndex = ptManager->GetReceiverHIndexByPGOObjectInfoType(pgoType, compilationEnv_->IsAotCompiler());
|
|
bool noNeedCheckHeapObject = acc_.IsHeapObjectFromElementsKind(receiver);
|
|
builder_.ObjectTypeCheck(noNeedCheckHeapObject, receiver, builder_.Int32(receiverHCIndex),
|
|
acc_.GetFrameState(gate));
|
|
auto currentLabel = env.GetCurrentLabel();
|
|
auto callState = currentLabel->GetControl();
|
|
auto callDepend = currentLabel->GetDepend();
|
|
auto frameState = acc_.GetFrameState(gate);
|
|
ArgumentAccessor argAcc(circuit_);
|
|
GateRef unsharedConstPool = argAcc.GetFrameArgsIn(gate, FrameArgIdx::UNSHARED_CONST_POOL);
|
|
GateRef ret = circuit_->NewGate(circuit_->PrototypeCheck(receiverHCIndex), MachineType::I1,
|
|
{callState, callDepend, unsharedConstPool, frameState}, GateType::NJSValue());
|
|
acc_.ReplaceStateIn(gate, ret);
|
|
acc_.ReplaceDependIn(gate, ret);
|
|
}
|
|
|
|
void TSInlineLowering::InlineCheck(InlineTypeInfoAccessor &info)
|
|
{
|
|
if (info.IsNormalCall()) {
|
|
InlineFuncCheck(info);
|
|
} else {
|
|
InlineAccessorCheck(info);
|
|
}
|
|
}
|
|
|
|
void TSInlineLowering::RemoveRoot()
|
|
{
|
|
GateRef circuitRoot = acc_.GetCircuitRoot();
|
|
auto uses = acc_.Uses(circuitRoot);
|
|
for (auto it = uses.begin(); it != uses.end();) {
|
|
it = acc_.DeleteGate(it);
|
|
}
|
|
|
|
acc_.DeleteGate(circuitRoot);
|
|
}
|
|
|
|
void TSInlineLowering::BuildFrameStateChain(InlineTypeInfoAccessor &info, BytecodeCircuitBuilder &builder)
|
|
{
|
|
GateRef preFrameState = GetFrameState(info);
|
|
ASSERT(acc_.GetOpCode(preFrameState) == OpCode::FRAME_STATE);
|
|
builder.SetPreFrameState(preFrameState);
|
|
GateRef frameArgs = acc_.GetFrameArgs(preFrameState);
|
|
builder.SetPreFrameArgs(frameArgs);
|
|
}
|
|
|
|
bool TSInlineLowering::FilterCallInTryCatch(GateRef gate)
|
|
{
|
|
auto uses = acc_.Uses(gate);
|
|
for (auto it = uses.begin(); it != uses.end(); ++it) {
|
|
if (acc_.GetOpCode(*it) == OpCode::IF_SUCCESS || acc_.GetOpCode(*it) == OpCode::IF_EXCEPTION) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void TSInlineLowering::SupplementType(GateRef callGate, GateRef targetGate)
|
|
{
|
|
GateType callGateType = acc_.GetGateType(callGate);
|
|
GateType targetGateType = acc_.GetGateType(targetGate);
|
|
if (!callGateType.IsAnyType() && targetGateType.IsAnyType()) {
|
|
acc_.SetGateType(targetGate, callGateType);
|
|
}
|
|
}
|
|
|
|
void TSInlineLowering::UpdateWorkList(ChunkQueue<InlineTypeInfoAccessor> &workList)
|
|
{
|
|
std::vector<GateRef> gateList;
|
|
circuit_->GetAllGates(gateList);
|
|
for (const auto &gate : gateList) {
|
|
if (acc_.GetId(gate) <= lastCallId_) {
|
|
continue;
|
|
}
|
|
auto op = acc_.GetOpCode(gate);
|
|
if (op == OpCode::JS_BYTECODE) {
|
|
CandidateInlineCall(gate, workList);
|
|
}
|
|
}
|
|
}
|
|
|
|
size_t TSInlineLowering::GetOrInitialInlineCounts(GateRef frameArgs)
|
|
{
|
|
auto it = inlinedCallMap_.find(frameArgs);
|
|
if (it == inlinedCallMap_.end()) {
|
|
inlinedCallMap_[frameArgs] = 0;
|
|
}
|
|
return inlinedCallMap_[frameArgs];
|
|
}
|
|
|
|
bool TSInlineLowering::IsRecursiveFunc(InlineTypeInfoAccessor &info, size_t calleeMethodOffset)
|
|
{
|
|
GateRef caller = info.GetCallGate();
|
|
auto callerMethodOffset = acc_.TryGetMethodOffset(caller);
|
|
if (callerMethodOffset == calleeMethodOffset) {
|
|
return true;
|
|
}
|
|
GateRef frameArgs = GetFrameArgs(info);
|
|
caller = acc_.GetValueIn(frameArgs);
|
|
while (acc_.GetOpCode(caller) == OpCode::JS_BYTECODE) {
|
|
callerMethodOffset = acc_.TryGetMethodOffset(caller);
|
|
if (callerMethodOffset == calleeMethodOffset) {
|
|
return true;
|
|
}
|
|
frameArgs = acc_.GetFrameArgs(frameArgs);
|
|
if (frameArgs == Circuit::NullGate()) {
|
|
break;
|
|
}
|
|
caller = acc_.GetValueIn(frameArgs);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void TSInlineLowering::CandidateAccessor(GateRef gate, ChunkQueue<InlineTypeInfoAccessor> &workList, CallKind kind)
|
|
{
|
|
GateRef receiver = GetAccessorReceiver(gate);
|
|
InlineTypeInfoAccessor tacc(compilationEnv_, circuit_, gate, receiver, kind);
|
|
if (tacc.IsEnableAccessorInline()) {
|
|
workList.emplace(compilationEnv_, circuit_, gate, receiver, kind);
|
|
lastCallId_ = acc_.GetId(gate);
|
|
}
|
|
}
|
|
|
|
void TSInlineLowering::CandidateNormalCall(GateRef gate, ChunkQueue<InlineTypeInfoAccessor> &workList, CallKind kind)
|
|
{
|
|
ASSERT(acc_.GetNumValueIn(gate) > 0);
|
|
size_t funcIndex = acc_.GetNumValueIn(gate) - 1;
|
|
auto func = acc_.GetValueIn(gate, funcIndex);
|
|
InlineTypeInfoAccessor tacc(compilationEnv_, circuit_, gate, func, kind);
|
|
if (tacc.IsEnableNormalInline()) {
|
|
workList.push(tacc);
|
|
lastCallId_ = acc_.GetId(gate);
|
|
}
|
|
}
|
|
|
|
GateRef TSInlineLowering::GetAccessorReceiver(GateRef gate)
|
|
{
|
|
EcmaOpcode ecmaOpcode = acc_.GetByteCodeOpcode(gate);
|
|
if (UNLIKELY(ecmaOpcode == EcmaOpcode::STTHISBYNAME_IMM8_ID16 ||
|
|
ecmaOpcode == EcmaOpcode::STTHISBYNAME_IMM16_ID16)) {
|
|
return argAcc_.GetFrameArgsIn(gate, FrameArgIdx::THIS_OBJECT);
|
|
}
|
|
return acc_.GetValueIn(gate, 2); // 2: receiver
|
|
}
|
|
|
|
GateRef TSInlineLowering::GetCallSetterValue(GateRef gate)
|
|
{
|
|
EcmaOpcode ecmaOpcode = acc_.GetByteCodeOpcode(gate);
|
|
if (ecmaOpcode == EcmaOpcode::STTHISBYNAME_IMM8_ID16 ||
|
|
ecmaOpcode == EcmaOpcode::STTHISBYNAME_IMM16_ID16) {
|
|
return acc_.GetValueIn(gate, 2); // 2: value
|
|
}
|
|
return acc_.GetValueIn(gate, 3); // 3: value
|
|
}
|
|
|
|
GateRef TSInlineLowering::GetFrameState(InlineTypeInfoAccessor &info)
|
|
{
|
|
GateRef gate = info.GetCallGate();
|
|
ASSERT(info.IsCallAccessor() || info.IsNormalCall());
|
|
return acc_.GetFrameState(gate);
|
|
}
|
|
|
|
GateRef TSInlineLowering::GetFrameArgs(InlineTypeInfoAccessor &info)
|
|
{
|
|
GateRef frameState = GetFrameState(info);
|
|
return acc_.GetValueIn(frameState);
|
|
}
|
|
|
|
void TSInlineLowering::SetInitCallTargetAndConstPoolId(InlineTypeInfoAccessor &info)
|
|
{
|
|
if (initCallTarget_ == Circuit::NullGate()) {
|
|
GateRef frameArgs = GetFrameArgs(info);
|
|
initCallTarget_ = acc_.GetValueIn(frameArgs, 0);
|
|
initConstantPoolId_ = ptManager_->GetConstantPoolIDByMethodOffset(initMethodOffset_);
|
|
}
|
|
}
|
|
|
|
uint32_t TSInlineLowering::GetAccessorConstpoolId(InlineTypeInfoAccessor &info)
|
|
{
|
|
ASSERT(info.IsCallAccessor());
|
|
uint32_t inlineMethodOffset = info.GetCallMethodId();
|
|
return ptManager_->GetConstantPoolIDByMethodOffset(inlineMethodOffset);
|
|
}
|
|
|
|
bool TSInlineLowering::CalleePFIProcess(uint32_t methodOffset)
|
|
{
|
|
if (!compilationEnv_->IsJitCompiler()) {
|
|
return true;
|
|
}
|
|
auto jitCompilationEnv = static_cast<JitCompilationEnv *>(compilationEnv_);
|
|
JSFunction *calleeFunc = jitCompilationEnv->GetJsFunctionByMethodOffset(methodOffset);
|
|
if (!calleeFunc) {
|
|
return false;
|
|
}
|
|
auto calleeMethod = Method::Cast(calleeFunc->GetMethod());
|
|
ASSERT(calleeMethod->GetMethodId().GetOffset() == methodOffset);
|
|
auto profileTIVal = calleeFunc->GetProfileTypeInfo();
|
|
if (profileTIVal.IsUndefined()) {
|
|
return false;
|
|
}
|
|
auto profileTypeInfo = ProfileTypeInfo::Cast(profileTIVal.GetTaggedObject());
|
|
|
|
auto calleeLiteral = calleeMethod->GetMethodLiteral();
|
|
auto calleeFile = calleeMethod->GetJSPandaFile();
|
|
auto calleeAbcId = PGOProfiler::GetMethodAbcId(calleeFunc);
|
|
auto calleeCodeSize = calleeLiteral->GetCodeSize(calleeFile, calleeMethod->GetMethodId());
|
|
compilationEnv_->GetPGOProfiler()->GetJITProfile()->ProfileBytecode(
|
|
compilationEnv_->GetJSThread(), JSHandle<ProfileTypeInfo>(), profileTypeInfo, calleeMethod->GetMethodId(),
|
|
calleeAbcId, calleeMethod->GetBytecodeArray(), calleeCodeSize, calleeFile->GetPandaFile()->GetHeader(), true);
|
|
return true;
|
|
}
|
|
|
|
void TSInlineLowering::UpdateCallMethodFlagMap(uint32_t methodOffset, const MethodLiteral *method)
|
|
{
|
|
if (!compilationEnv_->IsJitCompiler()) {
|
|
return;
|
|
}
|
|
CString fileDesc = ctx_->GetJSPandaFile()->GetNormalizedFileDesc();
|
|
callMethodFlagMap_->SetIsJitCompile(fileDesc, methodOffset, true);
|
|
callMethodFlagMap_->SetIsFastCall(fileDesc, methodOffset, method->IsFastCall());
|
|
}
|
|
|
|
} // namespace panda::ecmascript
|