• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2025 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 /*
17 Codegen Hi-Level implementation
18 */
19 #include "operands.h"
20 #include "codegen.h"
21 #include "encode_visitor.h"
22 #include "compiler_options.h"
23 #include "relocations.h"
24 #include "include/compiler_interface.h"
25 #include "ir-dyn-base-types.h"
26 #include "runtime/include/coretypes/string.h"
27 #include "compiler/optimizer/ir/analysis.h"
28 #include "compiler/optimizer/ir/locations.h"
29 #include "compiler/optimizer/analysis/liveness_analyzer.h"
30 #include "optimizer/code_generator/method_properties.h"
31 #include "events/events.h"
32 #include "libpandabase/utils/tsan_interface.h"
33 #include "libpandabase/utils/utils.h"
34 #include <algorithm>
35 #include <iomanip>
36 
37 namespace ark::compiler {
38 
39 class OsrEntryStub {
FixIntervals(Codegen * codegen,Encoder * encoder)40     void FixIntervals(Codegen *codegen, Encoder *encoder)
41     {
42         auto &la = codegen->GetGraph()->GetAnalysis<LivenessAnalyzer>();
43         la.EnumerateLiveIntervalsForInst(saveState_, [this, codegen, encoder](const auto &li) {
44             auto inst = li->GetInst();
45             auto location = li->GetLocation();
46             // Skip live registers that are already in the input list of the OsrSaveState
47             const auto &ssInputs = saveState_->GetInputs();
48             if (std::find_if(ssInputs.begin(), ssInputs.end(),
49                              [inst](auto &input) { return input.GetInst() == inst; }) != ssInputs.end()) {
50                 return;
51             }
52             // Only constants allowed
53             switch (inst->GetOpcode()) {
54                 case Opcode::LoadAndInitClass: {
55                     auto klass = reinterpret_cast<uintptr_t>(inst->CastToLoadAndInitClass()->GetClass());
56                     encoder->EncodeMov(codegen->ConvertRegister(inst->GetDstReg(), inst->GetType()),
57                                        Imm(reinterpret_cast<uintptr_t>(klass)));
58                     break;
59                 }
60                 case Opcode::Constant: {
61                     if (location.IsFixedRegister()) {
62                         EncodeConstantMove(li, encoder);
63                     } else if (location.IsStack()) {
64                         auto slot = location.GetValue();
65                         encoder->EncodeSti(
66                             bit_cast<int64_t>(inst->CastToConstant()->GetRawValue()), DOUBLE_WORD_SIZE_BYTES,
67                             MemRef(codegen->SpReg(), codegen->GetFrameLayout().GetSpillOffsetFromSpInBytes(slot)));
68                     } else {
69                         ASSERT(location.IsConstant());
70                     }
71                     break;
72                 }
73                 // NOTE (ekudriashov): UnresolvedLoadAndInitClass
74                 default:
75                     break;
76             }
77         });
78     }
79 
EncodeConstantMove(const LifeIntervals * li,Encoder * encoder)80     static void EncodeConstantMove(const LifeIntervals *li, Encoder *encoder)
81     {
82         auto inst = li->GetInst();
83         switch (li->GetType()) {
84             case DataType::FLOAT64:
85                 encoder->EncodeMov(Reg(li->GetReg(), FLOAT64_TYPE),
86                                    Imm(bit_cast<double>(inst->CastToConstant()->GetDoubleValue())));
87                 break;
88             case DataType::FLOAT32:
89                 encoder->EncodeMov(Reg(li->GetReg(), FLOAT32_TYPE),
90                                    Imm(bit_cast<float>(inst->CastToConstant()->GetFloatValue())));
91                 break;
92             case DataType::UINT32:
93                 encoder->EncodeMov(Reg(li->GetReg(), INT32_TYPE), Imm(inst->CastToConstant()->GetRawValue()));
94                 break;
95             case DataType::UINT64:
96                 encoder->EncodeMov(Reg(li->GetReg(), INT64_TYPE), Imm(inst->CastToConstant()->GetRawValue()));
97                 break;
98             default:
99                 UNREACHABLE();
100         }
101     }
102 
103 public:
OsrEntryStub(Codegen * codegen,SaveStateInst * inst)104     OsrEntryStub(Codegen *codegen, SaveStateInst *inst) : label_(codegen->GetEncoder()->CreateLabel()), saveState_(inst)
105     {
106     }
107 
108     DEFAULT_MOVE_SEMANTIC(OsrEntryStub);
109     DEFAULT_COPY_SEMANTIC(OsrEntryStub);
110     ~OsrEntryStub() = default;
111 
Generate(Codegen * codegen)112     void Generate(Codegen *codegen)
113     {
114         auto encoder = codegen->GetEncoder();
115         auto lr = codegen->GetTarget().GetLinkReg();
116         auto fl = codegen->GetFrameLayout();
117         codegen->CreateStackMap(saveState_->CastToSaveStateOsr());
118         auto slot = static_cast<ssize_t>(CFrameLayout::LOCALS_START_SLOT + CFrameLayout::GetLocalsCount() - 1U);
119         encoder->EncodeStp(
120             codegen->FpReg(), lr,
121             MemRef(codegen->FpReg(),
122                    -fl.GetOffset<CFrameLayout::OffsetOrigin::FP, CFrameLayout::OffsetUnit::BYTES>(slot)));
123 
124         FixIntervals(codegen, encoder);
125         encoder->EncodeJump(label_);
126     }
127 
GetInst()128     SaveStateInst *GetInst()
129     {
130         return saveState_;
131     }
132 
GetLabel()133     auto &GetLabel()
134     {
135         return label_;
136     }
137 
138 private:
139     LabelHolder::LabelId label_;
140     SaveStateInst *saveState_ {nullptr};
141 };
142 
Codegen(Graph * graph)143 Codegen::Codegen(Graph *graph)
144     : Optimization(graph),
145       allocator_(graph->GetAllocator()),
146       localAllocator_(graph->GetLocalAllocator()),
147       codeBuilder_(allocator_->New<CodeInfoBuilder>(graph->GetArch(), allocator_)),
148       slowPaths_(graph->GetLocalAllocator()->Adapter()),
149       slowPathsMap_(graph->GetLocalAllocator()->Adapter()),
150       frameLayout_(CFrameLayout(graph->GetArch(), graph->GetStackSlotsCount(), !graph->GetMode().IsFastPath())),
151       osrEntries_(graph->GetLocalAllocator()->Adapter()),
152       vregIndices_(GetAllocator()->Adapter()),
153       runtime_(graph->GetRuntime()),
154       target_(graph->GetArch()),
155       liveOuts_(graph->GetLocalAllocator()->Adapter()),
156       disasm_(this),
157       spillFillsResolver_(graph)
158 {
159     graph->SetCodeBuilder(codeBuilder_);
160     regfile_ = graph->GetRegisters();
161     if (regfile_ != nullptr) {
162         ASSERT(regfile_->IsValid());
163         ArenaVector<Reg> regsUsage(allocator_->Adapter());
164         Convert(&regsUsage, graph->GetUsedRegs<DataType::INT64>(), INT64_TYPE);
165         Convert(&regsUsage, graph->GetUsedRegs<DataType::FLOAT64>(), FLOAT64_TYPE);
166         regfile_->SetUsedRegs(regsUsage);
167 #ifndef NDEBUG
168         COMPILER_LOG(DEBUG, CODEGEN) << "Regalloc used registers scalar " << graph->GetUsedRegs<DataType::INT64>();
169         COMPILER_LOG(DEBUG, CODEGEN) << "Regalloc used registers vector " << graph->GetUsedRegs<DataType::FLOAT64>();
170 #endif
171     }
172 
173     enc_ = graph->GetEncoder();
174     ASSERT(enc_ != nullptr && enc_->IsValid());
175     enc_->SetRegfile(regfile_);
176     if (enc_->InitMasm()) {
177         enc_->SetFrameLayout(GetFrameLayout());
178     }
179 
180     callconv_ = graph->GetCallingConvention();
181     if (callconv_ != nullptr) {
182         ASSERT(callconv_->IsValid());
183         if (callconv_->GetEncoder() == nullptr) {
184             callconv_->SetEncoder(enc_);
185         }
186     }
187 
188     auto method = graph->GetMethod();
189     // workaround for test
190     if (method != nullptr) {
191         methodId_ = graph->GetRuntime()->GetMethodId(method);
192     }
193     GetDisasm()->Init();
194     GetDisasm()->SetEncoder(GetEncoder());
195 }
196 
GetPassName() const197 const char *Codegen::GetPassName() const
198 {
199     return "Codegen";
200 }
201 
AbortIfFailed() const202 bool Codegen::AbortIfFailed() const
203 {
204     return true;
205 }
206 
CreateFrameInfo()207 void Codegen::CreateFrameInfo()
208 {
209     // Create FrameInfo for CFrame
210     auto &fl = GetFrameLayout();
211     auto frame = GetGraph()->GetLocalAllocator()->New<FrameInfo>(
212         FrameInfo::PositionedCallers::Encode(true) | FrameInfo::PositionedCallees::Encode(true) |
213         FrameInfo::CallersRelativeFp::Encode(false) | FrameInfo::CalleesRelativeFp::Encode(true));
214     ASSERT(frame != nullptr);
215     frame->SetFrameSize(fl.GetFrameSize<CFrameLayout::OffsetUnit::BYTES>());
216     frame->SetSpillsCount(fl.GetSpillsCount());
217 
218     frame->SetCallersOffset(fl.GetOffset<CFrameLayout::OffsetOrigin::SP, CFrameLayout::OffsetUnit::SLOTS>(
219         fl.GetStackStartSlot() + fl.GetCallerLastSlot(false)));
220     frame->SetFpCallersOffset(fl.GetOffset<CFrameLayout::OffsetOrigin::SP, CFrameLayout::OffsetUnit::SLOTS>(
221         fl.GetStackStartSlot() + fl.GetCallerLastSlot(true)));
222     frame->SetCalleesOffset(-fl.GetOffset<CFrameLayout::OffsetOrigin::FP, CFrameLayout::OffsetUnit::SLOTS>(
223         fl.GetStackStartSlot() + fl.GetCalleeLastSlot(false)));
224     frame->SetFpCalleesOffset(-fl.GetOffset<CFrameLayout::OffsetOrigin::FP, CFrameLayout::OffsetUnit::SLOTS>(
225         fl.GetStackStartSlot() + fl.GetCalleeLastSlot(true)));
226 
227     frame->SetSetupFrame(true);
228     frame->SetSaveFrameAndLinkRegs(true);
229     frame->SetSaveUnusedCalleeRegs(!GetGraph()->GetMethodProperties().GetCompactPrologueAllowed());
230     frame->SetAdjustSpReg(true);
231     frame->SetHasFloatRegs(GetGraph()->HasFloatRegs());
232 
233     GetCodeBuilder()->SetHasFloatRegs(GetGraph()->HasFloatRegs());
234 
235     SetFrameInfo(frame);
236 }
237 
FillOnlyParameters(RegMask * liveRegs,uint32_t numParams,bool isFastpath) const238 void Codegen::FillOnlyParameters(RegMask *liveRegs, uint32_t numParams, bool isFastpath) const
239 {
240     ASSERT(numParams <= 6U);
241     if (GetArch() == Arch::AARCH64 && !isFastpath) {
242         numParams = AlignUp(numParams, 2U);
243     }
244     *liveRegs &= GetTarget().GetParamRegsMask(numParams);
245 }
246 
Convert(ArenaVector<Reg> * regsUsage,const ArenaVector<bool> * mask,TypeInfo typeInfo)247 void Codegen::Convert(ArenaVector<Reg> *regsUsage, const ArenaVector<bool> *mask, TypeInfo typeInfo)
248 {
249     ASSERT(regsUsage != nullptr);
250     // There are no used registers
251     if (mask == nullptr) {
252         return;
253     }
254     ASSERT(mask->size() == MAX_NUM_REGS);
255     for (uint32_t i = 0; i < MAX_NUM_REGS; ++i) {
256         if ((*mask)[i]) {
257             regsUsage->emplace_back(i, typeInfo);
258         }
259     }
260 }
261 
262 #ifdef IRTOC_INTRINSICS_ENABLED
EmitSimdIntrinsic(IntrinsicInst * inst,Reg dst,SRCREGS src)263 void Codegen::EmitSimdIntrinsic([[maybe_unused]] IntrinsicInst *inst, [[maybe_unused]] Reg dst,
264                                 [[maybe_unused]] SRCREGS src)
265 {
266     ASSERT(0);
267     GetEncoder()->SetFalseResult();
268 }
269 
EmitReverseIntrinsic(IntrinsicInst * inst,Reg dst,SRCREGS src)270 void Codegen::EmitReverseIntrinsic([[maybe_unused]] IntrinsicInst *inst, [[maybe_unused]] Reg dst,
271                                    [[maybe_unused]] SRCREGS src)
272 {
273     ASSERT(0);
274     GetEncoder()->SetFalseResult();
275 }
276 
EmitJsCastDoubleToCharIntrinsic(IntrinsicInst * inst,Reg dst,SRCREGS src)277 void Codegen::EmitJsCastDoubleToCharIntrinsic([[maybe_unused]] IntrinsicInst *inst, [[maybe_unused]] Reg dst,
278                                               [[maybe_unused]] SRCREGS src)
279 {
280     ASSERT(0);
281     GetEncoder()->SetFalseResult();
282 }
283 
EmitMarkWordIntrinsic(IntrinsicInst * inst,Reg dst,SRCREGS src)284 void Codegen::EmitMarkWordIntrinsic([[maybe_unused]] IntrinsicInst *inst, [[maybe_unused]] Reg dst,
285                                     [[maybe_unused]] SRCREGS src)
286 {
287     ASSERT(0);
288     GetEncoder()->SetFalseResult();
289 }
290 
EmitDataMemoryBarrierFullIntrinsic(IntrinsicInst * inst,Reg dst,SRCREGS src)291 void Codegen::EmitDataMemoryBarrierFullIntrinsic([[maybe_unused]] IntrinsicInst *inst, [[maybe_unused]] Reg dst,
292                                                  [[maybe_unused]] SRCREGS src)
293 {
294     ASSERT(0);
295     GetEncoder()->SetFalseResult();
296 }
297 
EmitWriteTlabStatsSafeIntrinsic(IntrinsicInst * inst,Reg dst,SRCREGS src)298 void Codegen::EmitWriteTlabStatsSafeIntrinsic([[maybe_unused]] IntrinsicInst *inst, [[maybe_unused]] Reg dst,
299                                               [[maybe_unused]] SRCREGS src)
300 {
301     ASSERT(0);
302     GetEncoder()->SetFalseResult();
303 }
304 
EmitExpandU8ToU16Intrinsic(IntrinsicInst * inst,Reg dst,SRCREGS src)305 void Codegen::EmitExpandU8ToU16Intrinsic([[maybe_unused]] IntrinsicInst *inst, [[maybe_unused]] Reg dst,
306                                          [[maybe_unused]] SRCREGS src)
307 {
308     ASSERT(0);
309     GetEncoder()->SetFalseResult();
310 }
311 
EmitAtomicByteOrIntrinsic(IntrinsicInst * inst,Reg dst,SRCREGS src)312 void Codegen::EmitAtomicByteOrIntrinsic([[maybe_unused]] IntrinsicInst *inst, [[maybe_unused]] Reg dst,
313                                         [[maybe_unused]] SRCREGS src)
314 {
315     ASSERT(0);
316     GetEncoder()->SetFalseResult();
317 }
318 
EmitSaveOrRestoreRegsEpIntrinsic(IntrinsicInst * inst,Reg dst,SRCREGS src)319 void Codegen::EmitSaveOrRestoreRegsEpIntrinsic([[maybe_unused]] IntrinsicInst *inst, [[maybe_unused]] Reg dst,
320                                                [[maybe_unused]] SRCREGS src)
321 {
322     ASSERT(0);
323     GetEncoder()->SetFalseResult();
324 }
325 
EmitTailCallIntrinsic(IntrinsicInst * inst,Reg dst,SRCREGS src)326 void Codegen::EmitTailCallIntrinsic([[maybe_unused]] IntrinsicInst *inst, [[maybe_unused]] Reg dst,
327                                     [[maybe_unused]] SRCREGS src)
328 {
329     ASSERT(0);
330     GetEncoder()->SetFalseResult();
331 }
332 
EmitSlowPathEntryIntrinsic(IntrinsicInst * inst,Reg dst,SRCREGS src)333 void Codegen::EmitSlowPathEntryIntrinsic([[maybe_unused]] IntrinsicInst *inst, [[maybe_unused]] Reg dst,
334                                          [[maybe_unused]] SRCREGS src)
335 {
336     ASSERT(0);
337     GetEncoder()->SetFalseResult();
338 }
339 
340 /* It is the same for CodegenFastPath and CodegenInterpreter */
EmitUnreachableIntrinsic(IntrinsicInst * inst,Reg dst,SRCREGS src)341 void Codegen::EmitUnreachableIntrinsic([[maybe_unused]] IntrinsicInst *inst, [[maybe_unused]] Reg dst,
342                                        [[maybe_unused]] SRCREGS src)
343 {
344     ASSERT(inst->GetIntrinsicId() == RuntimeInterface::IntrinsicId::INTRINSIC_UNREACHABLE);
345     GetEncoder()->EncodeAbort();
346 }
347 
348 /* It is the same for CodegenFastPath and CodegenInterpreter */
EmitInterpreterReturnIntrinsic(IntrinsicInst * inst,Reg dst,SRCREGS src)349 void Codegen::EmitInterpreterReturnIntrinsic([[maybe_unused]] IntrinsicInst *inst, [[maybe_unused]] Reg dst,
350                                              [[maybe_unused]] SRCREGS src)
351 {
352     ASSERT(inst->GetIntrinsicId() == RuntimeInterface::IntrinsicId::INTRINSIC_INTERPRETER_RETURN);
353     GetCallingConvention()->GenerateNativeEpilogue(*GetFrameInfo(), []() {});
354 }
355 #endif  // IRTOC_INTRINSICS_ENABLED
356 
BeginMethod()357 bool Codegen::BeginMethod()
358 {
359     // Do not try to encode too large graph
360     auto estimatedCodeSize = GetGraph()->EstimateCodeSize();
361     if (estimatedCodeSize > g_options.GetCompilerMaxGenCodeSize()) {
362         COMPILER_LOG(WARNING, CODEGEN) << "Code is too large - estimated code size: " << estimatedCodeSize;
363         return false;
364     }
365     // Limit encoder to match estimated to actual code size.
366     // After this - encoder aborted, if allocated too much size.
367     GetEncoder()->SetMaxAllocatedBytes(g_options.GetCompilerMaxGenCodeSize());
368 
369     if (!IsCompressedStringsEnabled()) {
370 #ifndef NDEBUG
371         LOG(FATAL, COMPILER) << "String compression must be enabled";
372 #else
373         LOG(ERROR, COMPILER) << "String compression must be enabled";
374 #endif
375         return false;
376     }
377 
378     if (GetGraph()->IsAotMode()) {
379         GetEncoder()->SetCodeOffset(GetGraph()->GetAotData()->GetCodeOffset() + CodeInfo::GetCodeOffset(GetArch()));
380     } else {
381         GetEncoder()->SetCodeOffset(0);
382     }
383 
384     codeBuilder_->BeginMethod(GetFrameLayout().GetFrameSize<CFrameLayout::OffsetUnit::BYTES>(),
385                               GetGraph()->GetVRegsCount() + GetGraph()->GetEnvCount());
386 
387     GetEncoder()->BindLabel(GetLabelEntry());
388     SetStartCodeOffset(GetEncoder()->GetCursorOffset());
389 
390     GeneratePrologue();
391 
392     return GetEncoder()->GetResult();
393 }
394 
GeneratePrologue()395 void Codegen::GeneratePrologue()
396 {
397     SCOPED_DISASM_STR(this, "Method Prologue");
398 
399     GetCallingConvention()->GeneratePrologue(*frameInfo_);
400 
401     if (!GetGraph()->GetMode().IsNative()) {
402         GetEncoder()->EncodeSti(1, 1, MemRef(ThreadReg(), GetRuntime()->GetTlsFrameKindOffset(GetArch())));
403     }
404     if (!GetGraph()->GetMode().IsNative()) {
405         // Create stack overflow check
406         GetEncoder()->EncodeStackOverflowCheck(-GetRuntime()->GetStackOverflowCheckOffset());
407         // Create empty stackmap for the stack overflow check
408         SaveStateInst ss(Opcode::SaveState, 0, nullptr, nullptr, 0);
409         GetCodeBuilder()->BeginStackMap(0, 0, &ss, false);
410         GetCodeBuilder()->EndStackMap();
411     }
412 
413 #if defined(EVENT_METHOD_ENTER_ENABLED) && EVENT_METHOD_ENTER_ENABLED != 0
414     MakeTrace();
415 #endif
416 }
417 
418 #if defined(EVENT_METHOD_ENTER_ENABLED) && EVENT_METHOD_ENTER_ENABLED != 0
MakeTrace()419 void Codegen::MakeTrace()
420 {
421     if (GetGraph()->IsAotMode()) {
422         SCOPED_DISASM_STR(this, "LoadMethod for trace");
423         ScopedTmpReg method_reg(GetEncoder());
424         LoadMethod(method_reg);
425         InsertTrace(Imm(static_cast<size_t>(TraceId::METHOD_ENTER)), method_reg,
426                     Imm(static_cast<size_t>(events::MethodEnterKind::COMPILED)));
427     } else {
428         InsertTrace(Imm(static_cast<size_t>(TraceId::METHOD_ENTER)),
429                     Imm(reinterpret_cast<size_t>(GetGraph()->GetMethod())),
430                     Imm(static_cast<size_t>(events::MethodEnterKind::COMPILED)));
431     }
432 }
433 #endif
434 
GenerateEpilogue()435 void Codegen::GenerateEpilogue()
436 {
437     SCOPED_DISASM_STR(this, "Method Epilogue");
438 
439 #if defined(EVENT_METHOD_EXIT_ENABLED) && EVENT_METHOD_EXIT_ENABLED != 0
440     GetCallingConvention()->GenerateEpilogue(*frame_info_, MakeTrace);
441 #else
442     GetCallingConvention()->GenerateEpilogue(*frameInfo_, []() {});
443 #endif
444 }
445 
VisitGraph()446 bool Codegen::VisitGraph()
447 {
448     EncodeVisitor visitor(this);
449     visitor_ = &visitor;
450 
451     const auto &blocks = GetGraph()->GetBlocksLinearOrder();
452 
453     for (auto bb : blocks) {
454         GetEncoder()->BindLabel(bb->GetId());
455         for (auto inst : bb->AllInsts()) {
456             SCOPED_DISASM_INST(this, inst);
457 
458 #ifdef PANDA_COMPILER_DEBUG_INFO
459             auto debugInfo = inst->GetDebugInfo();
460             if (GetGraph()->IsLineDebugInfoEnabled() && debugInfo != nullptr) {
461                 debugInfo->SetOffset(GetEncoder()->GetCursorOffset());
462             }
463 #endif
464             visitor.VisitInstruction(inst);
465             if (!visitor.GetResult()) {
466                 COMPILER_LOG(DEBUG, CODEGEN)
467                     << "Can't encode instruction: " << GetOpcodeString(inst->GetOpcode()) << *inst;
468                 break;
469             }
470         }
471 
472         if (bb->NeedsJump()) {
473             EmitJump(bb);
474         }
475 
476         if (!visitor.GetResult()) {
477             return false;
478         }
479     }
480 
481     auto estimatedSlowPathsSize = slowPaths_.size() * SLOW_PATH_SIZE;
482     auto estimatedCodeSize = GetGraph()->EstimateCodeSize<true>() + estimatedSlowPathsSize;
483     if (estimatedCodeSize > g_options.GetCompilerMaxGenCodeSize()) {
484         COMPILER_LOG(WARNING, CODEGEN) << "Code is too large - code size: " << GetGraph()->EstimateCodeSize<true>()
485                                        << ", slow paths' code estimation: " << estimatedSlowPathsSize;
486         return false;
487     }
488 
489     EmitSlowPaths();
490     visitor_ = nullptr;
491 
492     return true;
493 }
494 
EmitJump(const BasicBlock * bb)495 void Codegen::EmitJump(const BasicBlock *bb)
496 {
497     BasicBlock *sucBb = nullptr;
498 
499     if (bb->GetLastInst() == nullptr) {
500         ASSERT(bb->IsEmpty());
501         sucBb = bb->GetSuccsBlocks()[0];
502     } else {
503         switch (bb->GetLastInst()->GetOpcode()) {
504             case Opcode::If:
505             case Opcode::IfImm:
506                 ASSERT(bb->GetSuccsBlocks().size() == MAX_SUCCS_NUM);
507                 sucBb = bb->GetFalseSuccessor();
508                 break;
509             default:
510                 sucBb = bb->GetSuccsBlocks()[0];
511                 break;
512         }
513     }
514     SCOPED_DISASM_STR(this, std::string("Jump from  BB_") + std::to_string(bb->GetId()) + " to BB_" +
515                                 std::to_string(sucBb->GetId()));
516 
517     auto label = sucBb->GetId();
518     GetEncoder()->EncodeJump(label);
519 }
520 
EndMethod()521 void Codegen::EndMethod()
522 {
523     for (auto &osrStub : osrEntries_) {
524         SCOPED_DISASM_STR(this,
525                           std::string("Osr stub for OsrStateStump ") + std::to_string(osrStub->GetInst()->GetId()));
526         osrStub->Generate(this);
527     }
528 
529     GetEncoder()->Finalize();
530 }
531 
532 // Allocates memory, copies generated code to it, sets the code to the graph's codegen data. Also this function
533 // encodes the code info and sets it to the graph.
CopyToCodeCache()534 bool Codegen::CopyToCodeCache()
535 {
536     auto codeEntry = reinterpret_cast<char *>(GetEncoder()->GetLabelAddress(GetLabelEntry()));
537     auto codeSize = GetEncoder()->GetCursorOffset();
538     bool saveAllCalleeRegisters = !GetGraph()->GetMethodProperties().GetCompactPrologueAllowed();
539 
540     auto code = reinterpret_cast<uint8_t *>(GetAllocator()->Alloc(codeSize));
541     if (code == nullptr) {
542         return false;
543     }
544     std::copy_n(codeEntry, codeSize, code);
545     GetGraph()->SetCode(EncodeDataType(code, codeSize));
546 
547     RegMask calleeRegs;
548     VRegMask calleeVregs;
549     GetRegfile()->FillUsedCalleeSavedRegisters(&calleeRegs, &calleeVregs, saveAllCalleeRegisters);
550     constexpr size_t MAX_NUM_REGISTERS = 32;
551     static_assert(MAX_NUM_REGS <= MAX_NUM_REGISTERS && MAX_NUM_VREGS <= MAX_NUM_REGISTERS);
552     codeBuilder_->SetSavedCalleeRegsMask(static_cast<uint32_t>(calleeRegs.to_ulong()),
553                                          static_cast<uint32_t>(calleeVregs.to_ulong()));
554 
555     ArenaVector<uint8_t> codeInfoData(GetGraph()->GetAllocator()->Adapter());
556     codeBuilder_->Encode(&codeInfoData);
557     GetGraph()->SetCodeInfo(Span(codeInfoData));
558 
559     return true;
560 }
561 
IssueDisasm()562 void Codegen::IssueDisasm()
563 {
564     if (GetGraph()->GetMode().SupportManagedCode() && g_options.IsCompilerDisasmDumpCodeInfo()) {
565         GetDisasm()->PrintCodeInfo(this);
566     }
567     GetDisasm()->PrintCodeStatistics(this);
568     auto &stream = GetDisasm()->GetStream();
569     for (auto &item : GetDisasm()->GetItems()) {
570         if (std::holds_alternative<CodeItem>(item)) {
571             auto &code = std::get<CodeItem>(item);
572             for (size_t pc = code.GetPosition(); pc < code.GetCursorOffset();) {
573                 stream << GetDisasm()->GetIndent(code.GetDepth());
574                 auto newPc = GetEncoder()->DisasmInstr(stream, pc, 0);
575                 stream << "\n";
576                 pc = newPc;
577             }
578         } else if (std::holds_alternative<std::string>(item)) {
579             stream << (std::get<std::string>(item));
580         }
581     }
582 }
583 
RunImpl()584 bool Codegen::RunImpl()
585 {
586     Initialize();
587 
588     auto encoder = GetEncoder();
589     encoder->GetLabels()->CreateLabels(GetGraph()->GetVectorBlocks().size());
590     labelEntry_ = encoder->CreateLabel();
591     labelExit_ = encoder->CreateLabel();
592 
593 #ifndef NDEBUG
594     if (g_options.IsCompilerNonOptimizing()) {
595         // In case of non-optimizing compiler lowering pass is not run but low-level instructions may
596         // also appear on codegen so, to satisfy GraphChecker, the flag should be raised.
597         GetGraph()->SetLowLevelInstructionsEnabled();
598     }
599 #endif  // NDEBUG
600 
601     if ((GetCallingConvention() == nullptr) || (GetEncoder() == nullptr)) {
602         return false;
603     }
604 
605     if (GetCallingConvention()->IsDynCallMode()) {
606         auto numExpectedArgs = GetRuntime()->GetMethodTotalArgumentsCount(GetGraph()->GetMethod());
607         if (numExpectedArgs > GetRuntime()->GetDynamicNumFixedArgs()) {
608             CallConvDynInfo dynInfo(numExpectedArgs,
609                                     GetRuntime()->GetEntrypointTlsOffset(
610                                         GetArch(), RuntimeInterface::EntrypointId::EXPAND_COMPILED_CODE_ARGS_DYN));
611             GetCallingConvention()->SetDynInfo(dynInfo);
612             frameInfo_->SetSaveFrameAndLinkRegs(true);
613         } else {
614             GetCallingConvention()->SetDynInfo(CallConvDynInfo());
615         }
616     }
617 
618     if (!GetEncoder()->GetResult()) {
619         return false;
620     }
621 
622     // Remove registers from the temp registers, if they are in the regalloc mask, i.e. available for regalloc.
623     auto usedRegs = ~GetGraph()->GetArchUsedRegs();
624     auto forbiddenTemps = usedRegs & GetTarget().GetTempRegsMask();
625     if (forbiddenTemps.Any()) {
626         for (size_t i = forbiddenTemps.GetMinRegister(); i <= forbiddenTemps.GetMaxRegister(); i++) {
627             if (forbiddenTemps[i] && encoder->IsScratchRegisterReleased(Reg(i, INT64_TYPE))) {
628                 encoder->AcquireScratchRegister(Reg(i, INT64_TYPE));
629             }
630         }
631     }
632 
633     return Finalize();
634 }
635 
Initialize()636 void Codegen::Initialize()
637 {
638     CreateFrameInfo();
639 
640     GetRegfile()->SetCalleeSaved(GetRegfile()->GetCalleeSaved());
641 
642     if (!GetGraph()->SupportManagedCode()) {
643         for (auto inst : GetGraph()->GetStartBlock()->AllInsts()) {
644             if (inst->GetOpcode() == Opcode::LiveIn && GetTarget().GetTempRegsMask().Test(inst->GetDstReg())) {
645                 GetEncoder()->AcquireScratchRegister(Reg(inst->GetDstReg(), INT64_TYPE));
646             }
647         }
648     }
649 
650     bool hasCalls = false;
651 
652     for (auto bb : GetGraph()->GetBlocksLinearOrder()) {
653         // Calls may be in the middle of method
654         for (auto inst : bb->Insts()) {
655             // For throw instruction need jump2runtime same way
656             if (inst->IsCall() || inst->GetOpcode() == Opcode::Throw) {
657                 hasCalls = true;
658                 break;
659             }
660         }
661         if (hasCalls) {
662             break;
663         }
664     }
665 
666     /* Convert Graph::GetUsedRegs(), which is std::vector<bool>, to simple
667      * RegMask and save it in the Codegen. These masks are used to determine
668      * which registers we need to save in prologue.
669      *
670      * NB! It's related to IRTOC specific prologue generation (see CodegenFastPath etc.).
671      * Arch specific CallingConvention::GenerateProlog() relies on reg usage information
672      * prepared in the Codegen constructor (before Initialize() is called).
673      */
674     auto fillMask = [](RegMask *mask, auto *vector) {
675         if (vector == nullptr) {
676             return;
677         }
678         ASSERT(mask->size() >= vector->size());
679         mask->reset();
680         for (size_t i = 0; i < mask->size(); i++) {
681             if ((*vector)[i]) {
682                 mask->set(i);
683             }
684         }
685     };
686     fillMask(&usedRegs_, GetGraph()->GetUsedRegs<DataType::INT64>());
687     fillMask(&usedVregs_, GetGraph()->GetUsedRegs<DataType::FLOAT64>());
688     usedVregs_ &= GetTarget().GetAvailableVRegsMask();
689     usedRegs_ &= GetTarget().GetAvailableRegsMask();
690     usedRegs_ &= ~GetGraph()->GetArchUsedRegs();
691     usedVregs_ &= ~GetGraph()->GetArchUsedVRegs();
692 
693     /* Remove used registers from Encoder's scratch registers */
694     RegMask usedTemps = usedRegs_ & GetTarget().GetTempRegsMask();
695     if (usedTemps.any()) {
696         for (size_t i = 0; i < usedTemps.size(); i++) {
697             if (usedTemps[i]) {
698                 GetEncoder()->AcquireScratchRegister(Reg(i, INT64_TYPE));
699             }
700         }
701     }
702 }
703 
Finalize()704 bool Codegen::Finalize()
705 {
706     if (GetDisasm()->IsEnabled()) {
707         GetDisasm()->PrintMethodEntry(this);
708     }
709 
710     if (!BeginMethod()) {
711         return false;
712     }
713 
714     if (!VisitGraph()) {
715         return false;
716     }
717     EndMethod();
718 
719     if (!CopyToCodeCache()) {
720         return false;
721     }
722 
723     if (GetDisasm()->IsEnabled()) {
724         IssueDisasm();
725     }
726 
727     return true;
728 }
729 
ConvertRegister(Register r,DataType::Type type)730 Reg Codegen::ConvertRegister(Register r, DataType::Type type)
731 {
732     switch (type) {
733         case DataType::BOOL:
734         case DataType::UINT8:
735         case DataType::INT8: {
736             return Reg(r, INT8_TYPE);
737         }
738         case DataType::UINT16:
739         case DataType::INT16: {
740             return Reg(r, INT16_TYPE);
741         }
742         case DataType::UINT32:
743         case DataType::INT32: {
744             return Reg(r, INT32_TYPE);
745         }
746         case DataType::UINT64:
747         case DataType::INT64:
748         case DataType::ANY: {
749             return Reg(r, INT64_TYPE);
750         }
751         case DataType::FLOAT32: {
752             return Reg(r, FLOAT32_TYPE);
753         }
754         case DataType::FLOAT64: {
755             return Reg(r, FLOAT64_TYPE);
756         }
757         case DataType::REFERENCE: {
758             return ConvertRegister(r, DataType::GetIntTypeForReference(GetArch()));
759         }
760         case DataType::POINTER: {
761             return Reg(r, ConvertDataType(DataType::POINTER, GetArch()));
762         }
763         default:
764             // Invalid converted register
765             return INVALID_REGISTER;
766     }
767 }
768 
769 // Panda don't support types less then 32, so we need sign or zero extended to 32
ConvertImmWithExtend(uint64_t imm,DataType::Type type)770 Imm Codegen::ConvertImmWithExtend(uint64_t imm, DataType::Type type)
771 {
772     switch (type) {
773         case DataType::BOOL:
774         case DataType::UINT8:
775             return Imm(static_cast<uint32_t>(static_cast<uint8_t>(imm)));
776         case DataType::INT8:
777             return Imm(static_cast<int32_t>(bit_cast<int8_t, uint8_t>(imm)));
778         case DataType::UINT16:
779             return Imm(static_cast<uint32_t>(static_cast<uint16_t>(imm)));
780         case DataType::INT16:
781             return Imm(static_cast<int32_t>(bit_cast<int16_t, uint16_t>(imm)));
782         // NOLINTNEXTLINE(bugprone-branch-clone)
783         case DataType::UINT32:
784             return Imm(bit_cast<int32_t, uint32_t>(imm));
785         case DataType::INT32:
786             return Imm(bit_cast<int32_t, uint32_t>(imm));
787         // NOLINTNEXTLINE(bugprone-branch-clone)
788         case DataType::UINT64:
789             return Imm(bit_cast<int64_t, uint64_t>(imm));
790         case DataType::INT64:
791             return Imm(bit_cast<int64_t, uint64_t>(imm));
792         case DataType::FLOAT32:
793             return Imm(bit_cast<float, uint32_t>(static_cast<uint32_t>(imm)));
794         case DataType::FLOAT64:
795             return Imm(bit_cast<double, uint64_t>(imm));
796         case DataType::ANY:
797             return Imm(bit_cast<uint64_t, uint64_t>(imm));
798         case DataType::REFERENCE:
799             if (imm == 0) {
800                 return Imm(0);
801             }
802             [[fallthrough]]; /* fall-through */
803         default:
804             // Invalid converted immediate
805             UNREACHABLE();
806     }
807     return Imm(0);
808 }
809 
ConvertCc(ConditionCode cc)810 Condition Codegen::ConvertCc(ConditionCode cc)
811 {
812     switch (cc) {
813         case CC_EQ:
814             return Condition::EQ;
815         case CC_NE:
816             return Condition::NE;
817         case CC_LT:
818             return Condition::LT;
819         case CC_LE:
820             return Condition::LE;
821         case CC_GT:
822             return Condition::GT;
823         case CC_GE:
824             return Condition::GE;
825         case CC_B:
826             return Condition::LO;
827         case CC_BE:
828             return Condition::LS;
829         case CC_A:
830             return Condition::HI;
831         case CC_AE:
832             return Condition::HS;
833         case CC_TST_EQ:
834             return Condition::TST_EQ;
835         case CC_TST_NE:
836             return Condition::TST_NE;
837         default:
838             UNREACHABLE();
839             return Condition::EQ;
840     }
841     return Condition::EQ;
842 }
843 
ConvertCcOverflow(ConditionCode cc)844 Condition Codegen::ConvertCcOverflow(ConditionCode cc)
845 {
846     switch (cc) {
847         case CC_EQ:
848             return Condition::VS;
849         case CC_NE:
850             return Condition::VC;
851         default:
852             UNREACHABLE();
853             return Condition::VS;
854     }
855     return Condition::VS;
856 }
857 
EmitSlowPaths()858 void Codegen::EmitSlowPaths()
859 {
860     for (auto slowPath : slowPaths_) {
861         slowPath->Generate(this);
862     }
863 }
864 
CreateStackMap(Inst * inst,Inst * user)865 void Codegen::CreateStackMap(Inst *inst, Inst *user)
866 {
867     SaveStateInst *saveState = nullptr;
868     if (inst->IsSaveState()) {
869         saveState = static_cast<SaveStateInst *>(inst);
870     } else {
871         saveState = inst->GetSaveState();
872     }
873     ASSERT(saveState != nullptr);
874 
875     bool requireVregMap = inst->RequireRegMap();
876     uint32_t outerBpc = inst->GetPc();
877     for (auto callInst = saveState->GetCallerInst(); callInst != nullptr;
878          callInst = callInst->GetSaveState()->GetCallerInst()) {
879         outerBpc = callInst->GetPc();
880     }
881     codeBuilder_->BeginStackMap(outerBpc, GetEncoder()->GetCursorOffset(), saveState, requireVregMap);
882     if (user == nullptr) {
883         user = inst;
884         if (inst == saveState && inst->HasUsers()) {
885             auto users = inst->GetUsers();
886             auto it = std::find_if(users.begin(), users.end(),
887                                    [](auto &u) { return u.GetInst()->GetOpcode() != Opcode::ReturnInlined; });
888             user = it->GetInst();
889         }
890     }
891     CreateStackMapRec(saveState, requireVregMap, user);
892 
893     codeBuilder_->EndStackMap();
894     if (GetDisasm()->IsEnabled()) {
895         GetDisasm()->PrintStackMap(this);
896     }
897 }
898 
CreateStackMapRec(SaveStateInst * saveState,bool requireVregMap,Inst * targetSite)899 void Codegen::CreateStackMapRec(SaveStateInst *saveState, bool requireVregMap, Inst *targetSite)
900 {
901     bool hasInlineInfo = saveState->GetCallerInst() != nullptr;
902     size_t vregsCount = 0;
903     if (requireVregMap) {
904         auto runtime = GetRuntime();
905         if (auto caller = saveState->GetCallerInst()) {
906             vregsCount = runtime->GetMethodRegistersCount(caller->GetCallMethod()) +
907                          runtime->GetMethodArgumentsCount(caller->GetCallMethod());
908         } else {
909             vregsCount = runtime->GetMethodRegistersCount(saveState->GetMethod()) +
910                          runtime->GetMethodArgumentsCount(saveState->GetMethod());
911         }
912         ASSERT(!GetGraph()->IsBytecodeOptimizer());
913         // 1 for acc, number of env regs for dynamic method
914         vregsCount += 1U + GetGraph()->GetEnvCount();
915 #ifndef NDEBUG
916         ASSERT_PRINT(!saveState->GetInputsWereDeleted() || saveState->GetInputsWereDeletedSafely(),
917                      "Some vregs were deleted from the save state");
918 #endif
919     }
920 
921     if (auto callInst = saveState->GetCallerInst()) {
922         CreateStackMapRec(callInst->GetSaveState(), requireVregMap, targetSite);
923         auto method = GetGraph()->IsAotMode() ? nullptr : callInst->GetCallMethod();
924         codeBuilder_->BeginInlineInfo(method, GetRuntime()->GetMethodId(callInst->GetCallMethod()), saveState->GetPc(),
925                                       vregsCount);
926     }
927 
928     if (requireVregMap) {
929         CreateVRegMap(saveState, vregsCount, targetSite);
930     }
931 
932     if (hasInlineInfo) {
933         codeBuilder_->EndInlineInfo();
934     }
935 }
936 
CreateVRegMap(SaveStateInst * saveState,size_t vregsCount,Inst * targetSite)937 void Codegen::CreateVRegMap(SaveStateInst *saveState, size_t vregsCount, Inst *targetSite)
938 {
939     vregIndices_.clear();
940     vregIndices_.resize(vregsCount, {-1, -1});
941     FillVregIndices(saveState);
942 
943     ASSERT(GetGraph()->IsAnalysisValid<LivenessAnalyzer>());
944     auto &la = GetGraph()->GetAnalysis<LivenessAnalyzer>();
945     auto targetLifeNumber = la.GetInstLifeIntervals(targetSite)->GetBegin();
946 
947     for (auto &inputIndex : vregIndices_) {
948         if (inputIndex.first == -1 && inputIndex.second == -1) {
949             codeBuilder_->AddVReg(VRegInfo());
950             continue;
951         }
952         if (inputIndex.second != -1) {
953             auto imm = saveState->GetImmediate(inputIndex.second);
954             codeBuilder_->AddConstant(imm.value, IrTypeToMetainfoType(imm.type), imm.vregType);
955             continue;
956         }
957         ASSERT(inputIndex.first != -1);
958         auto vreg = saveState->GetVirtualRegister(inputIndex.first);
959         auto inputInst = saveState->GetDataFlowInput(inputIndex.first);
960         auto interval = la.GetInstLifeIntervals(inputInst)->FindSiblingAt(targetLifeNumber);
961         ASSERT(interval != nullptr);
962         CreateVreg(interval->GetLocation(), inputInst, vreg);
963     }
964 }
965 
CreateVreg(const Location & location,Inst * inst,const VirtualRegister & vreg)966 void Codegen::CreateVreg(const Location &location, Inst *inst, const VirtualRegister &vreg)
967 {
968     switch (location.GetKind()) {
969         case LocationType::FP_REGISTER:
970         case LocationType::REGISTER: {
971             CreateVRegForRegister(location, inst, vreg);
972             break;
973         }
974         case LocationType::STACK_PARAMETER: {
975             auto slot = location.GetValue();
976             codeBuilder_->AddVReg(VRegInfo(GetFrameLayout().GetStackArgsStartSlot() - slot - CFrameSlots::Start(),
977                                            VRegInfo::Location::SLOT, IrTypeToMetainfoType(inst->GetType()),
978                                            vreg.GetVRegType()));
979             break;
980         }
981         case LocationType::STACK: {
982             auto slot = location.GetValue();
983             if (!Is64BitsArch(GetArch())) {
984                 slot = ((location.GetValue() << 1U) + 1);
985             }
986             codeBuilder_->AddVReg(VRegInfo(GetFrameLayout().GetFirstSpillSlot() + slot, VRegInfo::Location::SLOT,
987                                            IrTypeToMetainfoType(inst->GetType()), vreg.GetVRegType()));
988             break;
989         }
990         case LocationType::IMMEDIATE: {
991             ASSERT(inst->IsConst());
992             auto type = inst->GetType();
993             // There are no int64 type for dynamic methods.
994             if (GetGraph()->IsDynamicMethod() && type == DataType::INT64) {
995                 type = DataType::INT32;
996             }
997             codeBuilder_->AddConstant(inst->CastToConstant()->GetRawValue(), IrTypeToMetainfoType(type),
998                                       vreg.GetVRegType());
999             break;
1000         }
1001         default:
1002             // Reg-to-reg spill fill must not occurs within SaveState
1003             UNREACHABLE();
1004     }
1005 }
1006 
FillVregIndices(SaveStateInst * saveState)1007 void Codegen::FillVregIndices(SaveStateInst *saveState)
1008 {
1009     for (size_t i = 0; i < saveState->GetInputsCount(); ++i) {
1010         size_t vregIndex = saveState->GetVirtualRegister(i).Value();
1011         if (vregIndex == VirtualRegister::BRIDGE) {
1012             continue;
1013         }
1014         ASSERT(vregIndex < vregIndices_.size());
1015         vregIndices_[vregIndex].first = i;
1016     }
1017     for (size_t i = 0; i < saveState->GetImmediatesCount(); i++) {
1018         auto vregImm = saveState->GetImmediate(i);
1019         if (vregImm.vreg == VirtualRegister::BRIDGE) {
1020             continue;
1021         }
1022         ASSERT(vregImm.vreg < vregIndices_.size());
1023         ASSERT(vregIndices_[vregImm.vreg].first == -1);
1024         vregIndices_[vregImm.vreg].second = i;
1025     }
1026 }
1027 
CreateVRegForRegister(const Location & location,Inst * inst,const VirtualRegister & vreg)1028 void Codegen::CreateVRegForRegister(const Location &location, Inst *inst, const VirtualRegister &vreg)
1029 {
1030     bool isOsr = GetGraph()->IsOsrMode();
1031     bool isFp = (location.GetKind() == LocationType::FP_REGISTER);
1032     auto regNum = location.GetValue();
1033     auto reg = Reg(regNum, isFp ? FLOAT64_TYPE : INT64_TYPE);
1034     if (!isOsr && GetRegfile()->GetZeroReg() == reg) {
1035         codeBuilder_->AddConstant(0, IrTypeToMetainfoType(inst->GetType()), vreg.GetVRegType());
1036     } else if (isOsr || GetRegfile()->IsCalleeRegister(reg)) {
1037         if (isFp) {
1038             ASSERT(inst->GetType() != DataType::REFERENCE);
1039             ASSERT(isOsr || regNum >= GetFirstCalleeReg(GetArch(), true));
1040             codeBuilder_->AddVReg(VRegInfo(regNum, VRegInfo::Location::FP_REGISTER,
1041                                            IrTypeToMetainfoType(inst->GetType()), vreg.GetVRegType()));
1042         } else {
1043             ASSERT(isOsr || regNum >= GetFirstCalleeReg(GetArch(), false));
1044             codeBuilder_->AddVReg(VRegInfo(regNum, VRegInfo::Location::REGISTER, IrTypeToMetainfoType(inst->GetType()),
1045                                            vreg.GetVRegType()));
1046         }
1047     } else {
1048         ASSERT(regNum >= GetFirstCallerReg(GetArch(), isFp));
1049         auto lastSlot = GetFrameLayout().GetCallerLastSlot(isFp);
1050         RegMask mask(GetCallerRegsMask(GetArch(), isFp));
1051         regNum = mask.GetDistanceFromTail(regNum);
1052         codeBuilder_->AddVReg(VRegInfo(lastSlot - regNum, VRegInfo::Location::SLOT,
1053                                        IrTypeToMetainfoType(inst->GetType()), vreg.GetVRegType()));
1054     }
1055 }
1056 
CreateOsrEntry(SaveStateInst * saveState)1057 void Codegen::CreateOsrEntry(SaveStateInst *saveState)
1058 {
1059     auto &stub = osrEntries_.emplace_back(GetAllocator()->New<OsrEntryStub>(this, saveState));
1060     GetEncoder()->BindLabel(stub->GetLabel());
1061 }
1062 
CallIntrinsic(Inst * inst,RuntimeInterface::IntrinsicId id)1063 void Codegen::CallIntrinsic(Inst *inst, RuntimeInterface::IntrinsicId id)
1064 {
1065     SCOPED_DISASM_STR(this, "CallIntrinsic");
1066     // Call intrinsics isn't supported for IrToc, because we don't know address of intrinsics during IRToc compilation.
1067     // We store adresses of intrinsics in aot file in AOT mode.
1068     ASSERT(GetGraph()->SupportManagedCode());
1069     if (GetGraph()->IsAotMode()) {
1070         auto aotData = GetGraph()->GetAotData();
1071         intptr_t offset = aotData->GetEntrypointOffset(GetEncoder()->GetCursorOffset(), static_cast<int32_t>(id));
1072         GetEncoder()->MakeCallAot(offset);
1073     } else {
1074         GetEncoder()->MakeCall(reinterpret_cast<const void *>(GetRuntime()->GetIntrinsicAddress(
1075             inst->IsRuntimeCall(), GetRuntime()->GetMethodSourceLanguage(GetGraph()->GetMethod()), id)));
1076     }
1077 }
1078 
EmitCallRuntimeCode(Inst * inst,std::variant<EntrypointId,Reg> entrypoint)1079 bool Codegen::EmitCallRuntimeCode(Inst *inst, std::variant<EntrypointId, Reg> entrypoint)
1080 {
1081     auto encoder = GetEncoder();
1082     if (std::holds_alternative<Reg>(entrypoint)) {
1083         auto reg = std::get<Reg>(entrypoint);
1084         ASSERT(reg.IsValid());
1085         encoder->MakeCall(reg);
1086     } else {
1087         auto id = std::get<EntrypointId>(entrypoint);
1088         MemRef entry(ThreadReg(), GetRuntime()->GetEntrypointTlsOffset(GetArch(), id));
1089         encoder->MakeCall(entry);
1090     }
1091 
1092     SaveStateInst *saveState =
1093         (inst == nullptr || inst->IsSaveState()) ? static_cast<SaveStateInst *>(inst) : inst->GetSaveState();
1094     // StackMap should follow the call as the bridge function expects retaddr points to the stackmap
1095     if (saveState != nullptr) {
1096         CreateStackMap(inst);
1097     }
1098 
1099     if (std::holds_alternative<EntrypointId>(entrypoint) &&
1100         GetRuntime()->IsEntrypointNoreturn(std::get<EntrypointId>(entrypoint))) {
1101         if constexpr (DEBUG_BUILD) {  // NOLINT
1102             encoder->EncodeAbort();
1103         }
1104         return false;
1105     }
1106     ASSERT(saveState == nullptr || inst->IsRuntimeCall());
1107 
1108     return true;
1109 }
1110 
SaveRegistersForImplicitRuntime(Inst * inst,RegMask * paramsMask,RegMask * mask)1111 void Codegen::SaveRegistersForImplicitRuntime(Inst *inst, RegMask *paramsMask, RegMask *mask)
1112 {
1113     ASSERT(inst->GetSaveState() != nullptr);
1114     auto &rootsMask = inst->GetSaveState()->GetRootsRegsMask();
1115     for (auto i = 0U; i < inst->GetInputsCount(); i++) {
1116         auto input = inst->GetDataFlowInput(i);
1117         if (!input->IsMovableObject()) {
1118             continue;
1119         }
1120         auto location = inst->GetLocation(i);
1121         if (location.IsRegister() && location.IsRegisterValid()) {
1122             auto val = location.GetValue();
1123             auto reg = Reg(val, INT64_TYPE);
1124             GetEncoder()->SetRegister(mask, nullptr, reg, true);
1125             if (DataType::IsReference(inst->GetInputType(i)) && !rootsMask.test(val)) {
1126                 paramsMask->set(val);
1127                 rootsMask.set(val);
1128             }
1129         }
1130     }
1131 }
1132 
CreateCheckForTLABWithConstSize(Inst * inst,Reg regTlabStart,Reg regTlabSize,size_t size,LabelHolder::LabelId label)1133 void Codegen::CreateCheckForTLABWithConstSize([[maybe_unused]] Inst *inst, Reg regTlabStart, Reg regTlabSize,
1134                                               size_t size, LabelHolder::LabelId label)
1135 {
1136     SCOPED_DISASM_STR(this, "CreateCheckForTLABWithConstSize");
1137     auto encoder = GetEncoder();
1138     if (encoder->CanEncodeImmAddSubCmp(size, WORD_SIZE, false)) {
1139         encoder->EncodeJump(label, regTlabSize, Imm(size), Condition::LO);
1140         // Change pointer to start free memory
1141         encoder->EncodeAdd(regTlabSize, regTlabStart, Imm(size));
1142     } else {
1143         ScopedTmpReg sizeReg(encoder);
1144         encoder->EncodeMov(sizeReg, Imm(size));
1145         encoder->EncodeJump(label, regTlabSize, sizeReg, Condition::LO);
1146         encoder->EncodeAdd(regTlabSize, regTlabStart, sizeReg);
1147     }
1148 }
1149 
CreateDebugRuntimeCallsForNewObject(Inst * inst,Reg regTlabStart,size_t allocSize,RegMask preserved)1150 void Codegen::CreateDebugRuntimeCallsForNewObject(Inst *inst, [[maybe_unused]] Reg regTlabStart, size_t allocSize,
1151                                                   RegMask preserved)
1152 {
1153 #if defined(PANDA_ASAN_ON) || defined(PANDA_TSAN_ON)
1154     CallRuntime(inst, EntrypointId::ANNOTATE_SANITIZERS, INVALID_REGISTER, preserved, regTlabStart,
1155                 TypedImm(allocSize));
1156 #endif
1157     if (GetRuntime()->IsTrackTlabAlloc()) {
1158         CallRuntime(inst, EntrypointId::WRITE_TLAB_STATS, INVALID_REGISTER, preserved, regTlabStart,
1159                     TypedImm(allocSize));
1160     }
1161 }
1162 
CreateNewObjCall(NewObjectInst * newObj)1163 void Codegen::CreateNewObjCall(NewObjectInst *newObj)
1164 {
1165     auto dst = ConvertRegister(newObj->GetDstReg(), newObj->GetType());
1166     auto src = ConvertRegister(newObj->GetSrcReg(0), newObj->GetInputType(0));
1167     auto initClass = newObj->GetInput(0).GetInst();
1168     auto srcClass = ConvertRegister(newObj->GetSrcReg(0), DataType::POINTER);
1169     auto runtime = GetRuntime();
1170 
1171     auto maxTlabSize = runtime->GetTLABMaxSize();
1172     if (maxTlabSize == 0 ||
1173         (initClass->GetOpcode() != Opcode::LoadAndInitClass && initClass->GetOpcode() != Opcode::LoadImmediate)) {
1174         CallRuntime(newObj, EntrypointId::CREATE_OBJECT_BY_CLASS, dst, RegMask::GetZeroMask(), src);
1175         return;
1176     }
1177     RuntimeInterface::ClassPtr klass;
1178     if (initClass->GetOpcode() == Opcode::LoadAndInitClass) {
1179         klass = initClass->CastToLoadAndInitClass()->GetClass();
1180     } else {
1181         ASSERT(initClass->GetOpcode() == Opcode::LoadImmediate);
1182         klass = initClass->CastToLoadImmediate()->GetClass();
1183     }
1184     if (klass == nullptr || !runtime->CanUseTlabForClass(klass)) {
1185         CallRuntime(newObj, EntrypointId::CREATE_OBJECT_BY_CLASS, dst, RegMask::GetZeroMask(), src);
1186         return;
1187     }
1188     auto classSize = runtime->GetClassSize(klass);
1189     auto alignment = runtime->GetTLABAlignment();
1190 
1191     classSize = (classSize & ~(alignment - 1U)) + ((classSize % alignment) != 0U ? alignment : 0U);
1192     if (classSize > maxTlabSize) {
1193         CallRuntime(newObj, EntrypointId::CREATE_OBJECT_BY_CLASS, dst, RegMask::GetZeroMask(), src);
1194         return;
1195     }
1196     CallFastPath(newObj, EntrypointId::ALLOCATE_OBJECT_TLAB, dst, RegMask::GetZeroMask(), srcClass,
1197                  TypedImm(classSize));
1198 }
1199 
CreateNewObjCallOld(NewObjectInst * newObj)1200 void Codegen::CreateNewObjCallOld(NewObjectInst *newObj)
1201 {
1202     auto dst = ConvertRegister(newObj->GetDstReg(), newObj->GetType());
1203     auto src = ConvertRegister(newObj->GetSrcReg(0), newObj->GetInputType(0));
1204     auto initClass = newObj->GetInput(0).GetInst();
1205     auto runtime = GetRuntime();
1206     auto maxTlabSize = runtime->GetTLABMaxSize();
1207     auto encoder = GetEncoder();
1208 
1209     if (maxTlabSize == 0 ||
1210         (initClass->GetOpcode() != Opcode::LoadAndInitClass && initClass->GetOpcode() != Opcode::LoadImmediate)) {
1211         CallRuntime(newObj, EntrypointId::CREATE_OBJECT_BY_CLASS, dst, RegMask::GetZeroMask(), src);
1212         return;
1213     }
1214     RuntimeInterface::ClassPtr klass;
1215     if (initClass->GetOpcode() == Opcode::LoadAndInitClass) {
1216         klass = initClass->CastToLoadAndInitClass()->GetClass();
1217     } else {
1218         ASSERT(initClass->GetOpcode() == Opcode::LoadImmediate);
1219         klass = initClass->CastToLoadImmediate()->GetClass();
1220     }
1221     if (klass == nullptr || !runtime->CanUseTlabForClass(klass)) {
1222         CallRuntime(newObj, EntrypointId::CREATE_OBJECT_BY_CLASS, dst, RegMask::GetZeroMask(), src);
1223         return;
1224     }
1225     auto classSize = runtime->GetClassSize(klass);
1226     auto alignment = runtime->GetTLABAlignment();
1227 
1228     classSize = (classSize & ~(alignment - 1U)) + ((classSize % alignment) != 0U ? alignment : 0U);
1229     if (classSize > maxTlabSize) {
1230         CallRuntime(newObj, EntrypointId::CREATE_OBJECT_BY_CLASS, dst, RegMask::GetZeroMask(), src);
1231         return;
1232     }
1233     ScopedLiveTmpReg regTlabStart(encoder);
1234     ScopedLiveTmpReg regTlabSize(encoder);
1235 
1236     auto slowPath = CreateSlowPath<SlowPathEntrypoint>(newObj, EntrypointId::CREATE_OBJECT_BY_CLASS);
1237     CreateLoadTLABInformation(regTlabStart, regTlabSize);
1238     CreateCheckForTLABWithConstSize(newObj, regTlabStart, regTlabSize, classSize, slowPath->GetLabel());
1239 
1240     RegMask preservedRegs;
1241     encoder->SetRegister(&preservedRegs, nullptr, src);
1242     CreateDebugRuntimeCallsForNewObject(newObj, regTlabStart, reinterpret_cast<size_t>(classSize), preservedRegs);
1243 
1244     ScopedTmpReg tlabReg(encoder);
1245     // Load pointer to tlab
1246     encoder->EncodeLdr(tlabReg, false, MemRef(ThreadReg(), runtime->GetCurrentTLABOffset(GetArch())));
1247 
1248     // Store pointer to the class
1249     encoder->EncodeStr(src, MemRef(regTlabStart, runtime->GetObjClassOffset(GetArch())));
1250     encoder->EncodeMov(dst, regTlabStart);
1251     regTlabStart.Release();
1252     // Store new pointer to start free memory in TLAB
1253     encoder->EncodeStrRelease(regTlabSize, MemRef(tlabReg, runtime->GetTLABFreePointerOffset(GetArch())));
1254     slowPath->BindBackLabel(encoder);
1255 }
1256 
LoadClassFromObject(Reg classReg,Reg objReg)1257 void Codegen::LoadClassFromObject(Reg classReg, Reg objReg)
1258 {
1259     Reg reg = ConvertRegister(classReg.GetId(), DataType::REFERENCE);
1260     GetEncoder()->EncodeLdr(reg, false, MemRef(objReg, GetRuntime()->GetObjClassOffset(GetArch())));
1261 }
1262 
CreateMultiArrayCall(CallInst * callInst)1263 void Codegen::CreateMultiArrayCall(CallInst *callInst)
1264 {
1265     SCOPED_DISASM_STR(this, "Create Call for MultiArray");
1266 
1267     auto dstReg = ConvertRegister(callInst->GetDstReg(), callInst->GetType());
1268     auto numArgs = callInst->GetInputsCount() - 2U;  // first is class, last is save_state
1269 
1270     ScopedTmpReg classReg(GetEncoder());
1271     auto classType = ConvertDataType(DataType::REFERENCE, GetArch());
1272     Reg classOrig = classReg.GetReg().As(classType);
1273     auto location = callInst->GetLocation(0);
1274     ASSERT(location.IsFixedRegister() && location.IsRegisterValid());
1275 
1276     GetEncoder()->EncodeMov(classOrig, ConvertRegister(location.GetValue(), DataType::INT32));
1277     CallRuntime(callInst, EntrypointId::CREATE_MULTI_ARRAY, dstReg, RegMask::GetZeroMask(), classReg, TypedImm(numArgs),
1278                 SpReg());
1279     if (callInst->GetFlag(inst_flags::MEM_BARRIER)) {
1280         GetEncoder()->EncodeMemoryBarrier(memory_order::RELEASE);
1281     }
1282 }
1283 
CreateJumpToClassResolverPltShared(Inst * inst,Reg tmpReg,RuntimeInterface::EntrypointId id)1284 void Codegen::CreateJumpToClassResolverPltShared(Inst *inst, Reg tmpReg, RuntimeInterface::EntrypointId id)
1285 {
1286     auto encoder = GetEncoder();
1287     auto graph = GetGraph();
1288     auto aotData = graph->GetAotData();
1289     auto offset = aotData->GetSharedSlowPathOffset(id, encoder->GetCursorOffset());
1290     if (offset == 0 || !encoder->CanMakeCallByOffset(offset)) {
1291         SlowPathShared *slowPath;
1292         auto search = slowPathsMap_.find(id);
1293         if (search != slowPathsMap_.end()) {
1294             slowPath = search->second;
1295             ASSERT(slowPath->GetTmpReg().GetId() == tmpReg.GetId());
1296         } else {
1297             slowPath = CreateSlowPath<SlowPathShared>(inst, id);
1298             slowPath->SetTmpReg(tmpReg);
1299             slowPathsMap_[id] = slowPath;
1300         }
1301         encoder->MakeCall(slowPath->GetLabel());
1302     } else {
1303         encoder->MakeCallByOffset(offset);
1304     }
1305     CreateStackMap(inst);
1306 }
1307 
CreateLoadClassFromPLT(Inst * inst,Reg tmpReg,Reg dst,size_t classId)1308 void Codegen::CreateLoadClassFromPLT(Inst *inst, Reg tmpReg, Reg dst, size_t classId)
1309 {
1310     auto encoder = GetEncoder();
1311     auto graph = GetGraph();
1312     auto aotData = graph->GetAotData();
1313     intptr_t offset = aotData->GetClassSlotOffset(encoder->GetCursorOffset(), classId, false);
1314     auto label = encoder->CreateLabel();
1315     ASSERT(tmpReg.GetId() != dst.GetId());
1316     ASSERT(inst->IsRuntimeCall());
1317     encoder->MakeLoadAotTableAddr(offset, tmpReg, dst);
1318     encoder->EncodeJump(label, dst, Condition::NE);
1319 
1320     // PLT Class Resolver has special calling convention:
1321     // First encoder temporary (tmp_reg) works as parameter and return value
1322     CHECK_EQ(tmpReg.GetId(), encoder->GetTarget().GetTempRegsMask().GetMinRegister());
1323 
1324     CreateJumpToClassResolverPltShared(inst, tmpReg, EntrypointId::CLASS_RESOLVER);
1325 
1326     encoder->EncodeMov(dst, tmpReg);
1327     encoder->BindLabel(label);
1328 }
1329 
CreateLoadTLABInformation(Reg regTlabStart,Reg regTlabSize)1330 void Codegen::CreateLoadTLABInformation(Reg regTlabStart, Reg regTlabSize)
1331 {
1332     SCOPED_DISASM_STR(this, "LoadTLABInformation");
1333     auto runtime = GetRuntime();
1334     // Load pointer to tlab
1335     GetEncoder()->EncodeLdr(regTlabSize, false, MemRef(ThreadReg(), runtime->GetCurrentTLABOffset(GetArch())));
1336     // Load pointer to start free memory in TLAB
1337     GetEncoder()->EncodeLdr(regTlabStart, false, MemRef(regTlabSize, runtime->GetTLABFreePointerOffset(GetArch())));
1338     // Load pointer to end free memory in TLAB
1339     GetEncoder()->EncodeLdr(regTlabSize, false, MemRef(regTlabSize, runtime->GetTLABEndPointerOffset(GetArch())));
1340     // Calculate size of the free memory
1341     GetEncoder()->EncodeSub(regTlabSize, regTlabSize, regTlabStart);
1342 }
1343 
1344 // The function alignment up the value from alignment_reg using tmp_reg.
CreateAlignmentValue(Reg alignmentReg,Reg tmpReg,size_t alignment)1345 void Codegen::CreateAlignmentValue(Reg alignmentReg, Reg tmpReg, size_t alignment)
1346 {
1347     auto andVal = ~(alignment - 1U);
1348     // zeroed lower bits
1349     GetEncoder()->EncodeAnd(tmpReg, alignmentReg, Imm(andVal));
1350     GetEncoder()->EncodeSub(alignmentReg, alignmentReg, tmpReg);
1351 
1352     auto endLabel = GetEncoder()->CreateLabel();
1353 
1354     // if zeroed value is different, add alignment
1355     GetEncoder()->EncodeJump(endLabel, alignmentReg, Condition::EQ);
1356     GetEncoder()->EncodeAdd(tmpReg, tmpReg, Imm(alignment));
1357 
1358     GetEncoder()->BindLabel(endLabel);
1359     GetEncoder()->EncodeMov(alignmentReg, tmpReg);
1360 }
1361 
GetObjectReg(Codegen * codegen,Inst * inst)1362 Reg GetObjectReg(Codegen *codegen, Inst *inst)
1363 {
1364     ASSERT(inst->IsCall() || inst->GetOpcode() == Opcode::ResolveVirtual || inst->GetOpcode() == Opcode::ResolveByName);
1365     auto location = inst->GetLocation(0);
1366     ASSERT(location.IsFixedRegister() && location.IsRegisterValid());
1367     auto objReg = codegen->ConvertRegister(location.GetValue(), inst->GetInputType(0));
1368     ASSERT(objReg != INVALID_REGISTER);
1369     return objReg;
1370 }
1371 
CreateCallIntrinsic(IntrinsicInst * inst)1372 void Codegen::CreateCallIntrinsic(IntrinsicInst *inst)
1373 {
1374     if (inst->HasArgumentsOnStack()) {
1375         // Since for some intrinsics(f.e. TimedWaitNanos) we need SaveState instruction and
1376         // more than two arguments, we need to place arguments on the stack, but in same time we need to
1377         // create boundary frame
1378         LOG(WARNING, COMPILER) << "Intrinsics with arguments on stack are not supported";
1379         GetEncoder()->SetFalseResult();
1380         return;
1381     }
1382 
1383     ASSERT(!HasLiveCallerSavedRegs(inst));
1384     if (inst->HasImms() && GetGraph()->SupportManagedCode()) {
1385         EncodeImms(inst->GetImms(), (inst->HasIdInput() || inst->IsMethodFirstInput()));
1386     }
1387 
1388     size_t explicitArgs;
1389     if (IsStackRangeIntrinsic(inst->GetIntrinsicId(), &explicitArgs)) {
1390         auto paramInfo = GetCallingConvention()->GetParameterInfo(explicitArgs);
1391         auto rangePtrReg =
1392             ConvertRegister(paramInfo->GetNextLocation(DataType::POINTER).GetRegister(), DataType::POINTER);
1393         if (inst->GetInputsCount() > explicitArgs + (inst->RequireState() ? 1U : 0U)) {
1394             auto rangeSpOffs = GetStackOffset(inst->GetLocation(explicitArgs));
1395             GetEncoder()->EncodeAdd(rangePtrReg, GetTarget().GetStackReg(), Imm(rangeSpOffs));
1396         }
1397     }
1398     if (inst->IsMethodFirstInput()) {
1399         Reg param0 = GetTarget().GetParamReg(0);
1400         if (GetGraph()->IsAotMode()) {
1401             LoadMethod(param0);
1402         } else {
1403             GetEncoder()->EncodeMov(param0, Imm(reinterpret_cast<uintptr_t>(inst->GetMethod())));
1404         }
1405     }
1406     CallIntrinsic(inst, inst->GetIntrinsicId());
1407 
1408     if (inst->GetSaveState() != nullptr) {
1409         // StackMap should follow the call as the bridge function expects retaddr points to the stackmap
1410         CreateStackMap(inst);
1411     }
1412 
1413     if (inst->GetType() != DataType::VOID) {
1414         auto arch = GetArch();
1415         auto returnType = inst->GetType();
1416         auto dstReg = ConvertRegister(inst->GetDstReg(), inst->GetType());
1417         auto returnReg = GetTarget().GetReturnReg(dstReg.GetType());
1418         // We must:
1419         //  sign extended INT8 and INT16 to INT32
1420         //  zero extended UINT8 and UINT16 to UINT32
1421         if (DataType::ShiftByType(returnType, arch) < DataType::ShiftByType(DataType::INT32, arch)) {
1422             bool isSigned = DataType::IsTypeSigned(returnType);
1423             GetEncoder()->EncodeCast(dstReg, isSigned, Reg(returnReg.GetId(), INT32_TYPE), isSigned);
1424         } else {
1425             GetEncoder()->EncodeMov(dstReg, returnReg);
1426         }
1427     }
1428 }
1429 
IntfInlineCachePass(ResolveVirtualInst * resolver,Reg methodReg,Reg tmpReg,Reg objReg)1430 void Codegen::IntfInlineCachePass(ResolveVirtualInst *resolver, Reg methodReg, Reg tmpReg, Reg objReg)
1431 {
1432     // Cache structure:(offset addr)/(class addr) 32bit/32bit
1433     // -----------------------------------------------
1434     // (.aot_got)
1435     //     ...
1436     //     cache:offset/class   <----------|
1437     //     ...                             |
1438     // (.text)                             |
1439     // interface call start                |
1440     // call runtime irtoc function
1441     // if call class == cache.class <------|
1442     //    use cache.offset(method)  <------|
1443     // else                                |
1444     //    call RESOLVE_VIRTUAL_CALL_AOT    |
1445     //    save method‘s offset to cache >--|
1446     // return to (.text)
1447     // call method
1448     // -----------------------------------------------
1449     auto aotData = GetGraph()->GetAotData();
1450     uint64_t intfInlineCacheIndex = aotData->GetIntfInlineCacheIndex();
1451     // NOTE(liyiming): do LoadMethod in irtoc to reduce use tmp reg
1452     if (objReg.GetId() != tmpReg.GetId()) {
1453         auto regTmp64 = tmpReg.As(INT64_TYPE);
1454         uint64_t offset = aotData->GetInfInlineCacheSlotOffset(GetEncoder()->GetCursorOffset(), intfInlineCacheIndex);
1455         GetEncoder()->MakeLoadAotTableAddr(offset, regTmp64, INVALID_REGISTER);
1456         LoadMethod(methodReg);
1457         CallFastPath(resolver, EntrypointId::INTF_INLINE_CACHE, methodReg, {}, methodReg, objReg,
1458                      TypedImm(resolver->GetCallMethodId()), regTmp64);
1459     } else {
1460         // we don't have tmp reg here, so use x3 directly
1461         Reg reg3 = Reg(3U, INT64_TYPE);
1462         ScopedTmpRegF64 vtmp(GetEncoder());
1463         GetEncoder()->EncodeMov(vtmp, reg3);
1464         uint64_t offset = aotData->GetInfInlineCacheSlotOffset(GetEncoder()->GetCursorOffset(), intfInlineCacheIndex);
1465         GetEncoder()->MakeLoadAotTableAddr(offset, reg3, INVALID_REGISTER);
1466         LoadMethod(methodReg);
1467         CallFastPath(resolver, EntrypointId::INTF_INLINE_CACHE, methodReg, {}, methodReg, objReg,
1468                      TypedImm(resolver->GetCallMethodId()), reg3);
1469         GetEncoder()->EncodeMov(reg3, vtmp);
1470     }
1471 
1472     intfInlineCacheIndex++;
1473     aotData->SetIntfInlineCacheIndex(intfInlineCacheIndex);
1474 }
1475 
1476 /**
1477  * Returns a pointer to a caller of a virtual/static resolver.
1478  *
1479  * InstBuilder uses UnresolvedTypesInterface::AddTableSlot(method_ptr, ...) to allocate cache slots
1480  * for unresolved methods. The codegen uses UnresolvedTypesInterface::GetTableSlot(method_ptr, ...)
1481  * to get corresponding slots for unresolved methods.
1482  *
1483  * Since there is no valid method_ptr for an unresolved method at the point of IR construction
1484  * InstBuilder::BuildCallInst uses the pointer to the caller (GetGraph()->GetMethod()) as an argument
1485  * of UnresolvedTypesInterface::AddTableSlot. So the codegen has to deduce the pointer to the caller
1486  * and pass it to UnresolvedTypesInterface::GetTableSlot. If the caller was not inlined then
1487  * GetGraph()->GetMethod() works just fine, otherwise it is done by means of SaveState,
1488  * which is capable of remebering the corresponding caller instruction.
1489  *
1490  * To clarify the details let's consider the following sequence of calls:
1491  *
1492  * main() -> foo() -> bar()
1493  *
1494  * Suppose that foo() is a static method and bar() is an unresolved virtual method.
1495  *
1496  * 1) foo() is going to be inlined
1497  *
1498  * To inline foo() the inlining pass creates its IR by means of IRBuilder(..., caller_inst),
1499  * where caller_inst (the third argument) represents "CallStatic foo" instruction.
1500  * The caller_inst gets remembered in the corresponding SaveState created for bar's resolver
1501  * (see InstBuilder::CreateSaveState for the details).
1502  * At the same time "CallStatic foo" instruction contains a valid method pointer to foo()
1503  * (it can not be nullptr because we do not inline Unresolved).
1504  *
1505  * So we go through the following chain: bar's resolver->SaveState->caller_inst->pointer to the method.
1506  *
1507  * 2) foo() is not inlined
1508  *
1509  * In this case SaveState.caller_inst_ == nullptr (because foo() is not inlined).
1510  * Thus we just use plain GetGraph()->GetMethod().
1511  */
1512 template <typename T>
GetCallerOfUnresolvedMethod(T * resolver)1513 RuntimeInterface::MethodPtr Codegen::GetCallerOfUnresolvedMethod(T *resolver)
1514 {
1515     ASSERT(resolver->GetCallMethod() == nullptr);
1516     auto saveState = resolver->GetSaveState();
1517     ASSERT(saveState != nullptr);
1518     auto caller = saveState->GetCallerInst();
1519     auto method = (caller == nullptr ? GetGraph()->GetMethod() : caller->GetCallMethod());
1520     ASSERT(method != nullptr);
1521     return method;
1522 }
1523 
EmitResolveUnknownVirtual(ResolveVirtualInst * resolver,Reg methodReg)1524 void Codegen::EmitResolveUnknownVirtual(ResolveVirtualInst *resolver, Reg methodReg)
1525 {
1526     SCOPED_DISASM_STR(this, "Create runtime call to resolve an unknown virtual method");
1527     ASSERT(resolver->GetOpcode() == Opcode::ResolveVirtual);
1528     auto method = GetCallerOfUnresolvedMethod(resolver);
1529     ScopedTmpReg tmpReg(GetEncoder(), ConvertDataType(DataType::REFERENCE, GetArch()));
1530     Reg objReg = GetObjectReg(this, resolver);
1531     if (GetGraph()->IsAotMode()) {
1532         LoadMethod(methodReg);
1533         CallRuntime(resolver, EntrypointId::RESOLVE_VIRTUAL_CALL_AOT, methodReg, {}, methodReg, objReg,
1534                     TypedImm(resolver->GetCallMethodId()), TypedImm(0));
1535     } else {
1536         auto runtime = GetRuntime();
1537         auto utypes = runtime->GetUnresolvedTypes();
1538         auto skind = UnresolvedTypesInterface::SlotKind::VIRTUAL_METHOD;
1539         // Try to load vtable index
1540         auto methodSlotAddr = utypes->GetTableSlot(method, resolver->GetCallMethodId(), skind);
1541         GetEncoder()->EncodeMov(methodReg, Imm(methodSlotAddr));
1542         GetEncoder()->EncodeLdr(methodReg, false, MemRef(methodReg));
1543         // 0 means the virtual call is uninitialized or it is an interface call
1544         auto entrypoint = EntrypointId::RESOLVE_UNKNOWN_VIRTUAL_CALL;
1545         auto slowPath = CreateSlowPath<SlowPathUnresolved>(resolver, entrypoint);
1546         slowPath->SetUnresolvedType(method, resolver->GetCallMethodId());
1547         slowPath->SetDstReg(methodReg);
1548         slowPath->SetArgReg(objReg);
1549         slowPath->SetSlotAddr(methodSlotAddr);
1550         GetEncoder()->EncodeJump(slowPath->GetLabel(), methodReg, Condition::EQ);
1551         // Load klass into tmp_reg
1552         LoadClassFromObject(tmpReg, objReg);
1553         // Load from VTable, address = (klass + (index << shift)) + vtable_offset
1554         auto tmpReg64 = Reg(tmpReg.GetReg().GetId(), INT64_TYPE);
1555         GetEncoder()->EncodeAdd(tmpReg64, tmpReg, Shift(methodReg, GetVtableShift()));
1556         GetEncoder()->EncodeLdr(methodReg, false,
1557                                 MemRef(tmpReg64, runtime->GetVTableOffset(GetArch()) - (1U << GetVtableShift())));
1558         slowPath->BindBackLabel(GetEncoder());
1559     }
1560 }
1561 
EmitResolveVirtualAot(ResolveVirtualInst * resolver,Reg methodReg)1562 void Codegen::EmitResolveVirtualAot(ResolveVirtualInst *resolver, Reg methodReg)
1563 {
1564     SCOPED_DISASM_STR(this, "AOT ResolveVirtual using PLT-GOT");
1565     ASSERT(resolver->IsRuntimeCall());
1566     ScopedTmpReg tmpReg(GetEncoder(), ConvertDataType(DataType::REFERENCE, GetArch()));
1567     auto methodReg64 = Reg(methodReg.GetId(), INT64_TYPE);
1568     auto tmpReg64 = Reg(tmpReg.GetReg().GetId(), INT64_TYPE);
1569     auto aotData = GetGraph()->GetAotData();
1570     intptr_t offset = aotData->GetVirtIndexSlotOffset(GetEncoder()->GetCursorOffset(), resolver->GetCallMethodId());
1571     GetEncoder()->MakeLoadAotTableAddr(offset, tmpReg64, methodReg64);
1572     auto label = GetEncoder()->CreateLabel();
1573     GetEncoder()->EncodeJump(label, methodReg, Condition::NE);
1574     GetEncoder()->EncodeMov(methodReg64, tmpReg64);
1575     // PLT CallVirtual Resolver has a very special calling convention:
1576     //   First encoder temporary (method_reg) works as a parameter and return value
1577     CHECK_EQ(methodReg64.GetId(), GetTarget().GetTempRegsMask().GetMinRegister());
1578     MemRef entry(ThreadReg(), GetRuntime()->GetEntrypointTlsOffset(GetArch(), EntrypointId::CALL_VIRTUAL_RESOLVER));
1579     GetEncoder()->MakeCall(entry);
1580     // Need a stackmap to build correct boundary frame
1581     CreateStackMap(resolver);
1582     GetEncoder()->BindLabel(label);
1583     // Load class into method_reg
1584     Reg objReg = GetObjectReg(this, resolver);
1585     LoadClassFromObject(tmpReg, objReg);
1586     // Load from VTable, address = (klass + (index << shift)) + vtable_offset
1587     GetEncoder()->EncodeAdd(methodReg, tmpReg64, Shift(methodReg, GetVtableShift()));
1588     GetEncoder()->EncodeLdr(methodReg, false,
1589                             MemRef(methodReg, GetRuntime()->GetVTableOffset(GetArch()) - (1U << GetVtableShift())));
1590 }
1591 
EmitResolveVirtual(ResolveVirtualInst * resolver)1592 void Codegen::EmitResolveVirtual(ResolveVirtualInst *resolver)
1593 {
1594     auto methodReg = ConvertRegister(resolver->GetDstReg(), resolver->GetType());
1595     auto objectReg = ConvertRegister(resolver->GetSrcReg(0), DataType::REFERENCE);
1596     ScopedTmpReg tmpMethodReg(GetEncoder());
1597     if (resolver->GetCallMethod() == nullptr) {
1598         EmitResolveUnknownVirtual(resolver, tmpMethodReg);
1599         GetEncoder()->EncodeMov(methodReg, tmpMethodReg);
1600     } else if (GetRuntime()->IsInterfaceMethod(resolver->GetCallMethod())) {
1601         SCOPED_DISASM_STR(this, "Create runtime call to resolve a known virtual call");
1602         if (GetGraph()->IsAotMode()) {
1603             if (GetArch() == Arch::AARCH64) {
1604                 ScopedTmpReg tmpReg(GetEncoder(), ConvertDataType(DataType::REFERENCE, GetArch()));
1605                 IntfInlineCachePass(resolver, tmpMethodReg, tmpReg, objectReg);
1606             } else {
1607                 LoadMethod(tmpMethodReg);
1608                 CallRuntime(resolver, EntrypointId::RESOLVE_VIRTUAL_CALL_AOT, tmpMethodReg, {}, tmpMethodReg, objectReg,
1609                             TypedImm(resolver->GetCallMethodId()), TypedImm(0));
1610             }
1611         } else {
1612             CallRuntime(resolver, EntrypointId::RESOLVE_VIRTUAL_CALL, tmpMethodReg, {},
1613                         TypedImm(reinterpret_cast<size_t>(resolver->GetCallMethod())), objectReg);
1614         }
1615         GetEncoder()->EncodeMov(methodReg, tmpMethodReg);
1616     } else if (GetGraph()->IsAotNoChaMode()) {
1617         // ResolveVirtualAot requires method_reg to be the first tmp register.
1618         EmitResolveVirtualAot(resolver, tmpMethodReg);
1619         GetEncoder()->EncodeMov(methodReg, tmpMethodReg);
1620     } else {
1621         UNREACHABLE();
1622     }
1623 }
1624 
EmitCallResolvedVirtual(CallInst * call)1625 void Codegen::EmitCallResolvedVirtual(CallInst *call)
1626 {
1627     SCOPED_DISASM_STR(this, "Create a call to resolved virtual method");
1628     ASSERT(call->GetOpcode() == Opcode::CallResolvedVirtual);
1629     if (call->GetSaveState() != nullptr && call->IsInlined()) {
1630 #if defined(EVENT_METHOD_ENTER_ENABLED) && EVENT_METHOD_ENTER_ENABLED != 0
1631         if (!GetGraph()->IsAotMode()) {
1632             InsertTrace({Imm(static_cast<size_t>(TraceId::METHOD_ENTER)),
1633                          Imm(reinterpret_cast<size_t>(call->GetCallMethod())),
1634                          Imm(static_cast<size_t>(events::MethodEnterKind::INLINED))});
1635         }
1636 #endif
1637         return;
1638     }
1639     ASSERT(!HasLiveCallerSavedRegs(call));
1640     ASSERT(call->GetCallMethod() == nullptr || GetGraph()->GetRuntime()->IsInterfaceMethod(call->GetCallMethod()) ||
1641            GetGraph()->IsAotNoChaMode());
1642     Reg methodReg = ConvertRegister(call->GetSrcReg(0), DataType::POINTER);
1643     Reg param0 = GetTarget().GetParamReg(0);
1644     // Set method
1645     GetEncoder()->EncodeMov(param0, methodReg);
1646     GetEncoder()->MakeCall(MemRef(param0, GetRuntime()->GetCompiledEntryPointOffset(GetArch())));
1647     FinalizeCall(call);
1648 }
1649 
EmitCallVirtual(CallInst * call)1650 void Codegen::EmitCallVirtual(CallInst *call)
1651 {
1652     SCOPED_DISASM_STR(this, "Create a call to virtual method");
1653     ASSERT(call->GetOpcode() == Opcode::CallVirtual);
1654     if (call->GetSaveState() != nullptr && call->IsInlined()) {
1655 #if defined(EVENT_METHOD_ENTER_ENABLED) && EVENT_METHOD_ENTER_ENABLED != 0
1656         if (!GetGraph()->IsAotMode()) {
1657             InsertTrace({Imm(static_cast<size_t>(TraceId::METHOD_ENTER)),
1658                          Imm(reinterpret_cast<size_t>(call->GetCallMethod())),
1659                          Imm(static_cast<size_t>(events::MethodEnterKind::INLINED))});
1660         }
1661 #endif
1662         return;
1663     }
1664     auto runtime = GetRuntime();
1665     auto method = call->GetCallMethod();
1666     ASSERT(!HasLiveCallerSavedRegs(call));
1667     ASSERT(!call->IsUnresolved() && !runtime->IsInterfaceMethod(method) && !GetGraph()->IsAotNoChaMode());
1668     Reg methodReg = GetTarget().GetParamReg(0);
1669     ASSERT(!RegisterKeepCallArgument(call, methodReg));
1670     LoadClassFromObject(methodReg, GetObjectReg(this, call));
1671     // Get index
1672     auto vtableIndex = runtime->GetVTableIndex(method);
1673     // Load from VTable, address = klass + ((index << shift) + vtable_offset)
1674     auto totalOffset = runtime->GetVTableOffset(GetArch()) + (vtableIndex << GetVtableShift());
1675     // Class ref was loaded to method_reg
1676     GetEncoder()->EncodeLdr(methodReg, false, MemRef(methodReg, totalOffset));
1677     // Set method
1678     GetEncoder()->MakeCall(MemRef(methodReg, runtime->GetCompiledEntryPointOffset(GetArch())));
1679     FinalizeCall(call);
1680 }
1681 
EnsureParamsFitIn32Bit(std::initializer_list<std::variant<Reg,TypedImm>> params)1682 bool Codegen::EnsureParamsFitIn32Bit(std::initializer_list<std::variant<Reg, TypedImm>> params)
1683 {
1684     for (auto &param : params) {
1685         if (std::holds_alternative<Reg>(param)) {
1686             auto reg = std::get<Reg>(param);
1687             if (reg.GetSize() > WORD_SIZE) {
1688                 return false;
1689             }
1690         } else {
1691             auto immType = std::get<TypedImm>(param).GetType();
1692             if (immType.GetSize() > WORD_SIZE) {
1693                 return false;
1694             }
1695         }
1696     }
1697     return true;
1698 }
1699 
EmitResolveStatic(ResolveStaticInst * resolver)1700 void Codegen::EmitResolveStatic(ResolveStaticInst *resolver)
1701 {
1702     auto methodReg = ConvertRegister(resolver->GetDstReg(), resolver->GetType());
1703     if (GetGraph()->IsAotMode()) {
1704         LoadMethod(methodReg);
1705         CallRuntime(resolver, EntrypointId::GET_UNKNOWN_CALLEE_METHOD, methodReg, RegMask::GetZeroMask(), methodReg,
1706                     TypedImm(resolver->GetCallMethodId()), TypedImm(0));
1707         return;
1708     }
1709     auto method = GetCallerOfUnresolvedMethod(resolver);
1710     ScopedTmpReg tmp(GetEncoder());
1711     auto utypes = GetRuntime()->GetUnresolvedTypes();
1712     ASSERT(utypes != nullptr);
1713     auto skind = UnresolvedTypesInterface::SlotKind::METHOD;
1714     auto methodAddr = utypes->GetTableSlot(method, resolver->GetCallMethodId(), skind);
1715     GetEncoder()->EncodeMov(tmp.GetReg(), Imm(methodAddr));
1716     GetEncoder()->EncodeLdr(tmp.GetReg(), false, MemRef(tmp.GetReg()));
1717     auto slowPath = CreateSlowPath<SlowPathUnresolved>(resolver, EntrypointId::GET_UNKNOWN_CALLEE_METHOD);
1718     slowPath->SetUnresolvedType(method, resolver->GetCallMethodId());
1719     slowPath->SetDstReg(tmp.GetReg());
1720     slowPath->SetSlotAddr(methodAddr);
1721     GetEncoder()->EncodeJump(slowPath->GetLabel(), tmp.GetReg(), Condition::EQ);
1722     slowPath->BindBackLabel(GetEncoder());
1723     GetEncoder()->EncodeMov(methodReg, tmp.GetReg());
1724 }
1725 
EmitCallResolvedStatic(CallInst * call)1726 void Codegen::EmitCallResolvedStatic(CallInst *call)
1727 {
1728     ASSERT(call->GetOpcode() == Opcode::CallResolvedStatic && call->GetCallMethod() == nullptr);
1729     Reg methodReg = ConvertRegister(call->GetSrcReg(0), DataType::POINTER);
1730     Reg param0 = GetTarget().GetParamReg(0);
1731     GetEncoder()->EncodeMov(param0, methodReg);
1732     GetEncoder()->MakeCall(MemRef(param0, GetRuntime()->GetCompiledEntryPointOffset(GetArch())));
1733     FinalizeCall(call);
1734 }
1735 
EmitCallStatic(CallInst * call)1736 void Codegen::EmitCallStatic(CallInst *call)
1737 {
1738     ASSERT(call->IsStaticCall() && !call->IsUnresolved());
1739     if (call->GetSaveState() != nullptr && call->IsInlined()) {
1740 #if defined(EVENT_METHOD_ENTER_ENABLED) && EVENT_METHOD_ENTER_ENABLED != 0
1741         if (!GetGraph()->IsAotMode()) {
1742             InsertTrace({Imm(static_cast<size_t>(TraceId::METHOD_ENTER)),
1743                          Imm(reinterpret_cast<size_t>(call->GetCallMethod())),
1744                          Imm(static_cast<size_t>(events::MethodEnterKind::INLINED))});
1745         }
1746 #endif
1747         return;
1748     }
1749     SCOPED_DISASM_STR(this, "Create a call to static");
1750     ASSERT(!HasLiveCallerSavedRegs(call));
1751     // Now MakeCallByOffset is not supported in Arch32Encoder (support ADR instruction)
1752     Reg param0 = GetTarget().GetParamReg(0);
1753     if (call->GetCallMethod() == GetGraph()->GetMethod() && GetArch() != Arch::AARCH32 && !GetGraph()->IsOsrMode() &&
1754         !GetGraph()->GetMethodProperties().GetHasDeopt()) {
1755         if (GetGraph()->IsAotMode()) {
1756             LoadMethod(param0);
1757         } else {
1758             GetEncoder()->EncodeMov(param0, Imm(reinterpret_cast<size_t>(GetGraph()->GetMethod())));
1759         }
1760         GetEncoder()->MakeCallByOffset(GetStartCodeOffset() - GetEncoder()->GetCursorOffset());
1761     } else {
1762         if (GetGraph()->IsAotMode()) {
1763             auto aotData = GetGraph()->GetAotData();
1764             intptr_t offset = aotData->GetPltSlotOffset(GetEncoder()->GetCursorOffset(), call->GetCallMethodId());
1765             // PLT CallStatic Resolver transparently uses param_0 (Method) register
1766             GetEncoder()->MakeLoadAotTable(offset, param0);
1767         } else {  // usual JIT case
1768             auto method = call->GetCallMethod();
1769             GetEncoder()->EncodeMov(param0, Imm(reinterpret_cast<size_t>(method)));
1770         }
1771         GetEncoder()->MakeCall(MemRef(param0, GetRuntime()->GetCompiledEntryPointOffset(GetArch())));
1772     }
1773     FinalizeCall(call);
1774 }
1775 
EmitCallNative(CallInst * callNative)1776 void Codegen::EmitCallNative(CallInst *callNative)
1777 {
1778     SCOPED_DISASM_STR(this, "CallNative");
1779     ASSERT(GetGraph()->SupportManagedCode());
1780     ASSERT(!HasLiveCallerSavedRegs(callNative));
1781 
1782     auto nativePointerReg = ConvertRegister(callNative->GetSrcReg(0), DataType::POINTER);
1783     GetEncoder()->MakeCall(nativePointerReg);
1784 
1785     if (callNative->GetType() != DataType::VOID) {
1786         auto arch = GetArch();
1787         auto returnType = callNative->GetType();
1788         auto dstReg = ConvertRegister(callNative->GetDstReg(), callNative->GetType());
1789         auto returnReg = GetTarget().GetReturnReg(dstReg.GetType());
1790         // We must:
1791         //  sign extend INT8 and INT16 to INT32
1792         //  zero extend UINT8 and UINT16 to UINT32
1793         if (DataType::ShiftByType(returnType, arch) < DataType::ShiftByType(DataType::INT32, arch)) {
1794             bool isSigned = DataType::IsTypeSigned(returnType);
1795             GetEncoder()->EncodeCast(Reg(dstReg.GetId(), INT32_TYPE), isSigned, returnReg, isSigned);
1796         } else {
1797             GetEncoder()->EncodeMov(dstReg, returnReg);
1798         }
1799     }
1800 }
1801 
EmitCallDynamic(CallInst * call)1802 void Codegen::EmitCallDynamic(CallInst *call)
1803 {
1804     SCOPED_DISASM_STR(this, "Create a dynamic call");
1805     if (call->GetSaveState() != nullptr && call->IsInlined()) {
1806 #if defined(EVENT_METHOD_ENTER_ENABLED) && EVENT_METHOD_ENTER_ENABLED != 0
1807         if (!GetGraph()->IsAotMode()) {
1808             InsertTrace(Imm(static_cast<size_t>(TraceId::METHOD_ENTER)),
1809                         Imm(reinterpret_cast<size_t>(call->GetCallMethod())),
1810                         Imm(static_cast<size_t>(events::MethodEnterKind::INLINED)));
1811         }
1812 #endif
1813         return;
1814     }
1815     RuntimeInterface *runtime = GetRuntime();
1816     Encoder *encoder = GetEncoder();
1817 
1818     auto dstReg = ConvertRegister(call->GetDstReg(), call->GetType());
1819     Reg methodParamReg = GetTarget().GetParamReg(CallConvDynInfo::REG_METHOD).As(GetPtrRegType());
1820     Reg numArgsParamReg = GetTarget().GetParamReg(CallConvDynInfo::REG_NUM_ARGS);
1821     auto paramFuncObjLoc = Location::MakeStackArgument(CallConvDynInfo::SLOT_CALLEE);
1822 
1823     ASSERT(!HasLiveCallerSavedRegs(call));
1824 
1825     // Load method from callee object
1826     static_assert(coretypes::TaggedValue::TAG_OBJECT == 0);
1827     encoder->EncodeLdr(methodParamReg, false, MemRef(SpReg(), GetStackOffset(paramFuncObjLoc)));
1828     encoder->EncodeLdr(methodParamReg, false, MemRef(methodParamReg, runtime->GetFunctionTargetOffset(GetArch())));
1829 
1830     ASSERT(call->GetInputsCount() > 1);
1831     auto numArgs = static_cast<uint32_t>(call->GetInputsCount() - 1);  // '-1' means 1 for spill fill input
1832     encoder->EncodeMov(numArgsParamReg, Imm(numArgs));
1833 
1834     size_t entryPointOffset = runtime->GetCompiledEntryPointOffset(GetArch());
1835     encoder->MakeCall(MemRef(methodParamReg, entryPointOffset));
1836 
1837     CreateStackMap(call);
1838     // Dynamic callee may have moved sp if there was insufficient num_actual_args
1839     encoder->EncodeSub(
1840         SpReg(), FpReg(),
1841         Imm(GetFrameLayout().GetOffset<CFrameLayout::OffsetOrigin::SP, CFrameLayout::OffsetUnit::BYTES>(0)));
1842 
1843     if (dstReg.IsValid()) {
1844         Reg retReg = GetTarget().GetReturnReg(dstReg.GetType());
1845         encoder->EncodeMov(dstReg, retReg);
1846     }
1847 }
1848 
FinalizeCall(CallInst * call)1849 void Codegen::FinalizeCall(CallInst *call)
1850 {
1851     ASSERT(!call->IsDynamicCall());
1852     CreateStackMap(call);
1853     auto returnType = call->GetType();
1854     auto dstReg = ConvertRegister(call->GetDstReg(), returnType);
1855     // Restore frame pointer in the TLS
1856     GetEncoder()->EncodeStr(FpReg(), MemRef(ThreadReg(), GetRuntime()->GetTlsFrameOffset(GetArch())));
1857     // Sign/Zero extend return_reg if necessary
1858     if (dstReg.IsValid()) {
1859         auto arch = GetArch();
1860         auto returnReg = GetTarget().GetReturnReg(dstReg.GetType());
1861         //  INT8  and INT16  must be sign extended to INT32
1862         //  UINT8 and UINT16 must be zero extended to UINT32
1863         if (DataType::ShiftByType(returnType, arch) < DataType::ShiftByType(DataType::INT32, arch)) {
1864             bool isSigned = DataType::IsTypeSigned(returnType);
1865             GetEncoder()->EncodeCast(dstReg, isSigned, Reg(returnReg.GetId(), INT32_TYPE), isSigned);
1866         } else {
1867             GetEncoder()->EncodeMov(dstReg, returnReg);
1868         }
1869     }
1870 }
1871 
1872 template <bool IS_CLASS>
CreatePreWRB(Inst * inst,MemRef mem,RegMask preserved,bool storePair)1873 void Codegen::CreatePreWRB(Inst *inst, MemRef mem, RegMask preserved, bool storePair)
1874 {
1875     auto runtime = GetRuntime();
1876     auto *enc = GetEncoder();
1877 
1878     SCOPED_DISASM_STR(this, "Pre WRB");
1879     ScopedTmpReg entrypointReg(enc, enc->IsLrAsTempRegEnabledAndReleased());
1880     GetEncoder()->EncodeLdr(entrypointReg, false,
1881                             MemRef(ThreadReg(), runtime->GetTlsPreWrbEntrypointOffset(GetArch())));
1882 
1883     // Check entrypoint address
1884     auto label = GetEncoder()->CreateLabel();
1885     enc->EncodeJump(label, entrypointReg, Condition::EQ);
1886     auto refType =
1887         inst->GetType() == DataType::REFERENCE ? DataType::GetIntTypeForReference(enc->GetArch()) : DataType::INT64;
1888     ScopedTmpReg tmpRef(enc, ConvertDataType(refType, GetArch()));
1889     auto prevOffset = enc->GetCursorOffset();
1890     // Load old value
1891     if (IsVolatileMemInst(inst)) {
1892         enc->EncodeLdrAcquire(tmpRef, false, mem);
1893     } else {
1894         enc->EncodeLdr(tmpRef, false, mem);
1895     }
1896     TryInsertImplicitNullCheck(inst, prevOffset);
1897     if constexpr (IS_CLASS) {
1898         enc->EncodeLdr(tmpRef, false, MemRef(tmpRef, runtime->GetManagedClassOffset(GetArch())));
1899     } else {
1900         CheckObject(tmpRef, label);
1901     }
1902     auto [live_regs, live_vregs] = GetLiveRegisters<true>(inst);
1903     live_regs |= preserved;
1904     CallBarrier(live_regs, live_vregs, entrypointReg.GetReg(), tmpRef);
1905 
1906     if (storePair) {
1907         // store pair doesn't support index and scalar
1908         ASSERT(!mem.HasIndex() && !mem.HasScale());
1909         // calculate offset for second store
1910         // NOLINTNEXTLINE(bugprone-misplaced-widening-cast)
1911         auto secondOffset = static_cast<ssize_t>(1U << DataType::ShiftByType(DataType::REFERENCE, enc->GetArch()));
1912         if (mem.HasDisp()) {
1913             secondOffset += mem.GetDisp();
1914         }
1915         // Load old value
1916         if (IsVolatileMemInst(inst)) {
1917             enc->EncodeLdrAcquire(tmpRef, false, MemRef(mem.GetBase(), secondOffset));
1918         } else {
1919             enc->EncodeLdr(tmpRef, false, MemRef(mem.GetBase(), secondOffset));
1920         }
1921         CheckObject(tmpRef, label);
1922         CallBarrier(live_regs, live_vregs, entrypointReg.GetReg(), tmpRef);
1923     }
1924     enc->BindLabel(label);
1925 }
1926 
1927 // Force template instantiation to avoid "undefined reference" error from ld.
1928 template void Codegen::CreatePreWRB<true>(Inst *inst, MemRef mem, RegMask preserved, bool storePair);
1929 template void Codegen::CreatePreWRB<false>(Inst *inst, MemRef mem, RegMask preserved, bool storePair);
1930 
1931 /* RegMask 'preserved' is a way to explicitly specify registers that must be
1932  * saved before and restored after the PostWRB call.
1933  *
1934  * If codegen emits some new instructions after the PostWRB call and these
1935  * instructions use 'inst' operand registers, then the registers should live
1936  * through the PostWRB call. The problem is that the liveness analysis can't
1937  * detect if those registers are live because there are no instructions using
1938  * them at the moment.
1939  *
1940  * Note, only CALLER_REG_MASK registers are taken into account.
1941  */
CreatePostWRB(Inst * inst,MemRef mem,Reg reg1,Reg reg2,RegMask preserved)1942 void Codegen::CreatePostWRB(Inst *inst, MemRef mem, Reg reg1, Reg reg2, RegMask preserved)
1943 {
1944     ASSERT(reg1 != INVALID_REGISTER);
1945 
1946     if (!GetGraph()->SupportsIrtocBarriers() || !GetGraph()->IsOfflineCompilationMode()) {
1947         auto barrierType = GetRuntime()->GetPostType();
1948         if (barrierType == ark::mem::BarrierType::POST_WRB_NONE) {
1949             return;
1950         }
1951         ASSERT(barrierType == ark::mem::BarrierType::POST_INTERGENERATIONAL_BARRIER ||
1952                barrierType == ark::mem::BarrierType::POST_INTERREGION_BARRIER);
1953     }
1954 
1955     // For dynamic methods, another check
1956     if (GetGraph()->IsDynamicMethod()) {
1957         CreatePostWRBForDynamic(inst, mem, reg1, reg2, preserved);
1958         return;
1959     }
1960     PostWriteBarrier pwb(this, inst);
1961     Inst *secondValue;
1962     Inst *val = InstStoredValue(inst, &secondValue);
1963     ASSERT(val != nullptr);
1964     ASSERT(secondValue == nullptr || reg2 != INVALID_REGISTER);
1965     if (val->GetOpcode() == Opcode::NullPtr) {
1966         // We can don't encode Post barrier for nullptr
1967         if (secondValue == nullptr || secondValue->GetOpcode() == Opcode::NullPtr) {
1968             return;
1969         }
1970         // CallPostWRB only for second reg
1971         auto secondMemOffset = static_cast<ssize_t>(reg1.GetSize() / BITS_PER_BYTE);
1972         if (!mem.HasIndex()) {
1973             MemRef secondMem(mem.GetBase(), mem.GetDisp() + secondMemOffset);
1974             pwb.Encode(secondMem, reg2, INVALID_REGISTER, !IsInstNotNull(secondValue), preserved);
1975             return;
1976         }
1977         ASSERT(mem.GetScale() == 0 && !mem.HasDisp());
1978         ASSERT(GetTarget().GetTempRegsMask().Test(mem.GetIndex().GetId()));
1979         GetEncoder()->EncodeAdd(mem.GetIndex(), mem.GetIndex(), Imm(secondMemOffset));
1980         pwb.Encode(mem, reg2, INVALID_REGISTER, !IsInstNotNull(secondValue), preserved);
1981         return;
1982     }
1983     // Create PostWRB only for first value
1984     if (secondValue != nullptr && secondValue->GetOpcode() == Opcode::NullPtr) {
1985         reg2 = INVALID_REGISTER;
1986     }
1987     bool checkObject = true;
1988     if (reg2 == INVALID_REGISTER) {
1989         if (IsInstNotNull(val)) {
1990             checkObject = false;
1991         }
1992     } else {
1993         if (IsInstNotNull(val) && IsInstNotNull(secondValue)) {
1994             checkObject = false;
1995         }
1996     }
1997     pwb.Encode(mem, reg1, reg2, checkObject, preserved);
1998 }
1999 
CreatePostWRBForDynamic(Inst * inst,MemRef mem,Reg reg1,Reg reg2,RegMask preserved)2000 void Codegen::CreatePostWRBForDynamic(Inst *inst, MemRef mem, Reg reg1, Reg reg2, RegMask preserved)
2001 {
2002     PostWriteBarrier pwb(this, inst);
2003     if (reg2 == INVALID_REGISTER) {
2004         int storeIndex;
2005         if (inst->GetOpcode() == Opcode::StoreObject || inst->GetOpcode() == Opcode::StoreI ||
2006             inst->GetOpcode() == Opcode::StoreArrayI) {
2007             storeIndex = 1_I;
2008         } else {
2009             ASSERT(inst->GetOpcode() == Opcode::StoreArray || inst->GetOpcode() == Opcode::Store);
2010             storeIndex = 2_I;
2011         }
2012         if (StoreValueCanBeObject(inst->GetDataFlowInput(storeIndex))) {
2013             pwb.Encode(mem, reg1, reg2, true, preserved);
2014         }
2015     } else {
2016         int storeIndex;
2017         if (inst->GetOpcode() == Opcode::StoreArrayPairI) {
2018             storeIndex = 1_I;
2019         } else {
2020             ASSERT(inst->GetOpcode() == Opcode::StoreArrayPair);
2021             storeIndex = 2_I;
2022         }
2023         bool firstIsObject = StoreValueCanBeObject(inst->GetDataFlowInput(storeIndex));
2024         bool secondIsObject = StoreValueCanBeObject(inst->GetDataFlowInput(storeIndex + 1));
2025         if (firstIsObject || secondIsObject) {
2026             if (firstIsObject && !secondIsObject) {
2027                 reg2 = INVALID_REGISTER;
2028             } else if (!firstIsObject && secondIsObject) {
2029                 reg1 = reg2;
2030                 reg2 = INVALID_REGISTER;
2031             }
2032             pwb.Encode(mem, reg1, reg2, true, preserved);
2033         }
2034     }
2035 }
2036 
CheckObject(Reg reg,LabelHolder::LabelId label)2037 void Codegen::CheckObject(Reg reg, LabelHolder::LabelId label)
2038 {
2039     auto graph = GetGraph();
2040     auto *enc = GetEncoder();
2041 
2042     // interpreter use x20 in IrToc and we don't have enough temporary registers
2043     // remove after PR 98 or PR 47
2044     if (graph->IsDynamicMethod()) {
2045         ASSERT(reg.IsScalar());
2046         reg = reg.As(INT64_TYPE);
2047         auto tagMask = graph->GetRuntime()->GetTaggedTagMask();
2048         // Check that the value is object(not int and not double)
2049         enc->EncodeJumpTest(label, reg, Imm(tagMask), Condition::TST_NE);
2050         auto specialMask = graph->GetRuntime()->GetTaggedSpecialMask();
2051         // Check that the value is not special value
2052         enc->EncodeJumpTest(label, reg, Imm(~specialMask), Condition::TST_EQ);
2053     } else {
2054         enc->EncodeJump(label, reg, Condition::EQ);
2055     }
2056 }
2057 
HasLiveCallerSavedRegs(Inst * inst)2058 bool Codegen::HasLiveCallerSavedRegs(Inst *inst)
2059 {
2060     auto [live_regs, live_fp_regs] = GetLiveRegisters<false>(inst);
2061     live_regs &= GetCallerRegsMask(GetArch(), false);
2062     live_fp_regs &= GetCallerRegsMask(GetArch(), true);
2063     return live_regs.Any() || live_fp_regs.Any();
2064 }
2065 
SaveCallerRegisters(RegMask liveRegs,VRegMask liveVregs,bool adjustRegs)2066 void Codegen::SaveCallerRegisters(RegMask liveRegs, VRegMask liveVregs, bool adjustRegs)
2067 {
2068     SCOPED_DISASM_STR(this, "Save caller saved regs");
2069     auto base = GetFrameInfo()->GetCallersRelativeFp() ? GetTarget().GetFrameReg() : GetTarget().GetStackReg();
2070     liveRegs &= ~GetEncoder()->GetAvailableScratchRegisters();
2071     liveVregs &= ~GetEncoder()->GetAvailableScratchFpRegisters();
2072     if (adjustRegs) {
2073         liveRegs &= GetRegfile()->GetCallerSavedRegMask();
2074         liveVregs &= GetRegfile()->GetCallerSavedVRegMask();
2075     } else {
2076         liveRegs &= GetCallerRegsMask(GetArch(), false);
2077         liveVregs &= GetCallerRegsMask(GetArch(), true);
2078     }
2079     if (GetFrameInfo()->GetPushCallers()) {
2080         GetEncoder()->PushRegisters(liveRegs, liveVregs);
2081     } else {
2082         GetEncoder()->SaveRegisters(liveRegs, false, GetFrameInfo()->GetCallersOffset(), base,
2083                                     GetCallerRegsMask(GetArch(), false));
2084         GetEncoder()->SaveRegisters(liveVregs, true, GetFrameInfo()->GetFpCallersOffset(), base,
2085                                     GetFrameInfo()->GetPositionedCallers() ? GetCallerRegsMask(GetArch(), true)
2086                                                                            : RegMask());
2087     }
2088 }
2089 
LoadCallerRegisters(RegMask liveRegs,VRegMask liveVregs,bool adjustRegs)2090 void Codegen::LoadCallerRegisters(RegMask liveRegs, VRegMask liveVregs, bool adjustRegs)
2091 {
2092     SCOPED_DISASM_STR(this, "Restore caller saved regs");
2093     auto base = GetFrameInfo()->GetCallersRelativeFp() ? GetTarget().GetFrameReg() : GetTarget().GetStackReg();
2094     liveRegs &= ~GetEncoder()->GetAvailableScratchRegisters();
2095     liveVregs &= ~GetEncoder()->GetAvailableScratchFpRegisters();
2096     if (adjustRegs) {
2097         liveRegs &= GetRegfile()->GetCallerSavedRegMask();
2098         liveVregs &= GetRegfile()->GetCallerSavedVRegMask();
2099     } else {
2100         liveRegs &= GetCallerRegsMask(GetArch(), false);
2101         liveVregs &= GetCallerRegsMask(GetArch(), true);
2102     }
2103     if (GetFrameInfo()->GetPushCallers()) {
2104         GetEncoder()->PopRegisters(liveRegs, liveVregs);
2105     } else {
2106         GetEncoder()->LoadRegisters(liveRegs, false, GetFrameInfo()->GetCallersOffset(), base,
2107                                     GetCallerRegsMask(GetArch(), false));
2108         GetEncoder()->LoadRegisters(liveVregs, true, GetFrameInfo()->GetFpCallersOffset(), base,
2109                                     GetCallerRegsMask(GetArch(), true));
2110     }
2111 }
2112 
RegisterKeepCallArgument(CallInst * callInst,Reg reg)2113 bool Codegen::RegisterKeepCallArgument(CallInst *callInst, Reg reg)
2114 {
2115     for (auto i = 0U; i < callInst->GetInputsCount(); i++) {
2116         auto location = callInst->GetLocation(i);
2117         if (location.IsRegister() && location.GetValue() == reg.GetId()) {
2118             return true;
2119         }
2120     }
2121     return false;
2122 }
2123 
LoadMethod(Reg dst)2124 void Codegen::LoadMethod(Reg dst)
2125 {
2126     ASSERT((CFrameMethod::GetOffsetFromSpInBytes(GetFrameLayout()) -
2127             (GetFrameLayout().GetMethodOffset<CFrameLayout::OffsetOrigin::SP, CFrameLayout::OffsetUnit::BYTES>())) ==
2128            0);
2129     auto spOffset = CFrameMethod::GetOffsetFromSpInBytes(GetFrameLayout());
2130     auto mem = MemRef(SpReg(), spOffset);
2131     GetEncoder()->EncodeLdr(dst, false, mem);
2132 }
2133 
StoreFreeSlot(Reg src)2134 void Codegen::StoreFreeSlot(Reg src)
2135 {
2136     ASSERT(src.GetSize() <= (GetFrameLayout().GetSlotSize() << 3U));
2137     auto spOffset =
2138         GetFrameLayout().GetFreeSlotOffset<CFrameLayout::OffsetOrigin::SP, CFrameLayout::OffsetUnit::BYTES>();
2139     auto mem = MemRef(SpReg(), spOffset);
2140     GetEncoder()->EncodeStr(src, mem);
2141 }
2142 
LoadFreeSlot(Reg dst)2143 void Codegen::LoadFreeSlot(Reg dst)
2144 {
2145     ASSERT(dst.GetSize() <= (GetFrameLayout().GetSlotSize() << 3U));
2146     auto spOffset =
2147         GetFrameLayout().GetFreeSlotOffset<CFrameLayout::OffsetOrigin::SP, CFrameLayout::OffsetUnit::BYTES>();
2148     auto mem = MemRef(SpReg(), spOffset);
2149     GetEncoder()->EncodeLdr(dst, false, mem);
2150 }
2151 
CreateReturn(const Inst * inst)2152 void Codegen::CreateReturn(const Inst *inst)
2153 {
2154     if (GetGraph()->GetMethodProperties().GetLastReturn() == inst) {
2155         GetEncoder()->BindLabel(GetLabelExit());
2156         GenerateEpilogue();
2157     } else {
2158         GetEncoder()->EncodeJump(GetLabelExit());
2159     }
2160 }
2161 
EncodeDynamicCast(Inst * inst,Reg dst,bool dstSigned,Reg src)2162 void Codegen::EncodeDynamicCast(Inst *inst, Reg dst, bool dstSigned, Reg src)
2163 {
2164     CHECK_EQ(src.GetSize(), BITS_PER_UINT64);
2165     CHECK_GE(dst.GetSize(), BITS_PER_UINT32);
2166 
2167     bool isDst64 {dst.GetSize() == BITS_PER_UINT64};
2168     dst = dst.As(INT32_TYPE);
2169 
2170     auto enc {GetEncoder()};
2171     if (g_options.IsCpuFeatureEnabled(CpuFeature::JSCVT)) {
2172         // no slow path intended
2173         enc->EncodeFastPathDynamicCast(dst, src, LabelHolder::INVALID_LABEL);
2174     } else {
2175         auto slowPath {CreateSlowPath<SlowPathJsCastDoubleToInt32>(inst)};
2176         slowPath->SetDstReg(dst);
2177         slowPath->SetSrcReg(src);
2178 
2179         enc->EncodeFastPathDynamicCast(dst, src, slowPath->GetLabel());
2180         slowPath->BindBackLabel(enc);
2181     }
2182 
2183     if (isDst64) {
2184         enc->EncodeCast(dst.As(INT64_TYPE), dstSigned, dst, dstSigned);
2185     }
2186 }
2187 
2188 // Next visitors use calling convention
VisitCallIndirect(CallIndirectInst * inst)2189 void Codegen::VisitCallIndirect(CallIndirectInst *inst)
2190 {
2191     auto location = inst->GetLocation(0);
2192     ASSERT(location.IsFixedRegister() && location.IsRegisterValid());
2193     auto src = Reg(location.GetValue(), GetTarget().GetPtrRegType());
2194     auto dst = ConvertRegister(inst->GetDstReg(), inst->GetType());
2195 
2196     GetEncoder()->MakeCall(src);
2197     if (inst->HasUsers()) {
2198         GetEncoder()->EncodeMov(dst, GetTarget().GetReturnReg(dst.GetType()));
2199     }
2200 }
2201 
VisitCall(CallInst * inst)2202 void Codegen::VisitCall(CallInst *inst)
2203 {
2204     ASSERT(GetGraph()->GetRelocationHandler() != nullptr);
2205 
2206     auto mode = GetGraph()->GetMode();
2207     ASSERT(mode.IsFastPath() || mode.IsInterpreter() || mode.IsNative());
2208     ASSERT(mode.IsFastPath() || !HasLiveCallerSavedRegs(inst));
2209 
2210     RegMask callerRegs;
2211     RegMask callerFpRegs;
2212     auto dstReg = ConvertRegister(inst->GetDstReg(), inst->GetType());
2213 
2214     if (mode.IsFastPath()) {
2215         // irtoc fastpath needs to save all caller registers in case of call native function
2216         callerRegs = GetCallerRegsMask(GetArch(), false);
2217         callerFpRegs = GetCallerRegsMask(GetArch(), true);
2218         GetEncoder()->SetRegister(&callerRegs, &callerFpRegs, dstReg, false);
2219         SaveCallerRegisters(callerRegs, callerFpRegs, false);
2220     }
2221 
2222     RelocationInfo relocation;
2223     relocation.data = inst->GetCallMethodId();
2224     GetEncoder()->MakeCall(&relocation);
2225     GetGraph()->GetRelocationHandler()->AddRelocation(relocation);
2226 
2227     if (inst->HasUsers()) {
2228         ASSERT(dstReg.IsValid());
2229         GetEncoder()->EncodeMov(dstReg, GetTarget().GetReturnReg(dstReg.GetType()));
2230     }
2231 
2232     if (mode.IsFastPath()) {
2233         LoadCallerRegisters(callerRegs, callerFpRegs, false);
2234     }
2235 }
2236 
GetEntryPointId(uint64_t elementSize,RuntimeInterface::EntrypointId & eid)2237 static void GetEntryPointId(uint64_t elementSize, RuntimeInterface::EntrypointId &eid)
2238 {
2239     switch (elementSize) {
2240         case sizeof(uint8_t):
2241             eid = RuntimeInterface::EntrypointId::ALLOCATE_ARRAY_TLAB8;
2242             break;
2243         case sizeof(uint16_t):
2244             eid = RuntimeInterface::EntrypointId::ALLOCATE_ARRAY_TLAB16;
2245             break;
2246         case sizeof(uint32_t):
2247             eid = RuntimeInterface::EntrypointId::ALLOCATE_ARRAY_TLAB32;
2248             break;
2249         case sizeof(uint64_t):
2250             eid = RuntimeInterface::EntrypointId::ALLOCATE_ARRAY_TLAB64;
2251             break;
2252         default:
2253             UNREACHABLE();
2254     }
2255 }
2256 
VisitNewArray(Inst * inst)2257 void Codegen::VisitNewArray(Inst *inst)
2258 {
2259     auto method = inst->CastToNewArray()->GetMethod();
2260     auto dst = ConvertRegister(inst->GetDstReg(), inst->GetType());
2261     auto srcClass = ConvertRegister(inst->GetSrcReg(NewArrayInst::INDEX_CLASS), DataType::POINTER);
2262     auto srcSize = ConvertRegister(inst->GetSrcReg(NewArrayInst::INDEX_SIZE), DataType::Type::INT32);
2263     auto arrayType = inst->CastToNewArray()->GetTypeId();
2264     auto runtime = GetGraph()->GetRuntime();
2265     auto encoder = GetEncoder();
2266 
2267     auto maxTlabSize = runtime->GetTLABMaxSize();
2268     // NOTE(msherstennikov): support NewArray fast path for arm32
2269     if (maxTlabSize == 0 || GetArch() == Arch::AARCH32) {
2270         CallRuntime(inst, EntrypointId::CREATE_ARRAY, dst, RegMask::GetZeroMask(), srcClass, srcSize);
2271         if (inst->GetFlag(inst_flags::MEM_BARRIER)) {
2272             encoder->EncodeMemoryBarrier(memory_order::RELEASE);
2273         }
2274         return;
2275     }
2276 
2277     auto lenInst = inst->GetDataFlowInput(0);
2278     auto classArraySize = runtime->GetClassArraySize(GetArch());
2279     uint64_t arraySize = 0;
2280     uint64_t elementSize = runtime->GetArrayElementSize(method, arrayType);
2281     uint64_t alignment = runtime->GetTLABAlignment();
2282     if (lenInst->GetOpcode() == Opcode::Constant) {
2283         ASSERT(lenInst->GetType() == DataType::INT64);
2284         arraySize = lenInst->CastToConstant()->GetIntValue() * elementSize + classArraySize;
2285         arraySize = (arraySize & ~(alignment - 1U)) + ((arraySize % alignment) != 0U ? alignment : 0U);
2286         if (arraySize > maxTlabSize) {
2287             CallRuntime(inst, EntrypointId::CREATE_ARRAY, dst, RegMask::GetZeroMask(), srcClass, srcSize);
2288             if (inst->GetFlag(inst_flags::MEM_BARRIER)) {
2289                 encoder->EncodeMemoryBarrier(memory_order::RELEASE);
2290             }
2291             return;
2292         }
2293     }
2294 
2295     EntrypointId eid;
2296     GetEntryPointId(elementSize, eid);
2297     CallFastPath(inst, eid, dst, RegMask::GetZeroMask(), srcClass, srcSize);
2298     if (inst->GetFlag(inst_flags::MEM_BARRIER)) {
2299         encoder->EncodeMemoryBarrier(memory_order::RELEASE);
2300     }
2301 }
2302 
CreateMonitorCall(MonitorInst * inst)2303 void Codegen::CreateMonitorCall(MonitorInst *inst)
2304 {
2305     auto src = ConvertRegister(inst->GetSrcReg(0), DataType::REFERENCE);  // obj
2306     auto id = inst->IsExit() ? EntrypointId::MONITOR_EXIT_FAST_PATH : EntrypointId::MONITOR_ENTER_FAST_PATH;
2307     CallFastPath(inst, id, INVALID_REGISTER, RegMask::GetZeroMask(), src);
2308 }
2309 
CreateMonitorCallOld(MonitorInst * inst)2310 void Codegen::CreateMonitorCallOld(MonitorInst *inst)
2311 {
2312     auto src = ConvertRegister(inst->GetSrcReg(0), DataType::REFERENCE);  // obj
2313     auto dst = ConvertRegister(inst->GetDstReg(), inst->GetType());
2314     auto id = inst->IsExit() ? EntrypointId::UNLOCK_OBJECT : EntrypointId::LOCK_OBJECT;
2315     CallRuntime(inst, id, dst, RegMask::GetZeroMask(), src);
2316 }
2317 
CreateCheckCastInterfaceCall(Inst * inst)2318 void Codegen::CreateCheckCastInterfaceCall(Inst *inst)
2319 {
2320     auto obj = ConvertRegister(inst->GetSrcReg(0), DataType::REFERENCE);
2321     auto interface = ConvertRegister(inst->GetSrcReg(SECOND_OPERAND), DataType::REFERENCE);
2322     CallFastPath(inst, EntrypointId::CHECK_CAST_INTERFACE, INVALID_REGISTER, RegMask::GetZeroMask(), obj, interface);
2323 }
2324 
TryInsertImplicitNullCheck(Inst * inst,size_t prevOffset)2325 void Codegen::TryInsertImplicitNullCheck(Inst *inst, size_t prevOffset)
2326 {
2327     if (!IsSuitableForImplicitNullCheck(inst)) {
2328         return;
2329     }
2330     if (!inst->CanThrow()) {
2331         return;
2332     }
2333 
2334     auto nullcheck = inst->GetInput(0).GetInst();
2335     ASSERT(nullcheck->GetOpcode() == Opcode::NullCheck && nullcheck->CastToNullCheck()->IsImplicit());
2336     auto currOffset = GetEncoder()->GetCursorOffset();
2337     ASSERT(currOffset > prevOffset);
2338     GetCodeBuilder()->AddImplicitNullCheck(currOffset, currOffset - prevOffset);
2339     CreateStackMap(nullcheck, inst);
2340 }
2341 
CreateMemmoveUnchecked(IntrinsicInst * inst,Reg dst,SRCREGS src)2342 void Codegen::CreateMemmoveUnchecked([[maybe_unused]] IntrinsicInst *inst, [[maybe_unused]] Reg dst, SRCREGS src)
2343 {
2344     auto entrypointId = EntrypointId::INVALID;
2345     switch (inst->GetIntrinsicId()) {
2346         case RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_MEMMOVE_UNCHECKED_1_BYTE:
2347             entrypointId = EntrypointId::ARRAY_COPY_TO_UNCHECKED_1_BYTE;
2348             break;
2349 
2350         case RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_MEMMOVE_UNCHECKED_2_BYTE:
2351             entrypointId = EntrypointId::ARRAY_COPY_TO_UNCHECKED_2_BYTE;
2352             break;
2353 
2354         case RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_MEMMOVE_UNCHECKED_4_BYTE:
2355             entrypointId = EntrypointId::ARRAY_COPY_TO_UNCHECKED_4_BYTE;
2356             break;
2357 
2358         case RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_MEMMOVE_UNCHECKED_8_BYTE:
2359             entrypointId = EntrypointId::ARRAY_COPY_TO_UNCHECKED_8_BYTE;
2360             break;
2361 
2362         default:
2363             UNREACHABLE();
2364             break;
2365     }
2366     ASSERT(entrypointId != EntrypointId::INVALID);
2367     auto srcObj = src[FIRST_OPERAND];
2368     auto dstObj = src[SECOND_OPERAND];
2369     auto dstStart = src[THIRD_OPERAND];
2370     auto srcStart = src[FOURTH_OPERAND];
2371     auto srcEnd = src[FIFTH_OPERAND];
2372     CallFastPath(inst, entrypointId, INVALID_REGISTER, RegMask::GetZeroMask(), srcObj, dstObj, dstStart, srcStart,
2373                  srcEnd);
2374 }
2375 
CreateFloatIsInf(IntrinsicInst * inst,Reg dst,SRCREGS src)2376 void Codegen::CreateFloatIsInf([[maybe_unused]] IntrinsicInst *inst, Reg dst, SRCREGS src)
2377 {
2378     GetEncoder()->EncodeIsInf(dst, src[0]);
2379 }
2380 
CreateStringEquals(IntrinsicInst * inst,Reg dst,SRCREGS src)2381 void Codegen::CreateStringEquals([[maybe_unused]] IntrinsicInst *inst, Reg dst, SRCREGS src)
2382 {
2383     auto entrypointId = GetRuntime()->IsCompressedStringsEnabled() ? EntrypointId::STRING_EQUALS_COMPRESSED
2384                                                                    : EntrypointId::STRING_EQUALS;
2385     CallFastPath(inst, entrypointId, dst, {}, src[0], src[1U]);
2386 }
2387 
CreateMathCeil(IntrinsicInst * inst,Reg dst,SRCREGS src)2388 void Codegen::CreateMathCeil([[maybe_unused]] IntrinsicInst *inst, Reg dst, SRCREGS src)
2389 {
2390     GetEncoder()->EncodeCeil(dst, src[0]);
2391 }
2392 
CreateMathFloor(IntrinsicInst * inst,Reg dst,SRCREGS src)2393 void Codegen::CreateMathFloor([[maybe_unused]] IntrinsicInst *inst, Reg dst, SRCREGS src)
2394 {
2395     GetEncoder()->EncodeFloor(dst, src[0]);
2396 }
2397 
CreateCountLeadingZeroBits(IntrinsicInst * inst,Reg dst,SRCREGS src)2398 void Codegen::CreateCountLeadingZeroBits([[maybe_unused]] IntrinsicInst *inst, Reg dst, SRCREGS src)
2399 {
2400     GetEncoder()->EncodeCountLeadingZeroBits(Reg(dst.GetId(), src[0].GetType()), src[0]);
2401 }
2402 
CreateStringBuilderChar(IntrinsicInst * inst,Reg dst,SRCREGS src)2403 void Codegen::CreateStringBuilderChar(IntrinsicInst *inst, [[maybe_unused]] Reg dst, SRCREGS src)
2404 {
2405     CallFastPath(inst, EntrypointId::STRING_BUILDER_CHAR, dst, RegMask::GetZeroMask(), src[0], src[1U]);
2406 }
2407 
CreateStringBuilderBool(IntrinsicInst * inst,Reg dst,SRCREGS src)2408 void Codegen::CreateStringBuilderBool(IntrinsicInst *inst, [[maybe_unused]] Reg dst, SRCREGS src)
2409 {
2410     CallFastPath(inst, EntrypointId::STRING_BUILDER_BOOL, dst, RegMask::GetZeroMask(), src[0], src[1U]);
2411 }
2412 
CreateStringBuilderString(IntrinsicInst * inst,Reg dst,SRCREGS src)2413 void Codegen::CreateStringBuilderString(IntrinsicInst *inst, [[maybe_unused]] Reg dst, SRCREGS src)
2414 {
2415     auto entrypointId = GetRuntime()->IsCompressedStringsEnabled() ? EntrypointId::STRING_BUILDER_STRING_COMPRESSED
2416                                                                    : EntrypointId::STRING_BUILDER_STRING;
2417 
2418     CallFastPath(inst, entrypointId, dst, RegMask::GetZeroMask(), src[0], src[1U]);
2419 }
2420 
CreateStringFromCharArrayTlab(IntrinsicInst * inst,Reg dst,SRCREGS src)2421 void Codegen::CreateStringFromCharArrayTlab(IntrinsicInst *inst, Reg dst, SRCREGS src)
2422 {
2423     CreateStringFromCharArrayTlab(static_cast<Inst *>(inst), dst, src);
2424 }
2425 
CreateStringFromCharArrayTlab(Inst * inst,Reg dst,SRCREGS src)2426 void Codegen::CreateStringFromCharArrayTlab(Inst *inst, Reg dst, SRCREGS src)
2427 {
2428     auto runtime = GetGraph()->GetRuntime();
2429     auto offset = src[FIRST_OPERAND];
2430     auto count = src[SECOND_OPERAND];
2431     auto array = src[THIRD_OPERAND];
2432 
2433     auto entryId = GetRuntime()->IsCompressedStringsEnabled()
2434                        ? EntrypointId::CREATE_STRING_FROM_CHAR_ARRAY_TLAB_COMPRESSED
2435                        : EntrypointId::CREATE_STRING_FROM_CHAR_ARRAY_TLAB;
2436 
2437     if (GetRegfile()->GetZeroReg().GetId() == offset.GetId()) {
2438         entryId = GetRuntime()->IsCompressedStringsEnabled()
2439                       ? EntrypointId::CREATE_STRING_FROM_ZERO_BASED_CHAR_ARRAY_TLAB_COMPRESSED
2440                       : EntrypointId::CREATE_STRING_FROM_ZERO_BASED_CHAR_ARRAY_TLAB;
2441     }
2442 
2443     if (GetGraph()->IsAotMode()) {
2444         ScopedTmpReg klassReg(GetEncoder());
2445         GetEncoder()->EncodeLdr(klassReg, false,
2446                                 MemRef(ThreadReg(), runtime->GetStringClassPointerTlsOffset(GetArch())));
2447         if (GetRegfile()->GetZeroReg().GetId() == offset.GetId()) {
2448             CallFastPath(inst, entryId, dst, RegMask::GetZeroMask(), count, array, klassReg);
2449         } else {
2450             CallFastPath(inst, entryId, dst, RegMask::GetZeroMask(), offset, count, array, klassReg);
2451         }
2452     } else {
2453         auto klassImm =
2454             TypedImm(reinterpret_cast<uintptr_t>(runtime->GetStringClass(GetGraph()->GetMethod(), nullptr)));
2455         if (GetRegfile()->GetZeroReg().GetId() == offset.GetId()) {
2456             CallFastPath(inst, entryId, dst, RegMask::GetZeroMask(), count, array, klassImm);
2457         } else {
2458             CallFastPath(inst, entryId, dst, RegMask::GetZeroMask(), offset, count, array, klassImm);
2459         }
2460     }
2461 }
2462 
CreateStringFromStringTlab(IntrinsicInst * inst,Reg dst,SRCREGS src)2463 void Codegen::CreateStringFromStringTlab(IntrinsicInst *inst, Reg dst, SRCREGS src)
2464 {
2465     auto entryId = GetRuntime()->IsCompressedStringsEnabled() ? EntrypointId::CREATE_STRING_FROM_STRING_TLAB_COMPRESSED
2466                                                               : EntrypointId::CREATE_STRING_FROM_STRING_TLAB;
2467     auto srcStr = src[FIRST_OPERAND];
2468     CallFastPath(inst, entryId, dst, RegMask::GetZeroMask(), srcStr);
2469 }
2470 
CreateStringSubstringTlab(IntrinsicInst * inst,Reg dst,SRCREGS src)2471 void Codegen::CreateStringSubstringTlab([[maybe_unused]] IntrinsicInst *inst, Reg dst, SRCREGS src)
2472 {
2473     auto entrypointId = GetRuntime()->IsCompressedStringsEnabled()
2474                             ? EntrypointId::SUB_STRING_FROM_STRING_TLAB_COMPRESSED
2475                             : EntrypointId::SUB_STRING_FROM_STRING_TLAB;
2476     CallFastPath(inst, entrypointId, dst, {}, src[FIRST_OPERAND], src[SECOND_OPERAND], src[THIRD_OPERAND]);
2477 }
2478 
CreateStringGetCharsTlab(IntrinsicInst * inst,Reg dst,SRCREGS src)2479 void Codegen::CreateStringGetCharsTlab([[maybe_unused]] IntrinsicInst *inst, Reg dst, SRCREGS src)
2480 {
2481     auto entrypointId = GetRuntime()->IsCompressedStringsEnabled() ? EntrypointId::STRING_GET_CHARS_TLAB_COMPRESSED
2482                                                                    : EntrypointId::STRING_GET_CHARS_TLAB;
2483     auto runtime = GetGraph()->GetRuntime();
2484     if (GetGraph()->IsAotMode()) {
2485         ScopedTmpReg klassReg(GetEncoder());
2486         GetEncoder()->EncodeLdr(klassReg, false,
2487                                 MemRef(ThreadReg(), runtime->GetArrayU16ClassPointerTlsOffset(GetArch())));
2488         CallFastPath(inst, entrypointId, dst, {}, src[FIRST_OPERAND], src[SECOND_OPERAND], src[THIRD_OPERAND],
2489                      klassReg);
2490     } else {
2491         auto klassImm = TypedImm(reinterpret_cast<uintptr_t>(runtime->GetArrayU16Class(GetGraph()->GetMethod())));
2492         CallFastPath(inst, entrypointId, dst, {}, src[FIRST_OPERAND], src[SECOND_OPERAND], src[THIRD_OPERAND],
2493                      klassImm);
2494     }
2495 }
2496 
CreateStringHashCode(IntrinsicInst * inst,Reg dst,SRCREGS src)2497 void Codegen::CreateStringHashCode([[maybe_unused]] IntrinsicInst *inst, Reg dst, SRCREGS src)
2498 {
2499     auto entrypoint = GetRuntime()->IsCompressedStringsEnabled() ? EntrypointId::STRING_HASH_CODE_COMPRESSED
2500                                                                  : EntrypointId::STRING_HASH_CODE;
2501     auto strReg = src[FIRST_OPERAND];
2502     auto mref = MemRef(strReg, ark::coretypes::String::GetHashcodeOffset());
2503     auto slowPath = CreateSlowPath<SlowPathStringHashCode>(inst, entrypoint);
2504     slowPath->SetDstReg(dst);
2505     slowPath->SetSrcReg(strReg);
2506     if (dst.GetId() != strReg.GetId()) {
2507         GetEncoder()->EncodeLdr(ConvertRegister(dst.GetId(), DataType::INT32), false, mref);
2508         GetEncoder()->EncodeJump(slowPath->GetLabel(), dst, Condition::EQ);
2509     } else {
2510         ScopedTmpReg hashReg(GetEncoder(), INT32_TYPE);
2511         GetEncoder()->EncodeLdr(hashReg, false, mref);
2512         GetEncoder()->EncodeJump(slowPath->GetLabel(), hashReg, Condition::EQ);
2513         GetEncoder()->EncodeMov(dst, hashReg);
2514     }
2515     slowPath->BindBackLabel(GetEncoder());
2516 }
CreateStringCompareTo(IntrinsicInst * inst,Reg dst,SRCREGS src)2517 void Codegen::CreateStringCompareTo([[maybe_unused]] IntrinsicInst *inst, Reg dst, SRCREGS src)
2518 {
2519     auto str1 = src[FIRST_OPERAND];
2520     auto str2 = src[SECOND_OPERAND];
2521     CallFastPath(inst, EntrypointId::STRING_COMPARE_TO, dst, {}, str1, str2);
2522 }
2523 #include "intrinsics_codegen.inl"
2524 
CreateBuiltinIntrinsic(IntrinsicInst * inst)2525 void Codegen::CreateBuiltinIntrinsic(IntrinsicInst *inst)
2526 {
2527     Reg dst = INVALID_REGISTER;
2528     SRCREGS src;
2529 
2530     if (!inst->NoDest()) {
2531         dst = ConvertRegister(inst->GetDstReg(), inst->GetType());
2532     }
2533 
2534     for (size_t i = 0; i < inst->GetInputsCount(); ++i) {
2535         if (inst->GetInput(i).GetInst()->IsSaveState()) {
2536             continue;
2537         }
2538         auto location = inst->GetLocation(i);
2539         auto type = inst->GetInputType(i);
2540         src[i] = ConvertRegister(location.GetValue(), type);
2541     }
2542     FillBuiltin(inst, src, dst);
2543 }
2544 
GetNeedBarrierProperty(const Inst * inst)2545 static bool GetNeedBarrierProperty(const Inst *inst)
2546 {
2547     Opcode op = inst->GetOpcode();
2548     if (op == Opcode::LoadObject) {
2549         return inst->CastToLoadObject()->GetNeedBarrier();
2550     }
2551     if (op == Opcode::StoreObject) {
2552         return inst->CastToStoreObject()->GetNeedBarrier();
2553     }
2554     if (op == Opcode::LoadObjectPair) {
2555         return inst->CastToLoadObjectPair()->GetNeedBarrier();
2556     }
2557     if (op == Opcode::StoreObjectPair) {
2558         return inst->CastToStoreObjectPair()->GetNeedBarrier();
2559     }
2560     if (op == Opcode::LoadArray) {
2561         return inst->CastToLoadArray()->GetNeedBarrier();
2562     }
2563     if (op == Opcode::StoreArray) {
2564         return inst->CastToStoreArray()->GetNeedBarrier();
2565     }
2566     if (op == Opcode::LoadArrayI) {
2567         return inst->CastToLoadArrayI()->GetNeedBarrier();
2568     }
2569     if (op == Opcode::StoreArrayI) {
2570         return inst->CastToStoreArrayI()->GetNeedBarrier();
2571     }
2572     if (op == Opcode::LoadArrayPair) {
2573         return inst->CastToLoadArrayPair()->GetNeedBarrier();
2574     }
2575     if (op == Opcode::StoreArrayPair) {
2576         return inst->CastToStoreArrayPair()->GetNeedBarrier();
2577     }
2578     if (op == Opcode::LoadArrayPairI) {
2579         return inst->CastToLoadArrayPairI()->GetNeedBarrier();
2580     }
2581     if (op == Opcode::StoreArrayPairI) {
2582         return inst->CastToStoreArrayPairI()->GetNeedBarrier();
2583     }
2584     if (op == Opcode::LoadStatic) {
2585         return inst->CastToLoadStatic()->GetNeedBarrier();
2586     }
2587     if (op == Opcode::StoreStatic) {
2588         return inst->CastToStoreStatic()->GetNeedBarrier();
2589     }
2590     return false;
2591 }
2592 
2593 /**
2594  * Returns true if codegen emits call(s) to some library function(s)
2595  * while processing the instruction.
2596  */
InstEncodedWithLibCall(const Inst * inst,Arch arch)2597 bool Codegen::InstEncodedWithLibCall(const Inst *inst, Arch arch)
2598 {
2599     ASSERT(inst != nullptr);
2600     Opcode op = inst->GetOpcode();
2601     if (op == Opcode::Mod) {
2602         auto dstType = inst->GetType();
2603         if (arch == Arch::AARCH64 || arch == Arch::X86_64) {
2604             return dstType == DataType::FLOAT32 || dstType == DataType::FLOAT64;
2605         }
2606         return arch == Arch::AARCH32;
2607     }
2608     if (op == Opcode::Div && arch == Arch::AARCH32) {
2609         auto dstType = inst->GetType();
2610         return dstType == DataType::INT64 || dstType == DataType::UINT64;
2611     }
2612     if (op == Opcode::Cast && arch == Arch::AARCH32) {
2613         auto dstType = inst->GetType();
2614         auto srcType = inst->GetInputType(0);
2615         if (dstType == DataType::FLOAT32 || dstType == DataType::FLOAT64) {
2616             return srcType == DataType::INT64 || srcType == DataType::UINT64;
2617         }
2618         if (srcType == DataType::FLOAT32 || srcType == DataType::FLOAT64) {
2619             return dstType == DataType::INT64 || dstType == DataType::UINT64;
2620         }
2621         return false;
2622     }
2623 
2624     return GetNeedBarrierProperty(inst);
2625 }
2626 
ConvertInstTmpReg(const Inst * inst,DataType::Type type) const2627 Reg Codegen::ConvertInstTmpReg(const Inst *inst, DataType::Type type) const
2628 {
2629     ASSERT(inst->GetTmpLocation().IsFixedRegister());
2630     return Reg(inst->GetTmpLocation().GetValue(), ConvertDataType(type, GetArch()));
2631 }
2632 
ConvertInstTmpReg(const Inst * inst) const2633 Reg Codegen::ConvertInstTmpReg(const Inst *inst) const
2634 {
2635     return ConvertInstTmpReg(inst, Is64BitsArch(GetArch()) ? DataType::INT64 : DataType::INT32);
2636 }
2637 
Encode(MemRef mem,Reg reg1,Reg reg2,bool checkObject,RegMask preserved)2638 void PostWriteBarrier::Encode(MemRef mem, Reg reg1, Reg reg2, bool checkObject, RegMask preserved)
2639 {
2640     ASSERT(reg1.IsValid());
2641     auto reference {TypeInfo::FromDataType(DataType::REFERENCE, cg_->GetArch())};
2642     PostWriteBarrier::Args args;
2643     args.mem = mem;
2644     args.reg1 = reg1.As(reference);
2645     args.reg2 = reg2.IsValid() ? reg2.As(reference) : INVALID_REGISTER;
2646     args.checkObject = checkObject;
2647     args.preserved = preserved;
2648     if (cg_->GetGraph()->SupportsIrtocBarriers()) {
2649         if (cg_->GetGraph()->IsOfflineCompilationMode()) {
2650             EncodeOfflineIrtocBarrier(args);
2651         } else {
2652             EncodeOnlineIrtocBarrier(args);
2653         }
2654     } else if (type_ == ark::mem::BarrierType::POST_INTERREGION_BARRIER) {
2655         EncodeInterRegionBarrier(args);
2656     } else {
2657         ASSERT(type_ == ark::mem::BarrierType::POST_INTERGENERATIONAL_BARRIER);
2658         EncodeInterGenerationalBarrier(mem.GetBase().As(reference));
2659     }
2660 }
2661 
EncodeOfflineIrtocBarrier(Args args)2662 void PostWriteBarrier::EncodeOfflineIrtocBarrier(Args args)
2663 {
2664     auto hasObj2 = HasObject2(args);
2665     auto paramRegs = GetParamRegs(hasObj2 ? 4U : 3U, args);
2666     cg_->SaveCallerRegisters(paramRegs, VRegMask(), false);
2667     if (hasObj2) {
2668         FillCallParams(args.mem, args.reg1, args.reg2);
2669     } else {
2670         FillCallParams(args.mem, args.reg1);
2671     }
2672     // load function pointer from tls field
2673     auto offset {hasObj2 ? cross_values::GetManagedThreadPostWrbTwoObjectsOffset(cg_->GetArch())
2674                          : cross_values::GetManagedThreadPostWrbOneObjectOffset(cg_->GetArch())};
2675     MemRef entry(cg_->ThreadReg(), offset);
2676     cg_->GetEncoder()->MakeCall(entry);
2677     cg_->LoadCallerRegisters(paramRegs, VRegMask(), false);
2678 }
2679 
EncodeOnlineIrtocBarrier(Args args)2680 void PostWriteBarrier::EncodeOnlineIrtocBarrier(Args args)
2681 {
2682     SCOPED_DISASM_STR(cg_, "Post Online Irtoc-WRB");
2683     auto *enc {cg_->GetEncoder()};
2684     auto hasObj2 = HasObject2(args);
2685     if (type_ == ark::mem::BarrierType::POST_INTERREGION_BARRIER) {
2686         if (hasObj2) {
2687             EncodeOnlineIrtocRegionTwoRegsBarrier(args);
2688         } else {
2689             EncodeOnlineIrtocRegionOneRegBarrier(args);
2690         }
2691     } else {
2692         static constexpr auto ENTRYPOINT_ID {RuntimeInterface::EntrypointId::POST_INTER_GENERATIONAL_BARRIER0};
2693         auto base = GetBase(args);
2694         auto paramRegs = GetParamRegs(1U, args);
2695         cg_->SaveCallerRegisters(paramRegs, VRegMask(), false);
2696         auto paramReg0 = enc->GetTarget().GetParamReg(0);
2697         enc->EncodeMov(paramReg0, base);
2698         MemRef entry(cg_->ThreadReg(), cg_->GetRuntime()->GetEntrypointTlsOffset(cg_->GetArch(), ENTRYPOINT_ID));
2699         enc->MakeCall(entry);
2700         cg_->LoadCallerRegisters(paramRegs, VRegMask(), false);
2701     }
2702 }
2703 
2704 /**
2705  * Post-write barrier for StorePair case.
2706  * The code below may mark 0, 1 or 2 cards. The idea is following:
2707  *  if (the second object isn't null and is allocated in other from base object's region)
2708  *      MarkOneCard(CardOfSecondField(mem))
2709  *      if (address of the second field isn't aligned at the size of a card)
2710  *          # i.e. each of store address (fields of the base objects) are related to the same card
2711  *          goto Done
2712  *
2713  *  if (the first object isn't null and is allocated in other from base object's region)
2714  *      MarkOneCard(CardOfFirstField(mem))
2715  *
2716  *  label: Done
2717  */
EncodeOnlineIrtocRegionTwoRegsBarrier(Args args)2718 void PostWriteBarrier::EncodeOnlineIrtocRegionTwoRegsBarrier(Args args)
2719 {
2720     static constexpr auto ENTRYPOINT_ID {RuntimeInterface::EntrypointId::POST_INTER_REGION_BARRIER_SLOW};
2721     auto *enc {cg_->GetEncoder()};
2722     auto base = GetBase(args);
2723     auto paramReg0 = enc->GetTarget().GetParamReg(0);
2724     MemRef entry(cg_->ThreadReg(), cg_->GetRuntime()->GetEntrypointTlsOffset(cg_->GetArch(), ENTRYPOINT_ID));
2725     auto paramRegs = GetParamRegs(1U, args);
2726     auto lblMarkCardAndExit = enc->CreateLabel();
2727     auto lblCheck1Obj = enc->CreateLabel();
2728     auto lblDone = enc->CreateLabel();
2729     EncodeCheckObject(base, args.reg2, lblCheck1Obj, args.checkObject);
2730     cg_->SaveCallerRegisters(paramRegs, VRegMask(), false);
2731     EncodeWrapOneArg(paramReg0, base, args.mem, args.reg1.GetSize() / BITS_PER_BYTE);
2732     {
2733         ScopedTmpReg tmp(enc, cg_->ConvertDataType(DataType::REFERENCE, cg_->GetArch()));
2734         enc->EncodeAnd(tmp, paramReg0, Imm(cross_values::GetCardAlignmentMask(cg_->GetArch())));
2735         enc->EncodeJump(lblMarkCardAndExit, tmp, Condition::NE);
2736     }
2737     enc->MakeCall(entry);
2738     cg_->LoadCallerRegisters(paramRegs, VRegMask(), false);
2739     enc->BindLabel(lblCheck1Obj);
2740     EncodeCheckObject(base, args.reg1, lblDone, args.checkObject);
2741     cg_->SaveCallerRegisters(paramRegs, VRegMask(), false);
2742     EncodeWrapOneArg(paramReg0, base, args.mem);
2743     enc->BindLabel(lblMarkCardAndExit);
2744     enc->MakeCall(entry);
2745     cg_->LoadCallerRegisters(paramRegs, VRegMask(), false);
2746     enc->BindLabel(lblDone);
2747 }
2748 
EncodeOnlineIrtocRegionOneRegBarrier(Args args)2749 void PostWriteBarrier::EncodeOnlineIrtocRegionOneRegBarrier(Args args)
2750 {
2751     static constexpr auto ENTRYPOINT_ID {RuntimeInterface::EntrypointId::POST_INTER_REGION_BARRIER_SLOW};
2752     auto *enc {cg_->GetEncoder()};
2753     auto base = GetBase(args);
2754     auto paramReg0 = enc->GetTarget().GetParamReg(0);
2755     MemRef entry(cg_->ThreadReg(), cg_->GetRuntime()->GetEntrypointTlsOffset(cg_->GetArch(), ENTRYPOINT_ID));
2756     auto paramRegs = GetParamRegs(1U, args);
2757     auto skip = enc->CreateLabel();
2758     EncodeCheckObject(base, args.reg1, skip, args.checkObject);
2759     cg_->SaveCallerRegisters(paramRegs, VRegMask(), false);
2760     EncodeWrapOneArg(paramReg0, base, args.mem);
2761     enc->MakeCall(entry);
2762     cg_->LoadCallerRegisters(paramRegs, VRegMask(), false);
2763     enc->BindLabel(skip);
2764 }
2765 
EncodeInterRegionBarrier(Args args)2766 void PostWriteBarrier::EncodeInterRegionBarrier(Args args)
2767 {
2768     SCOPED_DISASM_STR(cg_, "Post IR-WRB");
2769     ASSERT(type_ == ark::mem::BarrierType::POST_INTERREGION_BARRIER);
2770 
2771     static constexpr auto ENTRYPOINT_ID {RuntimeInterface::EntrypointId::POST_WRB_UPDATE_CARD_FUNC_NO_BRIDGE};
2772     auto *enc {cg_->GetEncoder()};
2773     auto label = enc->CreateLabel();
2774     if (args.checkObject) {
2775         cg_->CheckObject(args.reg1, label);
2776     }
2777     auto regionSizeBit = GetBarrierOperandValue<uint8_t>(BARRIER_POSITION, "REGION_SIZE_BITS");
2778     auto base = GetBase(args);
2779     ScopedTmpReg tmp(enc, cg_->ConvertDataType(DataType::REFERENCE, cg_->GetArch()));
2780     // Compare first store value with mem
2781     enc->EncodeXor(tmp, base, args.reg1);
2782     enc->EncodeShr(tmp, tmp, Imm(regionSizeBit));
2783     enc->EncodeJump(label, tmp, Condition::EQ);
2784     auto [live_regs, live_vregs] = cg_->GetLiveRegisters<true>(inst_);
2785     live_regs |= args.preserved;
2786 
2787     if (args.mem.HasIndex()) {
2788         ASSERT(args.mem.GetScale() == 0 && !args.mem.HasDisp());
2789         enc->EncodeAdd(tmp, base, args.mem.GetIndex());
2790         cg_->CallBarrier(live_regs, live_vregs, ENTRYPOINT_ID, tmp, args.reg1);
2791     } else if (args.mem.HasDisp()) {
2792         ASSERT(!args.mem.HasIndex());
2793         enc->EncodeAdd(tmp, base, Imm(args.mem.GetDisp()));
2794         cg_->CallBarrier(live_regs, live_vregs, ENTRYPOINT_ID, tmp, args.reg1);
2795     } else {
2796         cg_->CallBarrier(live_regs, live_vregs, ENTRYPOINT_ID, base, args.reg1);
2797     }
2798     enc->BindLabel(label);
2799 
2800     if (HasObject2(args)) {
2801         auto label1 = enc->CreateLabel();
2802         if (args.checkObject) {
2803             cg_->CheckObject(args.reg2, label1);
2804         }
2805         // Compare second store value with mem
2806         enc->EncodeXor(tmp, base, args.reg2);
2807         enc->EncodeShr(tmp, tmp, Imm(regionSizeBit));
2808         enc->EncodeJump(label1, tmp, Condition::EQ);
2809         enc->EncodeAdd(tmp, base, Imm(args.reg1.GetSize() / BITS_PER_BYTE));
2810         if (args.mem.HasIndex()) {
2811             ASSERT(args.mem.GetScale() == 0 && !args.mem.HasDisp());
2812             enc->EncodeAdd(tmp, tmp, args.mem.GetIndex());
2813         } else if (args.mem.HasDisp()) {
2814             ASSERT(!args.mem.HasIndex());
2815             enc->EncodeAdd(tmp, tmp, Imm(args.mem.GetDisp()));
2816         }
2817         cg_->CallBarrier(live_regs, live_vregs, ENTRYPOINT_ID, tmp, args.reg2);
2818         enc->BindLabel(label1);
2819     }
2820 }
2821 
EncodeInterGenerationalBarrier(Reg base)2822 void PostWriteBarrier::EncodeInterGenerationalBarrier(Reg base)
2823 {
2824     ASSERT(type_ == ark::mem::BarrierType::POST_INTERGENERATIONAL_BARRIER);
2825     SCOPED_DISASM_STR(cg_, "Post IG-WRB");
2826     auto *runtime {cg_->GetRuntime()};
2827     auto *enc {cg_->GetEncoder()};
2828     ScopedTmpReg tmp(enc);
2829     ScopedTmpReg tmp1(enc);
2830     // load AddressOf(MIN_ADDR) -> min_addr
2831     if (cg_->GetGraph()->IsOfflineCompilationMode()) {
2832         enc->EncodeLdr(tmp, false, MemRef(cg_->ThreadReg(), runtime->GetTlsCardTableMinAddrOffset(cg_->GetArch())));
2833     } else {
2834         auto minAddress = reinterpret_cast<uintptr_t>(GetBarrierOperandValue<void *>(BARRIER_POSITION, "MIN_ADDR"));
2835         enc->EncodeMov(tmp, Imm(minAddress));
2836     }
2837     // card_index = (AddressOf(obj.field) - min_addr) >> CARD_BITS
2838     EncodeCalculateCardIndex(base, &tmp, &tmp1);
2839     // load AddressOf(CARD_TABLE_ADDR) -> card_table_addr
2840     if (cg_->GetGraph()->IsOfflineCompilationMode()) {
2841         enc->EncodeLdr(tmp1.GetReg().As(INT64_TYPE), false,
2842                        MemRef(cg_->ThreadReg(), runtime->GetTlsCardTableAddrOffset(cg_->GetArch())));
2843     } else {
2844         auto cardTableAddr =
2845             reinterpret_cast<uintptr_t>(GetBarrierOperandValue<uint8_t *>(BARRIER_POSITION, "CARD_TABLE_ADDR"));
2846         enc->EncodeMov(tmp1, Imm(cardTableAddr));
2847     }
2848     // card_addr = card_table_addr + card_index
2849     enc->EncodeAdd(tmp, tmp1, tmp);
2850     // store card_addr <- DIRTY_VAL
2851     auto dirtyVal = GetBarrierOperandValue<uint8_t>(BARRIER_POSITION, "DIRTY_VAL");
2852     auto tmp1B = cg_->ConvertRegister(tmp1.GetReg().GetId(), DataType::INT8);
2853     enc->EncodeMov(tmp1B, Imm(dirtyVal));
2854     enc->EncodeStr(tmp1B, MemRef(tmp));
2855 }
2856 
EncodeCalculateCardIndex(Reg baseReg,ScopedTmpReg * tmp,ScopedTmpReg * tmp1)2857 void PostWriteBarrier::EncodeCalculateCardIndex(Reg baseReg, ScopedTmpReg *tmp, ScopedTmpReg *tmp1)
2858 {
2859     ASSERT(baseReg != INVALID_REGISTER);
2860     auto tmpType = tmp->GetReg().GetType();
2861     auto tmp1Type = tmp1->GetReg().GetType();
2862     if (baseReg.GetSize() < Reg(*tmp).GetSize()) {
2863         tmp->ChangeType(baseReg.GetType());
2864         tmp1->ChangeType(baseReg.GetType());
2865     }
2866     auto enc {cg_->GetEncoder()};
2867     enc->EncodeSub(*tmp, baseReg, *tmp);
2868     enc->EncodeShr(*tmp, *tmp, Imm(GetBarrierOperandValue<uint8_t>(BARRIER_POSITION, "CARD_BITS")));
2869     tmp->ChangeType(tmpType);
2870     tmp1->ChangeType(tmp1Type);
2871 }
2872 
EncodeCheckObject(Reg base,Reg reg1,LabelHolder::LabelId skipLabel,bool checkNull)2873 void PostWriteBarrier::EncodeCheckObject(Reg base, Reg reg1, LabelHolder::LabelId skipLabel, bool checkNull)
2874 {
2875     auto enc {cg_->GetEncoder()};
2876     if (checkNull) {
2877         // Fast null check in-place for one register
2878         enc->EncodeJump(skipLabel, reg1, Condition::EQ);
2879     }
2880     ScopedTmpReg tmp(enc, cg_->ConvertDataType(DataType::REFERENCE, cg_->GetArch()));
2881     auto regionSizeBit = GetBarrierOperandValue<uint8_t>(BARRIER_POSITION, "REGION_SIZE_BITS");
2882     enc->EncodeXor(tmp, base, reg1);
2883     enc->EncodeShr(tmp, tmp, Imm(regionSizeBit));
2884     enc->EncodeJump(skipLabel, tmp, Condition::EQ);
2885 }
2886 
EncodeWrapOneArg(Reg param,Reg base,MemRef mem,size_t additionalOffset)2887 void PostWriteBarrier::EncodeWrapOneArg(Reg param, Reg base, MemRef mem, size_t additionalOffset)
2888 {
2889     auto enc {cg_->GetEncoder()};
2890     if (mem.HasIndex()) {
2891         ASSERT(mem.GetScale() == 0 && !mem.HasDisp());
2892         enc->EncodeAdd(param, base, mem.GetIndex());
2893         if (additionalOffset != 0) {
2894             enc->EncodeAdd(param, param, Imm(additionalOffset));
2895         }
2896     } else if (mem.HasDisp()) {
2897         ASSERT(!mem.HasIndex());
2898         enc->EncodeAdd(param, base, Imm(mem.GetDisp() + additionalOffset));
2899     } else {
2900         enc->EncodeAdd(param, base, Imm(additionalOffset));
2901     }
2902 }
2903 
2904 }  // namespace ark::compiler
2905