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