arkcompiler_runtime_core/bytecode_optimizer/bytecodeopt_peepholes.cpp
huangyu c658ccf319 Update runtime_core code
Issue: https://gitee.com/openharmony/arkcompiler_runtime_core/issues/I5G96F
Test: Test262 suit, ark unittest, rk3568 XTS, ark previewer demo

Signed-off-by: huangyu <huangyu76@huawei.com>
Change-Id: I3f63d129a07deaa27a390f556dcaa5651c098185
2022-07-17 10:20:32 +08:00

154 lines
5.6 KiB
C++

/**
* Copyright (c) 2021-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 "bytecodeopt_peepholes.h"
#include "libpandafile/bytecode_instruction-inl.h"
namespace panda::bytecodeopt {
bool BytecodeOptPeepholes::RunImpl()
{
VisitGraph();
return IsApplied();
}
CallInst *FindCtorCall(Inst *new_object, const compiler::ClassInst *load)
{
auto *adapter = new_object->GetBasicBlock()->GetGraph()->GetRuntime();
for (auto &user : new_object->GetUsers()) {
auto *inst = user.GetInst();
if (inst->GetOpcode() == Opcode::NullCheck) {
return FindCtorCall(inst, load);
}
if (inst->GetOpcode() != Opcode::CallStatic) {
continue;
}
auto call = inst->CastToCallStatic();
if (adapter->IsConstructor(call->GetCallMethod(), load->GetTypeId())) {
return call;
}
}
return nullptr;
}
CallInst *CreateInitObject(compiler::GraphVisitor *v, compiler::ClassInst *load, const CallInst *call_init)
{
auto *graph = static_cast<BytecodeOptPeepholes *>(v)->GetGraph();
auto *init_object = static_cast<CallInst *>(graph->CreateInst(compiler::Opcode::InitObject));
init_object->SetType(compiler::DataType::REFERENCE);
auto input_types_count = call_init->GetInputsCount();
init_object->AllocateInputTypes(graph->GetAllocator(), input_types_count);
init_object->AddInputType(compiler::DataType::REFERENCE);
init_object->AppendInput(load);
for (size_t i = 1; i < input_types_count; ++i) {
auto input_inst = call_init->GetInput(i).GetInst();
init_object->AddInputType(input_inst->GetType());
init_object->AppendInput(input_inst);
}
init_object->SetCallMethodId(call_init->GetCallMethodId());
init_object->SetCallMethod(static_cast<const CallInst *>(call_init)->GetCallMethod());
return init_object;
}
void ReplaceNewObjectUsers(Inst *new_object, Inst *null_check, CallInst *init_object)
{
for (auto it = new_object->GetUsers().begin(); it != new_object->GetUsers().end();
it = new_object->GetUsers().begin()) {
auto user = it->GetInst();
if (user != null_check) {
user->SetInput(it->GetIndex(), init_object);
} else {
new_object->RemoveUser(&(*it));
}
}
// Update throwable instructions data
auto graph = new_object->GetBasicBlock()->GetGraph();
if (graph->IsInstThrowable(new_object)) {
graph->ReplaceThrowableInst(new_object, init_object);
}
}
void BytecodeOptPeepholes::VisitNewObject(GraphVisitor *v, Inst *inst)
{
auto load = static_cast<compiler::ClassInst *>(inst->GetInput(0).GetInst());
ASSERT(load != nullptr);
CallInst *call_init = FindCtorCall(inst, load);
if (call_init == nullptr) {
return;
}
if (inst->GetBasicBlock() != call_init->GetBasicBlock()) {
return;
}
// The optimization is correct only if there are no side-effects between NewObject and constructor call.
// For simplicity, we abort it if any instruction except NullCheck and SaveState appears in-between.
// Moreover, when we are inside a try block, local register state also matters, because it may be used inside
// catch blocks. In such case we also abort if there are any instructions in corresponding bytecode.
const auto graph = static_cast<BytecodeOptPeepholes *>(v)->GetGraph();
const size_t NEWOBJ_SIZE =
BytecodeInstruction::Size( // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
BytecodeInstruction(graph->GetRuntime()->GetMethodCode(graph->GetMethod()) + inst->GetPc()).GetFormat());
if (inst->GetBasicBlock()->IsTry() && call_init->GetPc() - inst->GetPc() > NEWOBJ_SIZE) {
return;
}
Inst *null_check = nullptr;
for (auto *i = inst->GetNext(); i != call_init; i = i->GetNext()) {
if (i->GetOpcode() != Opcode::SaveState && i->GetOpcode() != Opcode::NullCheck) {
return;
}
if (i->GetOpcode() == Opcode::SaveState) {
continue;
}
for (auto null_check_input : i->GetInputs()) {
if (null_check_input.GetInst() == inst) {
ASSERT(null_check == nullptr);
null_check = i;
}
}
if (null_check == nullptr) {
return;
}
}
auto *init_object = CreateInitObject(v, load, call_init);
call_init->InsertBefore(init_object);
init_object->SetPc(call_init->GetPc());
ReplaceNewObjectUsers(inst, null_check, init_object);
inst->ClearFlag(compiler::inst_flags::NO_DCE);
if (null_check != nullptr) {
null_check->ReplaceUsers(init_object);
null_check->ClearFlag(compiler::inst_flags::NO_DCE);
null_check->RemoveInputs();
null_check->GetBasicBlock()->ReplaceInst(null_check,
static_cast<BytecodeOptPeepholes *>(v)->GetGraph()->CreateInstNOP());
}
ASSERT(!call_init->HasUsers());
call_init->ClearFlag(compiler::inst_flags::NO_DCE);
static_cast<BytecodeOptPeepholes *>(v)->SetIsApplied();
}
} // namespace panda::bytecodeopt