• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023-2025 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 "bytecodeopt_peepholes.h"
17 
18 #include "libpandafile/bytecode_instruction-inl.h"
19 
20 namespace ark::bytecodeopt {
RunImpl()21 bool BytecodeOptPeepholes::RunImpl()
22 {
23     VisitGraph();
24     return IsApplied();
25 }
26 
FindCtorCall(Inst * newObject)27 CallInst *FindCtorCall(Inst *newObject)
28 {
29     auto *graph = newObject->GetBasicBlock()->GetGraph();
30     auto *adapter = graph->GetRuntime();
31     for (auto &user : newObject->GetUsers()) {
32         auto *inst = user.GetInst();
33         if (inst->GetOpcode() == Opcode::NullCheck) {
34             return FindCtorCall(inst);
35         }
36         if (inst->GetOpcode() != Opcode::CallStatic) {
37             continue;
38         }
39         auto call = inst->CastToCallStatic();
40         auto callFirstArg = call->GetInput(0U).GetInst();
41         if (callFirstArg->GetOpcode() == Opcode::NewObject) {
42             auto newObjectAsArg = callFirstArg->CastToNewObject();
43             if (newObjectAsArg != newObject) {
44                 continue;
45             }
46         }
47         if (adapter->IsConstructor(call->GetCallMethod(), graph->GetLanguage())) {
48             return call;
49         }
50     }
51 
52     return nullptr;
53 }
54 
CreateInitObject(compiler::GraphVisitor * v,compiler::ClassInst * load,const CallInst * callInit)55 CallInst *CreateInitObject(compiler::GraphVisitor *v, compiler::ClassInst *load, const CallInst *callInit)
56 {
57     auto *graph = static_cast<BytecodeOptPeepholes *>(v)->GetGraph();
58     auto *initObject = static_cast<CallInst *>(graph->CreateInst(compiler::Opcode::InitObject));
59     initObject->SetType(compiler::DataType::REFERENCE);
60 
61     auto inputTypesCount = callInit->GetInputsCount();
62     initObject->AllocateInputTypes(graph->GetAllocator(), inputTypesCount);
63     initObject->AppendInput(load, compiler::DataType::REFERENCE);
64     for (size_t i = 1; i < inputTypesCount; ++i) {
65         auto inputInst = callInit->GetInput(i).GetInst();
66         initObject->AppendInput(inputInst, inputInst->GetType());
67     }
68 
69     initObject->SetCallMethodId(callInit->GetCallMethodId());
70     initObject->SetCallMethod(static_cast<const CallInst *>(callInit)->GetCallMethod());
71     return initObject;
72 }
73 
ReplaceNewObjectUsers(Inst * newObject,Inst * nullCheck,CallInst * initObject)74 void ReplaceNewObjectUsers(Inst *newObject, Inst *nullCheck, CallInst *initObject)
75 {
76     for (auto it = newObject->GetUsers().begin(); it != newObject->GetUsers().end();
77          it = newObject->GetUsers().begin()) {
78         auto user = it->GetInst();
79         if (user != nullCheck) {
80             user->SetInput(it->GetIndex(), initObject);
81         } else {
82             newObject->RemoveUser(&(*it));
83         }
84     }
85 
86     // Update throwable instructions data
87     auto graph = newObject->GetBasicBlock()->GetGraph();
88     if (graph->IsInstThrowable(newObject)) {
89         graph->ReplaceThrowableInst(newObject, initObject);
90     }
91 }
92 
VisitNewObject(GraphVisitor * v,Inst * inst)93 void BytecodeOptPeepholes::VisitNewObject(GraphVisitor *v, Inst *inst)
94 {
95     ASSERT(inst != nullptr);
96     CallInst *callInit = FindCtorCall(inst);
97     if (callInit == nullptr || inst->GetBasicBlock() != callInit->GetBasicBlock()) {
98         return;
99     }
100 
101     // The optimization is correct only if there are no side-effects between NewObject and constructor call.
102     // For simplicity, we abort it if any instruction except NullCheck and SaveState appears in-between.
103     // Moreover, when we are inside a try block, local register state also matters, because it may be used inside
104     // catch blocks. In such case we also abort if there are any instructions in corresponding bytecode.
105     const auto graph = static_cast<BytecodeOptPeepholes *>(v)->GetGraph();
106     const size_t newobjSize =
107         BytecodeInstruction::Size(  // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
108             BytecodeInstruction(graph->GetRuntime()->GetMethodCode(graph->GetMethod()) + inst->GetPc()).GetFormat());
109     if (inst->GetBasicBlock()->IsTry() && callInit->GetPc() - inst->GetPc() > newobjSize) {
110         return;
111     }
112 
113     Inst *nullCheck = nullptr;
114     for (auto *i = inst->GetNext(); i != callInit; i = i->GetNext()) {
115         if (i->GetOpcode() != Opcode::SaveState && i->GetOpcode() != Opcode::NullCheck) {
116             return;
117         }
118 
119         if (i->GetOpcode() == Opcode::SaveState) {
120             continue;
121         }
122 
123         for (auto nullCheckInput : i->GetInputs()) {
124             if (nullCheckInput.GetInst() == inst) {
125                 ASSERT(nullCheck == nullptr);
126                 nullCheck = i;
127             }
128         }
129         if (nullCheck == nullptr) {
130             return;
131         }
132     }
133 
134     auto load = static_cast<compiler::ClassInst *>(inst->GetInput(0U).GetInst());
135     auto *initObject = CreateInitObject(v, load, callInit);
136     callInit->InsertBefore(initObject);
137     initObject->SetPc(callInit->GetPc());
138 
139     ReplaceNewObjectUsers(inst, nullCheck, initObject);
140     inst->ClearFlag(compiler::inst_flags::NO_DCE);
141     if (nullCheck != nullptr) {
142         nullCheck->ReplaceUsers(initObject);
143         nullCheck->ClearFlag(compiler::inst_flags::NO_DCE);
144         nullCheck->RemoveInputs();
145         nullCheck->GetBasicBlock()->ReplaceInst(nullCheck,
146                                                 static_cast<BytecodeOptPeepholes *>(v)->GetGraph()->CreateInstNOP());
147     }
148     ASSERT(!callInit->HasUsers());
149     callInit->ClearFlag(compiler::inst_flags::NO_DCE);
150 
151     static_cast<BytecodeOptPeepholes *>(v)->SetIsApplied();
152 }
153 
154 }  // namespace ark::bytecodeopt
155