1 /**
2 * Copyright (c) 2021-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 <cstdint>
17 #include "libpandabase/utils/utils.h"
18 #include "compiler_logger.h"
19 #include "optimizer/ir/datatype.h"
20 #include "optimizer/ir/runtime_interface.h"
21 #include "optimizer/ir_builder/inst_builder.h"
22 #include "optimizer/ir_builder/ir_builder.h"
23 #include "optimizer/ir/inst.h"
24 #include "bytecode_instruction.h"
25 #include "bytecode_instruction-inl.h"
26
27 namespace ark::compiler {
28
GetIntrinsicId(DataType::Type type)29 static RuntimeInterface::IntrinsicId GetIntrinsicId(DataType::Type type)
30 {
31 switch (type) {
32 case DataType::REFERENCE:
33 return RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_ETS_LD_OBJ_BY_NAME_OBJ;
34 case DataType::FLOAT64:
35 return RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_ETS_LD_OBJ_BY_NAME_F64;
36 case DataType::FLOAT32:
37 return RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_ETS_LD_OBJ_BY_NAME_F32;
38 case DataType::UINT64:
39 case DataType::INT64:
40 return RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_ETS_LD_OBJ_BY_NAME_I64;
41 case DataType::UINT8:
42 case DataType::INT8:
43 case DataType::UINT16:
44 case DataType::INT16:
45 case DataType::UINT32:
46 case DataType::INT32:
47 return RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_ETS_LD_OBJ_BY_NAME_I32;
48 default:
49 UNREACHABLE();
50 }
51 }
52
53 template <bool IS_ABC_KIT>
BuildLdObjByName(const BytecodeInstruction * bcInst,compiler::DataType::Type type)54 void InstBuilder::BuildLdObjByName(const BytecodeInstruction *bcInst, compiler::DataType::Type type)
55 {
56 auto pc = GetPc(bcInst->GetAddress());
57
58 auto runtime = GetRuntime();
59 auto fieldIndex = bcInst->GetId(0).AsIndex();
60 auto fieldId = runtime->ResolveFieldIndex(GetMethod(), fieldIndex);
61 if (type != DataType::REFERENCE) {
62 type = runtime->GetFieldTypeById(GetMethod(), fieldId);
63 }
64
65 auto intrinsic = GetGraph()->CreateInstIntrinsic(type, pc, GetIntrinsicId(type));
66 if constexpr (!IS_ABC_KIT) {
67 intrinsic->AllocateInputTypes(GetGraph()->GetAllocator(), 2_I);
68
69 // Create SaveState instruction
70 auto saveState = CreateSaveState(Opcode::SaveState, pc);
71
72 // Create NullCheck instruction
73 auto nullCheck =
74 graph_->CreateInstNullCheck(DataType::REFERENCE, pc, GetDefinition(bcInst->GetVReg(0)), saveState);
75
76 intrinsic->AppendInput(nullCheck);
77 intrinsic->AddInputType(DataType::REFERENCE);
78 intrinsic->AppendInput(saveState);
79 intrinsic->AddInputType(DataType::NO_TYPE);
80
81 AddInstruction(saveState);
82 AddInstruction(nullCheck);
83 }
84
85 intrinsic->AddImm(GetGraph()->GetAllocator(), fieldId);
86 intrinsic->AddImm(GetGraph()->GetAllocator(), pc);
87
88 intrinsic->SetMethodFirstInput();
89 intrinsic->SetMethod(GetMethod());
90
91 AddInstruction(intrinsic);
92
93 UpdateDefinitionAcc(intrinsic);
94 }
95
96 template void InstBuilder::BuildLdObjByName<true>(const BytecodeInstruction *bcInst, compiler::DataType::Type type);
97 template void InstBuilder::BuildLdObjByName<false>(const BytecodeInstruction *bcInst, compiler::DataType::Type type);
98
ExtractIntrinsicIdByType(DataType::Type type)99 std::pair<RuntimeInterface::IntrinsicId, DataType::Type> ExtractIntrinsicIdByType(DataType::Type type)
100 {
101 switch (type) {
102 case DataType::REFERENCE:
103 return {RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_ETS_ST_OBJ_BY_NAME_OBJ, type};
104 case DataType::FLOAT64:
105 return {RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_ETS_ST_OBJ_BY_NAME_F64, type};
106 case DataType::FLOAT32:
107 return {RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_ETS_ST_OBJ_BY_NAME_F32, type};
108 case DataType::UINT64:
109 case DataType::INT64:
110 return {RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_ETS_ST_OBJ_BY_NAME_I64, DataType::INT64};
111 case DataType::UINT8:
112 case DataType::INT8:
113 return {RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_ETS_ST_OBJ_BY_NAME_I8, DataType::INT8};
114 case DataType::UINT16:
115 case DataType::INT16:
116 return {RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_ETS_ST_OBJ_BY_NAME_I16, DataType::INT16};
117 case DataType::UINT32:
118 case DataType::INT32:
119 return {RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_ETS_ST_OBJ_BY_NAME_I32, DataType::INT32};
120 default:
121 UNREACHABLE();
122 break;
123 }
124 UNREACHABLE();
125 }
126
127 template <bool IS_ABC_KIT>
CreateStObjByNameIntrinsic(size_t pc,compiler::DataType::Type type)128 IntrinsicInst *InstBuilder::CreateStObjByNameIntrinsic(size_t pc, compiler::DataType::Type type)
129 {
130 auto [id, extractedType] = ExtractIntrinsicIdByType(type);
131 type = extractedType;
132 auto *intrinsic = GetGraph()->CreateInstIntrinsic(DataType::VOID, pc, id);
133
134 if (intrinsic->RequireState()) {
135 intrinsic->AllocateInputTypes(GetGraph()->GetAllocator(), 3_I);
136 intrinsic->AddInputType(DataType::REFERENCE);
137 intrinsic->AddInputType(type);
138 intrinsic->AddInputType(DataType::NO_TYPE);
139 } else {
140 intrinsic->AllocateInputTypes(GetGraph()->GetAllocator(), 1_I);
141 intrinsic->AddInputType(type);
142 }
143 return intrinsic;
144 }
145
146 template IntrinsicInst *InstBuilder::CreateStObjByNameIntrinsic<true>(size_t pc, compiler::DataType::Type type);
147 template IntrinsicInst *InstBuilder::CreateStObjByNameIntrinsic<false>(size_t pc, compiler::DataType::Type type);
148
149 template <bool IS_ABC_KIT>
BuildStObjByName(const BytecodeInstruction * bcInst,compiler::DataType::Type type)150 void InstBuilder::BuildStObjByName(const BytecodeInstruction *bcInst, compiler::DataType::Type type)
151 {
152 auto pc = GetPc(bcInst->GetAddress());
153
154 auto runtime = GetRuntime();
155 auto fieldIndex = bcInst->GetId(0).AsIndex();
156 auto fieldId = runtime->ResolveFieldIndex(GetMethod(), fieldIndex);
157 if (type != DataType::REFERENCE) {
158 type = runtime->GetFieldTypeById(GetMethod(), fieldId);
159 }
160
161 // Get a value to store
162 Inst *storeVal = nullptr;
163 storeVal = GetDefinitionAcc();
164
165 auto *intrinsic = CreateStObjByNameIntrinsic<IS_ABC_KIT>(pc, type);
166
167 if constexpr (!IS_ABC_KIT) {
168 // Create SaveState instruction
169 auto saveState = CreateSaveState(Opcode::SaveState, pc);
170 // Create NullCheck instruction
171 auto nullCheck =
172 graph_->CreateInstNullCheck(DataType::REFERENCE, pc, GetDefinition(bcInst->GetVReg(0)), saveState);
173
174 intrinsic->AppendInput(nullCheck);
175 intrinsic->AppendInput(storeVal);
176 intrinsic->AppendInput(saveState);
177 AddInstruction(saveState);
178 AddInstruction(nullCheck);
179 } else {
180 intrinsic->AppendInput(storeVal);
181 }
182
183 intrinsic->AddImm(GetGraph()->GetAllocator(), fieldId);
184 intrinsic->AddImm(GetGraph()->GetAllocator(), pc);
185
186 intrinsic->SetMethodFirstInput();
187 intrinsic->SetMethod(GetMethod());
188
189 AddInstruction(intrinsic);
190 }
191
192 template void InstBuilder::BuildStObjByName<true>(const BytecodeInstruction *bcInst, compiler::DataType::Type type);
193 template void InstBuilder::BuildStObjByName<false>(const BytecodeInstruction *bcInst, compiler::DataType::Type type);
194
BuildIsNullValue(const BytecodeInstruction * bcInst)195 void InstBuilder::BuildIsNullValue(const BytecodeInstruction *bcInst)
196 {
197 auto uniqueObjInst = graph_->GetOrCreateUniqueObjectInst();
198 auto cmpInst = graph_->CreateInstCompare(DataType::BOOL, GetPc(bcInst->GetAddress()), GetDefinitionAcc(),
199 uniqueObjInst, DataType::REFERENCE, ConditionCode::CC_EQ);
200 AddInstruction(cmpInst);
201 UpdateDefinitionAcc(cmpInst);
202 }
203
204 template <bool IS_STRICT>
BuildEquals(const BytecodeInstruction * bcInst)205 void InstBuilder::BuildEquals(const BytecodeInstruction *bcInst)
206 {
207 auto pc = GetPc(bcInst->GetAddress());
208
209 Inst *obj1 = GetDefinition(bcInst->GetVReg(0));
210 Inst *obj2 = GetDefinition(bcInst->GetVReg(1));
211
212 auto intrinsicId = RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_ETS_EQUALS;
213 if constexpr (IS_STRICT) {
214 intrinsicId = RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_ETS_STRICT_EQUALS;
215 }
216 #if defined(ENABLE_LIBABCKIT)
217 if (GetGraph()->IsAbcKit()) {
218 if constexpr (IS_STRICT) {
219 intrinsicId = RuntimeInterface::IntrinsicId::INTRINSIC_ABCKIT_STRICT_EQUALS;
220 } else {
221 intrinsicId = RuntimeInterface::IntrinsicId::INTRINSIC_ABCKIT_EQUALS;
222 }
223 }
224 #endif
225 auto intrinsic = GetGraph()->CreateInstIntrinsic(DataType::BOOL, pc, intrinsicId);
226 intrinsic->AllocateInputTypes(GetGraph()->GetAllocator(), 2_I);
227
228 intrinsic->AppendInput(obj1);
229 intrinsic->AddInputType(DataType::REFERENCE);
230 intrinsic->AppendInput(obj2);
231 intrinsic->AddInputType(DataType::REFERENCE);
232
233 AddInstruction(intrinsic);
234 UpdateDefinitionAcc(intrinsic);
235 }
236
237 template void InstBuilder::BuildEquals<true>(const BytecodeInstruction *bcInst);
238 template void InstBuilder::BuildEquals<false>(const BytecodeInstruction *bcInst);
239
BuildTypeof(const BytecodeInstruction * bcInst)240 void InstBuilder::BuildTypeof(const BytecodeInstruction *bcInst)
241 {
242 auto pc = GetPc(bcInst->GetAddress());
243 Inst *obj = GetDefinition(bcInst->GetVReg(0));
244
245 RuntimeInterface::IntrinsicId intrinsicId = RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_ETS_TYPEOF;
246 auto intrinsic = GetGraph()->CreateInstIntrinsic(DataType::REFERENCE, pc, intrinsicId);
247 auto saveState = CreateSaveState(Opcode::SaveState, pc);
248
249 intrinsic->AllocateInputTypes(GetGraph()->GetAllocator(), 2_I);
250 intrinsic->AppendInput(obj);
251 intrinsic->AddInputType(DataType::REFERENCE);
252 intrinsic->AppendInput(saveState);
253 intrinsic->AddInputType(DataType::NO_TYPE);
254
255 AddInstruction(saveState);
256 AddInstruction(intrinsic);
257 UpdateDefinitionAcc(intrinsic);
258 }
259
BuildIstrue(const BytecodeInstruction * bcInst)260 void InstBuilder::BuildIstrue(const BytecodeInstruction *bcInst)
261 {
262 auto pc = GetPc(bcInst->GetAddress());
263 Inst *obj = GetDefinition(bcInst->GetVReg(0));
264
265 RuntimeInterface::IntrinsicId intrinsicId = RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_ETS_ISTRUE;
266 auto intrinsic = GetGraph()->CreateInstIntrinsic(DataType::BOOL, pc, intrinsicId);
267 intrinsic->AllocateInputTypes(GetGraph()->GetAllocator(), 1_I);
268 intrinsic->AppendInput(obj);
269 intrinsic->AddInputType(DataType::REFERENCE);
270
271 AddInstruction(intrinsic);
272 UpdateDefinitionAcc(intrinsic);
273 }
274
275 template <bool IS_RANGE>
BuildCallByName(const BytecodeInstruction * bcInst)276 void InstBuilder::BuildCallByName(const BytecodeInstruction *bcInst)
277 {
278 // Suppress BytecodeOptimizer if call.name was encountered
279 if (GetGraph()->IsBytecodeOptimizer()) {
280 failed_ = true;
281 return;
282 }
283
284 auto pc = GetPc(bcInst->GetAddress());
285 auto startReg = bcInst->GetVReg(0);
286 auto objRef = GetDefinition(startReg++);
287 auto methodIndex = bcInst->GetId(0).AsIndex();
288 auto methodId = GetRuntime()->ResolveMethodIndex(GetMethod(), methodIndex);
289 auto argsCount = GetMethodArgumentsCount(methodId);
290 auto retType = GetMethodReturnType(methodId);
291
292 auto saveState = CreateSaveState(Opcode::SaveState, pc);
293 auto nullCheck = GetGraph()->CreateInstNullCheck(DataType::REFERENCE, pc, objRef, saveState);
294
295 ASSERT(saveState != nullptr);
296 ASSERT(nullCheck != nullptr);
297
298 ResolveVirtualInst *resolver = GetGraph()->CreateInstResolveByName(DataType::POINTER, pc, methodId, GetMethod());
299
300 ASSERT(resolver != nullptr);
301
302 resolver->SetInput(0, nullCheck);
303 resolver->SetInput(1, saveState);
304
305 CallInst *call = GetGraph()->CreateInstCallResolvedVirtual(retType, pc, methodId, nullptr);
306
307 // + 1 because the first param is objRef
308 // + 1 because of resolver
309 // + 1 because CallResolvedVirtual has saveState as input (last)
310 call->ReserveInputs(argsCount + 3_I);
311 call->AllocateInputTypes(GetGraph()->GetAllocator(), argsCount + 3_I);
312 call->AppendInput(resolver, DataType::POINTER);
313 call->AppendInput(nullCheck, DataType::REFERENCE);
314
315 if constexpr (IS_RANGE) {
316 for (size_t i = 0; i < argsCount; startReg++, i++) {
317 call->AppendInput(GetDefinition(startReg), GetMethodArgumentType(methodId, i));
318 }
319 } else {
320 for (size_t i = 0; i < argsCount; i++) {
321 call->AppendInput(GetDefinition(bcInst->GetVReg(i + 1)), GetMethodArgumentType(methodId, i));
322 }
323 }
324 call->AppendInput(saveState, DataType::NO_TYPE);
325
326 AddInstruction(saveState);
327 AddInstruction(nullCheck);
328 AddInstruction(resolver);
329 AddInstruction(call);
330
331 if (retType != DataType::VOID) {
332 UpdateDefinitionAcc(call);
333 } else {
334 UpdateDefinitionAcc(nullptr);
335 }
336 }
337
338 template void InstBuilder::BuildCallByName<true>(const BytecodeInstruction *bcInst);
339 template void InstBuilder::BuildCallByName<false>(const BytecodeInstruction *bcInst);
340
341 } // namespace ark::compiler
342