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 #ifndef PANDA_INST_BUILDER_INL_H
17 #define PANDA_INST_BUILDER_INL_H
18
19 #include "inst_builder.h"
20 #include "optimizer/code_generator/encode.h"
21
22 namespace ark::compiler {
23
24 template <Opcode OPCODE, bool IS_RANGE, bool ACC_READ, bool HAS_SAVE_STATE>
GetClassId()25 uint32_t InstBuilder::BuildCallHelper<OPCODE, IS_RANGE, ACC_READ, HAS_SAVE_STATE>::GetClassId()
26 {
27 if (method_ == nullptr) {
28 return 0;
29 }
30 if (GetGraph()->IsAotMode()) {
31 return GetRuntime()->GetClassIdWithinFile(GetMethod(), GetRuntime()->GetClass(method_));
32 }
33 return GetRuntime()->GetClassIdForMethod(GetMethod(), methodId_);
34 }
35
36 // NOLINTNEXTLINE(misc-definitions-in-headers,readability-function-size)
37 template <Opcode OPCODE, bool IS_RANGE, bool ACC_READ, bool HAS_SAVE_STATE>
BuildCallHelper(const BytecodeInstruction * bcInst,InstBuilder * builder,Inst * additionalInput)38 InstBuilder::BuildCallHelper<OPCODE, IS_RANGE, ACC_READ, HAS_SAVE_STATE>::BuildCallHelper(
39 const BytecodeInstruction *bcInst, InstBuilder *builder, Inst *additionalInput)
40 : builder_(builder), bcInst_(bcInst)
41 {
42 methodId_ = GetRuntime()->ResolveMethodIndex(Builder()->GetMethod(), bcInst->GetId(0).AsIndex());
43 pc_ = Builder()->GetPc(bcInst->GetAddress());
44 hasImplicitArg_ = !GetRuntime()->IsMethodStatic(Builder()->GetMethod(), methodId_);
45
46 if (GetRuntime()->IsMethodIntrinsic(Builder()->GetMethod(), methodId_)) {
47 // Do not move "GetMethodId" ouside this if! Success of "IsMethodIntrinsic" guarantees that class and method are
48 // loaded. Thus value of "method_" is not nullptr and can be used in BuildIntrinsic.
49 method_ = GetRuntime()->GetMethodById(Builder()->GetMethod(), methodId_);
50 if (TryBuildIntrinsic()) {
51 return;
52 }
53 }
54 // Here "GetMethodById" can be used without additional checks, result may be nullptr and it is normal situation
55 method_ = GetRuntime()->GetMethodById(Builder()->GetMethod(), methodId_);
56 saveState_ = nullptr;
57 uint32_t classId = 0;
58 if constexpr (HAS_SAVE_STATE) {
59 saveState_ = Builder()->CreateSaveState(Opcode::SaveState, pc_);
60 if (hasImplicitArg_) {
61 nullCheck_ = GetGraph()->CreateInstNullCheck(DataType::REFERENCE, pc_,
62 Builder()->GetArgDefinition(bcInst, 0, ACC_READ), saveState_);
63 } else {
64 classId = GetClassId();
65 }
66 } else {
67 classId = GetClassId();
68 }
69
70 // NOLINTNEXTLINE(readability-magic-numbers)
71 BuildCallInst(classId);
72 SetCallArgs(additionalInput);
73
74 if constexpr (HAS_SAVE_STATE) {
75 // Add SaveState
76 if (saveState_ != nullptr) {
77 Builder()->AddInstruction(saveState_);
78 }
79 // Add NullCheck
80 if (hasImplicitArg_) {
81 ASSERT(nullCheck_ != nullptr);
82 Builder()->AddInstruction(nullCheck_);
83 } else if (!call_->IsUnresolved() && static_cast<CallInst *>(call_)->GetCallMethod() != nullptr) {
84 // Initialize class as call is resolved
85 BuildInitClassInstForCallStatic(classId);
86 }
87 // Add resolver
88 if (resolver_ != nullptr) {
89 if (call_->IsStaticCall()) {
90 resolver_->SetInput(0, saveState_);
91 } else {
92 resolver_->SetInput(0, nullCheck_);
93 resolver_->SetInput(1, saveState_);
94 }
95 Builder()->AddInstruction(resolver_);
96 }
97 }
98 // Add Call
99 AddCallInstruction();
100 }
101
102 // NOLINTNEXTLINE(misc-definitions-in-headers)
103 template <Opcode OPCODE, bool IS_RANGE, bool ACC_READ, bool HAS_SAVE_STATE>
SetCallArgs(Inst * additionalInput)104 void InstBuilder::BuildCallHelper<OPCODE, IS_RANGE, ACC_READ, HAS_SAVE_STATE>::SetCallArgs(Inst *additionalInput)
105 {
106 size_t hiddenArgsCount = hasImplicitArg_ ? 1 : 0; // +1 for non-static call
107 size_t additionalArgsCount = additionalInput == nullptr ? 0 : 1; // +1 for launch call
108 size_t argsCount = Builder()->GetMethodArgumentsCount(methodId_);
109 size_t totalArgsCount = hiddenArgsCount + argsCount + additionalArgsCount;
110 size_t inputsCount = totalArgsCount + (call_->RequireState() ? 0 : 1) + (resolver_ == nullptr ? 0 : 1);
111 call_->ReserveInputs(inputsCount);
112 call_->AllocateInputTypes(GetGraph()->GetAllocator(), inputsCount);
113 if (resolver_ != nullptr) {
114 call_->AppendInput(resolver_);
115 call_->AddInputType(DataType::POINTER);
116 }
117 if (additionalInput != nullptr) {
118 ASSERT(call_->RequireState() && saveState_ != nullptr);
119 call_->AppendInput(additionalInput);
120 call_->AddInputType(DataType::REFERENCE);
121 saveState_->AppendBridge(additionalInput);
122 }
123 if (hasImplicitArg_ && nullCheck_ != nullptr) {
124 call_->AppendInput(nullCheck_);
125 call_->AddInputType(DataType::REFERENCE);
126 }
127 if constexpr (!HAS_SAVE_STATE) {
128 if (hasImplicitArg_) {
129 call_->AppendInput(Builder()->GetArgDefinition(bcInst_, 0, ACC_READ));
130 call_->AddInputType(DataType::REFERENCE);
131 }
132 }
133 if constexpr (IS_RANGE) {
134 auto startReg = bcInst_->GetVReg(0);
135 // start reg for Virtual call was added
136 if (hasImplicitArg_) {
137 ++startReg;
138 }
139 for (size_t i = 0; i < argsCount; startReg++, i++) {
140 call_->AppendInput(Builder()->GetDefinition(startReg));
141 call_->AddInputType(Builder()->GetMethodArgumentType(methodId_, i));
142 }
143 } else {
144 for (size_t i = 0; i < argsCount; i++) {
145 call_->AppendInput(Builder()->GetArgDefinition(bcInst_, i + hiddenArgsCount, ACC_READ));
146 call_->AddInputType(Builder()->GetMethodArgumentType(methodId_, i));
147 }
148 }
149 if (call_->RequireState()) {
150 call_->AppendInput(saveState_);
151 call_->AddInputType(DataType::NO_TYPE);
152 }
153 }
154
155 // NOLINTNEXTLINE(misc-definitions-in-headers)
156 template <Opcode OPCODE, bool IS_RANGE, bool ACC_READ, bool HAS_SAVE_STATE>
BuildInitClassInstForCallStatic(uint32_t classId)157 void InstBuilder::BuildCallHelper<OPCODE, IS_RANGE, ACC_READ, HAS_SAVE_STATE>::BuildInitClassInstForCallStatic(
158 uint32_t classId)
159 {
160 if (Builder()->GetClassId() != classId) {
161 auto initClass = GetGraph()->CreateInstInitClass(DataType::NO_TYPE, pc_, saveState_,
162 TypeIdMixin {classId, GetGraph()->GetMethod()},
163 GetRuntime()->GetClass(method_));
164 Builder()->AddInstruction(initClass);
165 }
166 }
167
168 // NOLINTNEXTLINE(misc-definitions-in-headers)
169 template <Opcode OPCODE, bool IS_RANGE, bool ACC_READ, bool HAS_SAVE_STATE>
BuildCallStaticInst(uint32_t classId)170 void InstBuilder::BuildCallHelper<OPCODE, IS_RANGE, ACC_READ, HAS_SAVE_STATE>::BuildCallStaticInst(uint32_t classId)
171 {
172 constexpr auto SLOT_KIND = UnresolvedTypesInterface::SlotKind::METHOD;
173 if (method_ == nullptr || (GetRuntime()->IsMethodStatic(GetMethod(), methodId_) && classId == 0) ||
174 Builder()->ForceUnresolved()) {
175 resolver_ = GetGraph()->CreateInstResolveStatic(DataType::POINTER, pc_, methodId_, nullptr);
176 if constexpr (OPCODE == Opcode::CallStatic) {
177 call_ = GetGraph()->CreateInstCallResolvedStatic(Builder()->GetMethodReturnType(methodId_), pc_, methodId_);
178 }
179 if (!GetGraph()->IsAotMode() && !GetGraph()->IsBytecodeOptimizer()) {
180 GetRuntime()->GetUnresolvedTypes()->AddTableSlot(GetMethod(), methodId_, SLOT_KIND);
181 }
182 } else {
183 if constexpr (OPCODE == Opcode::CallStatic) {
184 call_ =
185 GetGraph()->CreateInstCallStatic(Builder()->GetMethodReturnType(methodId_), pc_, methodId_, method_);
186 }
187 }
188 }
189
190 // NOLINTNEXTLINE(misc-definitions-in-headers)
191 template <Opcode OPCODE, bool IS_RANGE, bool ACC_READ, bool HAS_SAVE_STATE>
BuildCallVirtualInst()192 void InstBuilder::BuildCallHelper<OPCODE, IS_RANGE, ACC_READ, HAS_SAVE_STATE>::BuildCallVirtualInst()
193 {
194 constexpr auto SLOT_KIND = UnresolvedTypesInterface::SlotKind::VIRTUAL_METHOD;
195 ASSERT(!GetRuntime()->IsMethodStatic(Builder()->GetMethod(), methodId_));
196 if (method_ != nullptr && (GetRuntime()->IsInterfaceMethod(method_) || GetGraph()->IsAotNoChaMode())) {
197 resolver_ = GetGraph()->CreateInstResolveVirtual(DataType::POINTER, pc_, methodId_, method_);
198 if constexpr (OPCODE == Opcode::CallVirtual) {
199 call_ = GetGraph()->CreateInstCallResolvedVirtual(Builder()->GetMethodReturnType(methodId_), pc_, methodId_,
200 method_);
201 }
202 } else if (method_ == nullptr || Builder()->ForceUnresolved()) {
203 resolver_ = GetGraph()->CreateInstResolveVirtual(DataType::POINTER, pc_, methodId_, nullptr);
204 if constexpr (OPCODE == Opcode::CallVirtual) {
205 call_ =
206 GetGraph()->CreateInstCallResolvedVirtual(Builder()->GetMethodReturnType(methodId_), pc_, methodId_);
207 }
208 if (!GetGraph()->IsAotMode() && !GetGraph()->IsBytecodeOptimizer()) {
209 GetRuntime()->GetUnresolvedTypes()->AddTableSlot(Builder()->GetMethod(), methodId_, SLOT_KIND);
210 }
211 } else {
212 ASSERT(method_ != nullptr);
213 if constexpr (OPCODE == Opcode::CallVirtual) {
214 call_ =
215 GetGraph()->CreateInstCallVirtual(Builder()->GetMethodReturnType(methodId_), pc_, methodId_, method_);
216 }
217 }
218 }
219
220 // NOLINTNEXTLINE(misc-definitions-in-headers)
221 template <Opcode OPCODE, bool IS_RANGE, bool ACC_READ, bool HAS_SAVE_STATE>
BuildCallInst(uint32_t classId)222 void InstBuilder::BuildCallHelper<OPCODE, IS_RANGE, ACC_READ, HAS_SAVE_STATE>::BuildCallInst(
223 // CC-OFFNXT(G.FMT.06) false positive
224 [[maybe_unused]] uint32_t classId)
225 {
226 // NOLINTNEXTLINE(readability-magic-numbers,readability-braces-around-statements,bugprone-suspicious-semicolon)
227 if constexpr (OPCODE == Opcode::CallStatic) {
228 BuildCallStaticInst(classId);
229 }
230 // NOLINTNEXTLINE(readability-magic-numbers,readability-braces-around-statements,bugprone-suspicious-semicolon)
231 if constexpr (OPCODE == Opcode::CallVirtual) {
232 BuildCallVirtualInst();
233 }
234 if (UNLIKELY(call_ == nullptr)) {
235 UNREACHABLE();
236 }
237 builder_->SetCallNativeFlags(static_cast<CallInst *>(call_), method_);
238 }
239
240 // NOLINTNEXTLINE(misc-definitions-in-headers)
BuildMonitor(const BytecodeInstruction * bcInst,Inst * def,bool isEnter)241 void InstBuilder::BuildMonitor(const BytecodeInstruction *bcInst, Inst *def, bool isEnter)
242 {
243 auto saveState = CreateSaveState(Opcode::SaveState, GetPc(bcInst->GetAddress()));
244 auto inst = GetGraph()->CreateInstMonitor(DataType::VOID, GetPc(bcInst->GetAddress()));
245 AddInstruction(saveState);
246 if (!isEnter) {
247 inst->CastToMonitor()->SetExit();
248 } else {
249 // Create NullCheck instruction
250 auto nullCheck = graph_->CreateInstNullCheck(DataType::REFERENCE, GetPc(bcInst->GetAddress()), def, saveState);
251 def = nullCheck;
252 AddInstruction(nullCheck);
253 }
254 inst->SetInput(0, def);
255 inst->SetInput(1, saveState);
256
257 AddInstruction(inst);
258 }
259
260 #include <intrinsics_ir_build.inl>
261
262 // NOLINTNEXTLINE(misc-definitions-in-headers)
263 template <Opcode OPCODE, bool IS_RANGE, bool ACC_READ, bool HAS_SAVE_STATE>
BuildDefaultStaticIntrinsic(RuntimeInterface::IntrinsicId intrinsicId)264 void InstBuilder::BuildCallHelper<OPCODE, IS_RANGE, ACC_READ, HAS_SAVE_STATE>::BuildDefaultStaticIntrinsic(
265 RuntimeInterface::IntrinsicId intrinsicId)
266 {
267 ASSERT(intrinsicId != RuntimeInterface::IntrinsicId::COUNT);
268 auto retType = Builder()->GetMethodReturnType(methodId_);
269 call_ = GetGraph()->CreateInstIntrinsic(retType, pc_, intrinsicId);
270 // If an intrinsic may call runtime then we need a SaveState
271 saveState_ = call_->RequireState() ? Builder()->CreateSaveState(Opcode::SaveState, pc_) : nullptr;
272 SetCallArgs();
273 if (saveState_ != nullptr) {
274 Builder()->AddInstruction(saveState_);
275 }
276 /* if there are reference type args to be checked for NULL ('need_nullcheck' intrinsic property) */
277 Builder()->template AddArgNullcheckIfNeeded<false>(intrinsicId, call_, saveState_, pc_);
278 AddCallInstruction();
279 if (NeedSafePointAfterIntrinsic(intrinsicId)) {
280 Builder()->AddInstruction(Builder()->CreateSafePoint(Builder()->GetCurrentBlock()));
281 }
282 }
283
284 // NOLINTNEXTLINE(misc-definitions-in-headers)
BuildAbsIntrinsic(const BytecodeInstruction * bcInst,bool accRead)285 void InstBuilder::BuildAbsIntrinsic(const BytecodeInstruction *bcInst, bool accRead)
286 {
287 auto methodIndex = bcInst->GetId(0).AsIndex();
288 auto methodId = GetRuntime()->ResolveMethodIndex(GetMethod(), methodIndex);
289 auto inst = GetGraph()->CreateInstAbs(GetMethodReturnType(methodId), GetPc(bcInst->GetAddress()));
290 ASSERT(GetMethodArgumentsCount(methodId) == 1);
291 inst->SetInput(0, GetArgDefinition(bcInst, 0, accRead));
292 AddInstruction(inst);
293 UpdateDefinitionAcc(inst);
294 }
295
296 template <Opcode OPCODE>
297 static BinaryOperation *CreateBinaryOperation(Graph *graph, DataType::Type returnType, size_t pc) = delete;
298
299 template <>
300 BinaryOperation *CreateBinaryOperation<Opcode::Min>(Graph *graph, DataType::Type returnType, size_t pc)
301 {
302 return graph->CreateInstMin(returnType, pc);
303 }
304
305 template <>
306 BinaryOperation *CreateBinaryOperation<Opcode::Max>(Graph *graph, DataType::Type returnType, size_t pc)
307 {
308 return graph->CreateInstMax(returnType, pc);
309 }
310
311 template <>
312 BinaryOperation *CreateBinaryOperation<Opcode::Mod>(Graph *graph, DataType::Type returnType, size_t pc)
313 {
314 return graph->CreateInstMod(returnType, pc);
315 }
316
317 template void InstBuilder::BuildBinaryOperationIntrinsic<Opcode::Mod>(const BytecodeInstruction *bcInst, bool accRead);
318
319 template <Opcode OPCODE>
BuildBinaryOperationIntrinsic(const BytecodeInstruction * bcInst,bool accRead)320 void InstBuilder::BuildBinaryOperationIntrinsic(const BytecodeInstruction *bcInst, bool accRead)
321 {
322 auto methodIndex = bcInst->GetId(0).AsIndex();
323 [[maybe_unused]] auto methodId = GetRuntime()->ResolveMethodIndex(GetMethod(), methodIndex);
324 ASSERT(GetMethodArgumentsCount(methodId) == 2U);
325 // NOLINTNEXTLINE(readability-magic-numbers)
326 auto inst = CreateBinaryOperation<OPCODE>(GetGraph(), GetMethodReturnType(methodId), GetPc(bcInst->GetAddress()));
327 inst->SetInput(0, GetArgDefinition(bcInst, 0, accRead));
328 inst->SetInput(1, GetArgDefinition(bcInst, 1, accRead));
329 AddInstruction(inst);
330 UpdateDefinitionAcc(inst);
331 }
332
333 // NOLINTNEXTLINE(misc-definitions-in-headers)
BuildSqrtIntrinsic(const BytecodeInstruction * bcInst,bool accRead)334 void InstBuilder::BuildSqrtIntrinsic(const BytecodeInstruction *bcInst, bool accRead)
335 {
336 auto methodIndex = bcInst->GetId(0).AsIndex();
337 [[maybe_unused]] auto methodId = GetRuntime()->ResolveMethodIndex(GetMethod(), methodIndex);
338 auto inst = GetGraph()->CreateInstSqrt(GetMethodReturnType(methodId), GetPc(bcInst->GetAddress()));
339 ASSERT(GetMethodArgumentsCount(methodId) == 1);
340 Inst *def = GetArgDefinition(bcInst, 0, accRead);
341 inst->SetInput(0, def);
342 AddInstruction(inst);
343 UpdateDefinitionAcc(inst);
344 }
345
346 // NOLINTNEXTLINE(misc-definitions-in-headers)
BuildIsNanIntrinsic(const BytecodeInstruction * bcInst,bool accRead)347 void InstBuilder::BuildIsNanIntrinsic(const BytecodeInstruction *bcInst, bool accRead)
348 {
349 auto methodIndex = bcInst->GetId(0).AsIndex();
350 auto methodId = GetRuntime()->ResolveMethodIndex(GetMethod(), methodIndex);
351 // No need to create specialized node for isNaN. Since NaN != NaN, simple float compare node is fine.
352 // Also, ensure that float comparison node is implemented for specific architecture
353 auto vreg = GetArgDefinition(bcInst, 0, accRead);
354 auto inst = GetGraph()->CreateInstCompare(DataType::BOOL, GetPc(bcInst->GetAddress()), vreg, vreg,
355 GetMethodArgumentType(methodId, 0), ConditionCode::CC_NE);
356 inst->SetOperandsType(GetMethodArgumentType(methodId, 0));
357 AddInstruction(inst);
358 UpdateDefinitionAcc(inst);
359 }
360
361 // NOLINTNEXTLINE(misc-definitions-in-headers)
BuildStringLengthIntrinsic(const BytecodeInstruction * bcInst,bool accRead)362 void InstBuilder::BuildStringLengthIntrinsic(const BytecodeInstruction *bcInst, bool accRead)
363 {
364 auto bcAddr = GetPc(bcInst->GetAddress());
365 auto saveState = CreateSaveState(Opcode::SaveState, bcAddr);
366
367 auto nullCheck =
368 graph_->CreateInstNullCheck(DataType::REFERENCE, bcAddr, GetArgDefinition(bcInst, 0, accRead), saveState);
369 auto arrayLength = graph_->CreateInstLenArray(DataType::INT32, bcAddr, nullCheck, false);
370
371 AddInstruction(saveState);
372 AddInstruction(nullCheck);
373 AddInstruction(arrayLength);
374
375 Inst *stringLength;
376 if (graph_->GetRuntime()->IsCompressedStringsEnabled()) {
377 auto constOneInst = graph_->FindOrCreateConstant(1);
378 stringLength = graph_->CreateInstShr(DataType::INT32, bcAddr, arrayLength, constOneInst);
379 AddInstruction(stringLength);
380 } else {
381 stringLength = arrayLength;
382 }
383 UpdateDefinitionAcc(stringLength);
384 }
385
386 // NOLINTNEXTLINE(misc-definitions-in-headers)
BuildStringIsEmptyIntrinsic(const BytecodeInstruction * bcInst,bool accRead)387 void InstBuilder::BuildStringIsEmptyIntrinsic(const BytecodeInstruction *bcInst, bool accRead)
388 {
389 auto bcAddr = GetPc(bcInst->GetAddress());
390 auto saveState = CreateSaveState(Opcode::SaveState, bcAddr);
391 auto nullCheck =
392 graph_->CreateInstNullCheck(DataType::REFERENCE, bcAddr, GetArgDefinition(bcInst, 0, accRead), saveState);
393 auto length = graph_->CreateInstLenArray(DataType::INT32, bcAddr, nullCheck, false);
394 auto zeroConst = graph_->FindOrCreateConstant(0);
395 auto checkZeroLength =
396 graph_->CreateInstCompare(DataType::BOOL, bcAddr, length, zeroConst, DataType::INT32, ConditionCode::CC_EQ);
397 AddInstruction(saveState);
398 AddInstruction(nullCheck);
399 AddInstruction(length);
400 AddInstruction(checkZeroLength);
401 UpdateDefinitionAcc(checkZeroLength);
402 }
403
404 // NOLINTNEXTLINE(misc-definitions-in-headers)
BuildCharIsUpperCaseIntrinsic(const BytecodeInstruction * bcInst,bool accRead)405 void InstBuilder::BuildCharIsUpperCaseIntrinsic(const BytecodeInstruction *bcInst, bool accRead)
406 {
407 // IsUpperCase(char) = (char - 'A') < ('Z' - 'A')
408
409 ASSERT(GetMethodArgumentsCount(GetRuntime()->ResolveMethodIndex(GetMethod(), bcInst->GetId(0).AsIndex())) == 1);
410
411 // Adding InstCast here as the aternative compiler backend requires inputs of InstSub to be of the same type.
412 // The InstCast instructon makes no harm as normally they are removed by the following compiler stages.
413 auto argInput = GetArgDefinition(bcInst, 0, accRead);
414 auto constInput = graph_->FindOrCreateConstant('A');
415 auto arg = GetGraph()->CreateInstCast(DataType::UINT16, GetPc(bcInst->GetAddress()), argInput, argInput->GetType());
416 auto constA =
417 GetGraph()->CreateInstCast(DataType::UINT16, GetPc(bcInst->GetAddress()), constInput, constInput->GetType());
418
419 auto inst1 = GetGraph()->CreateInstSub(DataType::UINT16, GetPc(bcInst->GetAddress()), arg, constA);
420 auto inst2 =
421 GetGraph()->CreateInstCompare(DataType::BOOL, GetPc(bcInst->GetAddress()), inst1,
422 graph_->FindOrCreateConstant('Z' - 'A'), DataType::UINT16, ConditionCode::CC_BE);
423
424 AddInstruction(arg);
425 AddInstruction(constA);
426 AddInstruction(inst1);
427 AddInstruction(inst2);
428 UpdateDefinitionAcc(inst2);
429 }
430
431 // NOLINTNEXTLINE(misc-definitions-in-headers)
BuildCharToUpperCaseIntrinsic(const BytecodeInstruction * bcInst,bool accRead)432 void InstBuilder::BuildCharToUpperCaseIntrinsic(const BytecodeInstruction *bcInst, bool accRead)
433 {
434 // ToUpperCase(char) = ((char - 'a') < ('z' - 'a')) * ('Z' - 'z') + char
435
436 ASSERT(GetMethodArgumentsCount(GetRuntime()->ResolveMethodIndex(GetMethod(), bcInst->GetId(0).AsIndex())) == 1);
437
438 auto argInput = GetArgDefinition(bcInst, 0, accRead);
439 auto constInput = graph_->FindOrCreateConstant('a');
440 auto arg = GetGraph()->CreateInstCast(DataType::UINT16, GetPc(bcInst->GetAddress()), argInput, argInput->GetType());
441 auto constA =
442 GetGraph()->CreateInstCast(DataType::UINT16, GetPc(bcInst->GetAddress()), constInput, constInput->GetType());
443
444 auto inst1 = GetGraph()->CreateInstSub(DataType::UINT16, GetPc(bcInst->GetAddress()), arg, constA);
445 auto inst2 =
446 GetGraph()->CreateInstCompare(DataType::BOOL, GetPc(bcInst->GetAddress()), inst1,
447 graph_->FindOrCreateConstant('z' - 'a'), DataType::UINT16, ConditionCode::CC_BE);
448 auto inst3 = GetGraph()->CreateInstMul(DataType::UINT16, GetPc(bcInst->GetAddress()), inst2,
449 graph_->FindOrCreateConstant('Z' - 'z'));
450 auto inst4 = GetGraph()->CreateInstAdd(DataType::UINT16, GetPc(bcInst->GetAddress()), arg, inst3);
451
452 AddInstruction(arg);
453 AddInstruction(constA);
454 AddInstruction(inst1);
455 AddInstruction(inst2);
456 AddInstruction(inst3);
457 AddInstruction(inst4);
458 UpdateDefinitionAcc(inst4);
459 }
460
461 // NOLINTNEXTLINE(misc-definitions-in-headers)
BuildCharIsLowerCaseIntrinsic(const BytecodeInstruction * bcInst,bool accRead)462 void InstBuilder::BuildCharIsLowerCaseIntrinsic(const BytecodeInstruction *bcInst, bool accRead)
463 {
464 ASSERT(GetMethodArgumentsCount(GetRuntime()->ResolveMethodIndex(GetMethod(), bcInst->GetId(0).AsIndex())) == 1);
465
466 auto argInput = GetArgDefinition(bcInst, 0, accRead);
467 auto constInput = graph_->FindOrCreateConstant('a');
468 auto arg = GetGraph()->CreateInstCast(DataType::UINT16, GetPc(bcInst->GetAddress()), argInput, argInput->GetType());
469 auto constA =
470 GetGraph()->CreateInstCast(DataType::UINT16, GetPc(bcInst->GetAddress()), constInput, constInput->GetType());
471
472 auto inst1 = GetGraph()->CreateInstSub(DataType::UINT16, GetPc(bcInst->GetAddress()), arg, constA);
473 auto inst2 =
474 GetGraph()->CreateInstCompare(DataType::BOOL, GetPc(bcInst->GetAddress()), inst1,
475 graph_->FindOrCreateConstant('z' - 'a'), DataType::UINT16, ConditionCode::CC_BE);
476
477 AddInstruction(arg);
478 AddInstruction(constA);
479 AddInstruction(inst1);
480 AddInstruction(inst2);
481 UpdateDefinitionAcc(inst2);
482 }
483
484 // NOLINTNEXTLINE(misc-definitions-in-headers)
BuildCharToLowerCaseIntrinsic(const BytecodeInstruction * bcInst,bool accRead)485 void InstBuilder::BuildCharToLowerCaseIntrinsic(const BytecodeInstruction *bcInst, bool accRead)
486 {
487 ASSERT(GetMethodArgumentsCount(GetRuntime()->ResolveMethodIndex(GetMethod(), bcInst->GetId(0).AsIndex())) == 1);
488
489 auto argInput = GetArgDefinition(bcInst, 0, accRead);
490 auto constInput = graph_->FindOrCreateConstant('A');
491 auto arg = GetGraph()->CreateInstCast(DataType::UINT16, GetPc(bcInst->GetAddress()), argInput, argInput->GetType());
492 auto constA =
493 GetGraph()->CreateInstCast(DataType::UINT16, GetPc(bcInst->GetAddress()), constInput, constInput->GetType());
494
495 auto inst1 = GetGraph()->CreateInstSub(DataType::UINT16, GetPc(bcInst->GetAddress()), arg, constA);
496 auto inst2 =
497 GetGraph()->CreateInstCompare(DataType::BOOL, GetPc(bcInst->GetAddress()), inst1,
498 graph_->FindOrCreateConstant('Z' - 'A'), DataType::UINT16, ConditionCode::CC_BE);
499 auto inst3 = GetGraph()->CreateInstMul(DataType::UINT16, GetPc(bcInst->GetAddress()), inst2,
500 graph_->FindOrCreateConstant('z' - 'Z'));
501 auto inst4 = GetGraph()->CreateInstAdd(DataType::UINT16, GetPc(bcInst->GetAddress()), arg, inst3);
502
503 AddInstruction(arg);
504 AddInstruction(constA);
505 AddInstruction(inst1);
506 AddInstruction(inst2);
507 AddInstruction(inst3);
508 AddInstruction(inst4);
509 UpdateDefinitionAcc(inst4);
510 }
511
512 // NOLINTNEXTLINE(misc-definitions-in-headers)
GetArgDefinition(const BytecodeInstruction * bcInst,size_t idx,bool accRead,bool isRange)513 Inst *InstBuilder::GetArgDefinition(const BytecodeInstruction *bcInst, size_t idx, bool accRead, bool isRange)
514 {
515 if (isRange) {
516 return GetArgDefinitionRange(bcInst, idx);
517 }
518 if (accRead) {
519 auto accPos = static_cast<size_t>(bcInst->GetImm64());
520 if (idx < accPos) {
521 return GetDefinition(bcInst->GetVReg(idx));
522 }
523 if (accPos == idx) {
524 return GetDefinitionAcc();
525 }
526 return GetDefinition(bcInst->GetVReg(idx - 1));
527 }
528 return GetDefinition(bcInst->GetVReg(idx));
529 }
530
531 // NOLINTNEXTLINE(misc-definitions-in-headers)
GetArgDefinitionRange(const BytecodeInstruction * bcInst,size_t idx)532 Inst *InstBuilder::GetArgDefinitionRange(const BytecodeInstruction *bcInst, size_t idx)
533 {
534 auto startReg = bcInst->GetVReg(0);
535 return GetDefinition(startReg + idx);
536 }
537
538 // NOLINTNEXTLINE(misc-definitions-in-headers)
539 template <Opcode OPCODE, bool IS_RANGE, bool ACC_READ, bool HAS_SAVE_STATE>
BuildMonitorIntrinsic(bool isEnter)540 void InstBuilder::BuildCallHelper<OPCODE, IS_RANGE, ACC_READ, HAS_SAVE_STATE>::BuildMonitorIntrinsic(bool isEnter)
541 {
542 ASSERT(Builder()->GetMethodReturnType(methodId_) == DataType::VOID);
543 ASSERT(Builder()->GetMethodArgumentsCount(methodId_) == 1);
544 Builder()->BuildMonitor(bcInst_, Builder()->GetArgDefinition(bcInst_, 0, ACC_READ), isEnter);
545 }
546
547 template <Opcode OPCODE, bool IS_RANGE, bool ACC_READ, bool HAS_SAVE_STATE>
TryBuildIntrinsic()548 bool InstBuilder::BuildCallHelper<OPCODE, IS_RANGE, ACC_READ, HAS_SAVE_STATE>::TryBuildIntrinsic()
549 {
550 auto runtime = GetRuntime();
551 auto isMethodStatic = runtime->IsMethodStatic(method_);
552 auto isMethodFinal = runtime->IsMethodFinal(method_);
553 auto isClassFinal = runtime->IsClassFinal(runtime->GetClass(method_));
554 if (isMethodStatic || isMethodFinal || isClassFinal) {
555 BuildIntrinsic();
556 return true;
557 }
558 COMPILER_LOG(DEBUG, IR_BUILDER) << "Skips building intrinsic '"
559 << GetIntrinsicName(runtime->GetIntrinsicId(method_))
560 << "' since the method and class is not final.";
561 return false;
562 }
563
564 // NOLINTNEXTLINE(misc-definitions-in-headers)
565 template <Opcode OPCODE, bool IS_RANGE, bool ACC_READ, bool HAS_SAVE_STATE>
BuildIntrinsic()566 void InstBuilder::BuildCallHelper<OPCODE, IS_RANGE, ACC_READ, HAS_SAVE_STATE>::BuildIntrinsic()
567 {
568 ASSERT(method_ != nullptr);
569 auto intrinsicId = GetRuntime()->GetIntrinsicId(method_);
570 auto isVirtual = IsVirtual(intrinsicId);
571 if (GetGraph()->IsBytecodeOptimizer() || !g_options.IsCompilerEncodeIntrinsics()) {
572 BuildDefaultIntrinsic(intrinsicId, isVirtual);
573 return;
574 }
575 if (!isVirtual) {
576 return BuildStaticCallIntrinsic(intrinsicId);
577 }
578 return BuildVirtualCallIntrinsic(intrinsicId);
579 }
580
581 // NOLINTNEXTLINE(misc-definitions-in-headers)
582 template <Opcode OPCODE, bool IS_RANGE, bool ACC_READ, bool HAS_SAVE_STATE>
BuildDefaultIntrinsic(RuntimeInterface::IntrinsicId intrinsicId,bool isVirtual)583 void InstBuilder::BuildCallHelper<OPCODE, IS_RANGE, ACC_READ, HAS_SAVE_STATE>::BuildDefaultIntrinsic(
584 RuntimeInterface::IntrinsicId intrinsicId, bool isVirtual)
585 {
586 if (intrinsicId == RuntimeInterface::IntrinsicId::INTRINSIC_OBJECT_MONITOR_ENTER ||
587 intrinsicId == RuntimeInterface::IntrinsicId::INTRINSIC_OBJECT_MONITOR_EXIT) {
588 BuildMonitorIntrinsic(intrinsicId == RuntimeInterface::IntrinsicId::INTRINSIC_OBJECT_MONITOR_ENTER);
589 return;
590 }
591 // NOLINTNEXTLINE(readability-braces-around-statements)
592 if (!isVirtual) {
593 BuildDefaultStaticIntrinsic(intrinsicId);
594 // NOLINTNEXTLINE(readability-misleading-indentation)
595 } else {
596 BuildDefaultVirtualCallIntrinsic(intrinsicId);
597 }
598 }
599
600 // do not specify reason for tidy suppression because comment does not fit single line
601 // NOLINTNEXTLINE
602 template <Opcode OPCODE, bool IS_RANGE, bool ACC_READ, bool HAS_SAVE_STATE>
BuildStaticCallIntrinsic(RuntimeInterface::IntrinsicId intrinsicId)603 void InstBuilder::BuildCallHelper<OPCODE, IS_RANGE, ACC_READ, HAS_SAVE_STATE>::BuildStaticCallIntrinsic(
604 RuntimeInterface::IntrinsicId intrinsicId)
605 {
606 switch (intrinsicId) {
607 case RuntimeInterface::IntrinsicId::INTRINSIC_OBJECT_MONITOR_ENTER:
608 case RuntimeInterface::IntrinsicId::INTRINSIC_OBJECT_MONITOR_EXIT: {
609 BuildMonitorIntrinsic(intrinsicId == RuntimeInterface::IntrinsicId::INTRINSIC_OBJECT_MONITOR_ENTER);
610 break;
611 }
612 case RuntimeInterface::IntrinsicId::INTRINSIC_MATH_ABS_I32:
613 case RuntimeInterface::IntrinsicId::INTRINSIC_MATH_ABS_I64:
614 case RuntimeInterface::IntrinsicId::INTRINSIC_MATH_ABS_F32:
615 case RuntimeInterface::IntrinsicId::INTRINSIC_MATH_ABS_F64: {
616 Builder()->BuildAbsIntrinsic(bcInst_, ACC_READ);
617 break;
618 }
619 case RuntimeInterface::IntrinsicId::INTRINSIC_MATH_SQRT_F32:
620 case RuntimeInterface::IntrinsicId::INTRINSIC_MATH_SQRT_F64: {
621 Builder()->BuildSqrtIntrinsic(bcInst_, ACC_READ);
622 break;
623 }
624 case RuntimeInterface::IntrinsicId::INTRINSIC_MATH_MIN_I32:
625 case RuntimeInterface::IntrinsicId::INTRINSIC_MATH_MIN_I64:
626 case RuntimeInterface::IntrinsicId::INTRINSIC_MATH_MIN_F32:
627 case RuntimeInterface::IntrinsicId::INTRINSIC_MATH_MIN_F64: {
628 Builder()->template BuildBinaryOperationIntrinsic<Opcode::Min>(bcInst_, ACC_READ);
629 break;
630 }
631 case RuntimeInterface::IntrinsicId::INTRINSIC_MATH_MAX_I32:
632 case RuntimeInterface::IntrinsicId::INTRINSIC_MATH_MAX_I64:
633 case RuntimeInterface::IntrinsicId::INTRINSIC_MATH_MAX_F32:
634 case RuntimeInterface::IntrinsicId::INTRINSIC_MATH_MAX_F64: {
635 Builder()->template BuildBinaryOperationIntrinsic<Opcode::Max>(bcInst_, ACC_READ);
636 break;
637 }
638 #include "intrinsics_ir_build_static_call.inl"
639 default: {
640 BuildDefaultStaticIntrinsic(intrinsicId);
641 }
642 }
643 }
644
645 // NOLINTNEXTLINE(misc-definitions-in-headers)
646 template <Opcode OPCODE, bool IS_RANGE, bool ACC_READ, bool HAS_SAVE_STATE>
AddCallInstruction()647 void InstBuilder::BuildCallHelper<OPCODE, IS_RANGE, ACC_READ, HAS_SAVE_STATE>::AddCallInstruction()
648 {
649 Builder()->AddInstruction(call_);
650 if (call_->GetType() != DataType::VOID) {
651 Builder()->UpdateDefinitionAcc(call_);
652 } else {
653 Builder()->UpdateDefinitionAcc(nullptr);
654 }
655 }
656
657 // NOLINTNEXTLINE(misc-definitions-in-headers)
658 template <Opcode OPCODE, bool IS_RANGE, bool ACC_READ, bool HAS_SAVE_STATE>
BuildDefaultVirtualCallIntrinsic(RuntimeInterface::IntrinsicId intrinsicId)659 void InstBuilder::BuildCallHelper<OPCODE, IS_RANGE, ACC_READ, HAS_SAVE_STATE>::BuildDefaultVirtualCallIntrinsic(
660 RuntimeInterface::IntrinsicId intrinsicId)
661 {
662 saveState_ = Builder()->CreateSaveState(Opcode::SaveState, pc_);
663 nullCheck_ = GetGraph()->CreateInstNullCheck(DataType::REFERENCE, pc_,
664 Builder()->GetArgDefinition(bcInst_, 0, ACC_READ), saveState_);
665
666 call_ = GetGraph()->CreateInstIntrinsic(Builder()->GetMethodReturnType(methodId_), pc_, intrinsicId);
667 SetCallArgs();
668
669 Builder()->AddInstruction(saveState_);
670 Builder()->AddInstruction(nullCheck_);
671
672 /* if there are reference type args to be checked for NULL */
673 Builder()->template AddArgNullcheckIfNeeded<true>(intrinsicId, call_, saveState_, pc_);
674
675 AddCallInstruction();
676 if (NeedSafePointAfterIntrinsic(intrinsicId)) {
677 Builder()->AddInstruction(Builder()->CreateSafePoint(Builder()->GetCurrentBlock()));
678 }
679 }
680
681 template InstBuilder::BuildCallHelper<Opcode::CallResolvedStatic, false, false>::BuildCallHelper(
682 const BytecodeInstruction *bcInst, InstBuilder *builder, Inst *additionalInput);
683 template InstBuilder::BuildCallHelper<Opcode::CallResolvedStatic, false, false, false>::BuildCallHelper(
684 const BytecodeInstruction *bcInst, InstBuilder *builder, Inst *additionalInput);
685
686 // NOLINTNEXTLINE(readability-function-size,misc-definitions-in-headers)
687 template <bool IS_ACC_WRITE>
BuildLoadObject(const BytecodeInstruction * bcInst,DataType::Type type)688 void InstBuilder::BuildLoadObject(const BytecodeInstruction *bcInst, DataType::Type type)
689 {
690 auto pc = GetPc(bcInst->GetAddress());
691 // Create SaveState instruction
692 auto saveState = CreateSaveState(Opcode::SaveState, pc);
693 // Create NullCheck instruction
694 auto nullCheck = graph_->CreateInstNullCheck(DataType::REFERENCE, pc,
695 GetDefinition(bcInst->GetVReg(IS_ACC_WRITE ? 0 : 1)), saveState);
696 auto runtime = GetRuntime();
697 auto fieldIndex = bcInst->GetId(0).AsIndex();
698 auto fieldId = runtime->ResolveFieldIndex(GetMethod(), fieldIndex);
699 auto field = runtime->ResolveField(GetMethod(), fieldId, false, !GetGraph()->IsAotMode(), nullptr);
700 if (type != DataType::REFERENCE) {
701 type = runtime->GetFieldTypeById(GetMethod(), fieldId);
702 }
703
704 // Create LoadObject instruction
705 Inst *inst;
706 AddInstruction(saveState, nullCheck);
707 if (field == nullptr || ForceUnresolved()) {
708 // 1. Create an instruction to resolve an object's field
709 if (!GetGraph()->IsAotMode() && !GetGraph()->IsBytecodeOptimizer()) {
710 GetRuntime()->GetUnresolvedTypes()->AddTableSlot(GetMethod(), fieldId,
711 UnresolvedTypesInterface::SlotKind::FIELD);
712 }
713 auto *resolveField = graph_->CreateInstResolveObjectField(DataType::UINT32, pc, saveState,
714 TypeIdMixin {fieldId, GetGraph()->GetMethod()});
715 AddInstruction(resolveField);
716 // 2. Create an instruction to load a value from the resolved field
717 auto loadField = graph_->CreateInstLoadResolvedObjectField(type, pc, nullCheck, resolveField,
718 TypeIdMixin {fieldId, GetGraph()->GetMethod()});
719 inst = loadField;
720 } else {
721 auto loadField =
722 graph_->CreateInstLoadObject(type, pc, nullCheck, TypeIdMixin {fieldId, GetGraph()->GetMethod()}, field,
723 runtime->IsFieldVolatile(field));
724 // 'final' field can be reassigned e. g. with reflection, but 'readonly' cannot
725 // `IsInConstructor` check should not be necessary, but need proper frontend support first
726 constexpr bool IS_STATIC = false;
727 if (runtime->IsFieldReadonly(field) && !IsInConstructor<IS_STATIC>()) {
728 loadField->ClearFlag(inst_flags::NO_CSE);
729 }
730 inst = loadField;
731 }
732
733 AddInstruction(inst);
734 // NOLINTNEXTLINE(readability-braces-around-statements)
735 if constexpr (IS_ACC_WRITE) {
736 UpdateDefinitionAcc(inst);
737 // NOLINTNEXTLINE(readability-misleading-indentation)
738 } else {
739 UpdateDefinition(bcInst->GetVReg(0), inst);
740 }
741 }
742
743 // NOLINTNEXTLINE(misc-definitions-in-headers)
BuildStoreObjectInst(const BytecodeInstruction * bcInst,DataType::Type type,RuntimeInterface::FieldPtr field,uint32_t fieldId,Inst ** resolveInst)744 Inst *InstBuilder::BuildStoreObjectInst(const BytecodeInstruction *bcInst, DataType::Type type,
745 RuntimeInterface::FieldPtr field, uint32_t fieldId, Inst **resolveInst)
746 {
747 auto pc = GetPc(bcInst->GetAddress());
748 if (field == nullptr || ForceUnresolved()) {
749 // The field is unresolved, so we have to resolve it and then store
750 // 1. Create an instruction to resolve an object's field
751 auto resolveField = graph_->CreateInstResolveObjectField(DataType::UINT32, pc, nullptr,
752 TypeIdMixin {fieldId, GetGraph()->GetMethod()});
753 if (!GetGraph()->IsAotMode() && !GetGraph()->IsBytecodeOptimizer()) {
754 GetRuntime()->GetUnresolvedTypes()->AddTableSlot(GetMethod(), fieldId,
755 UnresolvedTypesInterface::SlotKind::FIELD);
756 }
757 // 2. Create an instruction to store a value to the resolved field
758 auto storeField = graph_->CreateInstStoreResolvedObjectField(type, pc, nullptr, nullptr, nullptr,
759 TypeIdMixin {fieldId, GetGraph()->GetMethod()},
760 false, type == DataType::REFERENCE);
761 *resolveInst = resolveField;
762 return storeField;
763 }
764
765 ASSERT(field != nullptr);
766 auto storeField =
767 graph_->CreateInstStoreObject(type, pc, nullptr, nullptr, TypeIdMixin {fieldId, GetGraph()->GetMethod()}, field,
768 GetRuntime()->IsFieldVolatile(field), type == DataType::REFERENCE);
769 *resolveInst = nullptr; // resolver is not needed in this case
770 return storeField;
771 }
772
773 // NOLINTNEXTLINE(misc-definitions-in-headers)
774 template <bool IS_ACC_READ>
BuildStoreObject(const BytecodeInstruction * bcInst,DataType::Type type)775 void InstBuilder::BuildStoreObject(const BytecodeInstruction *bcInst, DataType::Type type)
776 {
777 // Create SaveState instruction
778 auto saveState = CreateSaveState(Opcode::SaveState, GetPc(bcInst->GetAddress()));
779
780 // Create NullCheck instruction
781 auto nullCheck = graph_->CreateInstNullCheck(DataType::REFERENCE, GetPc(bcInst->GetAddress()),
782 GetDefinition(bcInst->GetVReg(IS_ACC_READ ? 0 : 1)), saveState);
783
784 auto runtime = GetRuntime();
785 auto fieldIndex = bcInst->GetId(0).AsIndex();
786 auto fieldId = runtime->ResolveFieldIndex(GetMethod(), fieldIndex);
787 auto field = runtime->ResolveField(GetMethod(), fieldId, false, !GetGraph()->IsAotMode(), nullptr);
788 if (type != DataType::REFERENCE) {
789 type = runtime->GetFieldTypeById(GetMethod(), fieldId);
790 }
791
792 // Get a value to store
793 Inst *storeVal = nullptr;
794 // NOLINTNEXTLINE(readability-braces-around-statements)
795 if constexpr (IS_ACC_READ) {
796 storeVal = GetDefinitionAcc();
797 } else { // NOLINT(readability-misleading-indentation)
798 storeVal = GetDefinition(bcInst->GetVReg(0));
799 }
800
801 // Create StoreObject instruction
802 Inst *resolveField = nullptr;
803 Inst *storeField = BuildStoreObjectInst(bcInst, type, field, fieldId, &resolveField);
804 storeField->SetInput(0, nullCheck);
805 storeField->SetInput(1, storeVal);
806
807 AddInstruction(saveState);
808 AddInstruction(nullCheck);
809 if (resolveField != nullptr) {
810 ASSERT(field == nullptr || ForceUnresolved());
811 resolveField->SetInput(0, saveState);
812 storeField->SetInput(2U, resolveField);
813 AddInstruction(resolveField);
814 }
815 AddInstruction(storeField);
816 }
817
818 // NOLINTNEXTLINE(misc-definitions-in-headers)
BuildLoadStaticInst(size_t pc,DataType::Type type,uint32_t typeId,Inst * saveState)819 Inst *InstBuilder::BuildLoadStaticInst(size_t pc, DataType::Type type, uint32_t typeId, Inst *saveState)
820 {
821 uint32_t classId;
822 auto field = GetRuntime()->ResolveField(GetMethod(), typeId, true, !GetGraph()->IsAotMode(), &classId);
823 if (field == nullptr || ForceUnresolved()) {
824 // The static field is unresolved, so we have to resolve it and then load
825 // 1. Create an instruction to resolve an object's static field.
826 // Its result is a static field memory address (not an offset as there is no object)
827 auto resolveField = graph_->CreateInstResolveObjectFieldStatic(DataType::REFERENCE, pc, saveState,
828 TypeIdMixin {typeId, GetGraph()->GetMethod()});
829 if (!GetGraph()->IsAotMode() && !GetGraph()->IsBytecodeOptimizer()) {
830 GetRuntime()->GetUnresolvedTypes()->AddTableSlot(GetMethod(), typeId,
831 UnresolvedTypesInterface::SlotKind::FIELD);
832 }
833 AddInstruction(resolveField);
834 // 2. Create an instruction to load a value from the resolved static field address
835 auto loadField = graph_->CreateInstLoadResolvedObjectFieldStatic(type, pc, resolveField,
836 TypeIdMixin {typeId, GetGraph()->GetMethod()});
837 return loadField;
838 }
839
840 ASSERT(field != nullptr);
841 ASSERT(classId != 0);
842 auto initClass = graph_->CreateInstLoadAndInitClass(DataType::REFERENCE, pc, saveState,
843 TypeIdMixin {classId, GetGraph()->GetMethod()},
844 GetRuntime()->GetClassForField(field));
845 auto loadField = graph_->CreateInstLoadStatic(type, pc, initClass, TypeIdMixin {typeId, GetGraph()->GetMethod()},
846 field, GetRuntime()->IsFieldVolatile(field));
847 // 'final' field can be reassigned e. g. with reflection, but 'readonly' cannot
848 // `IsInConstructor` check should not be necessary, but need proper frontend support first
849 constexpr bool IS_STATIC = true;
850 if (GetRuntime()->IsFieldReadonly(field) && !IsInConstructor<IS_STATIC>()) {
851 loadField->ClearFlag(inst_flags::NO_CSE);
852 }
853 AddInstruction(initClass);
854 return loadField;
855 }
856
857 // NOLINTNEXTLINE(misc-definitions-in-headers)
BuildAnyTypeCheckInst(size_t bcAddr,Inst * input,Inst * saveState,AnyBaseType type)858 AnyTypeCheckInst *InstBuilder::BuildAnyTypeCheckInst(size_t bcAddr, Inst *input, Inst *saveState, AnyBaseType type)
859 {
860 auto anyCheck = graph_->CreateInstAnyTypeCheck(DataType::ANY, bcAddr, input, saveState, type);
861 AddInstruction(anyCheck);
862 return anyCheck;
863 }
864
865 // NOLINTNEXTLINE(misc-definitions-in-headers)
BuildLoadStatic(const BytecodeInstruction * bcInst,DataType::Type type)866 void InstBuilder::BuildLoadStatic(const BytecodeInstruction *bcInst, DataType::Type type)
867 {
868 auto fieldIndex = bcInst->GetId(0).AsIndex();
869 auto fieldId = GetRuntime()->ResolveFieldIndex(GetMethod(), fieldIndex);
870 if (type != DataType::REFERENCE) {
871 type = GetRuntime()->GetFieldTypeById(GetMethod(), fieldId);
872 }
873 auto saveState = CreateSaveState(Opcode::SaveState, GetPc(bcInst->GetAddress()));
874 AddInstruction(saveState);
875 Inst *inst = BuildLoadStaticInst(GetPc(bcInst->GetAddress()), type, fieldId, saveState);
876 AddInstruction(inst);
877 UpdateDefinitionAcc(inst);
878 }
879
880 // NOLINTNEXTLINE(readability-function-size,misc-definitions-in-headers)
BuildStoreStaticInst(const BytecodeInstruction * bcInst,DataType::Type type,uint32_t typeId,Inst * storeInput,Inst * saveState)881 Inst *InstBuilder::BuildStoreStaticInst(const BytecodeInstruction *bcInst, DataType::Type type, uint32_t typeId,
882 Inst *storeInput, Inst *saveState)
883 {
884 AddInstruction(saveState);
885
886 uint32_t classId;
887 auto field = GetRuntime()->ResolveField(GetMethod(), typeId, true, !GetGraph()->IsAotMode(), &classId);
888 auto pc = GetPc(bcInst->GetAddress());
889
890 if (field == nullptr || ForceUnresolved()) {
891 if (type == DataType::REFERENCE) {
892 // 1. Class initialization is needed.
893 // 2. GC Pre/Post write barriers may be needed.
894 // Just call runtime EntrypointId::UNRESOLVED_STORE_STATIC_BARRIERED,
895 // which performs all the necessary steps (see codegen.cpp for the details).
896 auto inst = graph_->CreateInstUnresolvedStoreStatic(type, pc, storeInput, saveState,
897 TypeIdMixin {typeId, GetGraph()->GetMethod()}, true);
898 return inst;
899 }
900 ASSERT(type != DataType::REFERENCE);
901 // 1. Create an instruction to resolve an object's static field.
902 // Its result is a static field memory address (REFERENCE)
903 auto resolveField = graph_->CreateInstResolveObjectFieldStatic(DataType::REFERENCE, pc, saveState,
904 TypeIdMixin {typeId, GetGraph()->GetMethod()});
905 AddInstruction(resolveField);
906 // 2. Create an instruction to store a value to the resolved static field address
907 auto storeField = graph_->CreateInstStoreResolvedObjectFieldStatic(
908 type, pc, resolveField, storeInput, TypeIdMixin {typeId, GetGraph()->GetMethod()});
909 if (!GetGraph()->IsAotMode() && !GetGraph()->IsBytecodeOptimizer()) {
910 GetRuntime()->GetUnresolvedTypes()->AddTableSlot(GetMethod(), typeId,
911 UnresolvedTypesInterface::SlotKind::FIELD);
912 }
913 return storeField;
914 }
915
916 ASSERT(field != nullptr);
917 ASSERT(classId != 0);
918 auto initClass = graph_->CreateInstLoadAndInitClass(DataType::REFERENCE, pc, saveState,
919 TypeIdMixin {classId, GetGraph()->GetMethod()},
920 GetRuntime()->GetClassForField(field));
921 auto storeField =
922 graph_->CreateInstStoreStatic(type, pc, initClass, storeInput, TypeIdMixin {typeId, GetGraph()->GetMethod()},
923 field, GetRuntime()->IsFieldVolatile(field), type == DataType::REFERENCE);
924 AddInstruction(initClass);
925 return storeField;
926 }
927
928 // NOLINTNEXTLINE(misc-definitions-in-headers)
BuildStoreStatic(const BytecodeInstruction * bcInst,DataType::Type type)929 void InstBuilder::BuildStoreStatic(const BytecodeInstruction *bcInst, DataType::Type type)
930 {
931 auto fieldIndex = bcInst->GetId(0).AsIndex();
932 auto fieldId = GetRuntime()->ResolveFieldIndex(GetMethod(), fieldIndex);
933 if (type != DataType::REFERENCE) {
934 type = GetRuntime()->GetFieldTypeById(GetMethod(), fieldId);
935 }
936 auto saveState = CreateSaveState(Opcode::SaveState, GetPc(bcInst->GetAddress()));
937 Inst *storeInput = GetDefinitionAcc();
938 Inst *inst = BuildStoreStaticInst(bcInst, type, fieldId, storeInput, saveState);
939 AddInstruction(inst);
940 }
941
942 // NOLINTNEXTLINE(misc-definitions-in-headers)
BuildChecksBeforeArray(size_t pc,Inst * arrayRef,bool withNullcheck)943 std::tuple<SaveStateInst *, Inst *, LengthMethodInst *, BoundsCheckInst *> InstBuilder::BuildChecksBeforeArray(
944 size_t pc, Inst *arrayRef, bool withNullcheck)
945 {
946 // Create SaveState instruction
947 auto saveState = CreateSaveState(Opcode::SaveState, pc);
948
949 // Create NullCheck instruction
950 Inst *nullCheck = nullptr;
951 if (withNullcheck) {
952 nullCheck = graph_->CreateInstNullCheck(DataType::REFERENCE, pc, arrayRef, saveState);
953 } else {
954 nullCheck = arrayRef;
955 }
956
957 // Create LenArray instruction
958 auto arrayLength = graph_->CreateInstLenArray(DataType::INT32, pc, nullCheck);
959
960 // Create BoundCheck instruction
961 auto boundsCheck = graph_->CreateInstBoundsCheck(DataType::INT32, pc, arrayLength, nullptr, saveState);
962
963 return std::make_tuple(saveState, nullCheck, arrayLength, boundsCheck);
964 }
965
966 // NOLINTNEXTLINE(misc-definitions-in-headers)
BuildLoadArray(const BytecodeInstruction * bcInst,DataType::Type type)967 void InstBuilder::BuildLoadArray(const BytecodeInstruction *bcInst, DataType::Type type)
968 {
969 ASSERT(type != DataType::NO_TYPE);
970 auto pc = GetPc(bcInst->GetAddress());
971
972 auto [saveState, nullCheck, arrayLength, boundsCheck] =
973 BuildChecksBeforeArray(pc, GetDefinition(bcInst->GetVReg(0)));
974 ASSERT(saveState != nullptr && nullCheck != nullptr && arrayLength != nullptr && boundsCheck != nullptr);
975
976 // Create instruction
977 auto inst = graph_->CreateInstLoadArray(type, pc, nullCheck, boundsCheck);
978 boundsCheck->SetInput(1, GetDefinitionAcc());
979 AddInstruction(saveState, nullCheck, arrayLength, boundsCheck, inst);
980 UpdateDefinitionAcc(inst);
981 }
982
983 template <typename T>
BuildUnfoldLoadConstPrimitiveArray(const BytecodeInstruction * bcInst,DataType::Type type,const pandasm::LiteralArray & litArray,NewArrayInst * arrayInst)984 void InstBuilder::BuildUnfoldLoadConstPrimitiveArray(const BytecodeInstruction *bcInst, DataType::Type type,
985 const pandasm::LiteralArray &litArray, NewArrayInst *arrayInst)
986 {
987 [[maybe_unused]] auto tag = litArray.literals[0].tag;
988 auto arraySize = litArray.literals.size();
989 for (size_t i = 0; i < arraySize; i++) {
990 auto indexInst = graph_->FindOrCreateConstant(i);
991 ConstantInst *valueInst;
992 if constexpr (std::is_same_v<T, float>) {
993 ASSERT(tag == panda_file::LiteralTag::ARRAY_F32);
994 valueInst = FindOrCreateFloatConstant(std::get<T>(litArray.literals[i].value));
995 } else if constexpr (std::is_same_v<T, double>) {
996 ASSERT(tag == panda_file::LiteralTag::ARRAY_F64);
997 valueInst = FindOrCreateDoubleConstant(std::get<T>(litArray.literals[i].value));
998 } else if constexpr (std::is_same_v<T, bool>) {
999 ASSERT(tag == panda_file::LiteralTag::ARRAY_U1);
1000 valueInst = FindOrCreateConstant(std::get<T>(litArray.literals[i].value));
1001 } else {
1002 auto lit = litArray.literals[i];
1003 T val = std::get<T>(lit.value);
1004 if (lit.IsSigned()) {
1005 valueInst = FindOrCreateConstant(static_cast<std::make_signed_t<T>>(val));
1006 } else {
1007 valueInst = FindOrCreateConstant(val);
1008 }
1009 }
1010
1011 BuildStoreArrayInst<false>(bcInst, type, arrayInst, indexInst, valueInst);
1012 }
1013 }
1014
1015 template <typename T>
BuildUnfoldLoadConstStringArray(const BytecodeInstruction * bcInst,DataType::Type type,const pandasm::LiteralArray & litArray,NewArrayInst * arrayInst)1016 void InstBuilder::BuildUnfoldLoadConstStringArray(const BytecodeInstruction *bcInst, DataType::Type type,
1017 const pandasm::LiteralArray &litArray, NewArrayInst *arrayInst)
1018 {
1019 auto method = GetGraph()->GetMethod();
1020 auto arraySize = litArray.literals.size();
1021 for (size_t i = 0; i < arraySize; i++) {
1022 auto indexInst = graph_->FindOrCreateConstant(i);
1023 auto save = CreateSaveState(Opcode::SaveState, GetPc(bcInst->GetAddress()));
1024 auto typeId = static_cast<uint32_t>(std::get<T>(litArray.literals[i].value));
1025 auto loadStringInst = GetGraph()->CreateInstLoadString(DataType::REFERENCE, GetPc(bcInst->GetAddress()), save,
1026 TypeIdMixin {typeId, method});
1027 AddInstruction(save);
1028 AddInstruction(loadStringInst);
1029 if (GetGraph()->IsDynamicMethod()) {
1030 BuildCastToAnyString(bcInst);
1031 }
1032
1033 BuildStoreArrayInst<false>(bcInst, type, arrayInst, indexInst, loadStringInst);
1034 }
1035 }
1036
1037 template <typename T>
BuildUnfoldLoadConstArray(const BytecodeInstruction * bcInst,DataType::Type type,const pandasm::LiteralArray & litArray)1038 void InstBuilder::BuildUnfoldLoadConstArray(const BytecodeInstruction *bcInst, DataType::Type type,
1039 const pandasm::LiteralArray &litArray)
1040 {
1041 auto method = GetGraph()->GetMethod();
1042 auto arraySize = litArray.literals.size();
1043 auto typeId = GetRuntime()->GetLiteralArrayClassIdWithinFile(method, litArray.literals[0].tag);
1044
1045 // Create NewArray instruction
1046 auto sizeInst = graph_->FindOrCreateConstant(arraySize);
1047 auto saveState = CreateSaveState(Opcode::SaveState, GetPc(bcInst->GetAddress()));
1048 auto negCheck = graph_->CreateInstNegativeCheck(DataType::INT32, GetPc(bcInst->GetAddress()), sizeInst, saveState);
1049 auto initClass = CreateLoadAndInitClassGeneric(typeId, GetPc(bcInst->GetAddress()));
1050 initClass->SetInput(0, saveState);
1051 auto arrayInst = graph_->CreateInstNewArray(DataType::REFERENCE, GetPc(bcInst->GetAddress()), initClass, negCheck,
1052 saveState, TypeIdMixin {typeId, GetGraph()->GetMethod()});
1053 AddInstruction(saveState);
1054 AddInstruction(initClass);
1055 AddInstruction(negCheck);
1056 AddInstruction(arrayInst);
1057 UpdateDefinition(bcInst->GetVReg(0), arrayInst);
1058
1059 if (arraySize > g_options.GetCompilerUnfoldConstArrayMaxSize()) {
1060 // Create LoadConstArray instruction
1061 auto ss = CreateSaveState(Opcode::SaveState, GetPc(bcInst->GetAddress()));
1062 auto inst = GetGraph()->CreateInstFillConstArray(type, GetPc(bcInst->GetAddress()), arrayInst, ss,
1063 TypeIdMixin {bcInst->GetId(0).AsFileId().GetOffset(), method},
1064 arraySize);
1065 AddInstruction(ss);
1066 AddInstruction(inst);
1067 return;
1068 }
1069
1070 // Create instructions for array filling
1071 auto tag = litArray.literals[0].tag;
1072 if (tag != panda_file::LiteralTag::ARRAY_STRING) {
1073 BuildUnfoldLoadConstPrimitiveArray<T>(bcInst, type, litArray, arrayInst);
1074 return;
1075 }
1076
1077 [[maybe_unused]] auto arrayClass = GetRuntime()->ResolveType(method, typeId);
1078 ASSERT(GetRuntime()->CheckStoreArray(arrayClass, GetRuntime()->GetStringClass(method, nullptr)));
1079 // Special case for string array
1080 BuildUnfoldLoadConstStringArray<T>(bcInst, type, litArray, arrayInst);
1081 }
1082
1083 // NOLINTNEXTLINE(misc-definitions-in-headers)
BuildLoadConstStringArray(const BytecodeInstruction * bcInst)1084 void InstBuilder::BuildLoadConstStringArray(const BytecodeInstruction *bcInst)
1085 {
1086 auto literalArrayIdx = bcInst->GetId(0).AsIndex();
1087 auto litArray = GetRuntime()->GetLiteralArray(GetMethod(), literalArrayIdx);
1088 auto arraySize = litArray.literals.size();
1089 ASSERT(arraySize > 0);
1090 if (arraySize > g_options.GetCompilerUnfoldConstArrayMaxSize()) {
1091 // Create LoadConstArray instruction for String array, because we calls runtime for the case.
1092 auto saveState = CreateSaveState(Opcode::SaveState, GetPc(bcInst->GetAddress()));
1093 auto method = GetGraph()->GetMethod();
1094 auto inst = GetGraph()->CreateInstLoadConstArray(DataType::REFERENCE, GetPc(bcInst->GetAddress()), saveState,
1095 TypeIdMixin {literalArrayIdx, method});
1096 AddInstruction(saveState);
1097 AddInstruction(inst);
1098 UpdateDefinition(bcInst->GetVReg(0), inst);
1099 } else {
1100 BuildUnfoldLoadConstArray<uint32_t>(bcInst, DataType::REFERENCE, litArray);
1101 }
1102 }
1103
1104 // NOLINTNEXTLINE(misc-definitions-in-headers)
BuildLoadConstArray(const BytecodeInstruction * bcInst)1105 void InstBuilder::BuildLoadConstArray(const BytecodeInstruction *bcInst)
1106 {
1107 auto literalArrayIdx = bcInst->GetId(0).AsIndex();
1108 auto litArray = GetRuntime()->GetLiteralArray(GetMethod(), literalArrayIdx);
1109 // Unfold LoadConstArray instruction
1110 auto tag = litArray.literals[0].tag;
1111 switch (tag) {
1112 case panda_file::LiteralTag::ARRAY_U1: {
1113 BuildUnfoldLoadConstArray<bool>(bcInst, DataType::INT8, litArray);
1114 break;
1115 }
1116 case panda_file::LiteralTag::ARRAY_I8:
1117 case panda_file::LiteralTag::ARRAY_U8: {
1118 BuildUnfoldLoadConstArray<uint8_t>(bcInst, DataType::INT8, litArray);
1119 break;
1120 }
1121 case panda_file::LiteralTag::ARRAY_I16:
1122 case panda_file::LiteralTag::ARRAY_U16: {
1123 BuildUnfoldLoadConstArray<uint16_t>(bcInst, DataType::INT16, litArray);
1124 break;
1125 }
1126 case panda_file::LiteralTag::ARRAY_I32:
1127 case panda_file::LiteralTag::ARRAY_U32: {
1128 BuildUnfoldLoadConstArray<uint32_t>(bcInst, DataType::INT32, litArray);
1129 break;
1130 }
1131 case panda_file::LiteralTag::ARRAY_I64:
1132 case panda_file::LiteralTag::ARRAY_U64: {
1133 BuildUnfoldLoadConstArray<uint64_t>(bcInst, DataType::INT64, litArray);
1134 break;
1135 }
1136 case panda_file::LiteralTag::ARRAY_F32: {
1137 BuildUnfoldLoadConstArray<float>(bcInst, DataType::FLOAT32, litArray);
1138 break;
1139 }
1140 case panda_file::LiteralTag::ARRAY_F64: {
1141 BuildUnfoldLoadConstArray<double>(bcInst, DataType::FLOAT64, litArray);
1142 break;
1143 }
1144 case panda_file::LiteralTag::ARRAY_STRING: {
1145 BuildLoadConstStringArray(bcInst);
1146 break;
1147 }
1148 default: {
1149 UNREACHABLE();
1150 break;
1151 }
1152 }
1153 }
1154
1155 // NOLINTNEXTLINE(misc-definitions-in-headers)
BuildStoreArray(const BytecodeInstruction * bcInst,DataType::Type type)1156 void InstBuilder::BuildStoreArray(const BytecodeInstruction *bcInst, DataType::Type type)
1157 {
1158 BuildStoreArrayInst<true>(bcInst, type, GetDefinition(bcInst->GetVReg(0)), GetDefinition(bcInst->GetVReg(1)),
1159 GetDefinitionAcc());
1160 }
1161
1162 // NOLINTNEXTLINE(misc-definitions-in-headers)
1163 template <bool CREATE_REF_CHECK>
BuildStoreArrayInst(const BytecodeInstruction * bcInst,DataType::Type type,Inst * arrayRef,Inst * index,Inst * value)1164 void InstBuilder::BuildStoreArrayInst(const BytecodeInstruction *bcInst, DataType::Type type, Inst *arrayRef,
1165 Inst *index, Inst *value)
1166 {
1167 ASSERT(type != DataType::NO_TYPE);
1168 Inst *refCheck = nullptr;
1169
1170 auto pc = GetPc(bcInst->GetAddress());
1171 auto [saveState, nullCheck, arrayLength, boundsCheck] = BuildChecksBeforeArray(pc, arrayRef);
1172 ASSERT(saveState != nullptr && nullCheck != nullptr && arrayLength != nullptr && boundsCheck != nullptr);
1173
1174 // Create instruction
1175 auto inst = graph_->CreateInstStoreArray(type, pc);
1176 boundsCheck->SetInput(1, index);
1177 auto storeDef = value;
1178 if (type == DataType::REFERENCE) {
1179 // NOLINTNEXTLINE(readability-braces-around-statements,bugprone-suspicious-semicolon)
1180 if constexpr (CREATE_REF_CHECK) {
1181 refCheck = graph_->CreateInstRefTypeCheck(DataType::REFERENCE, pc, nullCheck, storeDef, saveState);
1182 storeDef = refCheck;
1183 }
1184 inst->CastToStoreArray()->SetNeedBarrier(true);
1185 }
1186 inst->SetInput(0, nullCheck);
1187 inst->SetInput(1, boundsCheck);
1188 inst->SetInput(2U, storeDef);
1189 if (refCheck != nullptr) {
1190 AddInstruction(saveState, nullCheck, arrayLength, boundsCheck, refCheck, inst);
1191 } else {
1192 AddInstruction(saveState, nullCheck, arrayLength, boundsCheck, inst);
1193 }
1194 }
1195
1196 // NOLINTNEXTLINE(misc-definitions-in-headers)
BuildLenArray(const BytecodeInstruction * bcInst)1197 void InstBuilder::BuildLenArray(const BytecodeInstruction *bcInst)
1198 {
1199 auto saveState = CreateSaveState(Opcode::SaveState, GetPc(bcInst->GetAddress()));
1200 auto nullCheck = graph_->CreateInstNullCheck(DataType::REFERENCE, GetPc(bcInst->GetAddress()));
1201 nullCheck->SetInput(0, GetDefinition(bcInst->GetVReg(0)));
1202 nullCheck->SetInput(1, saveState);
1203 auto inst = graph_->CreateInstLenArray(DataType::INT32, GetPc(bcInst->GetAddress()), nullCheck);
1204 AddInstruction(saveState);
1205 AddInstruction(nullCheck);
1206 AddInstruction(inst);
1207 UpdateDefinitionAcc(inst);
1208 }
1209
1210 // NOLINTNEXTLINE(misc-definitions-in-headers)
BuildNewArray(const BytecodeInstruction * bcInst)1211 void InstBuilder::BuildNewArray(const BytecodeInstruction *bcInst)
1212 {
1213 auto saveState = CreateSaveState(Opcode::SaveState, GetPc(bcInst->GetAddress()));
1214 auto negCheck = graph_->CreateInstNegativeCheck(DataType::INT32, GetPc(bcInst->GetAddress()),
1215 GetDefinition(bcInst->GetVReg(1)), saveState);
1216
1217 auto typeIndex = bcInst->GetId(0).AsIndex();
1218 auto typeId = GetRuntime()->ResolveTypeIndex(GetMethod(), typeIndex);
1219
1220 auto initClass = CreateLoadAndInitClassGeneric(typeId, GetPc(bcInst->GetAddress()));
1221 initClass->SetInput(0, saveState);
1222
1223 auto inst = graph_->CreateInstNewArray(DataType::REFERENCE, GetPc(bcInst->GetAddress()), initClass, negCheck,
1224 saveState, TypeIdMixin {typeId, GetGraph()->GetMethod()});
1225 AddInstruction(saveState, initClass, negCheck, inst);
1226 UpdateDefinition(bcInst->GetVReg(0), inst);
1227 }
1228
1229 // NOLINTNEXTLINE(misc-definitions-in-headers)
BuildNewObject(const BytecodeInstruction * bcInst)1230 void InstBuilder::BuildNewObject(const BytecodeInstruction *bcInst)
1231 {
1232 auto classId = GetRuntime()->ResolveTypeIndex(GetMethod(), bcInst->GetId(0).AsIndex());
1233 auto pc = GetPc(bcInst->GetAddress());
1234 auto saveState = CreateSaveState(Opcode::SaveState, pc);
1235 auto initClass = CreateLoadAndInitClassGeneric(classId, pc);
1236 auto inst = CreateNewObjectInst(pc, classId, saveState, initClass);
1237 initClass->SetInput(0, saveState);
1238 AddInstruction(saveState, initClass, inst);
1239 UpdateDefinition(bcInst->GetVReg(0), inst);
1240 }
1241
1242 // NOLINTNEXTLINE(misc-definitions-in-headers)
BuildMultiDimensionalArrayObject(const BytecodeInstruction * bcInst,bool isRange)1243 void InstBuilder::BuildMultiDimensionalArrayObject(const BytecodeInstruction *bcInst, bool isRange)
1244 {
1245 auto methodIndex = bcInst->GetId(0).AsIndex();
1246 auto methodId = GetRuntime()->ResolveMethodIndex(GetMethod(), methodIndex);
1247 auto pc = GetPc(bcInst->GetAddress());
1248 auto classId = GetRuntime()->GetClassIdForMethod(GetMethod(), methodId);
1249 auto saveState = CreateSaveState(Opcode::SaveState, pc);
1250 auto initClass = CreateLoadAndInitClassGeneric(classId, pc);
1251 size_t argsCount = GetMethodArgumentsCount(methodId);
1252 auto inst = GetGraph()->CreateInstMultiArray(DataType::REFERENCE, pc, methodId);
1253
1254 initClass->SetInput(0, saveState);
1255
1256 inst->ReserveInputs(ONE_FOR_OBJECT + argsCount + ONE_FOR_SSTATE);
1257 inst->AllocateInputTypes(GetGraph()->GetAllocator(), ONE_FOR_OBJECT + argsCount + ONE_FOR_SSTATE);
1258 inst->AppendInput(initClass);
1259 inst->AddInputType(DataType::REFERENCE);
1260
1261 AddInstruction(saveState, initClass);
1262
1263 if (isRange) {
1264 auto startReg = bcInst->GetVReg(0);
1265 for (size_t i = 0; i < argsCount; startReg++, i++) {
1266 auto negCheck = graph_->CreateInstNegativeCheck(DataType::INT32, pc, GetDefinition(startReg), saveState);
1267 AddInstruction(negCheck);
1268 inst->AppendInput(negCheck);
1269 inst->AddInputType(DataType::INT32);
1270 }
1271 } else {
1272 for (size_t i = 0; i < argsCount; i++) {
1273 auto negCheck =
1274 graph_->CreateInstNegativeCheck(DataType::INT32, pc, GetDefinition(bcInst->GetVReg(i)), saveState);
1275 AddInstruction(negCheck);
1276 inst->AppendInput(negCheck);
1277 inst->AddInputType(DataType::INT32);
1278 }
1279 }
1280 inst->AppendInput(saveState);
1281 inst->AddInputType(DataType::NO_TYPE);
1282 AddInstruction(inst);
1283 UpdateDefinitionAcc(inst);
1284 }
1285
1286 // NOLINTNEXTLINE(misc-definitions-in-headers)
BuildInitObjectMultiDimensionalArray(const BytecodeInstruction * bcInst,bool isRange)1287 void InstBuilder::BuildInitObjectMultiDimensionalArray(const BytecodeInstruction *bcInst, bool isRange)
1288 {
1289 auto pc = GetPc(bcInst->GetAddress());
1290 auto methodIndex = bcInst->GetId(0).AsIndex();
1291 auto methodId = GetRuntime()->ResolveMethodIndex(GetMethod(), methodIndex);
1292 auto classId = GetRuntime()->GetClassIdForMethod(GetMethod(), methodId);
1293 auto saveState = CreateSaveState(Opcode::SaveState, pc);
1294 ASSERT(classId != 0);
1295 auto initClass = graph_->CreateInstLoadAndInitClass(DataType::REFERENCE, pc, saveState,
1296 TypeIdMixin {classId, GetGraph()->GetMethod()},
1297 GetRuntime()->ResolveType(GetGraph()->GetMethod(), classId));
1298 auto inst = GetGraph()->CreateInstInitObject(DataType::REFERENCE, pc, methodId);
1299
1300 size_t argsCount = GetMethodArgumentsCount(methodId);
1301
1302 inst->ReserveInputs(ONE_FOR_OBJECT + argsCount + ONE_FOR_SSTATE);
1303 inst->AllocateInputTypes(GetGraph()->GetAllocator(), ONE_FOR_OBJECT + argsCount + ONE_FOR_SSTATE);
1304 inst->AppendInput(initClass);
1305 inst->AddInputType(DataType::REFERENCE);
1306 if (isRange) {
1307 auto startReg = bcInst->GetVReg(0);
1308 for (size_t i = 0; i < argsCount; startReg++, i++) {
1309 inst->AppendInput(GetDefinition(startReg));
1310 inst->AddInputType(GetMethodArgumentType(methodId, i));
1311 }
1312 } else {
1313 for (size_t i = 0; i < argsCount; i++) {
1314 inst->AppendInput(GetDefinition(bcInst->GetVReg(i)));
1315 inst->AddInputType(GetMethodArgumentType(methodId, i));
1316 }
1317 }
1318 inst->AppendInput(saveState);
1319 inst->AddInputType(DataType::NO_TYPE);
1320 inst->SetCallMethod(GetRuntime()->GetMethodById(GetGraph()->GetMethod(), methodId));
1321
1322 AddInstruction(saveState, initClass, inst);
1323 UpdateDefinitionAcc(inst);
1324 }
1325
1326 // NOLINTNEXTLINE(misc-definitions-in-headers)
BuildCallStaticForInitObject(const BytecodeInstruction * bcInst,uint32_t methodId,Inst ** resolver)1327 CallInst *InstBuilder::BuildCallStaticForInitObject(const BytecodeInstruction *bcInst, uint32_t methodId,
1328 Inst **resolver)
1329 {
1330 auto pc = GetPc(bcInst->GetAddress());
1331 size_t argsCount = GetMethodArgumentsCount(methodId);
1332 size_t inputsCount = ONE_FOR_OBJECT + argsCount + ONE_FOR_SSTATE;
1333 auto method = GetRuntime()->GetMethodById(graph_->GetMethod(), methodId);
1334 CallInst *call = nullptr;
1335 if (method == nullptr || ForceUnresolved()) {
1336 ResolveStaticInst *resolveStatic = graph_->CreateInstResolveStatic(DataType::POINTER, pc, methodId, nullptr);
1337 *resolver = resolveStatic;
1338 call = graph_->CreateInstCallResolvedStatic(GetMethodReturnType(methodId), pc, methodId);
1339 if (!graph_->IsAotMode() && !graph_->IsBytecodeOptimizer()) {
1340 GetRuntime()->GetUnresolvedTypes()->AddTableSlot(GetMethod(), methodId,
1341 UnresolvedTypesInterface::SlotKind::METHOD);
1342 }
1343 inputsCount += 1; // resolver
1344 } else {
1345 call = graph_->CreateInstCallStatic(GetMethodReturnType(methodId), pc, methodId, method);
1346 }
1347 call->ReserveInputs(inputsCount);
1348 call->AllocateInputTypes(graph_->GetAllocator(), inputsCount);
1349 SetCallNativeFlags(call, method);
1350 return call;
1351 }
1352
1353 // NOLINTNEXTLINE(misc-definitions-in-headers)
BuildInitString(const BytecodeInstruction * bcInst)1354 void InstBuilder::BuildInitString(const BytecodeInstruction *bcInst)
1355 {
1356 auto pc = GetPc(bcInst->GetAddress());
1357 auto saveState = CreateSaveState(Opcode::SaveState, pc);
1358 AddInstruction(saveState);
1359
1360 auto ctorMethodIndex = bcInst->GetId(0).AsIndex();
1361 auto ctorMethodId = GetRuntime()->ResolveMethodIndex(GetMethod(), ctorMethodIndex);
1362 size_t argsCount = GetMethodArgumentsCount(ctorMethodId);
1363
1364 Inst *inst = nullptr;
1365 if (argsCount == 0) {
1366 inst = GetGraph()->CreateInstInitEmptyString(DataType::REFERENCE, pc, saveState);
1367 } else {
1368 ASSERT(argsCount == 1);
1369 auto nullCheck =
1370 graph_->CreateInstNullCheck(DataType::REFERENCE, pc, GetDefinition(bcInst->GetVReg(0)), saveState);
1371 AddInstruction(nullCheck);
1372
1373 auto ctorMethod = GetRuntime()->GetMethodById(GetMethod(), ctorMethodId);
1374 auto ctorType = GetRuntime()->GetStringCtorType(ctorMethod);
1375 inst = GetGraph()->CreateInstInitString(DataType::REFERENCE, pc, nullCheck, saveState, ctorType);
1376 }
1377 AddInstruction(inst);
1378 UpdateDefinitionAcc(inst);
1379 }
1380
1381 // NOLINTNEXTLINE(misc-definitions-in-headers)
BuildInitObject(const BytecodeInstruction * bcInst,bool isRange)1382 void InstBuilder::BuildInitObject(const BytecodeInstruction *bcInst, bool isRange)
1383 {
1384 auto methodId = GetRuntime()->ResolveMethodIndex(GetMethod(), bcInst->GetId(0).AsIndex());
1385 auto typeId = GetRuntime()->GetClassIdForMethod(GetMethod(), methodId);
1386 if (GetRuntime()->IsArrayClass(GetMethod(), typeId)) {
1387 if (GetGraph()->IsBytecodeOptimizer()) {
1388 BuildInitObjectMultiDimensionalArray(bcInst, isRange);
1389 } else {
1390 BuildMultiDimensionalArrayObject(bcInst, isRange);
1391 }
1392 return;
1393 }
1394
1395 if (GetRuntime()->IsStringClass(GetMethod(), typeId) && !GetGraph()->IsBytecodeOptimizer()) {
1396 BuildInitString(bcInst);
1397 return;
1398 }
1399
1400 auto pc = GetPc(bcInst->GetAddress());
1401 auto saveState = CreateSaveState(Opcode::SaveState, pc);
1402 auto initClass = CreateLoadAndInitClassGeneric(typeId, pc);
1403 initClass->SetInput(0, saveState);
1404 auto newObj = CreateNewObjectInst(pc, typeId, saveState, initClass);
1405 UpdateDefinitionAcc(newObj);
1406 Inst *resolver = nullptr;
1407 CallInst *call = BuildCallStaticForInitObject(bcInst, methodId, &resolver);
1408 if (resolver != nullptr) {
1409 call->AppendInput(resolver, DataType::POINTER);
1410 }
1411 call->AppendInput(newObj, DataType::REFERENCE);
1412
1413 size_t argsCount = GetMethodArgumentsCount(methodId);
1414 if (isRange) {
1415 auto startReg = bcInst->GetVReg(0);
1416 for (size_t i = 0; i < argsCount; startReg++, i++) {
1417 call->AppendInput(GetDefinition(startReg), GetMethodArgumentType(methodId, i));
1418 }
1419 } else {
1420 for (size_t i = 0; i < argsCount; i++) {
1421 call->AppendInput(GetDefinition(bcInst->GetVReg(i)), GetMethodArgumentType(methodId, i));
1422 }
1423 }
1424 auto saveStateForCall = CreateSaveState(Opcode::SaveState, pc);
1425 call->AppendInput(saveStateForCall, DataType::NO_TYPE);
1426 if (resolver != nullptr) {
1427 resolver->SetInput(0, saveStateForCall);
1428 AddInstruction(saveState, initClass, newObj, saveStateForCall, resolver, call);
1429 } else {
1430 AddInstruction(saveState, initClass, newObj, saveStateForCall, call);
1431 }
1432 }
1433
1434 // NOLINTNEXTLINE(misc-definitions-in-headers)
BuildCheckCast(const BytecodeInstruction * bcInst)1435 void InstBuilder::BuildCheckCast(const BytecodeInstruction *bcInst)
1436 {
1437 auto typeIndex = bcInst->GetId(0).AsIndex();
1438 auto typeId = GetRuntime()->ResolveTypeIndex(GetMethod(), typeIndex);
1439 auto klassType = GetRuntime()->GetClassType(GetGraph()->GetMethod(), typeId);
1440 auto pc = GetPc(bcInst->GetAddress());
1441 auto saveState = CreateSaveState(Opcode::SaveState, pc);
1442 auto loadClass = BuildLoadClass(typeId, pc, saveState);
1443 auto inst = GetGraph()->CreateInstCheckCast(DataType::NO_TYPE, pc, GetDefinitionAcc(), loadClass, saveState,
1444 TypeIdMixin {typeId, GetGraph()->GetMethod()}, klassType);
1445 AddInstruction(saveState, loadClass, inst);
1446 }
1447
1448 // NOLINTNEXTLINE(misc-definitions-in-headers)
BuildIsInstance(const BytecodeInstruction * bcInst)1449 void InstBuilder::BuildIsInstance(const BytecodeInstruction *bcInst)
1450 {
1451 auto typeIndex = bcInst->GetId(0).AsIndex();
1452 auto typeId = GetRuntime()->ResolveTypeIndex(GetMethod(), typeIndex);
1453 auto klassType = GetRuntime()->GetClassType(GetGraph()->GetMethod(), typeId);
1454 auto pc = GetPc(bcInst->GetAddress());
1455 auto saveState = CreateSaveState(Opcode::SaveState, pc);
1456 auto loadClass = BuildLoadClass(typeId, pc, saveState);
1457 auto inst = GetGraph()->CreateInstIsInstance(DataType::BOOL, pc, GetDefinitionAcc(), loadClass, saveState,
1458 TypeIdMixin {typeId, GetGraph()->GetMethod()}, klassType);
1459 AddInstruction(saveState, loadClass, inst);
1460 UpdateDefinitionAcc(inst);
1461 }
1462
1463 // NOLINTNEXTLINE(misc-definitions-in-headers)
BuildLoadClass(RuntimeInterface::IdType typeId,size_t pc,Inst * saveState)1464 Inst *InstBuilder::BuildLoadClass(RuntimeInterface::IdType typeId, size_t pc, Inst *saveState)
1465 {
1466 auto inst = GetGraph()->CreateInstLoadClass(DataType::REFERENCE, pc, saveState,
1467 TypeIdMixin {typeId, GetGraph()->GetMethod()}, nullptr);
1468 auto klass = GetRuntime()->ResolveType(GetGraph()->GetMethod(), typeId);
1469 if (klass != nullptr) {
1470 inst->SetClass(klass);
1471 } else if (!GetGraph()->IsAotMode() && !GetGraph()->IsBytecodeOptimizer()) {
1472 GetRuntime()->GetUnresolvedTypes()->AddTableSlot(GetGraph()->GetMethod(), typeId,
1473 UnresolvedTypesInterface::SlotKind::CLASS);
1474 }
1475 return inst;
1476 }
1477
1478 // NOLINTNEXTLINE(misc-definitions-in-headers)
BuildThrow(const BytecodeInstruction * bcInst)1479 void InstBuilder::BuildThrow(const BytecodeInstruction *bcInst)
1480 {
1481 auto saveState = CreateSaveState(Opcode::SaveState, GetPc(bcInst->GetAddress()));
1482 auto inst = graph_->CreateInstThrow(DataType::NO_TYPE, GetPc(bcInst->GetAddress()),
1483 GetDefinition(bcInst->GetVReg(0)), saveState);
1484 inst->SetCallMethod(GetMethod());
1485 AddInstruction(saveState);
1486 AddInstruction(inst);
1487 }
1488
1489 // NOLINTNEXTLINE(misc-definitions-in-headers)
1490 template <Opcode OPCODE>
BuildLoadFromPool(const BytecodeInstruction * bcInst)1491 void InstBuilder::BuildLoadFromPool(const BytecodeInstruction *bcInst)
1492 {
1493 auto method = GetGraph()->GetMethod();
1494 uint32_t typeId;
1495 Inst *inst;
1496 // NOLINTNEXTLINE(readability-magic-numbers,readability-braces-around-statements)
1497 if constexpr (OPCODE == Opcode::LoadType) {
1498 auto typeIndex = bcInst->GetId(0).AsIndex();
1499 typeId = GetRuntime()->ResolveTypeIndex(method, typeIndex);
1500 if (GetRuntime()->ResolveType(method, typeId) == nullptr) {
1501 inst = GetGraph()->CreateInstUnresolvedLoadType(DataType::REFERENCE, GetPc(bcInst->GetAddress()));
1502 if (!GetGraph()->IsAotMode() && !GetGraph()->IsBytecodeOptimizer()) {
1503 GetRuntime()->GetUnresolvedTypes()->AddTableSlot(method, typeId,
1504 UnresolvedTypesInterface::SlotKind::MANAGED_CLASS);
1505 }
1506 } else {
1507 inst = GetGraph()->CreateInstLoadType(DataType::REFERENCE, GetPc(bcInst->GetAddress()));
1508 }
1509 // NOLINTNEXTLINE(readability-misleading-indentation)
1510 } else {
1511 // NOLINTNEXTLINE(readability-magic-numbers)
1512 static_assert(OPCODE == Opcode::LoadString);
1513 typeId = bcInst->GetId(0).AsFileId().GetOffset();
1514 if (!GetGraph()->IsDynamicMethod() || GetGraph()->IsBytecodeOptimizer()) {
1515 inst = GetGraph()->CreateInstLoadString(DataType::REFERENCE, GetPc(bcInst->GetAddress()));
1516 } else {
1517 inst = GetGraph()->CreateInstLoadFromConstantPool(DataType::ANY, GetPc(bcInst->GetAddress()));
1518 inst->CastToLoadFromConstantPool()->SetString(true);
1519 }
1520 }
1521 if (!GetGraph()->IsDynamicMethod() || GetGraph()->IsBytecodeOptimizer()) {
1522 // Create SaveState instruction
1523 auto saveState = CreateSaveState(Opcode::SaveState, GetPc(bcInst->GetAddress()));
1524 inst->SetInput(0, saveState);
1525 static_cast<LoadFromPool *>(inst)->SetTypeId(typeId);
1526 static_cast<LoadFromPool *>(inst)->SetMethod(method);
1527 AddInstruction(saveState);
1528 } else {
1529 inst->SetInput(0, GetEnvDefinition(CONST_POOL_IDX));
1530 inst->CastToLoadFromConstantPool()->SetTypeId(typeId);
1531 inst->CastToLoadFromConstantPool()->SetMethod(method);
1532 }
1533
1534 AddInstruction(inst);
1535 UpdateDefinitionAcc(inst);
1536 // NOLINTNEXTLINE(readability-magic-numbers,readability-braces-around-statements,bugprone-suspicious-semicolon)
1537 if constexpr (OPCODE == Opcode::LoadString) {
1538 if (GetGraph()->IsDynamicMethod() && GetGraph()->IsBytecodeOptimizer()) {
1539 BuildCastToAnyString(bcInst);
1540 }
1541 }
1542 }
1543
1544 // NOLINTNEXTLINE(misc-definitions-in-headers)
BuildCastToAnyString(const BytecodeInstruction * bcInst)1545 void InstBuilder::BuildCastToAnyString(const BytecodeInstruction *bcInst)
1546 {
1547 auto input = GetDefinitionAcc();
1548 ASSERT(input->GetType() == DataType::REFERENCE);
1549
1550 auto language = GetRuntime()->GetMethodSourceLanguage(GetMethod());
1551 auto anyType = GetAnyStringType(language);
1552 ASSERT(anyType != AnyBaseType::UNDEFINED_TYPE);
1553
1554 auto box = graph_->CreateInstCastValueToAnyType(GetPc(bcInst->GetAddress()), anyType, input);
1555 UpdateDefinitionAcc(box);
1556 AddInstruction(box);
1557 }
1558
1559 // NOLINTNEXTLINE(misc-definitions-in-headers)
BuildCastToAnyNumber(const BytecodeInstruction * bcInst)1560 void InstBuilder::BuildCastToAnyNumber(const BytecodeInstruction *bcInst)
1561 {
1562 auto input = GetDefinitionAcc();
1563 auto type = input->GetType();
1564 if (input->IsConst() && !DataType::IsFloatType(type)) {
1565 auto constInsn = input->CastToConstant();
1566 if (constInsn->GetType() == DataType::INT64) {
1567 auto value = input->CastToConstant()->GetInt64Value();
1568 if (value == static_cast<uint32_t>(value)) {
1569 type = DataType::INT32;
1570 }
1571 }
1572 }
1573
1574 auto language = GetRuntime()->GetMethodSourceLanguage(GetMethod());
1575 auto anyType = NumericDataTypeToAnyType(type, language);
1576 ASSERT(anyType != AnyBaseType::UNDEFINED_TYPE);
1577
1578 auto box = graph_->CreateInstCastValueToAnyType(GetPc(bcInst->GetAddress()), anyType, input);
1579 UpdateDefinitionAcc(box);
1580 AddInstruction(box);
1581 }
1582
1583 // NOLINTNEXTLINE(misc-definitions-in-headers)
TryBuildStringCharAtIntrinsic(const BytecodeInstruction * bcInst,bool accRead)1584 bool InstBuilder::TryBuildStringCharAtIntrinsic(const BytecodeInstruction *bcInst, bool accRead)
1585 {
1586 auto bcAddr = GetPc(bcInst->GetAddress());
1587 auto compressionEnabled = graph_->GetRuntime()->IsCompressedStringsEnabled();
1588 auto canEncodeCompressedStrCharAt = graph_->GetEncoder()->CanEncodeCompressedStringCharAt();
1589 if (compressionEnabled && !canEncodeCompressedStrCharAt) {
1590 return false;
1591 }
1592 auto saveStateNullcheck = CreateSaveState(Opcode::SaveState, bcAddr);
1593 auto nullCheck = graph_->CreateInstNullCheck(DataType::REFERENCE, bcAddr, GetArgDefinition(bcInst, 0, accRead),
1594 saveStateNullcheck);
1595 auto arrayLength = graph_->CreateInstLenArray(DataType::INT32, bcAddr, nullCheck, false);
1596
1597 AddInstruction(saveStateNullcheck);
1598 AddInstruction(nullCheck);
1599 AddInstruction(arrayLength);
1600
1601 Inst *stringLength = nullptr;
1602 if (compressionEnabled) {
1603 auto constOneInst = graph_->FindOrCreateConstant(1);
1604 stringLength = graph_->CreateInstShr(DataType::INT32, bcAddr, arrayLength, constOneInst);
1605 AddInstruction(stringLength);
1606 } else {
1607 stringLength = arrayLength;
1608 }
1609
1610 auto boundsCheck = graph_->CreateInstBoundsCheck(DataType::INT32, bcAddr, stringLength,
1611 GetArgDefinition(bcInst, 1, accRead), saveStateNullcheck, false);
1612 AddInstruction(boundsCheck);
1613
1614 Inst *inst = nullptr;
1615 if (compressionEnabled) {
1616 inst =
1617 graph_->CreateInstLoadCompressedStringChar(DataType::UINT16, bcAddr, nullCheck, boundsCheck, arrayLength);
1618 } else {
1619 inst = graph_->CreateInstLoadArray(DataType::UINT16, bcAddr, nullCheck, boundsCheck);
1620 }
1621
1622 AddInstruction(inst);
1623 UpdateDefinitionAcc(inst);
1624 return true;
1625 }
1626
1627 } // namespace ark::compiler
1628
1629 #endif // PANDA_INST_BUILDER_INL_H
1630