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