• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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