xref: /aosp_15_r20/art/compiler/optimizing/code_generator_riscv64.cc (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "code_generator_riscv64.h"
18 
19 #include "android-base/logging.h"
20 #include "android-base/macros.h"
21 #include "arch/riscv64/jni_frame_riscv64.h"
22 #include "arch/riscv64/registers_riscv64.h"
23 #include "base/arena_containers.h"
24 #include "base/macros.h"
25 #include "class_root-inl.h"
26 #include "code_generator_utils.h"
27 #include "dwarf/register.h"
28 #include "gc/heap.h"
29 #include "gc/space/image_space.h"
30 #include "heap_poisoning.h"
31 #include "intrinsics_list.h"
32 #include "intrinsics_riscv64.h"
33 #include "jit/profiling_info.h"
34 #include "linker/linker_patch.h"
35 #include "mirror/class-inl.h"
36 #include "optimizing/nodes.h"
37 #include "optimizing/profiling_info_builder.h"
38 #include "runtime.h"
39 #include "scoped_thread_state_change-inl.h"
40 #include "stack_map_stream.h"
41 #include "trace.h"
42 #include "utils/label.h"
43 #include "utils/riscv64/assembler_riscv64.h"
44 #include "utils/stack_checks.h"
45 
46 namespace art HIDDEN {
47 namespace riscv64 {
48 
49 // Placeholder values embedded in instructions, patched at link time.
50 constexpr uint32_t kLinkTimeOffsetPlaceholderHigh = 0x12345;
51 constexpr uint32_t kLinkTimeOffsetPlaceholderLow = 0x678;
52 
53 // Compare-and-jump packed switch generates approx. 3 + 1.5 * N 32-bit
54 // instructions for N cases.
55 // Table-based packed switch generates approx. 10 32-bit instructions
56 // and N 32-bit data words for N cases.
57 // We switch to the table-based method starting with 6 entries.
58 static constexpr uint32_t kPackedSwitchCompareJumpThreshold = 6;
59 
60 static constexpr XRegister kCoreCalleeSaves[] = {
61     // S1(TR) is excluded as the ART thread register.
62     S0, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, RA
63 };
64 
65 static constexpr FRegister kFpuCalleeSaves[] = {
66     FS0, FS1, FS2, FS3, FS4, FS5, FS6, FS7, FS8, FS9, FS10, FS11
67 };
68 
69 #define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kRiscv64PointerSize, x).Int32Value()
70 
RegisterOrZeroBitPatternLocation(HInstruction * instruction)71 Location RegisterOrZeroBitPatternLocation(HInstruction* instruction) {
72   DCHECK(!DataType::IsFloatingPointType(instruction->GetType()));
73   return IsZeroBitPattern(instruction)
74       ? Location::ConstantLocation(instruction)
75       : Location::RequiresRegister();
76 }
77 
FpuRegisterOrZeroBitPatternLocation(HInstruction * instruction)78 Location FpuRegisterOrZeroBitPatternLocation(HInstruction* instruction) {
79   DCHECK(DataType::IsFloatingPointType(instruction->GetType()));
80   return IsZeroBitPattern(instruction)
81       ? Location::ConstantLocation(instruction)
82       : Location::RequiresFpuRegister();
83 }
84 
InputXRegisterOrZero(Location location)85 XRegister InputXRegisterOrZero(Location location) {
86   if (location.IsConstant()) {
87     DCHECK(location.GetConstant()->IsZeroBitPattern());
88     return Zero;
89   } else {
90     return location.AsRegister<XRegister>();
91   }
92 }
93 
ValueLocationForStore(HInstruction * value)94 Location ValueLocationForStore(HInstruction* value) {
95   if (IsZeroBitPattern(value)) {
96     return Location::ConstantLocation(value);
97   } else if (DataType::IsFloatingPointType(value->GetType())) {
98     return Location::RequiresFpuRegister();
99   } else {
100     return Location::RequiresRegister();
101   }
102 }
103 
Riscv64ReturnLocation(DataType::Type return_type)104 Location Riscv64ReturnLocation(DataType::Type return_type) {
105   switch (return_type) {
106     case DataType::Type::kBool:
107     case DataType::Type::kUint8:
108     case DataType::Type::kInt8:
109     case DataType::Type::kUint16:
110     case DataType::Type::kInt16:
111     case DataType::Type::kUint32:
112     case DataType::Type::kInt32:
113     case DataType::Type::kReference:
114     case DataType::Type::kUint64:
115     case DataType::Type::kInt64:
116       return Location::RegisterLocation(A0);
117 
118     case DataType::Type::kFloat32:
119     case DataType::Type::kFloat64:
120       return Location::FpuRegisterLocation(FA0);
121 
122     case DataType::Type::kVoid:
123       return Location::NoLocation();
124   }
125 }
126 
OneRegInReferenceOutSaveEverythingCallerSaves()127 static RegisterSet OneRegInReferenceOutSaveEverythingCallerSaves() {
128   InvokeRuntimeCallingConvention calling_convention;
129   RegisterSet caller_saves = RegisterSet::Empty();
130   caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
131   DCHECK_EQ(
132       calling_convention.GetRegisterAt(0),
133       calling_convention.GetReturnLocation(DataType::Type::kReference).AsRegister<XRegister>());
134   return caller_saves;
135 }
136 
137 template <ClassStatus kStatus>
ShiftedSignExtendedClassStatusValue()138 static constexpr int64_t ShiftedSignExtendedClassStatusValue() {
139   // This is used only for status values that have the highest bit set.
140   static_assert(CLZ(enum_cast<uint32_t>(kStatus)) == kClassStatusLsbPosition);
141   constexpr uint32_t kShiftedStatusValue = enum_cast<uint32_t>(kStatus) << kClassStatusLsbPosition;
142   static_assert(kShiftedStatusValue >= 0x80000000u);
143   return static_cast<int64_t>(kShiftedStatusValue) - (INT64_C(1) << 32);
144 }
145 
146 // Split a 64-bit address used by JIT to the nearest 4KiB-aligned base address and a 12-bit
147 // signed offset. It is usually cheaper to materialize the aligned address than the full address.
SplitJitAddress(uint64_t address)148 std::pair<uint64_t, int32_t> SplitJitAddress(uint64_t address) {
149   uint64_t bits0_11 = address & UINT64_C(0xfff);
150   uint64_t bit11 = address & UINT64_C(0x800);
151   // Round the address to nearest 4KiB address because the `imm12` has range [-0x800, 0x800).
152   uint64_t base_address = (address & ~UINT64_C(0xfff)) + (bit11 << 1);
153   int32_t imm12 = dchecked_integral_cast<int32_t>(bits0_11) -
154                   dchecked_integral_cast<int32_t>(bit11 << 1);
155   return {base_address, imm12};
156 }
157 
ReadBarrierMarkEntrypointOffset(Location ref)158 int32_t ReadBarrierMarkEntrypointOffset(Location ref) {
159   DCHECK(ref.IsRegister());
160   int reg = ref.reg();
161   DCHECK(T0 <= reg && reg <= T6 && reg != TR) << reg;
162   // Note: Entrypoints for registers X30 (T5) and X31 (T6) are stored in entries
163   // for X0 (Zero) and X1 (RA) because these are not valid registers for marking
164   // and we currently have slots only up to register 29.
165   int entry_point_number = (reg >= 30) ? reg - 30 : reg;
166   return Thread::ReadBarrierMarkEntryPointsOffset<kRiscv64PointerSize>(entry_point_number);
167 }
168 
GetReturnLocation(DataType::Type return_type)169 Location InvokeRuntimeCallingConvention::GetReturnLocation(DataType::Type return_type) {
170   return Riscv64ReturnLocation(return_type);
171 }
172 
GetReturnLocation(DataType::Type type) const173 Location InvokeDexCallingConventionVisitorRISCV64::GetReturnLocation(DataType::Type type) const {
174   return Riscv64ReturnLocation(type);
175 }
176 
GetMethodLocation() const177 Location InvokeDexCallingConventionVisitorRISCV64::GetMethodLocation() const {
178   return Location::RegisterLocation(kArtMethodRegister);
179 }
180 
GetNextLocation(DataType::Type type)181 Location InvokeDexCallingConventionVisitorRISCV64::GetNextLocation(DataType::Type type) {
182   Location next_location;
183   if (type == DataType::Type::kVoid) {
184     LOG(FATAL) << "Unexpected parameter type " << type;
185   }
186 
187   // Note: Unlike the RISC-V C/C++ calling convention, managed ABI does not use
188   // GPRs to pass FP args when we run out of FPRs.
189   if (DataType::IsFloatingPointType(type) &&
190       float_index_ < calling_convention.GetNumberOfFpuRegisters()) {
191     next_location =
192         Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(float_index_++));
193   } else if (!DataType::IsFloatingPointType(type) &&
194              (gp_index_ < calling_convention.GetNumberOfRegisters())) {
195     next_location = Location::RegisterLocation(calling_convention.GetRegisterAt(gp_index_++));
196   } else {
197     size_t stack_offset = calling_convention.GetStackOffsetOf(stack_index_);
198     next_location = DataType::Is64BitType(type) ? Location::DoubleStackSlot(stack_offset) :
199                                                   Location::StackSlot(stack_offset);
200   }
201 
202   // Space on the stack is reserved for all arguments.
203   stack_index_ += DataType::Is64BitType(type) ? 2 : 1;
204 
205   return next_location;
206 }
207 
GetNextLocation(DataType::Type type)208 Location CriticalNativeCallingConventionVisitorRiscv64::GetNextLocation(DataType::Type type) {
209   DCHECK_NE(type, DataType::Type::kReference);
210 
211   Location location = Location::NoLocation();
212   if (DataType::IsFloatingPointType(type)) {
213     if (fpr_index_ < kParameterFpuRegistersLength) {
214       location = Location::FpuRegisterLocation(kParameterFpuRegisters[fpr_index_]);
215       ++fpr_index_;
216     } else {
217       // Native ABI allows passing excessive FP args in GPRs. This is facilitated by
218       // inserting fake conversion intrinsic calls (`Double.doubleToRawLongBits()`
219       // or `Float.floatToRawIntBits()`) by `CriticalNativeAbiFixupRiscv64`.
220       // Remaining FP args shall be passed on the stack.
221       CHECK_EQ(gpr_index_, kRuntimeParameterCoreRegistersLength);
222     }
223   } else {
224     // Native ABI uses the same core registers as a runtime call.
225     if (gpr_index_ < kRuntimeParameterCoreRegistersLength) {
226       location = Location::RegisterLocation(kRuntimeParameterCoreRegisters[gpr_index_]);
227       ++gpr_index_;
228     }
229   }
230   if (location.IsInvalid()) {
231     // Only a `float` gets a single slot. Integral args need to be sign-extended to 64 bits.
232     if (type == DataType::Type::kFloat32) {
233       location = Location::StackSlot(stack_offset_);
234     } else {
235       location = Location::DoubleStackSlot(stack_offset_);
236     }
237     stack_offset_ += kFramePointerSize;
238 
239     if (for_register_allocation_) {
240       location = Location::Any();
241     }
242   }
243   return location;
244 }
245 
GetReturnLocation(DataType::Type type) const246 Location CriticalNativeCallingConventionVisitorRiscv64::GetReturnLocation(
247     DataType::Type type) const {
248   // The result is returned the same way in native ABI and managed ABI. No result conversion is
249   // needed, see comments in `Riscv64JniCallingConvention::RequiresSmallResultTypeExtension()`.
250   InvokeDexCallingConventionVisitorRISCV64 dex_calling_convention;
251   return dex_calling_convention.GetReturnLocation(type);
252 }
253 
GetMethodLocation() const254 Location CriticalNativeCallingConventionVisitorRiscv64::GetMethodLocation() const {
255   // Pass the method in the hidden argument T0.
256   return Location::RegisterLocation(T0);
257 }
258 
259 #define __ down_cast<CodeGeneratorRISCV64*>(codegen)->GetAssembler()->  // NOLINT
260 
HandleInvoke(HInvoke * instruction)261 void LocationsBuilderRISCV64::HandleInvoke(HInvoke* instruction) {
262   InvokeDexCallingConventionVisitorRISCV64 calling_convention_visitor;
263   CodeGenerator::CreateCommonInvokeLocationSummary(instruction, &calling_convention_visitor);
264 }
265 
266 class CompileOptimizedSlowPathRISCV64 : public SlowPathCodeRISCV64 {
267  public:
CompileOptimizedSlowPathRISCV64(HSuspendCheck * suspend_check,XRegister base,int32_t imm12)268   CompileOptimizedSlowPathRISCV64(HSuspendCheck* suspend_check, XRegister base, int32_t imm12)
269       : SlowPathCodeRISCV64(suspend_check),
270         base_(base),
271         imm12_(imm12) {}
272 
EmitNativeCode(CodeGenerator * codegen)273   void EmitNativeCode(CodeGenerator* codegen) override {
274     uint32_t entrypoint_offset =
275         GetThreadOffset<kRiscv64PointerSize>(kQuickCompileOptimized).Int32Value();
276     __ Bind(GetEntryLabel());
277     CodeGeneratorRISCV64* riscv64_codegen = down_cast<CodeGeneratorRISCV64*>(codegen);
278     riscv64::ScratchRegisterScope srs(riscv64_codegen->GetAssembler());
279     XRegister counter = srs.AllocateXRegister();
280     __ LoadConst32(counter, ProfilingInfo::GetOptimizeThreshold());
281     __ Sh(counter, base_, imm12_);
282     if (instruction_ != nullptr) {
283       // Only saves live vector regs for SIMD.
284       SaveLiveRegisters(codegen, instruction_->GetLocations());
285     }
286     __ Loadd(RA, TR, entrypoint_offset);
287     // Note: we don't record the call here (and therefore don't generate a stack
288     // map), as the entrypoint should never be suspended.
289     __ Jalr(RA);
290     if (instruction_ != nullptr) {
291       // Only restores live vector regs for SIMD.
292       RestoreLiveRegisters(codegen, instruction_->GetLocations());
293     }
294     __ J(GetExitLabel());
295   }
296 
GetDescription() const297   const char* GetDescription() const override { return "CompileOptimizedSlowPath"; }
298 
299  private:
300   XRegister base_;
301   const int32_t imm12_;
302 
303   DISALLOW_COPY_AND_ASSIGN(CompileOptimizedSlowPathRISCV64);
304 };
305 
306 class SuspendCheckSlowPathRISCV64 : public SlowPathCodeRISCV64 {
307  public:
SuspendCheckSlowPathRISCV64(HSuspendCheck * instruction,HBasicBlock * successor)308   SuspendCheckSlowPathRISCV64(HSuspendCheck* instruction, HBasicBlock* successor)
309       : SlowPathCodeRISCV64(instruction), successor_(successor) {}
310 
EmitNativeCode(CodeGenerator * codegen)311   void EmitNativeCode(CodeGenerator* codegen) override {
312     LocationSummary* locations = instruction_->GetLocations();
313     CodeGeneratorRISCV64* riscv64_codegen = down_cast<CodeGeneratorRISCV64*>(codegen);
314     __ Bind(GetEntryLabel());
315     SaveLiveRegisters(codegen, locations);  // Only saves live vector registers for SIMD.
316     riscv64_codegen->InvokeRuntime(kQuickTestSuspend, instruction_, instruction_->GetDexPc(), this);
317     CheckEntrypointTypes<kQuickTestSuspend, void, void>();
318     RestoreLiveRegisters(codegen, locations);  // Only restores live vector registers for SIMD.
319     if (successor_ == nullptr) {
320       __ J(GetReturnLabel());
321     } else {
322       __ J(riscv64_codegen->GetLabelOf(successor_));
323     }
324   }
325 
GetReturnLabel()326   Riscv64Label* GetReturnLabel() {
327     DCHECK(successor_ == nullptr);
328     return &return_label_;
329   }
330 
GetDescription() const331   const char* GetDescription() const override { return "SuspendCheckSlowPathRISCV64"; }
332 
GetSuccessor() const333   HBasicBlock* GetSuccessor() const { return successor_; }
334 
335  private:
336   // If not null, the block to branch to after the suspend check.
337   HBasicBlock* const successor_;
338 
339   // If `successor_` is null, the label to branch to after the suspend check.
340   Riscv64Label return_label_;
341 
342   DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathRISCV64);
343 };
344 
345 class NullCheckSlowPathRISCV64 : public SlowPathCodeRISCV64 {
346  public:
NullCheckSlowPathRISCV64(HNullCheck * instr)347   explicit NullCheckSlowPathRISCV64(HNullCheck* instr) : SlowPathCodeRISCV64(instr) {}
348 
EmitNativeCode(CodeGenerator * codegen)349   void EmitNativeCode(CodeGenerator* codegen) override {
350     CodeGeneratorRISCV64* riscv64_codegen = down_cast<CodeGeneratorRISCV64*>(codegen);
351     __ Bind(GetEntryLabel());
352     if (instruction_->CanThrowIntoCatchBlock()) {
353       // Live registers will be restored in the catch block if caught.
354       SaveLiveRegisters(codegen, instruction_->GetLocations());
355     }
356     riscv64_codegen->InvokeRuntime(
357         kQuickThrowNullPointer, instruction_, instruction_->GetDexPc(), this);
358     CheckEntrypointTypes<kQuickThrowNullPointer, void, void>();
359   }
360 
IsFatal() const361   bool IsFatal() const override { return true; }
362 
GetDescription() const363   const char* GetDescription() const override { return "NullCheckSlowPathRISCV64"; }
364 
365  private:
366   DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathRISCV64);
367 };
368 
369 class BoundsCheckSlowPathRISCV64 : public SlowPathCodeRISCV64 {
370  public:
BoundsCheckSlowPathRISCV64(HBoundsCheck * instruction)371   explicit BoundsCheckSlowPathRISCV64(HBoundsCheck* instruction)
372       : SlowPathCodeRISCV64(instruction) {}
373 
EmitNativeCode(CodeGenerator * codegen)374   void EmitNativeCode(CodeGenerator* codegen) override {
375     LocationSummary* locations = instruction_->GetLocations();
376     CodeGeneratorRISCV64* riscv64_codegen = down_cast<CodeGeneratorRISCV64*>(codegen);
377     __ Bind(GetEntryLabel());
378     if (instruction_->CanThrowIntoCatchBlock()) {
379       // Live registers will be restored in the catch block if caught.
380       SaveLiveRegisters(codegen, instruction_->GetLocations());
381     }
382     // We're moving two locations to locations that could overlap, so we need a parallel
383     // move resolver.
384     InvokeRuntimeCallingConvention calling_convention;
385     codegen->EmitParallelMoves(locations->InAt(0),
386                                Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
387                                DataType::Type::kInt32,
388                                locations->InAt(1),
389                                Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
390                                DataType::Type::kInt32);
391     QuickEntrypointEnum entrypoint = instruction_->AsBoundsCheck()->IsStringCharAt() ?
392                                          kQuickThrowStringBounds :
393                                          kQuickThrowArrayBounds;
394     riscv64_codegen->InvokeRuntime(entrypoint, instruction_, instruction_->GetDexPc(), this);
395     CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>();
396     CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>();
397   }
398 
IsFatal() const399   bool IsFatal() const override { return true; }
400 
GetDescription() const401   const char* GetDescription() const override { return "BoundsCheckSlowPathRISCV64"; }
402 
403  private:
404   DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathRISCV64);
405 };
406 
407 class LoadClassSlowPathRISCV64 : public SlowPathCodeRISCV64 {
408  public:
LoadClassSlowPathRISCV64(HLoadClass * cls,HInstruction * at)409   LoadClassSlowPathRISCV64(HLoadClass* cls, HInstruction* at) : SlowPathCodeRISCV64(at), cls_(cls) {
410     DCHECK(at->IsLoadClass() || at->IsClinitCheck());
411     DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_);
412   }
413 
EmitNativeCode(CodeGenerator * codegen)414   void EmitNativeCode(CodeGenerator* codegen) override {
415     LocationSummary* locations = instruction_->GetLocations();
416     Location out = locations->Out();
417     const uint32_t dex_pc = instruction_->GetDexPc();
418     bool must_resolve_type = instruction_->IsLoadClass() && cls_->MustResolveTypeOnSlowPath();
419     bool must_do_clinit = instruction_->IsClinitCheck() || cls_->MustGenerateClinitCheck();
420 
421     CodeGeneratorRISCV64* riscv64_codegen = down_cast<CodeGeneratorRISCV64*>(codegen);
422     __ Bind(GetEntryLabel());
423     SaveLiveRegisters(codegen, locations);
424 
425     InvokeRuntimeCallingConvention calling_convention;
426     if (must_resolve_type) {
427       DCHECK(IsSameDexFile(cls_->GetDexFile(), riscv64_codegen->GetGraph()->GetDexFile()) ||
428              riscv64_codegen->GetCompilerOptions().WithinOatFile(&cls_->GetDexFile()) ||
429              ContainsElement(Runtime::Current()->GetClassLinker()->GetBootClassPath(),
430                              &cls_->GetDexFile()));
431       dex::TypeIndex type_index = cls_->GetTypeIndex();
432       __ LoadConst32(calling_convention.GetRegisterAt(0), type_index.index_);
433       if (cls_->NeedsAccessCheck()) {
434         CheckEntrypointTypes<kQuickResolveTypeAndVerifyAccess, void*, uint32_t>();
435         riscv64_codegen->InvokeRuntime(
436             kQuickResolveTypeAndVerifyAccess, instruction_, dex_pc, this);
437       } else {
438         CheckEntrypointTypes<kQuickResolveType, void*, uint32_t>();
439         riscv64_codegen->InvokeRuntime(kQuickResolveType, instruction_, dex_pc, this);
440       }
441       // If we also must_do_clinit, the resolved type is now in the correct register.
442     } else {
443       DCHECK(must_do_clinit);
444       Location source = instruction_->IsLoadClass() ? out : locations->InAt(0);
445       riscv64_codegen->MoveLocation(
446           Location::RegisterLocation(calling_convention.GetRegisterAt(0)), source, cls_->GetType());
447     }
448     if (must_do_clinit) {
449       riscv64_codegen->InvokeRuntime(kQuickInitializeStaticStorage, instruction_, dex_pc, this);
450       CheckEntrypointTypes<kQuickInitializeStaticStorage, void*, mirror::Class*>();
451     }
452 
453     // Move the class to the desired location.
454     if (out.IsValid()) {
455       DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
456       DataType::Type type = DataType::Type::kReference;
457       DCHECK_EQ(type, instruction_->GetType());
458       riscv64_codegen->MoveLocation(out, calling_convention.GetReturnLocation(type), type);
459     }
460     RestoreLiveRegisters(codegen, locations);
461 
462     __ J(GetExitLabel());
463   }
464 
GetDescription() const465   const char* GetDescription() const override { return "LoadClassSlowPathRISCV64"; }
466 
467  private:
468   // The class this slow path will load.
469   HLoadClass* const cls_;
470 
471   DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathRISCV64);
472 };
473 
474 class DeoptimizationSlowPathRISCV64 : public SlowPathCodeRISCV64 {
475  public:
DeoptimizationSlowPathRISCV64(HDeoptimize * instruction)476   explicit DeoptimizationSlowPathRISCV64(HDeoptimize* instruction)
477       : SlowPathCodeRISCV64(instruction) {}
478 
EmitNativeCode(CodeGenerator * codegen)479   void EmitNativeCode(CodeGenerator* codegen) override {
480     CodeGeneratorRISCV64* riscv64_codegen = down_cast<CodeGeneratorRISCV64*>(codegen);
481     __ Bind(GetEntryLabel());
482     LocationSummary* locations = instruction_->GetLocations();
483     SaveLiveRegisters(codegen, locations);
484     InvokeRuntimeCallingConvention calling_convention;
485     __ LoadConst32(calling_convention.GetRegisterAt(0),
486                    static_cast<uint32_t>(instruction_->AsDeoptimize()->GetDeoptimizationKind()));
487     riscv64_codegen->InvokeRuntime(kQuickDeoptimize, instruction_, instruction_->GetDexPc(), this);
488     CheckEntrypointTypes<kQuickDeoptimize, void, DeoptimizationKind>();
489   }
490 
GetDescription() const491   const char* GetDescription() const override { return "DeoptimizationSlowPathRISCV64"; }
492 
493  private:
494   DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathRISCV64);
495 };
496 
497 // Slow path generating a read barrier for a GC root.
498 class ReadBarrierForRootSlowPathRISCV64 : public SlowPathCodeRISCV64 {
499  public:
ReadBarrierForRootSlowPathRISCV64(HInstruction * instruction,Location out,Location root)500   ReadBarrierForRootSlowPathRISCV64(HInstruction* instruction, Location out, Location root)
501       : SlowPathCodeRISCV64(instruction), out_(out), root_(root) {
502   }
503 
EmitNativeCode(CodeGenerator * codegen)504   void EmitNativeCode(CodeGenerator* codegen) override {
505     DCHECK(codegen->EmitReadBarrier());
506     LocationSummary* locations = instruction_->GetLocations();
507     DataType::Type type = DataType::Type::kReference;
508     XRegister reg_out = out_.AsRegister<XRegister>();
509     DCHECK(locations->CanCall());
510     DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out));
511     DCHECK(instruction_->IsLoadClass() ||
512            instruction_->IsLoadString() ||
513            (instruction_->IsInvoke() && instruction_->GetLocations()->Intrinsified()))
514         << "Unexpected instruction in read barrier for GC root slow path: "
515         << instruction_->DebugName();
516 
517     __ Bind(GetEntryLabel());
518     SaveLiveRegisters(codegen, locations);
519 
520     InvokeRuntimeCallingConvention calling_convention;
521     CodeGeneratorRISCV64* riscv64_codegen = down_cast<CodeGeneratorRISCV64*>(codegen);
522     riscv64_codegen->MoveLocation(Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
523                                   root_,
524                                   DataType::Type::kReference);
525     riscv64_codegen->InvokeRuntime(kQuickReadBarrierForRootSlow,
526                                    instruction_,
527                                    instruction_->GetDexPc(),
528                                    this);
529     CheckEntrypointTypes<kQuickReadBarrierForRootSlow, mirror::Object*, GcRoot<mirror::Object>*>();
530     riscv64_codegen->MoveLocation(out_, calling_convention.GetReturnLocation(type), type);
531 
532     RestoreLiveRegisters(codegen, locations);
533     __ J(GetExitLabel());
534   }
535 
GetDescription() const536   const char* GetDescription() const override { return "ReadBarrierForRootSlowPathRISCV64"; }
537 
538  private:
539   const Location out_;
540   const Location root_;
541 
542   DISALLOW_COPY_AND_ASSIGN(ReadBarrierForRootSlowPathRISCV64);
543 };
544 
545 class MethodEntryExitHooksSlowPathRISCV64 : public SlowPathCodeRISCV64 {
546  public:
MethodEntryExitHooksSlowPathRISCV64(HInstruction * instruction)547   explicit MethodEntryExitHooksSlowPathRISCV64(HInstruction* instruction)
548       : SlowPathCodeRISCV64(instruction) {}
549 
EmitNativeCode(CodeGenerator * codegen)550   void EmitNativeCode(CodeGenerator* codegen) override {
551     LocationSummary* locations = instruction_->GetLocations();
552     QuickEntrypointEnum entry_point =
553         (instruction_->IsMethodEntryHook()) ? kQuickMethodEntryHook : kQuickMethodExitHook;
554     CodeGeneratorRISCV64* riscv64_codegen = down_cast<CodeGeneratorRISCV64*>(codegen);
555     __ Bind(GetEntryLabel());
556     SaveLiveRegisters(codegen, locations);
557     if (instruction_->IsMethodExitHook()) {
558       __ Li(A4, riscv64_codegen->GetFrameSize());
559     }
560     riscv64_codegen->InvokeRuntime(entry_point, instruction_, instruction_->GetDexPc(), this);
561     RestoreLiveRegisters(codegen, locations);
562     __ J(GetExitLabel());
563   }
564 
GetDescription() const565   const char* GetDescription() const override {
566     return "MethodEntryExitHooksSlowPathRISCV";
567   }
568 
569  private:
570   DISALLOW_COPY_AND_ASSIGN(MethodEntryExitHooksSlowPathRISCV64);
571 };
572 
573 class ArraySetSlowPathRISCV64 : public SlowPathCodeRISCV64 {
574  public:
ArraySetSlowPathRISCV64(HInstruction * instruction)575   explicit ArraySetSlowPathRISCV64(HInstruction* instruction) : SlowPathCodeRISCV64(instruction) {}
576 
EmitNativeCode(CodeGenerator * codegen)577   void EmitNativeCode(CodeGenerator* codegen) override {
578     LocationSummary* locations = instruction_->GetLocations();
579     __ Bind(GetEntryLabel());
580     SaveLiveRegisters(codegen, locations);
581 
582     InvokeRuntimeCallingConvention calling_convention;
583     HParallelMove parallel_move(codegen->GetGraph()->GetAllocator());
584     parallel_move.AddMove(
585         locations->InAt(0),
586         Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
587         DataType::Type::kReference,
588         nullptr);
589     parallel_move.AddMove(
590         locations->InAt(1),
591         Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
592         DataType::Type::kInt32,
593         nullptr);
594     parallel_move.AddMove(
595         locations->InAt(2),
596         Location::RegisterLocation(calling_convention.GetRegisterAt(2)),
597         DataType::Type::kReference,
598         nullptr);
599     codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
600 
601     CodeGeneratorRISCV64* riscv64_codegen = down_cast<CodeGeneratorRISCV64*>(codegen);
602     riscv64_codegen->InvokeRuntime(kQuickAputObject, instruction_, instruction_->GetDexPc(), this);
603     CheckEntrypointTypes<kQuickAputObject, void, mirror::Array*, int32_t, mirror::Object*>();
604     RestoreLiveRegisters(codegen, locations);
605     __ J(GetExitLabel());
606   }
607 
GetDescription() const608   const char* GetDescription() const override { return "ArraySetSlowPathRISCV64"; }
609 
610  private:
611   DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathRISCV64);
612 };
613 
614 class TypeCheckSlowPathRISCV64 : public SlowPathCodeRISCV64 {
615  public:
TypeCheckSlowPathRISCV64(HInstruction * instruction,bool is_fatal)616   explicit TypeCheckSlowPathRISCV64(HInstruction* instruction, bool is_fatal)
617       : SlowPathCodeRISCV64(instruction), is_fatal_(is_fatal) {}
618 
EmitNativeCode(CodeGenerator * codegen)619   void EmitNativeCode(CodeGenerator* codegen) override {
620     LocationSummary* locations = instruction_->GetLocations();
621 
622     uint32_t dex_pc = instruction_->GetDexPc();
623     DCHECK(instruction_->IsCheckCast()
624            || !locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
625     CodeGeneratorRISCV64* riscv64_codegen = down_cast<CodeGeneratorRISCV64*>(codegen);
626 
627     __ Bind(GetEntryLabel());
628     if (!is_fatal_ || instruction_->CanThrowIntoCatchBlock()) {
629       SaveLiveRegisters(codegen, locations);
630     }
631 
632     // We're moving two locations to locations that could overlap, so we need a parallel
633     // move resolver.
634     InvokeRuntimeCallingConvention calling_convention;
635     codegen->EmitParallelMoves(locations->InAt(0),
636                                Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
637                                DataType::Type::kReference,
638                                locations->InAt(1),
639                                Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
640                                DataType::Type::kReference);
641     if (instruction_->IsInstanceOf()) {
642       riscv64_codegen->InvokeRuntime(kQuickInstanceofNonTrivial, instruction_, dex_pc, this);
643       CheckEntrypointTypes<kQuickInstanceofNonTrivial, size_t, mirror::Object*, mirror::Class*>();
644       DataType::Type ret_type = instruction_->GetType();
645       Location ret_loc = calling_convention.GetReturnLocation(ret_type);
646       riscv64_codegen->MoveLocation(locations->Out(), ret_loc, ret_type);
647     } else {
648       DCHECK(instruction_->IsCheckCast());
649       riscv64_codegen->InvokeRuntime(kQuickCheckInstanceOf, instruction_, dex_pc, this);
650       CheckEntrypointTypes<kQuickCheckInstanceOf, void, mirror::Object*, mirror::Class*>();
651     }
652 
653     if (!is_fatal_) {
654       RestoreLiveRegisters(codegen, locations);
655       __ J(GetExitLabel());
656     }
657   }
658 
GetDescription() const659   const char* GetDescription() const override { return "TypeCheckSlowPathRISCV64"; }
660 
IsFatal() const661   bool IsFatal() const override { return is_fatal_; }
662 
663  private:
664   const bool is_fatal_;
665 
666   DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathRISCV64);
667 };
668 
669 class DivZeroCheckSlowPathRISCV64 : public SlowPathCodeRISCV64 {
670  public:
DivZeroCheckSlowPathRISCV64(HDivZeroCheck * instruction)671   explicit DivZeroCheckSlowPathRISCV64(HDivZeroCheck* instruction)
672       : SlowPathCodeRISCV64(instruction) {}
673 
EmitNativeCode(CodeGenerator * codegen)674   void EmitNativeCode(CodeGenerator* codegen) override {
675     CodeGeneratorRISCV64* riscv64_codegen = down_cast<CodeGeneratorRISCV64*>(codegen);
676     __ Bind(GetEntryLabel());
677     riscv64_codegen->InvokeRuntime(
678         kQuickThrowDivZero, instruction_, instruction_->GetDexPc(), this);
679     CheckEntrypointTypes<kQuickThrowDivZero, void, void>();
680   }
681 
IsFatal() const682   bool IsFatal() const override { return true; }
683 
GetDescription() const684   const char* GetDescription() const override { return "DivZeroCheckSlowPathRISCV64"; }
685 
686  private:
687   DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathRISCV64);
688 };
689 
690 class ReadBarrierMarkSlowPathRISCV64 : public SlowPathCodeRISCV64 {
691  public:
ReadBarrierMarkSlowPathRISCV64(HInstruction * instruction,Location ref,Location entrypoint)692   ReadBarrierMarkSlowPathRISCV64(HInstruction* instruction, Location ref, Location entrypoint)
693       : SlowPathCodeRISCV64(instruction), ref_(ref), entrypoint_(entrypoint) {
694     DCHECK(entrypoint.IsRegister());
695   }
696 
GetDescription() const697   const char* GetDescription() const override { return "ReadBarrierMarkSlowPathRISCV64"; }
698 
EmitNativeCode(CodeGenerator * codegen)699   void EmitNativeCode(CodeGenerator* codegen) override {
700     DCHECK(codegen->EmitReadBarrier());
701     LocationSummary* locations = instruction_->GetLocations();
702     XRegister ref_reg = ref_.AsRegister<XRegister>();
703     DCHECK(locations->CanCall());
704     DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_reg)) << ref_reg;
705     DCHECK(instruction_->IsInstanceFieldGet() ||
706            instruction_->IsStaticFieldGet() ||
707            instruction_->IsArrayGet() ||
708            instruction_->IsArraySet() ||
709            instruction_->IsLoadClass() ||
710            instruction_->IsLoadString() ||
711            instruction_->IsInstanceOf() ||
712            instruction_->IsCheckCast() ||
713            (instruction_->IsInvoke() && instruction_->GetLocations()->Intrinsified()))
714         << "Unexpected instruction in read barrier marking slow path: "
715         << instruction_->DebugName();
716 
717     __ Bind(GetEntryLabel());
718     // No need to save live registers; it's taken care of by the
719     // entrypoint. Also, there is no need to update the stack mask,
720     // as this runtime call will not trigger a garbage collection.
721     CodeGeneratorRISCV64* riscv64_codegen = down_cast<CodeGeneratorRISCV64*>(codegen);
722     DCHECK(ref_reg >= T0 && ref_reg != TR);
723 
724     // "Compact" slow path, saving two moves.
725     //
726     // Instead of using the standard runtime calling convention (input
727     // and output in A0 and V0 respectively):
728     //
729     //   A0 <- ref
730     //   V0 <- ReadBarrierMark(A0)
731     //   ref <- V0
732     //
733     // we just use rX (the register containing `ref`) as input and output
734     // of a dedicated entrypoint:
735     //
736     //   rX <- ReadBarrierMarkRegX(rX)
737     //
738     riscv64_codegen->ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction_, this);
739     DCHECK_NE(entrypoint_.AsRegister<XRegister>(), TMP);  // A taken branch can clobber `TMP`.
740     __ Jalr(entrypoint_.AsRegister<XRegister>());  // Clobbers `RA` (used as the `entrypoint_`).
741     __ J(GetExitLabel());
742   }
743 
744  private:
745   // The location (register) of the marked object reference.
746   const Location ref_;
747 
748   // The location of the already loaded entrypoint.
749   const Location entrypoint_;
750 
751   DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkSlowPathRISCV64);
752 };
753 
754 class LoadStringSlowPathRISCV64 : public SlowPathCodeRISCV64 {
755  public:
LoadStringSlowPathRISCV64(HLoadString * instruction)756   explicit LoadStringSlowPathRISCV64(HLoadString* instruction)
757       : SlowPathCodeRISCV64(instruction) {}
758 
EmitNativeCode(CodeGenerator * codegen)759   void EmitNativeCode(CodeGenerator* codegen) override {
760     DCHECK(instruction_->IsLoadString());
761     DCHECK_EQ(instruction_->AsLoadString()->GetLoadKind(), HLoadString::LoadKind::kBssEntry);
762     LocationSummary* locations = instruction_->GetLocations();
763     DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
764     const dex::StringIndex string_index = instruction_->AsLoadString()->GetStringIndex();
765     CodeGeneratorRISCV64* riscv64_codegen = down_cast<CodeGeneratorRISCV64*>(codegen);
766     InvokeRuntimeCallingConvention calling_convention;
767     __ Bind(GetEntryLabel());
768     SaveLiveRegisters(codegen, locations);
769 
770     __ LoadConst32(calling_convention.GetRegisterAt(0), string_index.index_);
771     riscv64_codegen->InvokeRuntime(
772         kQuickResolveString, instruction_, instruction_->GetDexPc(), this);
773     CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
774 
775     DataType::Type type = DataType::Type::kReference;
776     DCHECK_EQ(type, instruction_->GetType());
777     riscv64_codegen->MoveLocation(
778         locations->Out(), calling_convention.GetReturnLocation(type), type);
779     RestoreLiveRegisters(codegen, locations);
780 
781     __ J(GetExitLabel());
782   }
783 
GetDescription() const784   const char* GetDescription() const override { return "LoadStringSlowPathRISCV64"; }
785 
786  private:
787   DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathRISCV64);
788 };
789 
790 #undef __
791 #define __ down_cast<Riscv64Assembler*>(GetAssembler())->  // NOLINT
792 
793 template <typename Reg,
794           void (Riscv64Assembler::*opS)(Reg, FRegister, FRegister),
795           void (Riscv64Assembler::*opD)(Reg, FRegister, FRegister)>
FpBinOp(Reg rd,FRegister rs1,FRegister rs2,DataType::Type type)796 inline void InstructionCodeGeneratorRISCV64::FpBinOp(
797     Reg rd, FRegister rs1, FRegister rs2, DataType::Type type) {
798   Riscv64Assembler* assembler = down_cast<CodeGeneratorRISCV64*>(codegen_)->GetAssembler();
799   if (type == DataType::Type::kFloat32) {
800     (assembler->*opS)(rd, rs1, rs2);
801   } else {
802     DCHECK_EQ(type, DataType::Type::kFloat64);
803     (assembler->*opD)(rd, rs1, rs2);
804   }
805 }
806 
FAdd(FRegister rd,FRegister rs1,FRegister rs2,DataType::Type type)807 void InstructionCodeGeneratorRISCV64::FAdd(
808     FRegister rd, FRegister rs1, FRegister rs2, DataType::Type type) {
809   FpBinOp<FRegister, &Riscv64Assembler::FAddS, &Riscv64Assembler::FAddD>(rd, rs1, rs2, type);
810 }
811 
FSub(FRegister rd,FRegister rs1,FRegister rs2,DataType::Type type)812 inline void InstructionCodeGeneratorRISCV64::FSub(
813     FRegister rd, FRegister rs1, FRegister rs2, DataType::Type type) {
814   FpBinOp<FRegister, &Riscv64Assembler::FSubS, &Riscv64Assembler::FSubD>(rd, rs1, rs2, type);
815 }
816 
FDiv(FRegister rd,FRegister rs1,FRegister rs2,DataType::Type type)817 inline void InstructionCodeGeneratorRISCV64::FDiv(
818     FRegister rd, FRegister rs1, FRegister rs2, DataType::Type type) {
819   FpBinOp<FRegister, &Riscv64Assembler::FDivS, &Riscv64Assembler::FDivD>(rd, rs1, rs2, type);
820 }
821 
FMul(FRegister rd,FRegister rs1,FRegister rs2,DataType::Type type)822 inline void InstructionCodeGeneratorRISCV64::FMul(
823     FRegister rd, FRegister rs1, FRegister rs2, DataType::Type type) {
824   FpBinOp<FRegister, &Riscv64Assembler::FMulS, &Riscv64Assembler::FMulD>(rd, rs1, rs2, type);
825 }
826 
FMin(FRegister rd,FRegister rs1,FRegister rs2,DataType::Type type)827 inline void InstructionCodeGeneratorRISCV64::FMin(
828     FRegister rd, FRegister rs1, FRegister rs2, DataType::Type type) {
829   FpBinOp<FRegister, &Riscv64Assembler::FMinS, &Riscv64Assembler::FMinD>(rd, rs1, rs2, type);
830 }
831 
FMax(FRegister rd,FRegister rs1,FRegister rs2,DataType::Type type)832 inline void InstructionCodeGeneratorRISCV64::FMax(
833     FRegister rd, FRegister rs1, FRegister rs2, DataType::Type type) {
834   FpBinOp<FRegister, &Riscv64Assembler::FMaxS, &Riscv64Assembler::FMaxD>(rd, rs1, rs2, type);
835 }
836 
FEq(XRegister rd,FRegister rs1,FRegister rs2,DataType::Type type)837 inline void InstructionCodeGeneratorRISCV64::FEq(
838     XRegister rd, FRegister rs1, FRegister rs2, DataType::Type type) {
839   FpBinOp<XRegister, &Riscv64Assembler::FEqS, &Riscv64Assembler::FEqD>(rd, rs1, rs2, type);
840 }
841 
FLt(XRegister rd,FRegister rs1,FRegister rs2,DataType::Type type)842 inline void InstructionCodeGeneratorRISCV64::FLt(
843     XRegister rd, FRegister rs1, FRegister rs2, DataType::Type type) {
844   FpBinOp<XRegister, &Riscv64Assembler::FLtS, &Riscv64Assembler::FLtD>(rd, rs1, rs2, type);
845 }
846 
FLe(XRegister rd,FRegister rs1,FRegister rs2,DataType::Type type)847 inline void InstructionCodeGeneratorRISCV64::FLe(
848     XRegister rd, FRegister rs1, FRegister rs2, DataType::Type type) {
849   FpBinOp<XRegister, &Riscv64Assembler::FLeS, &Riscv64Assembler::FLeD>(rd, rs1, rs2, type);
850 }
851 
852 template <typename Reg,
853           void (Riscv64Assembler::*opS)(Reg, FRegister),
854           void (Riscv64Assembler::*opD)(Reg, FRegister)>
FpUnOp(Reg rd,FRegister rs1,DataType::Type type)855 inline void InstructionCodeGeneratorRISCV64::FpUnOp(
856     Reg rd, FRegister rs1, DataType::Type type) {
857   Riscv64Assembler* assembler = down_cast<CodeGeneratorRISCV64*>(codegen_)->GetAssembler();
858   if (type == DataType::Type::kFloat32) {
859     (assembler->*opS)(rd, rs1);
860   } else {
861     DCHECK_EQ(type, DataType::Type::kFloat64);
862     (assembler->*opD)(rd, rs1);
863   }
864 }
865 
FAbs(FRegister rd,FRegister rs1,DataType::Type type)866 inline void InstructionCodeGeneratorRISCV64::FAbs(
867     FRegister rd, FRegister rs1, DataType::Type type) {
868   FpUnOp<FRegister, &Riscv64Assembler::FAbsS, &Riscv64Assembler::FAbsD>(rd, rs1, type);
869 }
870 
FNeg(FRegister rd,FRegister rs1,DataType::Type type)871 inline void InstructionCodeGeneratorRISCV64::FNeg(
872     FRegister rd, FRegister rs1, DataType::Type type) {
873   FpUnOp<FRegister, &Riscv64Assembler::FNegS, &Riscv64Assembler::FNegD>(rd, rs1, type);
874 }
875 
FMv(FRegister rd,FRegister rs1,DataType::Type type)876 inline void InstructionCodeGeneratorRISCV64::FMv(
877     FRegister rd, FRegister rs1, DataType::Type type) {
878   FpUnOp<FRegister, &Riscv64Assembler::FMvS, &Riscv64Assembler::FMvD>(rd, rs1, type);
879 }
880 
FMvX(XRegister rd,FRegister rs1,DataType::Type type)881 inline void InstructionCodeGeneratorRISCV64::FMvX(
882     XRegister rd, FRegister rs1, DataType::Type type) {
883   FpUnOp<XRegister, &Riscv64Assembler::FMvXW, &Riscv64Assembler::FMvXD>(rd, rs1, type);
884 }
885 
FClass(XRegister rd,FRegister rs1,DataType::Type type)886 void InstructionCodeGeneratorRISCV64::FClass(
887     XRegister rd, FRegister rs1, DataType::Type type) {
888   FpUnOp<XRegister, &Riscv64Assembler::FClassS, &Riscv64Assembler::FClassD>(rd, rs1, type);
889 }
890 
Load(Location out,XRegister rs1,int32_t offset,DataType::Type type)891 void InstructionCodeGeneratorRISCV64::Load(
892     Location out, XRegister rs1, int32_t offset, DataType::Type type) {
893   switch (type) {
894     case DataType::Type::kBool:
895     case DataType::Type::kUint8:
896       __ Loadbu(out.AsRegister<XRegister>(), rs1, offset);
897       break;
898     case DataType::Type::kInt8:
899       __ Loadb(out.AsRegister<XRegister>(), rs1, offset);
900       break;
901     case DataType::Type::kUint16:
902       __ Loadhu(out.AsRegister<XRegister>(), rs1, offset);
903       break;
904     case DataType::Type::kInt16:
905       __ Loadh(out.AsRegister<XRegister>(), rs1, offset);
906       break;
907     case DataType::Type::kInt32:
908       __ Loadw(out.AsRegister<XRegister>(), rs1, offset);
909       break;
910     case DataType::Type::kInt64:
911       __ Loadd(out.AsRegister<XRegister>(), rs1, offset);
912       break;
913     case DataType::Type::kReference:
914       __ Loadwu(out.AsRegister<XRegister>(), rs1, offset);
915       break;
916     case DataType::Type::kFloat32:
917       __ FLoadw(out.AsFpuRegister<FRegister>(), rs1, offset);
918       break;
919     case DataType::Type::kFloat64:
920       __ FLoadd(out.AsFpuRegister<FRegister>(), rs1, offset);
921       break;
922     case DataType::Type::kUint32:
923     case DataType::Type::kUint64:
924     case DataType::Type::kVoid:
925       LOG(FATAL) << "Unreachable type " << type;
926       UNREACHABLE();
927   }
928 }
929 
Store(Location value,XRegister rs1,int32_t offset,DataType::Type type)930 void InstructionCodeGeneratorRISCV64::Store(
931     Location value, XRegister rs1, int32_t offset, DataType::Type type) {
932   DCHECK_IMPLIES(value.IsConstant(), IsZeroBitPattern(value.GetConstant()));
933   if (kPoisonHeapReferences && type == DataType::Type::kReference && !value.IsConstant()) {
934     riscv64::ScratchRegisterScope srs(GetAssembler());
935     XRegister tmp = srs.AllocateXRegister();
936     __ Mv(tmp, value.AsRegister<XRegister>());
937     codegen_->PoisonHeapReference(tmp);
938     __ Storew(tmp, rs1, offset);
939     return;
940   }
941   switch (type) {
942     case DataType::Type::kBool:
943     case DataType::Type::kUint8:
944     case DataType::Type::kInt8:
945       __ Storeb(InputXRegisterOrZero(value), rs1, offset);
946       break;
947     case DataType::Type::kUint16:
948     case DataType::Type::kInt16:
949       __ Storeh(InputXRegisterOrZero(value), rs1, offset);
950       break;
951     case DataType::Type::kFloat32:
952       if (!value.IsConstant()) {
953         __ FStorew(value.AsFpuRegister<FRegister>(), rs1, offset);
954         break;
955       }
956       FALLTHROUGH_INTENDED;
957     case DataType::Type::kInt32:
958     case DataType::Type::kReference:
959       __ Storew(InputXRegisterOrZero(value), rs1, offset);
960       break;
961     case DataType::Type::kFloat64:
962       if (!value.IsConstant()) {
963         __ FStored(value.AsFpuRegister<FRegister>(), rs1, offset);
964         break;
965       }
966       FALLTHROUGH_INTENDED;
967     case DataType::Type::kInt64:
968       __ Stored(InputXRegisterOrZero(value), rs1, offset);
969       break;
970     case DataType::Type::kUint32:
971     case DataType::Type::kUint64:
972     case DataType::Type::kVoid:
973       LOG(FATAL) << "Unreachable type " << type;
974       UNREACHABLE();
975   }
976 }
977 
StoreSeqCst(Location value,XRegister rs1,int32_t offset,DataType::Type type,HInstruction * instruction)978 void InstructionCodeGeneratorRISCV64::StoreSeqCst(Location value,
979                                                   XRegister rs1,
980                                                   int32_t offset,
981                                                   DataType::Type type,
982                                                   HInstruction* instruction) {
983   if (DataType::Size(type) >= 4u) {
984     // Use AMOSWAP for 32-bit and 64-bit data types.
985     ScratchRegisterScope srs(GetAssembler());
986     XRegister swap_src = kNoXRegister;
987     if (kPoisonHeapReferences && type == DataType::Type::kReference && !value.IsConstant()) {
988       swap_src = srs.AllocateXRegister();
989       __ Mv(swap_src, value.AsRegister<XRegister>());
990       codegen_->PoisonHeapReference(swap_src);
991     } else if (DataType::IsFloatingPointType(type) && !value.IsConstant()) {
992       swap_src = srs.AllocateXRegister();
993       FMvX(swap_src, value.AsFpuRegister<FRegister>(), type);
994     } else {
995       swap_src = InputXRegisterOrZero(value);
996     }
997     XRegister addr = rs1;
998     if (offset != 0) {
999       addr = srs.AllocateXRegister();
1000       __ AddConst64(addr, rs1, offset);
1001     }
1002     if (DataType::Is64BitType(type)) {
1003       __ AmoSwapD(Zero, swap_src, addr, AqRl::kRelease);
1004     } else {
1005       __ AmoSwapW(Zero, swap_src, addr, AqRl::kRelease);
1006     }
1007     if (instruction != nullptr) {
1008       codegen_->MaybeRecordImplicitNullCheck(instruction);
1009     }
1010   } else {
1011     // Use fences for smaller data types.
1012     codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
1013     Store(value, rs1, offset, type);
1014     if (instruction != nullptr) {
1015       codegen_->MaybeRecordImplicitNullCheck(instruction);
1016     }
1017     codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
1018   }
1019 }
1020 
ShNAdd(XRegister rd,XRegister rs1,XRegister rs2,DataType::Type type)1021 void InstructionCodeGeneratorRISCV64::ShNAdd(
1022     XRegister rd, XRegister rs1, XRegister rs2, DataType::Type type) {
1023   switch (type) {
1024     case DataType::Type::kBool:
1025     case DataType::Type::kUint8:
1026     case DataType::Type::kInt8:
1027       DCHECK_EQ(DataType::SizeShift(type), 0u);
1028       __ Add(rd, rs1, rs2);
1029       break;
1030     case DataType::Type::kUint16:
1031     case DataType::Type::kInt16:
1032       DCHECK_EQ(DataType::SizeShift(type), 1u);
1033       __ Sh1Add(rd, rs1, rs2);
1034       break;
1035     case DataType::Type::kInt32:
1036     case DataType::Type::kReference:
1037     case DataType::Type::kFloat32:
1038       DCHECK_EQ(DataType::SizeShift(type), 2u);
1039       __ Sh2Add(rd, rs1, rs2);
1040       break;
1041     case DataType::Type::kInt64:
1042     case DataType::Type::kFloat64:
1043       DCHECK_EQ(DataType::SizeShift(type), 3u);
1044       __ Sh3Add(rd, rs1, rs2);
1045       break;
1046     case DataType::Type::kUint32:
1047     case DataType::Type::kUint64:
1048     case DataType::Type::kVoid:
1049       LOG(FATAL) << "Unreachable type " << type;
1050       UNREACHABLE();
1051   }
1052 }
1053 
GetAssembler() const1054 Riscv64Assembler* ParallelMoveResolverRISCV64::GetAssembler() const {
1055   return codegen_->GetAssembler();
1056 }
1057 
EmitMove(size_t index)1058 void ParallelMoveResolverRISCV64::EmitMove(size_t index) {
1059   MoveOperands* move = moves_[index];
1060   codegen_->MoveLocation(move->GetDestination(), move->GetSource(), move->GetType());
1061 }
1062 
EmitSwap(size_t index)1063 void ParallelMoveResolverRISCV64::EmitSwap(size_t index) {
1064   MoveOperands* move = moves_[index];
1065   codegen_->SwapLocations(move->GetDestination(), move->GetSource(), move->GetType());
1066 }
1067 
SpillScratch(int reg)1068 void ParallelMoveResolverRISCV64::SpillScratch([[maybe_unused]] int reg) {
1069   LOG(FATAL) << "Unimplemented";
1070   UNREACHABLE();
1071 }
1072 
RestoreScratch(int reg)1073 void ParallelMoveResolverRISCV64::RestoreScratch([[maybe_unused]] int reg) {
1074   LOG(FATAL) << "Unimplemented";
1075   UNREACHABLE();
1076 }
1077 
Exchange(int index1,int index2,bool double_slot)1078 void ParallelMoveResolverRISCV64::Exchange(int index1, int index2, bool double_slot) {
1079   // We have 2 scratch X registers and 1 scratch F register that we can use. We prefer
1080   // to use X registers for the swap but if both offsets are too big, we need to reserve
1081   // one of the X registers for address adjustment and use an F register.
1082   bool use_fp_tmp2 = false;
1083   if (!IsInt<12>(index2)) {
1084     if (!IsInt<12>(index1)) {
1085       use_fp_tmp2 = true;
1086     } else {
1087       std::swap(index1, index2);
1088     }
1089   }
1090   DCHECK_IMPLIES(!IsInt<12>(index2), use_fp_tmp2);
1091 
1092   Location loc1(double_slot ? Location::DoubleStackSlot(index1) : Location::StackSlot(index1));
1093   Location loc2(double_slot ? Location::DoubleStackSlot(index2) : Location::StackSlot(index2));
1094   riscv64::ScratchRegisterScope srs(GetAssembler());
1095   Location tmp = Location::RegisterLocation(srs.AllocateXRegister());
1096   DataType::Type tmp_type = double_slot ? DataType::Type::kInt64 : DataType::Type::kInt32;
1097   Location tmp2 = use_fp_tmp2
1098       ? Location::FpuRegisterLocation(srs.AllocateFRegister())
1099       : Location::RegisterLocation(srs.AllocateXRegister());
1100   DataType::Type tmp2_type = use_fp_tmp2
1101       ? (double_slot ? DataType::Type::kFloat64 : DataType::Type::kFloat32)
1102       : tmp_type;
1103 
1104   codegen_->MoveLocation(tmp, loc1, tmp_type);
1105   codegen_->MoveLocation(tmp2, loc2, tmp2_type);
1106   if (use_fp_tmp2) {
1107     codegen_->MoveLocation(loc2, tmp, tmp_type);
1108   } else {
1109     // We cannot use `Stored()` or `Storew()` via `MoveLocation()` because we have
1110     // no more scratch registers available. Use `Sd()` or `Sw()` explicitly.
1111     DCHECK(IsInt<12>(index2));
1112     if (double_slot) {
1113       __ Sd(tmp.AsRegister<XRegister>(), SP, index2);
1114     } else {
1115       __ Sw(tmp.AsRegister<XRegister>(), SP, index2);
1116     }
1117     srs.FreeXRegister(tmp.AsRegister<XRegister>());  // Free a temporary for `MoveLocation()`.
1118   }
1119   codegen_->MoveLocation(loc1, tmp2, tmp2_type);
1120 }
1121 
InstructionCodeGeneratorRISCV64(HGraph * graph,CodeGeneratorRISCV64 * codegen)1122 InstructionCodeGeneratorRISCV64::InstructionCodeGeneratorRISCV64(HGraph* graph,
1123                                                                  CodeGeneratorRISCV64* codegen)
1124     : InstructionCodeGenerator(graph, codegen),
1125       assembler_(codegen->GetAssembler()),
1126       codegen_(codegen) {}
1127 
GenerateClassInitializationCheck(SlowPathCodeRISCV64 * slow_path,XRegister class_reg)1128 void InstructionCodeGeneratorRISCV64::GenerateClassInitializationCheck(
1129     SlowPathCodeRISCV64* slow_path, XRegister class_reg) {
1130   ScratchRegisterScope srs(GetAssembler());
1131   XRegister tmp = srs.AllocateXRegister();
1132   XRegister tmp2 = srs.AllocateXRegister();
1133 
1134   // We shall load the full 32-bit status word with sign-extension and compare as unsigned
1135   // to a sign-extended shifted status value. This yields the same comparison as loading and
1136   // materializing unsigned but the constant is materialized with a single LUI instruction.
1137   __ Loadw(tmp, class_reg, mirror::Class::StatusOffset().SizeValue());  // Sign-extended.
1138   __ Li(tmp2, ShiftedSignExtendedClassStatusValue<ClassStatus::kVisiblyInitialized>());
1139   __ Bltu(tmp, tmp2, slow_path->GetEntryLabel());
1140   __ Bind(slow_path->GetExitLabel());
1141 }
1142 
GenerateBitstringTypeCheckCompare(HTypeCheckInstruction * instruction,XRegister temp)1143 void InstructionCodeGeneratorRISCV64::GenerateBitstringTypeCheckCompare(
1144     HTypeCheckInstruction* instruction, XRegister temp) {
1145   UNUSED(instruction);
1146   UNUSED(temp);
1147   LOG(FATAL) << "Unimplemented";
1148 }
1149 
GenerateSuspendCheck(HSuspendCheck * instruction,HBasicBlock * successor)1150 void InstructionCodeGeneratorRISCV64::GenerateSuspendCheck(HSuspendCheck* instruction,
1151                                                            HBasicBlock* successor) {
1152   if (instruction->IsNoOp()) {
1153     if (successor != nullptr) {
1154       __ J(codegen_->GetLabelOf(successor));
1155     }
1156     return;
1157   }
1158 
1159   if (codegen_->CanUseImplicitSuspendCheck()) {
1160     LOG(FATAL) << "Unimplemented ImplicitSuspendCheck";
1161     return;
1162   }
1163 
1164   SuspendCheckSlowPathRISCV64* slow_path =
1165       down_cast<SuspendCheckSlowPathRISCV64*>(instruction->GetSlowPath());
1166 
1167   if (slow_path == nullptr) {
1168     slow_path =
1169         new (codegen_->GetScopedAllocator()) SuspendCheckSlowPathRISCV64(instruction, successor);
1170     instruction->SetSlowPath(slow_path);
1171     codegen_->AddSlowPath(slow_path);
1172     if (successor != nullptr) {
1173       DCHECK(successor->IsLoopHeader());
1174     }
1175   } else {
1176     DCHECK_EQ(slow_path->GetSuccessor(), successor);
1177   }
1178 
1179   ScratchRegisterScope srs(GetAssembler());
1180   XRegister tmp = srs.AllocateXRegister();
1181   __ Loadw(tmp, TR, Thread::ThreadFlagsOffset<kRiscv64PointerSize>().Int32Value());
1182   static_assert(Thread::SuspendOrCheckpointRequestFlags() != std::numeric_limits<uint32_t>::max());
1183   static_assert(IsPowerOfTwo(Thread::SuspendOrCheckpointRequestFlags() + 1u));
1184   // Shift out other bits. Use an instruction that can be 16-bit with the "C" Standard Extension.
1185   __ Slli(tmp, tmp, CLZ(static_cast<uint64_t>(Thread::SuspendOrCheckpointRequestFlags())));
1186   if (successor == nullptr) {
1187     __ Bnez(tmp, slow_path->GetEntryLabel());
1188     __ Bind(slow_path->GetReturnLabel());
1189   } else {
1190     __ Beqz(tmp, codegen_->GetLabelOf(successor));
1191     __ J(slow_path->GetEntryLabel());
1192     // slow_path will return to GetLabelOf(successor).
1193   }
1194 }
1195 
GenerateReferenceLoadOneRegister(HInstruction * instruction,Location out,uint32_t offset,Location maybe_temp,ReadBarrierOption read_barrier_option)1196 void InstructionCodeGeneratorRISCV64::GenerateReferenceLoadOneRegister(
1197     HInstruction* instruction,
1198     Location out,
1199     uint32_t offset,
1200     Location maybe_temp,
1201     ReadBarrierOption read_barrier_option) {
1202   XRegister out_reg = out.AsRegister<XRegister>();
1203   if (read_barrier_option == kWithReadBarrier) {
1204     DCHECK(codegen_->EmitReadBarrier());
1205     if (kUseBakerReadBarrier) {
1206       // Load with fast path based Baker's read barrier.
1207       // /* HeapReference<Object> */ out = *(out + offset)
1208       codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction,
1209                                                       out,
1210                                                       out_reg,
1211                                                       offset,
1212                                                       maybe_temp,
1213                                                       /* needs_null_check= */ false);
1214     } else {
1215       // Load with slow path based read barrier.
1216       // Save the value of `out` into `maybe_temp` before overwriting it
1217       // in the following move operation, as we will need it for the
1218       // read barrier below.
1219       __ Mv(maybe_temp.AsRegister<XRegister>(), out_reg);
1220       // /* HeapReference<Object> */ out = *(out + offset)
1221       __ Loadwu(out_reg, out_reg, offset);
1222       codegen_->GenerateReadBarrierSlow(instruction, out, out, maybe_temp, offset);
1223     }
1224   } else {
1225     // Plain load with no read barrier.
1226     // /* HeapReference<Object> */ out = *(out + offset)
1227     __ Loadwu(out_reg, out_reg, offset);
1228     codegen_->MaybeUnpoisonHeapReference(out_reg);
1229   }
1230 }
1231 
GenerateReferenceLoadTwoRegisters(HInstruction * instruction,Location out,Location obj,uint32_t offset,Location maybe_temp,ReadBarrierOption read_barrier_option)1232 void InstructionCodeGeneratorRISCV64::GenerateReferenceLoadTwoRegisters(
1233     HInstruction* instruction,
1234     Location out,
1235     Location obj,
1236     uint32_t offset,
1237     Location maybe_temp,
1238     ReadBarrierOption read_barrier_option) {
1239   XRegister out_reg = out.AsRegister<XRegister>();
1240   XRegister obj_reg = obj.AsRegister<XRegister>();
1241   if (read_barrier_option == kWithReadBarrier) {
1242     DCHECK(codegen_->EmitReadBarrier());
1243     if (kUseBakerReadBarrier) {
1244       // Load with fast path based Baker's read barrier.
1245       // /* HeapReference<Object> */ out = *(obj + offset)
1246       codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction,
1247                                                       out,
1248                                                       obj_reg,
1249                                                       offset,
1250                                                       maybe_temp,
1251                                                       /* needs_null_check= */ false);
1252     } else {
1253       // Load with slow path based read barrier.
1254       // /* HeapReference<Object> */ out = *(obj + offset)
1255       __ Loadwu(out_reg, obj_reg, offset);
1256       codegen_->GenerateReadBarrierSlow(instruction, out, out, obj, offset);
1257     }
1258   } else {
1259     // Plain load with no read barrier.
1260     // /* HeapReference<Object> */ out = *(obj + offset)
1261     __ Loadwu(out_reg, obj_reg, offset);
1262     codegen_->MaybeUnpoisonHeapReference(out_reg);
1263   }
1264 }
1265 
AddGcRootBakerBarrierBarrierSlowPath(HInstruction * instruction,Location root,Location temp)1266 SlowPathCodeRISCV64* CodeGeneratorRISCV64::AddGcRootBakerBarrierBarrierSlowPath(
1267     HInstruction* instruction, Location root, Location temp) {
1268   SlowPathCodeRISCV64* slow_path =
1269       new (GetScopedAllocator()) ReadBarrierMarkSlowPathRISCV64(instruction, root, temp);
1270   AddSlowPath(slow_path);
1271   return slow_path;
1272 }
1273 
EmitBakerReadBarierMarkingCheck(SlowPathCodeRISCV64 * slow_path,Location root,Location temp)1274 void CodeGeneratorRISCV64::EmitBakerReadBarierMarkingCheck(
1275     SlowPathCodeRISCV64* slow_path, Location root, Location temp) {
1276   const int32_t entry_point_offset = ReadBarrierMarkEntrypointOffset(root);
1277   // Loading the entrypoint does not require a load acquire since it is only changed when
1278   // threads are suspended or running a checkpoint.
1279   __ Loadd(temp.AsRegister<XRegister>(), TR, entry_point_offset);
1280   __ Bnez(temp.AsRegister<XRegister>(), slow_path->GetEntryLabel());
1281   __ Bind(slow_path->GetExitLabel());
1282 }
1283 
GenerateGcRootFieldLoad(HInstruction * instruction,Location root,XRegister obj,uint32_t offset,ReadBarrierOption read_barrier_option,Riscv64Label * label_low)1284 void CodeGeneratorRISCV64::GenerateGcRootFieldLoad(HInstruction* instruction,
1285                                                    Location root,
1286                                                    XRegister obj,
1287                                                    uint32_t offset,
1288                                                    ReadBarrierOption read_barrier_option,
1289                                                    Riscv64Label* label_low) {
1290   DCHECK_IMPLIES(label_low != nullptr, offset == kLinkTimeOffsetPlaceholderLow) << offset;
1291   XRegister root_reg = root.AsRegister<XRegister>();
1292   if (read_barrier_option == kWithReadBarrier) {
1293     DCHECK(EmitReadBarrier());
1294     if (kUseBakerReadBarrier) {
1295       // Note that we do not actually check the value of `GetIsGcMarking()`
1296       // to decide whether to mark the loaded GC root or not.  Instead, we
1297       // load into `temp` (T6) the read barrier mark entry point corresponding
1298       // to register `root`. If `temp` is null, it means that `GetIsGcMarking()`
1299       // is false, and vice versa.
1300       //
1301       //     GcRoot<mirror::Object> root = *(obj+offset);  // Original reference load.
1302       //     temp = Thread::Current()->pReadBarrierMarkReg ## root.reg()
1303       //     if (temp != null) {
1304       //       root = temp(root)
1305       //     }
1306       //
1307       // TODO(riscv64): Introduce a "marking register" that holds the pointer to one of the
1308       // register marking entrypoints if marking (null if not marking) and make sure that
1309       // marking entrypoints for other registers are at known offsets, so that we can call
1310       // them using the "marking register" plus the offset embedded in the JALR instruction.
1311 
1312       if (label_low != nullptr) {
1313         __ Bind(label_low);
1314       }
1315       // /* GcRoot<mirror::Object> */ root = *(obj + offset)
1316       __ Loadwu(root_reg, obj, offset);
1317       static_assert(
1318           sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(GcRoot<mirror::Object>),
1319           "art::mirror::CompressedReference<mirror::Object> and art::GcRoot<mirror::Object> "
1320           "have different sizes.");
1321       static_assert(sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(int32_t),
1322                     "art::mirror::CompressedReference<mirror::Object> and int32_t "
1323                     "have different sizes.");
1324 
1325       // Use RA as temp. It is clobbered in the slow path anyway.
1326       Location temp = Location::RegisterLocation(RA);
1327       SlowPathCodeRISCV64* slow_path =
1328           AddGcRootBakerBarrierBarrierSlowPath(instruction, root, temp);
1329       EmitBakerReadBarierMarkingCheck(slow_path, root, temp);
1330     } else {
1331       // GC root loaded through a slow path for read barriers other
1332       // than Baker's.
1333       // /* GcRoot<mirror::Object>* */ root = obj + offset
1334       if (label_low != nullptr) {
1335         __ Bind(label_low);
1336       }
1337       __ AddConst32(root_reg, obj, offset);
1338       // /* mirror::Object* */ root = root->Read()
1339       GenerateReadBarrierForRootSlow(instruction, root, root);
1340     }
1341   } else {
1342     // Plain GC root load with no read barrier.
1343     // /* GcRoot<mirror::Object> */ root = *(obj + offset)
1344     if (label_low != nullptr) {
1345       __ Bind(label_low);
1346     }
1347     __ Loadwu(root_reg, obj, offset);
1348     // Note that GC roots are not affected by heap poisoning, thus we
1349     // do not have to unpoison `root_reg` here.
1350   }
1351 }
1352 
GenerateTestAndBranch(HInstruction * instruction,size_t condition_input_index,Riscv64Label * true_target,Riscv64Label * false_target)1353 void InstructionCodeGeneratorRISCV64::GenerateTestAndBranch(HInstruction* instruction,
1354                                                             size_t condition_input_index,
1355                                                             Riscv64Label* true_target,
1356                                                             Riscv64Label* false_target) {
1357   HInstruction* cond = instruction->InputAt(condition_input_index);
1358 
1359   if (true_target == nullptr && false_target == nullptr) {
1360     // Nothing to do. The code always falls through.
1361     return;
1362   } else if (cond->IsIntConstant()) {
1363     // Constant condition, statically compared against "true" (integer value 1).
1364     if (cond->AsIntConstant()->IsTrue()) {
1365       if (true_target != nullptr) {
1366         __ J(true_target);
1367       }
1368     } else {
1369       DCHECK(cond->AsIntConstant()->IsFalse()) << cond->AsIntConstant()->GetValue();
1370       if (false_target != nullptr) {
1371         __ J(false_target);
1372       }
1373     }
1374     return;
1375   }
1376 
1377   // The following code generates these patterns:
1378   //  (1) true_target == nullptr && false_target != nullptr
1379   //        - opposite condition true => branch to false_target
1380   //  (2) true_target != nullptr && false_target == nullptr
1381   //        - condition true => branch to true_target
1382   //  (3) true_target != nullptr && false_target != nullptr
1383   //        - condition true => branch to true_target
1384   //        - branch to false_target
1385   if (IsBooleanValueOrMaterializedCondition(cond)) {
1386     // The condition instruction has been materialized, compare the output to 0.
1387     Location cond_val = instruction->GetLocations()->InAt(condition_input_index);
1388     DCHECK(cond_val.IsRegister());
1389     if (true_target == nullptr) {
1390       __ Beqz(cond_val.AsRegister<XRegister>(), false_target);
1391     } else {
1392       __ Bnez(cond_val.AsRegister<XRegister>(), true_target);
1393     }
1394   } else {
1395     // The condition instruction has not been materialized, use its inputs as
1396     // the comparison and its condition as the branch condition.
1397     HCondition* condition = cond->AsCondition();
1398     DataType::Type type = condition->InputAt(0)->GetType();
1399     LocationSummary* locations = condition->GetLocations();
1400     IfCondition if_cond = condition->GetCondition();
1401     Riscv64Label* branch_target = true_target;
1402 
1403     if (true_target == nullptr) {
1404       if_cond = condition->GetOppositeCondition();
1405       branch_target = false_target;
1406     }
1407 
1408     switch (type) {
1409       case DataType::Type::kFloat32:
1410       case DataType::Type::kFloat64:
1411         GenerateFpCondition(if_cond, condition->IsGtBias(), type, locations, branch_target);
1412         break;
1413       default:
1414         // Integral types and reference equality.
1415         GenerateIntLongCompareAndBranch(if_cond, locations, branch_target);
1416         break;
1417     }
1418   }
1419 
1420   // If neither branch falls through (case 3), the conditional branch to `true_target`
1421   // was already emitted (case 2) and we need to emit a jump to `false_target`.
1422   if (true_target != nullptr && false_target != nullptr) {
1423     __ J(false_target);
1424   }
1425 }
1426 
DivRemOneOrMinusOne(HBinaryOperation * instruction)1427 void InstructionCodeGeneratorRISCV64::DivRemOneOrMinusOne(HBinaryOperation* instruction) {
1428   DCHECK(instruction->IsDiv() || instruction->IsRem());
1429   DataType::Type type = instruction->GetResultType();
1430 
1431   LocationSummary* locations = instruction->GetLocations();
1432   Location second = locations->InAt(1);
1433   DCHECK(second.IsConstant());
1434 
1435   XRegister out = locations->Out().AsRegister<XRegister>();
1436   XRegister dividend = locations->InAt(0).AsRegister<XRegister>();
1437   int64_t imm = Int64FromConstant(second.GetConstant());
1438   DCHECK(imm == 1 || imm == -1);
1439 
1440   if (instruction->IsRem()) {
1441     __ Mv(out, Zero);
1442   } else {
1443     if (imm == -1) {
1444       if (type == DataType::Type::kInt32) {
1445         __ Subw(out, Zero, dividend);
1446       } else {
1447         DCHECK_EQ(type, DataType::Type::kInt64);
1448         __ Sub(out, Zero, dividend);
1449       }
1450     } else if (out != dividend) {
1451       __ Mv(out, dividend);
1452     }
1453   }
1454 }
1455 
DivRemByPowerOfTwo(HBinaryOperation * instruction)1456 void InstructionCodeGeneratorRISCV64::DivRemByPowerOfTwo(HBinaryOperation* instruction) {
1457   DCHECK(instruction->IsDiv() || instruction->IsRem());
1458   DataType::Type type = instruction->GetResultType();
1459   DCHECK(type == DataType::Type::kInt32 || type == DataType::Type::kInt64) << type;
1460 
1461   LocationSummary* locations = instruction->GetLocations();
1462   Location second = locations->InAt(1);
1463   DCHECK(second.IsConstant());
1464 
1465   XRegister out = locations->Out().AsRegister<XRegister>();
1466   XRegister dividend = locations->InAt(0).AsRegister<XRegister>();
1467   int64_t imm = Int64FromConstant(second.GetConstant());
1468   int64_t abs_imm = static_cast<uint64_t>(AbsOrMin(imm));
1469   int ctz_imm = CTZ(abs_imm);
1470   DCHECK_GE(ctz_imm, 1);  // Division by +/-1 is handled by `DivRemOneOrMinusOne()`.
1471 
1472   ScratchRegisterScope srs(GetAssembler());
1473   XRegister tmp = srs.AllocateXRegister();
1474   // Calculate the negative dividend adjustment `tmp = dividend < 0 ? abs_imm - 1 : 0`.
1475   // This adjustment is needed for rounding the division result towards zero.
1476   if (type == DataType::Type::kInt32 || ctz_imm == 1) {
1477     // A 32-bit dividend is sign-extended to 64-bit, so we can use the upper bits.
1478     // And for a 64-bit division by +/-2, we need just the sign bit.
1479     DCHECK_IMPLIES(type == DataType::Type::kInt32, ctz_imm < 32);
1480     __ Srli(tmp, dividend, 64 - ctz_imm);
1481   } else {
1482     // For other 64-bit divisions, we need to replicate the sign bit.
1483     __ Srai(tmp, dividend, 63);
1484     __ Srli(tmp, tmp, 64 - ctz_imm);
1485   }
1486   // The rest of the calculation can use 64-bit operations even for 32-bit div/rem.
1487   __ Add(tmp, tmp, dividend);
1488   if (instruction->IsDiv()) {
1489     __ Srai(out, tmp, ctz_imm);
1490     if (imm < 0) {
1491       __ Neg(out, out);
1492     }
1493   } else {
1494     if (ctz_imm <= 11) {
1495       __ Andi(tmp, tmp, -abs_imm);
1496     } else {
1497       ScratchRegisterScope srs2(GetAssembler());
1498       XRegister tmp2 = srs2.AllocateXRegister();
1499       __ Li(tmp2, -abs_imm);
1500       __ And(tmp, tmp, tmp2);
1501     }
1502     __ Sub(out, dividend, tmp);
1503   }
1504 }
1505 
GenerateDivRemWithAnyConstant(HBinaryOperation * instruction)1506 void InstructionCodeGeneratorRISCV64::GenerateDivRemWithAnyConstant(HBinaryOperation* instruction) {
1507   DCHECK(instruction->IsDiv() || instruction->IsRem());
1508   LocationSummary* locations = instruction->GetLocations();
1509   XRegister dividend = locations->InAt(0).AsRegister<XRegister>();
1510   XRegister out = locations->Out().AsRegister<XRegister>();
1511   Location second = locations->InAt(1);
1512   int64_t imm = Int64FromConstant(second.GetConstant());
1513   DataType::Type type = instruction->GetResultType();
1514   ScratchRegisterScope srs(GetAssembler());
1515   XRegister tmp = srs.AllocateXRegister();
1516 
1517   // TODO: optimize with constant.
1518   __ LoadConst64(tmp, imm);
1519   if (instruction->IsDiv()) {
1520     if (type == DataType::Type::kInt32) {
1521       __ Divw(out, dividend, tmp);
1522     } else {
1523       __ Div(out, dividend, tmp);
1524     }
1525   } else {
1526     if (type == DataType::Type::kInt32)  {
1527       __ Remw(out, dividend, tmp);
1528     } else {
1529       __ Rem(out, dividend, tmp);
1530     }
1531   }
1532 }
1533 
GenerateDivRemIntegral(HBinaryOperation * instruction)1534 void InstructionCodeGeneratorRISCV64::GenerateDivRemIntegral(HBinaryOperation* instruction) {
1535   DCHECK(instruction->IsDiv() || instruction->IsRem());
1536   DataType::Type type = instruction->GetResultType();
1537   DCHECK(type == DataType::Type::kInt32 || type == DataType::Type::kInt64) << type;
1538 
1539   LocationSummary* locations = instruction->GetLocations();
1540   XRegister out = locations->Out().AsRegister<XRegister>();
1541   Location second = locations->InAt(1);
1542 
1543   if (second.IsConstant()) {
1544     int64_t imm = Int64FromConstant(second.GetConstant());
1545     if (imm == 0) {
1546       // Do not generate anything. DivZeroCheck would prevent any code to be executed.
1547     } else if (imm == 1 || imm == -1) {
1548       DivRemOneOrMinusOne(instruction);
1549     } else if (IsPowerOfTwo(AbsOrMin(imm))) {
1550       DivRemByPowerOfTwo(instruction);
1551     } else {
1552       DCHECK(imm <= -2 || imm >= 2);
1553       GenerateDivRemWithAnyConstant(instruction);
1554     }
1555   } else {
1556     XRegister dividend = locations->InAt(0).AsRegister<XRegister>();
1557     XRegister divisor = second.AsRegister<XRegister>();
1558     if (instruction->IsDiv()) {
1559       if (type == DataType::Type::kInt32) {
1560         __ Divw(out, dividend, divisor);
1561       } else {
1562         __ Div(out, dividend, divisor);
1563       }
1564     } else {
1565       if (type == DataType::Type::kInt32) {
1566         __ Remw(out, dividend, divisor);
1567       } else {
1568         __ Rem(out, dividend, divisor);
1569       }
1570     }
1571   }
1572 }
1573 
GenerateIntLongCondition(IfCondition cond,LocationSummary * locations)1574 void InstructionCodeGeneratorRISCV64::GenerateIntLongCondition(IfCondition cond,
1575                                                                LocationSummary* locations) {
1576   XRegister rd = locations->Out().AsRegister<XRegister>();
1577   GenerateIntLongCondition(cond, locations, rd, /*to_all_bits=*/ false);
1578 }
1579 
GenerateIntLongCondition(IfCondition cond,LocationSummary * locations,XRegister rd,bool to_all_bits)1580 void InstructionCodeGeneratorRISCV64::GenerateIntLongCondition(IfCondition cond,
1581                                                                LocationSummary* locations,
1582                                                                XRegister rd,
1583                                                                bool to_all_bits) {
1584   XRegister rs1 = locations->InAt(0).AsRegister<XRegister>();
1585   Location rs2_location = locations->InAt(1);
1586   bool use_imm = rs2_location.IsConstant();
1587   int64_t imm = use_imm ? CodeGenerator::GetInt64ValueOf(rs2_location.GetConstant()) : 0;
1588   XRegister rs2 = use_imm ? kNoXRegister : rs2_location.AsRegister<XRegister>();
1589   bool reverse_condition = false;
1590   switch (cond) {
1591     case kCondEQ:
1592     case kCondNE:
1593       if (!use_imm) {
1594         __ Sub(rd, rs1, rs2);  // SUB is OK here even for 32-bit comparison.
1595       } else if (imm != 0) {
1596         DCHECK(IsInt<12>(-imm));
1597         __ Addi(rd, rs1, -imm);  // ADDI is OK here even for 32-bit comparison.
1598       }  // else test `rs1` directly without subtraction for `use_imm && imm == 0`.
1599       if (cond == kCondEQ) {
1600         __ Seqz(rd, (use_imm && imm == 0) ? rs1 : rd);
1601       } else {
1602         __ Snez(rd, (use_imm && imm == 0) ? rs1 : rd);
1603       }
1604       break;
1605 
1606     case kCondLT:
1607     case kCondGE:
1608       if (use_imm) {
1609         DCHECK(IsInt<12>(imm));
1610         __ Slti(rd, rs1, imm);
1611       } else {
1612         __ Slt(rd, rs1, rs2);
1613       }
1614       // Calculate `rs1 >= rhs` as `!(rs1 < rhs)` since there's only the SLT but no SGE.
1615       reverse_condition = (cond == kCondGE);
1616       break;
1617 
1618     case kCondLE:
1619     case kCondGT:
1620       if (use_imm) {
1621         // Calculate `rs1 <= imm` as `rs1 < imm + 1`.
1622         DCHECK(IsInt<12>(imm + 1));  // The value that overflows would fail this check.
1623         __ Slti(rd, rs1, imm + 1);
1624       } else {
1625         __ Slt(rd, rs2, rs1);
1626       }
1627       // Calculate `rs1 > imm` as `!(rs1 < imm + 1)` and calculate
1628       // `rs1 <= rs2` as `!(rs2 < rs1)` since there's only the SLT but no SGE.
1629       reverse_condition = ((cond == kCondGT) == use_imm);
1630       break;
1631 
1632     case kCondB:
1633     case kCondAE:
1634       if (use_imm) {
1635         // Sltiu sign-extends its 12-bit immediate operand before the comparison
1636         // and thus lets us compare directly with unsigned values in the ranges
1637         // [0, 0x7ff] and [0x[ffffffff]fffff800, 0x[ffffffff]ffffffff].
1638         DCHECK(IsInt<12>(imm));
1639         __ Sltiu(rd, rs1, imm);
1640       } else {
1641         __ Sltu(rd, rs1, rs2);
1642       }
1643       // Calculate `rs1 AE rhs` as `!(rs1 B rhs)` since there's only the SLTU but no SGEU.
1644       reverse_condition = (cond == kCondAE);
1645       break;
1646 
1647     case kCondBE:
1648     case kCondA:
1649       if (use_imm) {
1650         // Calculate `rs1 BE imm` as `rs1 B imm + 1`.
1651         // Sltiu sign-extends its 12-bit immediate operand before the comparison
1652         // and thus lets us compare directly with unsigned values in the ranges
1653         // [0, 0x7ff] and [0x[ffffffff]fffff800, 0x[ffffffff]ffffffff].
1654         DCHECK(IsInt<12>(imm + 1));  // The value that overflows would fail this check.
1655         __ Sltiu(rd, rs1, imm + 1);
1656       } else {
1657         __ Sltu(rd, rs2, rs1);
1658       }
1659       // Calculate `rs1 A imm` as `!(rs1 B imm + 1)` and calculate
1660       // `rs1 BE rs2` as `!(rs2 B rs1)` since there's only the SLTU but no SGEU.
1661       reverse_condition = ((cond == kCondA) == use_imm);
1662       break;
1663   }
1664   if (to_all_bits) {
1665     // Store the result to all bits; in other words, "true" is represented by -1.
1666     if (reverse_condition) {
1667       __ Addi(rd, rd, -1);  // 0 -> -1, 1 -> 0
1668     } else {
1669       __ Neg(rd, rd);  // 0 -> 0, 1 -> -1
1670     }
1671   } else {
1672     if (reverse_condition) {
1673       __ Xori(rd, rd, 1);
1674     }
1675   }
1676 }
1677 
GenerateIntLongCompareAndBranch(IfCondition cond,LocationSummary * locations,Riscv64Label * label)1678 void InstructionCodeGeneratorRISCV64::GenerateIntLongCompareAndBranch(IfCondition cond,
1679                                                                       LocationSummary* locations,
1680                                                                       Riscv64Label* label) {
1681   XRegister left = locations->InAt(0).AsRegister<XRegister>();
1682   Location right_location = locations->InAt(1);
1683   if (right_location.IsConstant()) {
1684     DCHECK_EQ(CodeGenerator::GetInt64ValueOf(right_location.GetConstant()), 0);
1685     switch (cond) {
1686       case kCondEQ:
1687       case kCondBE:  // <= 0 if zero
1688         __ Beqz(left, label);
1689         break;
1690       case kCondNE:
1691       case kCondA:  // > 0 if non-zero
1692         __ Bnez(left, label);
1693         break;
1694       case kCondLT:
1695         __ Bltz(left, label);
1696         break;
1697       case kCondGE:
1698         __ Bgez(left, label);
1699         break;
1700       case kCondLE:
1701         __ Blez(left, label);
1702         break;
1703       case kCondGT:
1704         __ Bgtz(left, label);
1705         break;
1706       case kCondB:  // always false
1707         break;
1708       case kCondAE:  // always true
1709         __ J(label);
1710         break;
1711     }
1712   } else {
1713     XRegister right_reg = right_location.AsRegister<XRegister>();
1714     switch (cond) {
1715       case kCondEQ:
1716         __ Beq(left, right_reg, label);
1717         break;
1718       case kCondNE:
1719         __ Bne(left, right_reg, label);
1720         break;
1721       case kCondLT:
1722         __ Blt(left, right_reg, label);
1723         break;
1724       case kCondGE:
1725         __ Bge(left, right_reg, label);
1726         break;
1727       case kCondLE:
1728         __ Ble(left, right_reg, label);
1729         break;
1730       case kCondGT:
1731         __ Bgt(left, right_reg, label);
1732         break;
1733       case kCondB:
1734         __ Bltu(left, right_reg, label);
1735         break;
1736       case kCondAE:
1737         __ Bgeu(left, right_reg, label);
1738         break;
1739       case kCondBE:
1740         __ Bleu(left, right_reg, label);
1741         break;
1742       case kCondA:
1743         __ Bgtu(left, right_reg, label);
1744         break;
1745     }
1746   }
1747 }
1748 
GenerateFpCondition(IfCondition cond,bool gt_bias,DataType::Type type,LocationSummary * locations,Riscv64Label * label)1749 void InstructionCodeGeneratorRISCV64::GenerateFpCondition(IfCondition cond,
1750                                                           bool gt_bias,
1751                                                           DataType::Type type,
1752                                                           LocationSummary* locations,
1753                                                           Riscv64Label* label) {
1754   DCHECK_EQ(label != nullptr, locations->Out().IsInvalid());
1755   ScratchRegisterScope srs(GetAssembler());
1756   XRegister rd =
1757       (label != nullptr) ? srs.AllocateXRegister() : locations->Out().AsRegister<XRegister>();
1758   GenerateFpCondition(cond, gt_bias, type, locations, label, rd, /*to_all_bits=*/ false);
1759 }
1760 
GenerateFpCondition(IfCondition cond,bool gt_bias,DataType::Type type,LocationSummary * locations,Riscv64Label * label,XRegister rd,bool to_all_bits)1761 void InstructionCodeGeneratorRISCV64::GenerateFpCondition(IfCondition cond,
1762                                                           bool gt_bias,
1763                                                           DataType::Type type,
1764                                                           LocationSummary* locations,
1765                                                           Riscv64Label* label,
1766                                                           XRegister rd,
1767                                                           bool to_all_bits) {
1768   // RISCV-V FP compare instructions yield the following values:
1769   //                      l<r  l=r  l>r Unordered
1770   //             FEQ l,r   0    1    0    0
1771   //             FLT l,r   1    0    0    0
1772   //             FLT r,l   0    0    1    0
1773   //             FLE l,r   1    1    0    0
1774   //             FLE r,l   0    1    1    0
1775   //
1776   // We can calculate the `Compare` results using the following formulas:
1777   //                      l<r  l=r  l>r Unordered
1778   //     Compare/gt_bias  -1    0    1    1       = ((FLE l,r) ^ 1) - (FLT l,r)
1779   //     Compare/lt_bias  -1    0    1   -1       = ((FLE r,l) - 1) + (FLT r,l)
1780   // These are emitted in `VisitCompare()`.
1781   //
1782   // This function emits a fused `Condition(Compare(., .), 0)`. If we compare the
1783   // `Compare` results above with 0, we get the following values and formulas:
1784   //                      l<r  l=r  l>r Unordered
1785   //     CondEQ/-          0    1    0    0       = (FEQ l, r)
1786   //     CondNE/-          1    0    1    1       = (FEQ l, r) ^ 1
1787   //     CondLT/gt_bias    1    0    0    0       = (FLT l,r)
1788   //     CondLT/lt_bias    1    0    0    1       = (FLE r,l) ^ 1
1789   //     CondLE/gt_bias    1    1    0    0       = (FLE l,r)
1790   //     CondLE/lt_bias    1    1    0    1       = (FLT r,l) ^ 1
1791   //     CondGT/gt_bias    0    0    1    1       = (FLE l,r) ^ 1
1792   //     CondGT/lt_bias    0    0    1    0       = (FLT r,l)
1793   //     CondGE/gt_bias    0    1    1    1       = (FLT l,r) ^ 1
1794   //     CondGE/lt_bias    0    1    1    0       = (FLE r,l)
1795   // (CondEQ/CondNE comparison with zero yields the same result with gt_bias and lt_bias.)
1796   //
1797   // If the condition is not materialized, the `^ 1` is not emitted,
1798   // instead the condition is reversed by emitting BEQZ instead of BNEZ.
1799 
1800   FRegister rs1 = locations->InAt(0).AsFpuRegister<FRegister>();
1801   FRegister rs2 = locations->InAt(1).AsFpuRegister<FRegister>();
1802 
1803   bool reverse_condition = false;
1804   switch (cond) {
1805     case kCondEQ:
1806       FEq(rd, rs1, rs2, type);
1807       break;
1808     case kCondNE:
1809       FEq(rd, rs1, rs2, type);
1810       reverse_condition = true;
1811       break;
1812     case kCondLT:
1813       if (gt_bias) {
1814         FLt(rd, rs1, rs2, type);
1815       } else {
1816         FLe(rd, rs2, rs1, type);
1817         reverse_condition = true;
1818       }
1819       break;
1820     case kCondLE:
1821       if (gt_bias) {
1822         FLe(rd, rs1, rs2, type);
1823       } else {
1824         FLt(rd, rs2, rs1, type);
1825         reverse_condition = true;
1826       }
1827       break;
1828     case kCondGT:
1829       if (gt_bias) {
1830         FLe(rd, rs1, rs2, type);
1831         reverse_condition = true;
1832       } else {
1833         FLt(rd, rs2, rs1, type);
1834       }
1835       break;
1836     case kCondGE:
1837       if (gt_bias) {
1838         FLt(rd, rs1, rs2, type);
1839         reverse_condition = true;
1840       } else {
1841         FLe(rd, rs2, rs1, type);
1842       }
1843       break;
1844     default:
1845       LOG(FATAL) << "Unexpected floating-point condition " << cond;
1846       UNREACHABLE();
1847   }
1848 
1849   if (label != nullptr) {
1850     if (reverse_condition) {
1851       __ Beqz(rd, label);
1852     } else {
1853       __ Bnez(rd, label);
1854     }
1855   } else if (to_all_bits) {
1856     // Store the result to all bits; in other words, "true" is represented by -1.
1857     if (reverse_condition) {
1858       __ Addi(rd, rd, -1);  // 0 -> -1, 1 -> 0
1859     } else {
1860       __ Neg(rd, rd);  // 0 -> 0, 1 -> -1
1861     }
1862   } else {
1863     if (reverse_condition) {
1864       __ Xori(rd, rd, 1);
1865     }
1866   }
1867 }
1868 
GenerateFieldLoadWithBakerReadBarrier(HInstruction * instruction,Location ref,XRegister obj,uint32_t offset,Location temp,bool needs_null_check)1869 void CodeGeneratorRISCV64::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
1870                                                                  Location ref,
1871                                                                  XRegister obj,
1872                                                                  uint32_t offset,
1873                                                                  Location temp,
1874                                                                  bool needs_null_check) {
1875   GenerateReferenceLoadWithBakerReadBarrier(
1876       instruction, ref, obj, offset, /*index=*/ Location::NoLocation(), temp, needs_null_check);
1877 }
1878 
GenerateArrayLoadWithBakerReadBarrier(HInstruction * instruction,Location ref,XRegister obj,uint32_t data_offset,Location index,Location temp,bool needs_null_check)1879 void CodeGeneratorRISCV64::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction,
1880                                                                  Location ref,
1881                                                                  XRegister obj,
1882                                                                  uint32_t data_offset,
1883                                                                  Location index,
1884                                                                  Location temp,
1885                                                                  bool needs_null_check) {
1886   GenerateReferenceLoadWithBakerReadBarrier(
1887       instruction, ref, obj, data_offset, index, temp, needs_null_check);
1888 }
1889 
GenerateReferenceLoadWithBakerReadBarrier(HInstruction * instruction,Location ref,XRegister obj,uint32_t offset,Location index,Location temp,bool needs_null_check)1890 void CodeGeneratorRISCV64::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
1891                                                                      Location ref,
1892                                                                      XRegister obj,
1893                                                                      uint32_t offset,
1894                                                                      Location index,
1895                                                                      Location temp,
1896                                                                      bool needs_null_check) {
1897   // For now, use the same approach as for GC roots plus unpoison the reference if needed.
1898   // TODO(riscv64): Implement checking if the holder is black.
1899   UNUSED(temp);
1900 
1901   DCHECK(EmitBakerReadBarrier());
1902   XRegister reg = ref.AsRegister<XRegister>();
1903   if (index.IsValid()) {
1904     DCHECK(!needs_null_check);
1905     DCHECK(index.IsRegister());
1906     DataType::Type type = DataType::Type::kReference;
1907     DCHECK_EQ(type, instruction->GetType());
1908     if (instruction->IsArrayGet()) {
1909       // /* HeapReference<Object> */ ref = *(obj + index * element_size + offset)
1910       instruction_visitor_.ShNAdd(reg, index.AsRegister<XRegister>(), obj, type);
1911     } else {
1912       // /* HeapReference<Object> */ ref = *(obj + index + offset)
1913       DCHECK(instruction->IsInvoke());
1914       DCHECK(instruction->GetLocations()->Intrinsified());
1915       __ Add(reg, index.AsRegister<XRegister>(), obj);
1916     }
1917     __ Loadwu(reg, reg, offset);
1918   } else {
1919     // /* HeapReference<Object> */ ref = *(obj + offset)
1920     __ Loadwu(reg, obj, offset);
1921     if (needs_null_check) {
1922       MaybeRecordImplicitNullCheck(instruction);
1923     }
1924   }
1925   MaybeUnpoisonHeapReference(reg);
1926 
1927   // Slow path marking the reference.
1928   XRegister tmp = RA;  // Use RA as temp. It is clobbered in the slow path anyway.
1929   SlowPathCodeRISCV64* slow_path = new (GetScopedAllocator()) ReadBarrierMarkSlowPathRISCV64(
1930       instruction, ref, Location::RegisterLocation(tmp));
1931   AddSlowPath(slow_path);
1932 
1933   const int32_t entry_point_offset = ReadBarrierMarkEntrypointOffset(ref);
1934   // Loading the entrypoint does not require a load acquire since it is only changed when
1935   // threads are suspended or running a checkpoint.
1936   __ Loadd(tmp, TR, entry_point_offset);
1937   __ Bnez(tmp, slow_path->GetEntryLabel());
1938   __ Bind(slow_path->GetExitLabel());
1939 }
1940 
AddReadBarrierSlowPath(HInstruction * instruction,Location out,Location ref,Location obj,uint32_t offset,Location index)1941 SlowPathCodeRISCV64* CodeGeneratorRISCV64::AddReadBarrierSlowPath(HInstruction* instruction,
1942                                                                   Location out,
1943                                                                   Location ref,
1944                                                                   Location obj,
1945                                                                   uint32_t offset,
1946                                                                   Location index) {
1947   UNUSED(instruction);
1948   UNUSED(out);
1949   UNUSED(ref);
1950   UNUSED(obj);
1951   UNUSED(offset);
1952   UNUSED(index);
1953   LOG(FATAL) << "Unimplemented";
1954   UNREACHABLE();
1955 }
1956 
GenerateReadBarrierSlow(HInstruction * instruction,Location out,Location ref,Location obj,uint32_t offset,Location index)1957 void CodeGeneratorRISCV64::GenerateReadBarrierSlow(HInstruction* instruction,
1958                                                    Location out,
1959                                                    Location ref,
1960                                                    Location obj,
1961                                                    uint32_t offset,
1962                                                    Location index) {
1963   UNUSED(instruction);
1964   UNUSED(out);
1965   UNUSED(ref);
1966   UNUSED(obj);
1967   UNUSED(offset);
1968   UNUSED(index);
1969   LOG(FATAL) << "Unimplemented";
1970 }
1971 
MaybeGenerateReadBarrierSlow(HInstruction * instruction,Location out,Location ref,Location obj,uint32_t offset,Location index)1972 void CodeGeneratorRISCV64::MaybeGenerateReadBarrierSlow(HInstruction* instruction,
1973                                                         Location out,
1974                                                         Location ref,
1975                                                         Location obj,
1976                                                         uint32_t offset,
1977                                                         Location index) {
1978   if (EmitReadBarrier()) {
1979     // Baker's read barriers shall be handled by the fast path
1980     // (CodeGeneratorRISCV64::GenerateReferenceLoadWithBakerReadBarrier).
1981     DCHECK(!kUseBakerReadBarrier);
1982     // If heap poisoning is enabled, unpoisoning will be taken care of
1983     // by the runtime within the slow path.
1984     GenerateReadBarrierSlow(instruction, out, ref, obj, offset, index);
1985   } else if (kPoisonHeapReferences) {
1986     UnpoisonHeapReference(out.AsRegister<XRegister>());
1987   }
1988 }
1989 
GenerateReadBarrierForRootSlow(HInstruction * instruction,Location out,Location root)1990 void CodeGeneratorRISCV64::GenerateReadBarrierForRootSlow(HInstruction* instruction,
1991                                                           Location out,
1992                                                           Location root) {
1993   DCHECK(EmitReadBarrier());
1994 
1995   // Insert a slow path based read barrier *after* the GC root load.
1996   //
1997   // Note that GC roots are not affected by heap poisoning, so we do
1998   // not need to do anything special for this here.
1999   SlowPathCodeRISCV64* slow_path =
2000       new (GetScopedAllocator()) ReadBarrierForRootSlowPathRISCV64(instruction, out, root);
2001   AddSlowPath(slow_path);
2002 
2003   __ J(slow_path->GetEntryLabel());
2004   __ Bind(slow_path->GetExitLabel());
2005 }
2006 
HandleGoto(HInstruction * instruction,HBasicBlock * successor)2007 void InstructionCodeGeneratorRISCV64::HandleGoto(HInstruction* instruction,
2008                                                  HBasicBlock* successor) {
2009   if (successor->IsExitBlock()) {
2010     DCHECK(instruction->GetPrevious()->AlwaysThrows());
2011     return;  // no code needed
2012   }
2013 
2014   HBasicBlock* block = instruction->GetBlock();
2015   HInstruction* previous = instruction->GetPrevious();
2016   HLoopInformation* info = block->GetLoopInformation();
2017 
2018   if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) {
2019     codegen_->MaybeIncrementHotness(info->GetSuspendCheck(), /*is_frame_entry=*/ false);
2020     GenerateSuspendCheck(info->GetSuspendCheck(), successor);
2021     return;  // `GenerateSuspendCheck()` emitted the jump.
2022   }
2023   if (block->IsEntryBlock() && previous != nullptr && previous->IsSuspendCheck()) {
2024     GenerateSuspendCheck(previous->AsSuspendCheck(), nullptr);
2025   }
2026   if (!codegen_->GoesToNextBlock(block, successor)) {
2027     __ J(codegen_->GetLabelOf(successor));
2028   }
2029 }
2030 
GenPackedSwitchWithCompares(XRegister adjusted,XRegister temp,uint32_t num_entries,HBasicBlock * switch_block)2031 void InstructionCodeGeneratorRISCV64::GenPackedSwitchWithCompares(XRegister adjusted,
2032                                                                   XRegister temp,
2033                                                                   uint32_t num_entries,
2034                                                                   HBasicBlock* switch_block) {
2035   // Note: The `adjusted` register holds `value - lower_bound`. If the `lower_bound` is 0,
2036   // `adjusted` is the original `value` register and we must not clobber it. Otherwise,
2037   // `adjusted` is the `temp`. The caller already emitted the `adjusted < num_entries` check.
2038 
2039   // Create a set of compare/jumps.
2040   ArrayRef<HBasicBlock* const> successors(switch_block->GetSuccessors());
2041   uint32_t index = 0;
2042   for (; num_entries - index >= 2u; index += 2u) {
2043     // Jump to `successors[index]` if `value == lower_bound + index`.
2044     // Note that `adjusted` holds `value - lower_bound - index`.
2045     __ Beqz(adjusted, codegen_->GetLabelOf(successors[index]));
2046     if (num_entries - index == 2u) {
2047       break;  // The last entry shall match, so the branch shall be unconditional.
2048     }
2049     // Jump to `successors[index + 1]` if `value == lower_bound + index + 1`.
2050     // Modify `adjusted` to hold `value - lower_bound - index - 2` for this comparison.
2051     __ Addi(temp, adjusted, -2);
2052     adjusted = temp;
2053     __ Bltz(adjusted, codegen_->GetLabelOf(successors[index + 1]));
2054   }
2055   // For the last entry, unconditionally jump to `successors[num_entries - 1]`.
2056   __ J(codegen_->GetLabelOf(successors[num_entries - 1u]));
2057 }
2058 
GenTableBasedPackedSwitch(XRegister adjusted,XRegister temp,uint32_t num_entries,HBasicBlock * switch_block)2059 void InstructionCodeGeneratorRISCV64::GenTableBasedPackedSwitch(XRegister adjusted,
2060                                                                 XRegister temp,
2061                                                                 uint32_t num_entries,
2062                                                                 HBasicBlock* switch_block) {
2063   // Note: The `adjusted` register holds `value - lower_bound`. If the `lower_bound` is 0,
2064   // `adjusted` is the original `value` register and we must not clobber it. Otherwise,
2065   // `adjusted` is the `temp`. The caller already emitted the `adjusted < num_entries` check.
2066 
2067   // Create a jump table.
2068   ArenaVector<Riscv64Label*> labels(num_entries,
2069                                     __ GetAllocator()->Adapter(kArenaAllocSwitchTable));
2070   const ArenaVector<HBasicBlock*>& successors = switch_block->GetSuccessors();
2071   for (uint32_t i = 0; i < num_entries; i++) {
2072     labels[i] = codegen_->GetLabelOf(successors[i]);
2073   }
2074   JumpTable* table = __ CreateJumpTable(std::move(labels));
2075 
2076   // Load the address of the jump table.
2077   // Note: The `LoadLabelAddress()` emits AUIPC+ADD. It is possible to avoid the ADD and
2078   // instead embed that offset in the LW below as well as all jump table entries but
2079   // that would need some invasive changes in the jump table handling in the assembler.
2080   ScratchRegisterScope srs(GetAssembler());
2081   XRegister table_base = srs.AllocateXRegister();
2082   __ LoadLabelAddress(table_base, table->GetLabel());
2083 
2084   // Load the PC difference from the jump table.
2085   // TODO(riscv64): Use SH2ADD from the Zba extension.
2086   __ Slli(temp, adjusted, 2);
2087   __ Add(temp, temp, table_base);
2088   __ Lw(temp, temp, 0);
2089 
2090   // Compute the absolute target address by adding the table start address
2091   // (the table contains offsets to targets relative to its start).
2092   __ Add(temp, temp, table_base);
2093   // And jump.
2094   __ Jr(temp);
2095 }
2096 
VecAddress(LocationSummary * locations,size_t size,XRegister * adjusted_base)2097 int32_t InstructionCodeGeneratorRISCV64::VecAddress(LocationSummary* locations,
2098                                                     size_t size,
2099                                                     /*out*/ XRegister* adjusted_base) {
2100   UNUSED(locations);
2101   UNUSED(size);
2102   UNUSED(adjusted_base);
2103   LOG(FATAL) << "Unimplemented";
2104   UNREACHABLE();
2105 }
2106 
HandleBinaryOp(HBinaryOperation * instruction)2107 void LocationsBuilderRISCV64::HandleBinaryOp(HBinaryOperation* instruction) {
2108   DCHECK_EQ(instruction->InputCount(), 2u);
2109   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
2110   DataType::Type type = instruction->GetResultType();
2111   switch (type) {
2112     case DataType::Type::kInt32:
2113     case DataType::Type::kInt64: {
2114       locations->SetInAt(0, Location::RequiresRegister());
2115       HInstruction* right = instruction->InputAt(1);
2116       bool can_use_imm = false;
2117       if (instruction->IsMin() || instruction->IsMax()) {
2118         can_use_imm = IsZeroBitPattern(instruction);
2119       } else if (right->IsConstant()) {
2120         int64_t imm = CodeGenerator::GetInt64ValueOf(right->AsConstant());
2121         can_use_imm = IsInt<12>(instruction->IsSub() ? -imm : imm);
2122       }
2123       if (can_use_imm) {
2124         locations->SetInAt(1, Location::ConstantLocation(right));
2125       } else {
2126         locations->SetInAt(1, Location::RequiresRegister());
2127       }
2128       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2129       break;
2130     }
2131 
2132     case DataType::Type::kFloat32:
2133     case DataType::Type::kFloat64:
2134       locations->SetInAt(0, Location::RequiresFpuRegister());
2135       locations->SetInAt(1, Location::RequiresFpuRegister());
2136       if (instruction->IsMin() || instruction->IsMax()) {
2137         locations->SetOut(Location::RequiresFpuRegister(), Location::kOutputOverlap);
2138       } else {
2139         locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2140       }
2141       break;
2142 
2143     default:
2144       LOG(FATAL) << "Unexpected " << instruction->DebugName() << " type " << type;
2145       UNREACHABLE();
2146   }
2147 }
2148 
HandleBinaryOp(HBinaryOperation * instruction)2149 void InstructionCodeGeneratorRISCV64::HandleBinaryOp(HBinaryOperation* instruction) {
2150   DataType::Type type = instruction->GetType();
2151   LocationSummary* locations = instruction->GetLocations();
2152 
2153   switch (type) {
2154     case DataType::Type::kInt32:
2155     case DataType::Type::kInt64: {
2156       XRegister rd = locations->Out().AsRegister<XRegister>();
2157       XRegister rs1 = locations->InAt(0).AsRegister<XRegister>();
2158       Location rs2_location = locations->InAt(1);
2159 
2160       bool use_imm = rs2_location.IsConstant();
2161       XRegister rs2 = use_imm ? kNoXRegister : rs2_location.AsRegister<XRegister>();
2162       int64_t imm = use_imm ? CodeGenerator::GetInt64ValueOf(rs2_location.GetConstant()) : 0;
2163 
2164       if (instruction->IsAnd()) {
2165         if (use_imm) {
2166           __ Andi(rd, rs1, imm);
2167         } else {
2168           __ And(rd, rs1, rs2);
2169         }
2170       } else if (instruction->IsOr()) {
2171         if (use_imm) {
2172           __ Ori(rd, rs1, imm);
2173         } else {
2174           __ Or(rd, rs1, rs2);
2175         }
2176       } else if (instruction->IsXor()) {
2177         if (use_imm) {
2178           __ Xori(rd, rs1, imm);
2179         } else {
2180           __ Xor(rd, rs1, rs2);
2181         }
2182       } else if (instruction->IsAdd() || instruction->IsSub()) {
2183         if (type == DataType::Type::kInt32) {
2184           if (use_imm) {
2185             __ Addiw(rd, rs1, instruction->IsSub() ? -imm : imm);
2186           } else if (instruction->IsAdd()) {
2187             __ Addw(rd, rs1, rs2);
2188           } else {
2189             DCHECK(instruction->IsSub());
2190             __ Subw(rd, rs1, rs2);
2191           }
2192         } else {
2193           if (use_imm) {
2194             __ Addi(rd, rs1, instruction->IsSub() ? -imm : imm);
2195           } else if (instruction->IsAdd()) {
2196             __ Add(rd, rs1, rs2);
2197           } else {
2198             DCHECK(instruction->IsSub());
2199             __ Sub(rd, rs1, rs2);
2200           }
2201         }
2202       } else if (instruction->IsMin()) {
2203         DCHECK_IMPLIES(use_imm, imm == 0);
2204         __ Min(rd, rs1, use_imm ? Zero : rs2);
2205       } else {
2206         DCHECK(instruction->IsMax());
2207         DCHECK_IMPLIES(use_imm, imm == 0);
2208         __ Max(rd, rs1, use_imm ? Zero : rs2);
2209       }
2210       break;
2211     }
2212     case DataType::Type::kFloat32:
2213     case DataType::Type::kFloat64: {
2214       FRegister rd = locations->Out().AsFpuRegister<FRegister>();
2215       FRegister rs1 = locations->InAt(0).AsFpuRegister<FRegister>();
2216       FRegister rs2 = locations->InAt(1).AsFpuRegister<FRegister>();
2217       if (instruction->IsAdd()) {
2218         FAdd(rd, rs1, rs2, type);
2219       } else if (instruction->IsSub()) {
2220         FSub(rd, rs1, rs2, type);
2221       } else {
2222         DCHECK(instruction->IsMin() || instruction->IsMax());
2223         // If one of the operands is NaN and the other is not, riscv64 instructions FMIN/FMAX
2224         // return the other operand while we want to return the NaN operand.
2225         DCHECK_NE(rd, rs1);  // Requested `Location::kOutputOverlap`.
2226         DCHECK_NE(rd, rs2);  // Requested `Location::kOutputOverlap`.
2227         ScratchRegisterScope srs(GetAssembler());
2228         XRegister tmp = srs.AllocateXRegister();
2229         XRegister tmp2 = srs.AllocateXRegister();
2230         Riscv64Label done;
2231         // Return `rs1` if it's NaN.
2232         FClass(tmp, rs1, type);
2233         __ Li(tmp2, kFClassNaNMinValue);
2234         FMv(rd, rs1, type);
2235         __ Bgeu(tmp, tmp2, &done);
2236         // Return `rs2` if it's NaN.
2237         FClass(tmp, rs2, type);
2238         FMv(rd, rs2, type);
2239         __ Bgeu(tmp, tmp2, &done);
2240         // Calculate Min/Max for non-NaN arguments.
2241         if (instruction->IsMin()) {
2242           FMin(rd, rs1, rs2, type);
2243         } else {
2244           FMax(rd, rs1, rs2, type);
2245         }
2246         __ Bind(&done);
2247       }
2248       break;
2249     }
2250     default:
2251       LOG(FATAL) << "Unexpected binary operation type " << type;
2252       UNREACHABLE();
2253   }
2254 }
2255 
HandleCondition(HCondition * instruction)2256 void LocationsBuilderRISCV64::HandleCondition(HCondition* instruction) {
2257   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
2258   switch (instruction->InputAt(0)->GetType()) {
2259     case DataType::Type::kFloat32:
2260     case DataType::Type::kFloat64:
2261       locations->SetInAt(0, Location::RequiresFpuRegister());
2262       locations->SetInAt(1, Location::RequiresFpuRegister());
2263       break;
2264 
2265     default: {
2266       locations->SetInAt(0, Location::RequiresRegister());
2267       HInstruction* rhs = instruction->InputAt(1);
2268       bool use_imm = false;
2269       if (rhs->IsConstant()) {
2270         int64_t imm = CodeGenerator::GetInt64ValueOf(rhs->AsConstant());
2271         if (instruction->IsEmittedAtUseSite()) {
2272           // For `HIf`, materialize all non-zero constants with an `HParallelMove`.
2273           // Note: For certain constants and conditions, the code could be improved.
2274           // For example, 2048 takes two instructions to materialize but the negative
2275           // -2048 could be embedded in ADDI for EQ/NE comparison.
2276           use_imm = (imm == 0);
2277         } else {
2278           // Constants that cannot be embedded in an instruction's 12-bit immediate shall be
2279           // materialized with an `HParallelMove`. This simplifies the code and avoids cases
2280           // with arithmetic overflow. Adjust the `imm` if needed for a particular instruction.
2281           switch (instruction->GetCondition()) {
2282             case kCondEQ:
2283             case kCondNE:
2284               imm = -imm;  // ADDI with negative immediate (there is no SUBI).
2285               break;
2286             case kCondLE:
2287             case kCondGT:
2288             case kCondBE:
2289             case kCondA:
2290               imm += 1;    // SLTI/SLTIU with adjusted immediate (there is no SLEI/SLEIU).
2291               break;
2292             default:
2293               break;
2294           }
2295           use_imm = IsInt<12>(imm);
2296         }
2297       }
2298       if (use_imm) {
2299         locations->SetInAt(1, Location::ConstantLocation(rhs));
2300       } else {
2301         locations->SetInAt(1, Location::RequiresRegister());
2302       }
2303       break;
2304     }
2305   }
2306   if (!instruction->IsEmittedAtUseSite()) {
2307     locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2308   }
2309 }
2310 
HandleCondition(HCondition * instruction)2311 void InstructionCodeGeneratorRISCV64::HandleCondition(HCondition* instruction) {
2312   if (instruction->IsEmittedAtUseSite()) {
2313     return;
2314   }
2315 
2316   DataType::Type type = instruction->InputAt(0)->GetType();
2317   LocationSummary* locations = instruction->GetLocations();
2318   switch (type) {
2319     case DataType::Type::kFloat32:
2320     case DataType::Type::kFloat64:
2321       GenerateFpCondition(instruction->GetCondition(), instruction->IsGtBias(), type, locations);
2322       return;
2323     default:
2324       // Integral types and reference equality.
2325       GenerateIntLongCondition(instruction->GetCondition(), locations);
2326       return;
2327   }
2328 }
2329 
HandleShift(HBinaryOperation * instruction)2330 void LocationsBuilderRISCV64::HandleShift(HBinaryOperation* instruction) {
2331   DCHECK(instruction->IsShl() ||
2332          instruction->IsShr() ||
2333          instruction->IsUShr() ||
2334          instruction->IsRol() ||
2335          instruction->IsRor());
2336 
2337   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
2338   DataType::Type type = instruction->GetResultType();
2339   switch (type) {
2340     case DataType::Type::kInt32:
2341     case DataType::Type::kInt64: {
2342       locations->SetInAt(0, Location::RequiresRegister());
2343       locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
2344       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2345       break;
2346     }
2347     default:
2348       LOG(FATAL) << "Unexpected shift type " << type;
2349       UNREACHABLE();
2350   }
2351 }
2352 
HandleShift(HBinaryOperation * instruction)2353 void InstructionCodeGeneratorRISCV64::HandleShift(HBinaryOperation* instruction) {
2354   DCHECK(instruction->IsShl() ||
2355          instruction->IsShr() ||
2356          instruction->IsUShr() ||
2357          instruction->IsRol() ||
2358          instruction->IsRor());
2359 
2360   LocationSummary* locations = instruction->GetLocations();
2361   DataType::Type type = instruction->GetType();
2362 
2363   switch (type) {
2364     case DataType::Type::kInt32:
2365     case DataType::Type::kInt64: {
2366       XRegister rd = locations->Out().AsRegister<XRegister>();
2367       XRegister rs1 = locations->InAt(0).AsRegister<XRegister>();
2368       Location rs2_location = locations->InAt(1);
2369 
2370       if (rs2_location.IsConstant()) {
2371         int64_t imm = CodeGenerator::GetInt64ValueOf(rs2_location.GetConstant());
2372         if (instruction->IsRol()) {
2373           imm = -imm;
2374         }
2375         uint32_t shamt =
2376             imm & (type == DataType::Type::kInt32 ? kMaxIntShiftDistance : kMaxLongShiftDistance);
2377 
2378         if (shamt == 0) {
2379           if (rd != rs1) {
2380             __ Mv(rd, rs1);
2381           }
2382         } else if (type == DataType::Type::kInt32) {
2383           if (instruction->IsShl()) {
2384             __ Slliw(rd, rs1, shamt);
2385           } else if (instruction->IsShr()) {
2386             __ Sraiw(rd, rs1, shamt);
2387           } else if (instruction->IsUShr()) {
2388             __ Srliw(rd, rs1, shamt);
2389           } else if (instruction->IsRol()) {
2390             __ Roriw(rd, rs1, shamt);
2391           } else {
2392             DCHECK(instruction->IsRor());
2393             __ Roriw(rd, rs1, shamt);
2394           }
2395         } else {
2396           if (instruction->IsShl()) {
2397             __ Slli(rd, rs1, shamt);
2398           } else if (instruction->IsShr()) {
2399             __ Srai(rd, rs1, shamt);
2400           } else if (instruction->IsUShr()) {
2401             __ Srli(rd, rs1, shamt);
2402           } else if (instruction->IsRol()) {
2403             __ Rori(rd, rs1, shamt);
2404           } else {
2405             DCHECK(instruction->IsRor());
2406             __ Rori(rd, rs1, shamt);
2407           }
2408         }
2409       } else {
2410         XRegister rs2 = rs2_location.AsRegister<XRegister>();
2411         if (type == DataType::Type::kInt32) {
2412           if (instruction->IsShl()) {
2413             __ Sllw(rd, rs1, rs2);
2414           } else if (instruction->IsShr()) {
2415             __ Sraw(rd, rs1, rs2);
2416           } else if (instruction->IsUShr()) {
2417             __ Srlw(rd, rs1, rs2);
2418           } else if (instruction->IsRol()) {
2419             __ Rolw(rd, rs1, rs2);
2420           } else {
2421             DCHECK(instruction->IsRor());
2422             __ Rorw(rd, rs1, rs2);
2423           }
2424         } else {
2425           if (instruction->IsShl()) {
2426             __ Sll(rd, rs1, rs2);
2427           } else if (instruction->IsShr()) {
2428             __ Sra(rd, rs1, rs2);
2429           } else if (instruction->IsUShr()) {
2430             __ Srl(rd, rs1, rs2);
2431           } else if (instruction->IsRol()) {
2432             __ Rol(rd, rs1, rs2);
2433           } else {
2434             DCHECK(instruction->IsRor());
2435             __ Ror(rd, rs1, rs2);
2436           }
2437         }
2438       }
2439       break;
2440     }
2441     default:
2442       LOG(FATAL) << "Unexpected shift operation type " << type;
2443   }
2444 }
2445 
MaybeMarkGCCard(XRegister object,XRegister value,bool value_can_be_null)2446 void CodeGeneratorRISCV64::MaybeMarkGCCard(XRegister object,
2447                                            XRegister value,
2448                                            bool value_can_be_null) {
2449   Riscv64Label done;
2450   if (value_can_be_null) {
2451     __ Beqz(value, &done);
2452   }
2453   MarkGCCard(object);
2454   __ Bind(&done);
2455 }
2456 
MarkGCCard(XRegister object)2457 void CodeGeneratorRISCV64::MarkGCCard(XRegister object) {
2458   ScratchRegisterScope srs(GetAssembler());
2459   XRegister card = srs.AllocateXRegister();
2460   XRegister temp = srs.AllocateXRegister();
2461   // Load the address of the card table into `card`.
2462   __ Loadd(card, TR, Thread::CardTableOffset<kRiscv64PointerSize>().Int32Value());
2463 
2464   // Calculate the address of the card corresponding to `object`.
2465   __ Srli(temp, object, gc::accounting::CardTable::kCardShift);
2466   __ Add(temp, card, temp);
2467   // Write the `art::gc::accounting::CardTable::kCardDirty` value into the
2468   // `object`'s card.
2469   //
2470   // Register `card` contains the address of the card table. Note that the card
2471   // table's base is biased during its creation so that it always starts at an
2472   // address whose least-significant byte is equal to `kCardDirty` (see
2473   // art::gc::accounting::CardTable::Create). Therefore the SB instruction
2474   // below writes the `kCardDirty` (byte) value into the `object`'s card
2475   // (located at `card + object >> kCardShift`).
2476   //
2477   // This dual use of the value in register `card` (1. to calculate the location
2478   // of the card to mark; and 2. to load the `kCardDirty` value) saves a load
2479   // (no need to explicitly load `kCardDirty` as an immediate value).
2480   __ Sb(card, temp, 0);  // No scratch register left for `Storeb()`.
2481 }
2482 
CheckGCCardIsValid(XRegister object)2483 void CodeGeneratorRISCV64::CheckGCCardIsValid(XRegister object) {
2484   Riscv64Label done;
2485   ScratchRegisterScope srs(GetAssembler());
2486   XRegister card = srs.AllocateXRegister();
2487   XRegister temp = srs.AllocateXRegister();
2488   // Load the address of the card table into `card`.
2489   __ Loadd(card, TR, Thread::CardTableOffset<kRiscv64PointerSize>().Int32Value());
2490 
2491   // Calculate the address of the card corresponding to `object`.
2492   __ Srli(temp, object, gc::accounting::CardTable::kCardShift);
2493   __ Add(temp, card, temp);
2494   // assert (!clean || !self->is_gc_marking)
2495   __ Lb(temp, temp, 0);
2496   static_assert(gc::accounting::CardTable::kCardClean == 0);
2497   __ Bnez(temp, &done);
2498   __ Loadw(temp, TR, Thread::IsGcMarkingOffset<kRiscv64PointerSize>().Int32Value());
2499   __ Beqz(temp, &done);
2500   __ Unimp();
2501   __ Bind(&done);
2502 }
2503 
HandleFieldSet(HInstruction * instruction)2504 void LocationsBuilderRISCV64::HandleFieldSet(HInstruction* instruction) {
2505   LocationSummary* locations =
2506       new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
2507   locations->SetInAt(0, Location::RequiresRegister());
2508   locations->SetInAt(1, ValueLocationForStore(instruction->InputAt(1)));
2509 }
2510 
HandleFieldSet(HInstruction * instruction,const FieldInfo & field_info,bool value_can_be_null,WriteBarrierKind write_barrier_kind)2511 void InstructionCodeGeneratorRISCV64::HandleFieldSet(HInstruction* instruction,
2512                                                      const FieldInfo& field_info,
2513                                                      bool value_can_be_null,
2514                                                      WriteBarrierKind write_barrier_kind) {
2515   DataType::Type type = field_info.GetFieldType();
2516   LocationSummary* locations = instruction->GetLocations();
2517   XRegister obj = locations->InAt(0).AsRegister<XRegister>();
2518   Location value = locations->InAt(1);
2519   DCHECK_IMPLIES(value.IsConstant(), IsZeroBitPattern(value.GetConstant()));
2520   bool is_volatile = field_info.IsVolatile();
2521   uint32_t offset = field_info.GetFieldOffset().Uint32Value();
2522 
2523   if (is_volatile) {
2524     StoreSeqCst(value, obj, offset, type, instruction);
2525   } else {
2526     Store(value, obj, offset, type);
2527     codegen_->MaybeRecordImplicitNullCheck(instruction);
2528   }
2529 
2530   bool needs_write_barrier =
2531       codegen_->StoreNeedsWriteBarrier(type, instruction->InputAt(1), write_barrier_kind);
2532   if (needs_write_barrier) {
2533     if (value.IsConstant()) {
2534       DCHECK_EQ(write_barrier_kind, WriteBarrierKind::kEmitBeingReliedOn);
2535       codegen_->MarkGCCard(obj);
2536     } else {
2537       codegen_->MaybeMarkGCCard(
2538           obj,
2539           value.AsRegister<XRegister>(),
2540           value_can_be_null && write_barrier_kind == WriteBarrierKind::kEmitNotBeingReliedOn);
2541     }
2542   } else if (codegen_->ShouldCheckGCCard(type, instruction->InputAt(1), write_barrier_kind)) {
2543     codegen_->CheckGCCardIsValid(obj);
2544   }
2545 }
2546 
HandleFieldGet(HInstruction * instruction)2547 void LocationsBuilderRISCV64::HandleFieldGet(HInstruction* instruction) {
2548   DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
2549 
2550   bool object_field_get_with_read_barrier =
2551       (instruction->GetType() == DataType::Type::kReference) && codegen_->EmitReadBarrier();
2552   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
2553       instruction,
2554       object_field_get_with_read_barrier
2555           ? LocationSummary::kCallOnSlowPath
2556           : LocationSummary::kNoCall);
2557 
2558   // Input for object receiver.
2559   locations->SetInAt(0, Location::RequiresRegister());
2560 
2561   if (DataType::IsFloatingPointType(instruction->GetType())) {
2562     locations->SetOut(Location::RequiresFpuRegister());
2563   } else {
2564     // The output overlaps for an object field get when read barriers
2565     // are enabled: we do not want the load to overwrite the object's
2566     // location, as we need it to emit the read barrier.
2567     locations->SetOut(
2568         Location::RequiresRegister(),
2569         object_field_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap);
2570   }
2571 
2572   if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
2573     locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
2574     // We need a temporary register for the read barrier marking slow
2575     // path in CodeGeneratorRISCV64::GenerateFieldLoadWithBakerReadBarrier.
2576     locations->AddTemp(Location::RequiresRegister());
2577   }
2578 }
2579 
HandleFieldGet(HInstruction * instruction,const FieldInfo & field_info)2580 void InstructionCodeGeneratorRISCV64::HandleFieldGet(HInstruction* instruction,
2581                                                      const FieldInfo& field_info) {
2582   DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
2583   DCHECK_EQ(DataType::Size(field_info.GetFieldType()), DataType::Size(instruction->GetType()));
2584   DataType::Type type = instruction->GetType();
2585   LocationSummary* locations = instruction->GetLocations();
2586   Location obj_loc = locations->InAt(0);
2587   XRegister obj = obj_loc.AsRegister<XRegister>();
2588   Location dst_loc = locations->Out();
2589   bool is_volatile = field_info.IsVolatile();
2590   uint32_t offset = field_info.GetFieldOffset().Uint32Value();
2591 
2592   if (is_volatile) {
2593     codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
2594   }
2595 
2596   if (type == DataType::Type::kReference && codegen_->EmitBakerReadBarrier()) {
2597     // /* HeapReference<Object> */ dst = *(obj + offset)
2598     Location temp_loc = locations->GetTemp(0);
2599     // Note that a potential implicit null check is handled in this
2600     // CodeGeneratorRISCV64::GenerateFieldLoadWithBakerReadBarrier call.
2601     codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction,
2602                                                     dst_loc,
2603                                                     obj,
2604                                                     offset,
2605                                                     temp_loc,
2606                                                     /* needs_null_check= */ true);
2607   } else {
2608     Load(dst_loc, obj, offset, type);
2609     codegen_->MaybeRecordImplicitNullCheck(instruction);
2610   }
2611 
2612   if (is_volatile) {
2613     codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
2614   }
2615 
2616   if (type == DataType::Type::kReference && !codegen_->EmitBakerReadBarrier()) {
2617     // If read barriers are enabled, emit read barriers other than
2618     // Baker's using a slow path (and also unpoison the loaded
2619     // reference, if heap poisoning is enabled).
2620     codegen_->MaybeGenerateReadBarrierSlow(instruction, dst_loc, dst_loc, obj_loc, offset);
2621   }
2622 }
2623 
GenerateMethodEntryExitHook(HInstruction * instruction)2624 void InstructionCodeGeneratorRISCV64::GenerateMethodEntryExitHook(HInstruction* instruction) {
2625   SlowPathCodeRISCV64* slow_path =
2626       new (codegen_->GetScopedAllocator()) MethodEntryExitHooksSlowPathRISCV64(instruction);
2627   codegen_->AddSlowPath(slow_path);
2628 
2629   ScratchRegisterScope temps(GetAssembler());
2630   XRegister tmp = temps.AllocateXRegister();
2631 
2632   if (instruction->IsMethodExitHook()) {
2633     // Check if we are required to check if the caller needs a deoptimization. Strictly speaking it
2634     // would be sufficient to check if CheckCallerForDeopt bit is set. Though it is faster to check
2635     // if it is just non-zero. kCHA bit isn't used in debuggable runtimes as cha optimization is
2636     // disabled in debuggable runtime. The other bit is used when this method itself requires a
2637     // deoptimization due to redefinition. So it is safe to just check for non-zero value here.
2638     __ Loadwu(tmp, SP, codegen_->GetStackOffsetOfShouldDeoptimizeFlag());
2639     __ Bnez(tmp, slow_path->GetEntryLabel());
2640   }
2641 
2642   uint64_t hook_offset = instruction->IsMethodExitHook() ?
2643       instrumentation::Instrumentation::HaveMethodExitListenersOffset().SizeValue() :
2644       instrumentation::Instrumentation::HaveMethodEntryListenersOffset().SizeValue();
2645   auto [base_hook_address, hook_imm12] = SplitJitAddress(
2646       reinterpret_cast64<uint64_t>(Runtime::Current()->GetInstrumentation()) + hook_offset);
2647   __ LoadConst64(tmp, base_hook_address);
2648   __ Lbu(tmp, tmp, hook_imm12);
2649   // Check if there are any method entry / exit listeners. If no, continue.
2650   __ Beqz(tmp, slow_path->GetExitLabel());
2651   // Check if there are any slow (jvmti / trace with thread cpu time) method entry / exit listeners.
2652   // If yes, just take the slow path.
2653   static_assert(instrumentation::Instrumentation::kFastTraceListeners == 1u);
2654   __ Addi(tmp, tmp, -1);
2655   __ Bnez(tmp, slow_path->GetEntryLabel());
2656 
2657   // Allocate second core scratch register. We can no longer use `Stored()`
2658   // and similar macro instructions because there is no core scratch register left.
2659   XRegister tmp2 = temps.AllocateXRegister();
2660 
2661   // Check if there is place in the buffer to store a new entry, if no, take the slow path.
2662   int32_t trace_buffer_curr_entry_offset =
2663       Thread::TraceBufferCurrPtrOffset<kRiscv64PointerSize>().Int32Value();
2664   __ Loadd(tmp, TR, trace_buffer_curr_entry_offset);
2665   __ Loadd(tmp2, TR, Thread::TraceBufferPtrOffset<kRiscv64PointerSize>().SizeValue());
2666   __ Addi(tmp, tmp, -dchecked_integral_cast<int32_t>(kNumEntriesForWallClock * sizeof(void*)));
2667   __ Blt(tmp, tmp2, slow_path->GetEntryLabel());
2668 
2669   // Update the index in the `Thread`. Temporarily free `tmp2` to be used by `Stored()`.
2670   temps.FreeXRegister(tmp2);
2671   __ Stored(tmp, TR, trace_buffer_curr_entry_offset);
2672   tmp2 = temps.AllocateXRegister();
2673 
2674   // Record method pointer and trace action.
2675   __ Ld(tmp2, SP, 0);
2676   // Use last two bits to encode trace method action. For MethodEntry it is 0
2677   // so no need to set the bits since they are 0 already.
2678   DCHECK_GE(ArtMethod::Alignment(kRuntimePointerSize), static_cast<size_t>(4));
2679   static_assert(enum_cast<int32_t>(TraceAction::kTraceMethodEnter) == 0);
2680   static_assert(enum_cast<int32_t>(TraceAction::kTraceMethodExit) == 1);
2681   if (instruction->IsMethodExitHook()) {
2682     __ Ori(tmp2, tmp2, enum_cast<int32_t>(TraceAction::kTraceMethodExit));
2683   }
2684   static_assert(IsInt<12>(kMethodOffsetInBytes));  // No free scratch register for `Stored()`.
2685   __ Sd(tmp2, tmp, kMethodOffsetInBytes);
2686 
2687   // Record the timestamp.
2688   __ RdTime(tmp2);
2689   static_assert(IsInt<12>(kTimestampOffsetInBytes));  // No free scratch register for `Stored()`.
2690   __ Sd(tmp2, tmp, kTimestampOffsetInBytes);
2691 
2692   __ Bind(slow_path->GetExitLabel());
2693 }
2694 
VisitAbove(HAbove * instruction)2695 void LocationsBuilderRISCV64::VisitAbove(HAbove* instruction) {
2696   HandleCondition(instruction);
2697 }
2698 
VisitAbove(HAbove * instruction)2699 void InstructionCodeGeneratorRISCV64::VisitAbove(HAbove* instruction) {
2700   HandleCondition(instruction);
2701 }
2702 
VisitAboveOrEqual(HAboveOrEqual * instruction)2703 void LocationsBuilderRISCV64::VisitAboveOrEqual(HAboveOrEqual* instruction) {
2704   HandleCondition(instruction);
2705 }
2706 
VisitAboveOrEqual(HAboveOrEqual * instruction)2707 void InstructionCodeGeneratorRISCV64::VisitAboveOrEqual(HAboveOrEqual* instruction) {
2708   HandleCondition(instruction);
2709 }
2710 
VisitAbs(HAbs * abs)2711 void LocationsBuilderRISCV64::VisitAbs(HAbs* abs) {
2712   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(abs);
2713   switch (abs->GetResultType()) {
2714     case DataType::Type::kInt32:
2715     case DataType::Type::kInt64:
2716       locations->SetInAt(0, Location::RequiresRegister());
2717       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2718       break;
2719     case DataType::Type::kFloat32:
2720     case DataType::Type::kFloat64:
2721       locations->SetInAt(0, Location::RequiresFpuRegister());
2722       locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2723       break;
2724     default:
2725       LOG(FATAL) << "Unexpected abs type " << abs->GetResultType();
2726   }
2727 }
2728 
VisitAbs(HAbs * abs)2729 void InstructionCodeGeneratorRISCV64::VisitAbs(HAbs* abs) {
2730   LocationSummary* locations = abs->GetLocations();
2731   switch (abs->GetResultType()) {
2732     case DataType::Type::kInt32: {
2733       XRegister in = locations->InAt(0).AsRegister<XRegister>();
2734       XRegister out = locations->Out().AsRegister<XRegister>();
2735       ScratchRegisterScope srs(GetAssembler());
2736       XRegister tmp = srs.AllocateXRegister();
2737       __ Sraiw(tmp, in, 31);
2738       __ Xor(out, in, tmp);
2739       __ Subw(out, out, tmp);
2740       break;
2741     }
2742     case DataType::Type::kInt64: {
2743       XRegister in = locations->InAt(0).AsRegister<XRegister>();
2744       XRegister out = locations->Out().AsRegister<XRegister>();
2745       ScratchRegisterScope srs(GetAssembler());
2746       XRegister tmp = srs.AllocateXRegister();
2747       __ Srai(tmp, in, 63);
2748       __ Xor(out, in, tmp);
2749       __ Sub(out, out, tmp);
2750       break;
2751     }
2752     case DataType::Type::kFloat32:
2753     case DataType::Type::kFloat64:
2754       FAbs(locations->Out().AsFpuRegister<FRegister>(),
2755            locations->InAt(0).AsFpuRegister<FRegister>(),
2756            abs->GetResultType());
2757       break;
2758     default:
2759       LOG(FATAL) << "Unexpected abs type " << abs->GetResultType();
2760   }
2761 }
2762 
VisitAdd(HAdd * instruction)2763 void LocationsBuilderRISCV64::VisitAdd(HAdd* instruction) {
2764   HandleBinaryOp(instruction);
2765 }
2766 
VisitAdd(HAdd * instruction)2767 void InstructionCodeGeneratorRISCV64::VisitAdd(HAdd* instruction) {
2768   HandleBinaryOp(instruction);
2769 }
2770 
VisitAnd(HAnd * instruction)2771 void LocationsBuilderRISCV64::VisitAnd(HAnd* instruction) {
2772   HandleBinaryOp(instruction);
2773 }
2774 
VisitAnd(HAnd * instruction)2775 void InstructionCodeGeneratorRISCV64::VisitAnd(HAnd* instruction) {
2776   HandleBinaryOp(instruction);
2777 }
2778 
VisitArrayGet(HArrayGet * instruction)2779 void LocationsBuilderRISCV64::VisitArrayGet(HArrayGet* instruction) {
2780   DataType::Type type = instruction->GetType();
2781   bool object_array_get_with_read_barrier =
2782       (type == DataType::Type::kReference) && codegen_->EmitReadBarrier();
2783   LocationSummary* locations = new (GetGraph()->GetAllocator())
2784       LocationSummary(instruction,
2785                       object_array_get_with_read_barrier ? LocationSummary::kCallOnSlowPath :
2786                                                            LocationSummary::kNoCall);
2787   locations->SetInAt(0, Location::RequiresRegister());
2788   locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
2789   if (DataType::IsFloatingPointType(type)) {
2790     locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2791   } else {
2792     // The output overlaps in the case of an object array get with
2793     // read barriers enabled: we do not want the move to overwrite the
2794     // array's location, as we need it to emit the read barrier.
2795     locations->SetOut(
2796         Location::RequiresRegister(),
2797         object_array_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap);
2798   }
2799   if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
2800     locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
2801     // We need a temporary register for the read barrier marking slow
2802     // path in CodeGeneratorRISCV64::GenerateArrayLoadWithBakerReadBarrier.
2803     locations->AddTemp(Location::RequiresRegister());
2804   }
2805 }
2806 
VisitArrayGet(HArrayGet * instruction)2807 void InstructionCodeGeneratorRISCV64::VisitArrayGet(HArrayGet* instruction) {
2808   LocationSummary* locations = instruction->GetLocations();
2809   Location obj_loc = locations->InAt(0);
2810   XRegister obj = obj_loc.AsRegister<XRegister>();
2811   Location out_loc = locations->Out();
2812   Location index = locations->InAt(1);
2813   uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
2814   DataType::Type type = instruction->GetType();
2815   const bool maybe_compressed_char_at =
2816       mirror::kUseStringCompression && instruction->IsStringCharAt();
2817 
2818   Riscv64Label string_char_at_done;
2819   if (maybe_compressed_char_at) {
2820     DCHECK_EQ(type, DataType::Type::kUint16);
2821     uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
2822     Riscv64Label uncompressed_load;
2823     {
2824       ScratchRegisterScope srs(GetAssembler());
2825       XRegister tmp = srs.AllocateXRegister();
2826       __ Loadw(tmp, obj, count_offset);
2827       codegen_->MaybeRecordImplicitNullCheck(instruction);
2828       __ Andi(tmp, tmp, 0x1);
2829       static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
2830                     "Expecting 0=compressed, 1=uncompressed");
2831       __ Bnez(tmp, &uncompressed_load);
2832     }
2833     XRegister out = out_loc.AsRegister<XRegister>();
2834     if (index.IsConstant()) {
2835         int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue();
2836       __ Loadbu(out, obj, data_offset + const_index);
2837     } else {
2838       __ Add(out, obj, index.AsRegister<XRegister>());
2839       __ Loadbu(out, out, data_offset);
2840     }
2841     __ J(&string_char_at_done);
2842     __ Bind(&uncompressed_load);
2843   }
2844 
2845   if (type == DataType::Type::kReference && codegen_->EmitBakerReadBarrier()) {
2846     static_assert(
2847         sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
2848         "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
2849     // /* HeapReference<Object> */ out =
2850     //     *(obj + data_offset + index * sizeof(HeapReference<Object>))
2851     // Note that a potential implicit null check could be handled in these
2852     // `CodeGeneratorRISCV64::Generate{Array,Field}LoadWithBakerReadBarrier()` calls
2853     // but we currently do not support implicit null checks on `HArrayGet`.
2854     DCHECK(!instruction->CanDoImplicitNullCheckOn(instruction->InputAt(0)));
2855     Location temp = locations->GetTemp(0);
2856     if (index.IsConstant()) {
2857       // Array load with a constant index can be treated as a field load.
2858       static constexpr size_t shift = DataType::SizeShift(DataType::Type::kReference);
2859       size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << shift) + data_offset;
2860       codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction,
2861                                                       out_loc,
2862                                                       obj,
2863                                                       offset,
2864                                                       temp,
2865                                                       /* needs_null_check= */ false);
2866     } else {
2867       codegen_->GenerateArrayLoadWithBakerReadBarrier(instruction,
2868                                                       out_loc,
2869                                                       obj,
2870                                                       data_offset,
2871                                                       index,
2872                                                       temp,
2873                                                       /* needs_null_check= */ false);
2874     }
2875   } else if (index.IsConstant()) {
2876     int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue();
2877     int32_t offset = data_offset + (const_index << DataType::SizeShift(type));
2878     Load(out_loc, obj, offset, type);
2879     if (!maybe_compressed_char_at) {
2880       codegen_->MaybeRecordImplicitNullCheck(instruction);
2881     }
2882     if (type == DataType::Type::kReference) {
2883       DCHECK(!codegen_->EmitBakerReadBarrier());
2884       // If read barriers are enabled, emit read barriers other than Baker's using
2885       // a slow path (and also unpoison the loaded reference, if heap poisoning is enabled).
2886       codegen_->MaybeGenerateReadBarrierSlow(instruction, out_loc, out_loc, obj_loc, offset);
2887     }
2888   } else {
2889     ScratchRegisterScope srs(GetAssembler());
2890     XRegister tmp = srs.AllocateXRegister();
2891     ShNAdd(tmp, index.AsRegister<XRegister>(), obj, type);
2892     Load(out_loc, tmp, data_offset, type);
2893     if (!maybe_compressed_char_at) {
2894       codegen_->MaybeRecordImplicitNullCheck(instruction);
2895     }
2896     if (type == DataType::Type::kReference) {
2897       DCHECK(!codegen_->EmitBakerReadBarrier());
2898       // If read barriers are enabled, emit read barriers other than Baker's using
2899       // a slow path (and also unpoison the loaded reference, if heap poisoning is enabled).
2900       codegen_->MaybeGenerateReadBarrierSlow(
2901           instruction, out_loc, out_loc, obj_loc, data_offset, index);
2902     }
2903   }
2904 
2905   if (maybe_compressed_char_at) {
2906     __ Bind(&string_char_at_done);
2907   }
2908 }
2909 
VisitArrayLength(HArrayLength * instruction)2910 void LocationsBuilderRISCV64::VisitArrayLength(HArrayLength* instruction) {
2911   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
2912   locations->SetInAt(0, Location::RequiresRegister());
2913   locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2914 }
2915 
VisitArrayLength(HArrayLength * instruction)2916 void InstructionCodeGeneratorRISCV64::VisitArrayLength(HArrayLength* instruction) {
2917   LocationSummary* locations = instruction->GetLocations();
2918   uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction);
2919   XRegister obj = locations->InAt(0).AsRegister<XRegister>();
2920   XRegister out = locations->Out().AsRegister<XRegister>();
2921   __ Loadwu(out, obj, offset);  // Unsigned for string length; does not matter for other arrays.
2922   codegen_->MaybeRecordImplicitNullCheck(instruction);
2923   // Mask out compression flag from String's array length.
2924   if (mirror::kUseStringCompression && instruction->IsStringLength()) {
2925     __ Srli(out, out, 1u);
2926   }
2927 }
2928 
VisitArraySet(HArraySet * instruction)2929 void LocationsBuilderRISCV64::VisitArraySet(HArraySet* instruction) {
2930   bool needs_type_check = instruction->NeedsTypeCheck();
2931   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
2932       instruction,
2933       needs_type_check ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall);
2934   locations->SetInAt(0, Location::RequiresRegister());
2935   locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
2936   locations->SetInAt(2, ValueLocationForStore(instruction->GetValue()));
2937   if (kPoisonHeapReferences &&
2938       instruction->GetComponentType() == DataType::Type::kReference &&
2939       !locations->InAt(1).IsConstant() &&
2940       !locations->InAt(2).IsConstant()) {
2941     locations->AddTemp(Location::RequiresRegister());
2942   }
2943 }
2944 
VisitArraySet(HArraySet * instruction)2945 void InstructionCodeGeneratorRISCV64::VisitArraySet(HArraySet* instruction) {
2946   LocationSummary* locations = instruction->GetLocations();
2947   XRegister array = locations->InAt(0).AsRegister<XRegister>();
2948   Location index = locations->InAt(1);
2949   Location value = locations->InAt(2);
2950   DataType::Type value_type = instruction->GetComponentType();
2951   bool needs_type_check = instruction->NeedsTypeCheck();
2952   const WriteBarrierKind write_barrier_kind = instruction->GetWriteBarrierKind();
2953   bool needs_write_barrier =
2954       codegen_->StoreNeedsWriteBarrier(value_type, instruction->GetValue(), write_barrier_kind);
2955   size_t data_offset = mirror::Array::DataOffset(DataType::Size(value_type)).Uint32Value();
2956   SlowPathCodeRISCV64* slow_path = nullptr;
2957 
2958   if (needs_write_barrier) {
2959     DCHECK_EQ(value_type, DataType::Type::kReference);
2960     DCHECK_IMPLIES(value.IsConstant(), value.GetConstant()->IsArithmeticZero());
2961     const bool storing_constant_zero = value.IsConstant();
2962     // The WriteBarrierKind::kEmitNotBeingReliedOn case is able to skip the write barrier when its
2963     // value is null (without an extra CompareAndBranchIfZero since we already checked if the
2964     // value is null for the type check).
2965     bool skip_marking_gc_card = false;
2966     Riscv64Label skip_writing_card;
2967     if (!storing_constant_zero) {
2968       Riscv64Label do_store;
2969 
2970       bool can_value_be_null = instruction->GetValueCanBeNull();
2971       skip_marking_gc_card =
2972           can_value_be_null && write_barrier_kind == WriteBarrierKind::kEmitNotBeingReliedOn;
2973       if (can_value_be_null) {
2974         if (skip_marking_gc_card) {
2975           __ Beqz(value.AsRegister<XRegister>(), &skip_writing_card);
2976         } else {
2977           __ Beqz(value.AsRegister<XRegister>(), &do_store);
2978         }
2979       }
2980 
2981       if (needs_type_check) {
2982         slow_path = new (codegen_->GetScopedAllocator()) ArraySetSlowPathRISCV64(instruction);
2983         codegen_->AddSlowPath(slow_path);
2984 
2985         uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
2986         uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
2987         uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
2988 
2989         ScratchRegisterScope srs(GetAssembler());
2990         XRegister temp1 = srs.AllocateXRegister();
2991         XRegister temp2 = srs.AllocateXRegister();
2992 
2993         // Note that when read barriers are enabled, the type checks are performed
2994         // without read barriers.  This is fine, even in the case where a class object
2995         // is in the from-space after the flip, as a comparison involving such a type
2996         // would not produce a false positive; it may of course produce a false
2997         // negative, in which case we would take the ArraySet slow path.
2998 
2999         // /* HeapReference<Class> */ temp1 = array->klass_
3000         __ Loadwu(temp1, array, class_offset);
3001         codegen_->MaybeRecordImplicitNullCheck(instruction);
3002         codegen_->MaybeUnpoisonHeapReference(temp1);
3003 
3004         // /* HeapReference<Class> */ temp2 = temp1->component_type_
3005         __ Loadwu(temp2, temp1, component_offset);
3006         // /* HeapReference<Class> */ temp1 = value->klass_
3007         __ Loadwu(temp1, value.AsRegister<XRegister>(), class_offset);
3008         // If heap poisoning is enabled, no need to unpoison `temp1`
3009         // nor `temp2`, as we are comparing two poisoned references.
3010         if (instruction->StaticTypeOfArrayIsObjectArray()) {
3011           Riscv64Label do_put;
3012           __ Beq(temp1, temp2, &do_put);
3013           // If heap poisoning is enabled, the `temp2` reference has
3014           // not been unpoisoned yet; unpoison it now.
3015           codegen_->MaybeUnpoisonHeapReference(temp2);
3016 
3017           // /* HeapReference<Class> */ temp1 = temp2->super_class_
3018           __ Loadwu(temp1, temp2, super_offset);
3019           // If heap poisoning is enabled, no need to unpoison
3020           // `temp1`, as we are comparing against null below.
3021           __ Bnez(temp1, slow_path->GetEntryLabel());
3022           __ Bind(&do_put);
3023         } else {
3024           __ Bne(temp1, temp2, slow_path->GetEntryLabel());
3025         }
3026       }
3027 
3028       if (can_value_be_null && !skip_marking_gc_card) {
3029         DCHECK(do_store.IsLinked());
3030         __ Bind(&do_store);
3031       }
3032     }
3033 
3034     DCHECK_NE(write_barrier_kind, WriteBarrierKind::kDontEmit);
3035     DCHECK_IMPLIES(storing_constant_zero,
3036                    write_barrier_kind == WriteBarrierKind::kEmitBeingReliedOn);
3037     codegen_->MarkGCCard(array);
3038 
3039     if (skip_marking_gc_card) {
3040       // Note that we don't check that the GC card is valid as it can be correctly clean.
3041       DCHECK(skip_writing_card.IsLinked());
3042       __ Bind(&skip_writing_card);
3043     }
3044   } else if (codegen_->ShouldCheckGCCard(value_type, instruction->GetValue(), write_barrier_kind)) {
3045     codegen_->CheckGCCardIsValid(array);
3046   }
3047 
3048   if (index.IsConstant()) {
3049     int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue();
3050     int32_t offset = data_offset + (const_index << DataType::SizeShift(value_type));
3051     Store(value, array, offset, value_type);
3052   } else {
3053     ScratchRegisterScope srs(GetAssembler());
3054     // Heap poisoning needs two scratch registers in `Store()`, except for null constants.
3055     XRegister tmp =
3056         (kPoisonHeapReferences && value_type == DataType::Type::kReference && !value.IsConstant())
3057             ? locations->GetTemp(0).AsRegister<XRegister>()
3058             : srs.AllocateXRegister();
3059     ShNAdd(tmp, index.AsRegister<XRegister>(), array, value_type);
3060     Store(value, tmp, data_offset, value_type);
3061   }
3062   // There must be no instructions between the `Store()` and the `MaybeRecordImplicitNullCheck()`.
3063   // We can avoid this if the type check makes the null check unconditionally.
3064   DCHECK_IMPLIES(needs_type_check, needs_write_barrier);
3065   if (!(needs_type_check && !instruction->GetValueCanBeNull())) {
3066     codegen_->MaybeRecordImplicitNullCheck(instruction);
3067   }
3068 
3069   if (slow_path != nullptr) {
3070     __ Bind(slow_path->GetExitLabel());
3071   }
3072 }
3073 
VisitBelow(HBelow * instruction)3074 void LocationsBuilderRISCV64::VisitBelow(HBelow* instruction) {
3075   HandleCondition(instruction);
3076 }
3077 
VisitBelow(HBelow * instruction)3078 void InstructionCodeGeneratorRISCV64::VisitBelow(HBelow* instruction) {
3079   HandleCondition(instruction);
3080 }
3081 
VisitBelowOrEqual(HBelowOrEqual * instruction)3082 void LocationsBuilderRISCV64::VisitBelowOrEqual(HBelowOrEqual* instruction) {
3083   HandleCondition(instruction);
3084 }
3085 
VisitBelowOrEqual(HBelowOrEqual * instruction)3086 void InstructionCodeGeneratorRISCV64::VisitBelowOrEqual(HBelowOrEqual* instruction) {
3087   HandleCondition(instruction);
3088 }
3089 
VisitBooleanNot(HBooleanNot * instruction)3090 void LocationsBuilderRISCV64::VisitBooleanNot(HBooleanNot* instruction) {
3091   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
3092   locations->SetInAt(0, Location::RequiresRegister());
3093   locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3094 }
3095 
VisitBooleanNot(HBooleanNot * instruction)3096 void InstructionCodeGeneratorRISCV64::VisitBooleanNot(HBooleanNot* instruction) {
3097   LocationSummary* locations = instruction->GetLocations();
3098   __ Xori(locations->Out().AsRegister<XRegister>(), locations->InAt(0).AsRegister<XRegister>(), 1);
3099 }
3100 
VisitBoundsCheck(HBoundsCheck * instruction)3101 void LocationsBuilderRISCV64::VisitBoundsCheck(HBoundsCheck* instruction) {
3102   RegisterSet caller_saves = RegisterSet::Empty();
3103   InvokeRuntimeCallingConvention calling_convention;
3104   caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
3105   caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
3106   LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction, caller_saves);
3107 
3108   HInstruction* index = instruction->InputAt(0);
3109   HInstruction* length = instruction->InputAt(1);
3110 
3111   bool const_index = false;
3112   bool const_length = false;
3113 
3114   if (length->IsConstant()) {
3115     if (index->IsConstant()) {
3116       const_index = true;
3117       const_length = true;
3118     } else {
3119       int32_t length_value = length->AsIntConstant()->GetValue();
3120       if (length_value == 0 || length_value == 1) {
3121         const_length = true;
3122       }
3123     }
3124   } else if (index->IsConstant()) {
3125     int32_t index_value = index->AsIntConstant()->GetValue();
3126     if (index_value <= 0) {
3127       const_index = true;
3128     }
3129   }
3130 
3131   locations->SetInAt(
3132       0,
3133       const_index ? Location::ConstantLocation(index) : Location::RequiresRegister());
3134   locations->SetInAt(
3135       1,
3136       const_length ? Location::ConstantLocation(length) : Location::RequiresRegister());
3137 }
3138 
VisitBoundsCheck(HBoundsCheck * instruction)3139 void InstructionCodeGeneratorRISCV64::VisitBoundsCheck(HBoundsCheck* instruction) {
3140   LocationSummary* locations = instruction->GetLocations();
3141   Location index_loc = locations->InAt(0);
3142   Location length_loc = locations->InAt(1);
3143 
3144   if (length_loc.IsConstant()) {
3145     int32_t length = length_loc.GetConstant()->AsIntConstant()->GetValue();
3146     if (index_loc.IsConstant()) {
3147       int32_t index = index_loc.GetConstant()->AsIntConstant()->GetValue();
3148       if (index < 0 || index >= length) {
3149         BoundsCheckSlowPathRISCV64* slow_path =
3150             new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathRISCV64(instruction);
3151         codegen_->AddSlowPath(slow_path);
3152         __ J(slow_path->GetEntryLabel());
3153       } else {
3154         // Nothing to be done.
3155       }
3156       return;
3157     }
3158 
3159     BoundsCheckSlowPathRISCV64* slow_path =
3160         new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathRISCV64(instruction);
3161     codegen_->AddSlowPath(slow_path);
3162     XRegister index = index_loc.AsRegister<XRegister>();
3163     if (length == 0) {
3164       __ J(slow_path->GetEntryLabel());
3165     } else {
3166       DCHECK_EQ(length, 1);
3167       __ Bnez(index, slow_path->GetEntryLabel());
3168     }
3169   } else {
3170     XRegister length = length_loc.AsRegister<XRegister>();
3171     BoundsCheckSlowPathRISCV64* slow_path =
3172         new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathRISCV64(instruction);
3173     codegen_->AddSlowPath(slow_path);
3174     if (index_loc.IsConstant()) {
3175       int32_t index = index_loc.GetConstant()->AsIntConstant()->GetValue();
3176       if (index < 0) {
3177         __ J(slow_path->GetEntryLabel());
3178       } else {
3179         DCHECK_EQ(index, 0);
3180         __ Blez(length, slow_path->GetEntryLabel());
3181       }
3182     } else {
3183       XRegister index = index_loc.AsRegister<XRegister>();
3184       __ Bgeu(index, length, slow_path->GetEntryLabel());
3185     }
3186   }
3187 }
3188 
VisitBoundType(HBoundType * instruction)3189 void LocationsBuilderRISCV64::VisitBoundType([[maybe_unused]] HBoundType* instruction) {
3190   // Nothing to do, this should be removed during prepare for register allocator.
3191   LOG(FATAL) << "Unreachable";
3192 }
3193 
VisitBoundType(HBoundType * instruction)3194 void InstructionCodeGeneratorRISCV64::VisitBoundType([[maybe_unused]] HBoundType* instruction) {
3195   // Nothing to do, this should be removed during prepare for register allocator.
3196   LOG(FATAL) << "Unreachable";
3197 }
3198 
3199 // Temp is used for read barrier.
NumberOfInstanceOfTemps(bool emit_read_barrier,TypeCheckKind type_check_kind)3200 static size_t NumberOfInstanceOfTemps(bool emit_read_barrier, TypeCheckKind type_check_kind) {
3201   if (emit_read_barrier &&
3202       (kUseBakerReadBarrier ||
3203        type_check_kind == TypeCheckKind::kAbstractClassCheck ||
3204        type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
3205        type_check_kind == TypeCheckKind::kArrayObjectCheck)) {
3206     return 1;
3207   }
3208   return 0;
3209 }
3210 
3211 // Interface case has 3 temps, one for holding the number of interfaces, one for the current
3212 // interface pointer, one for loading the current interface.
3213 // The other checks have one temp for loading the object's class and maybe a temp for read barrier.
NumberOfCheckCastTemps(bool emit_read_barrier,TypeCheckKind type_check_kind)3214 static size_t NumberOfCheckCastTemps(bool emit_read_barrier, TypeCheckKind type_check_kind) {
3215   if (type_check_kind == TypeCheckKind::kInterfaceCheck) {
3216     return 3;
3217   }
3218   return 1 + NumberOfInstanceOfTemps(emit_read_barrier, type_check_kind);
3219 }
3220 
VisitCheckCast(HCheckCast * instruction)3221 void LocationsBuilderRISCV64::VisitCheckCast(HCheckCast* instruction) {
3222   TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
3223   LocationSummary::CallKind call_kind = codegen_->GetCheckCastCallKind(instruction);
3224   LocationSummary* locations =
3225       new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
3226   locations->SetInAt(0, Location::RequiresRegister());
3227   if (type_check_kind == TypeCheckKind::kBitstringCheck) {
3228     locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)));
3229     locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)));
3230     locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)));
3231   } else {
3232     locations->SetInAt(1, Location::RequiresRegister());
3233   }
3234   locations->AddRegisterTemps(NumberOfCheckCastTemps(codegen_->EmitReadBarrier(), type_check_kind));
3235 }
3236 
VisitCheckCast(HCheckCast * instruction)3237 void InstructionCodeGeneratorRISCV64::VisitCheckCast(HCheckCast* instruction) {
3238 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
3239   LocationSummary* locations = instruction->GetLocations();
3240   Location obj_loc = locations->InAt(0);
3241   XRegister obj = obj_loc.AsRegister<XRegister>();
3242   Location cls = (type_check_kind == TypeCheckKind::kBitstringCheck)
3243       ? Location::NoLocation()
3244       : locations->InAt(1);
3245   Location temp_loc = locations->GetTemp(0);
3246   XRegister temp = temp_loc.AsRegister<XRegister>();
3247   const size_t num_temps = NumberOfCheckCastTemps(codegen_->EmitReadBarrier(), type_check_kind);
3248   DCHECK_GE(num_temps, 1u);
3249   DCHECK_LE(num_temps, 3u);
3250   Location maybe_temp2_loc = (num_temps >= 2) ? locations->GetTemp(1) : Location::NoLocation();
3251   Location maybe_temp3_loc = (num_temps >= 3) ? locations->GetTemp(2) : Location::NoLocation();
3252   const uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
3253   const uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
3254   const uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
3255   const uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
3256   const uint32_t iftable_offset = mirror::Class::IfTableOffset().Uint32Value();
3257   const uint32_t array_length_offset = mirror::Array::LengthOffset().Uint32Value();
3258   const uint32_t object_array_data_offset =
3259       mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value();
3260   Riscv64Label done;
3261 
3262   bool is_type_check_slow_path_fatal = codegen_->IsTypeCheckSlowPathFatal(instruction);
3263   SlowPathCodeRISCV64* slow_path =
3264       new (codegen_->GetScopedAllocator()) TypeCheckSlowPathRISCV64(
3265           instruction, is_type_check_slow_path_fatal);
3266   codegen_->AddSlowPath(slow_path);
3267 
3268   // Avoid this check if we know `obj` is not null.
3269   if (instruction->MustDoNullCheck()) {
3270     __ Beqz(obj, &done);
3271   }
3272 
3273   switch (type_check_kind) {
3274     case TypeCheckKind::kExactCheck:
3275     case TypeCheckKind::kArrayCheck: {
3276       // /* HeapReference<Class> */ temp = obj->klass_
3277       GenerateReferenceLoadTwoRegisters(instruction,
3278                                         temp_loc,
3279                                         obj_loc,
3280                                         class_offset,
3281                                         maybe_temp2_loc,
3282                                         kWithoutReadBarrier);
3283       // Jump to slow path for throwing the exception or doing a
3284       // more involved array check.
3285       __ Bne(temp, cls.AsRegister<XRegister>(), slow_path->GetEntryLabel());
3286       break;
3287     }
3288 
3289     case TypeCheckKind::kAbstractClassCheck: {
3290       // /* HeapReference<Class> */ temp = obj->klass_
3291       GenerateReferenceLoadTwoRegisters(instruction,
3292                                         temp_loc,
3293                                         obj_loc,
3294                                         class_offset,
3295                                         maybe_temp2_loc,
3296                                         kWithoutReadBarrier);
3297       // If the class is abstract, we eagerly fetch the super class of the
3298       // object to avoid doing a comparison we know will fail.
3299       Riscv64Label loop;
3300       __ Bind(&loop);
3301       // /* HeapReference<Class> */ temp = temp->super_class_
3302       GenerateReferenceLoadOneRegister(instruction,
3303                                        temp_loc,
3304                                        super_offset,
3305                                        maybe_temp2_loc,
3306                                        kWithoutReadBarrier);
3307       // If the class reference currently in `temp` is null, jump to the slow path to throw the
3308       // exception.
3309       __ Beqz(temp, slow_path->GetEntryLabel());
3310       // Otherwise, compare the classes.
3311       __ Bne(temp, cls.AsRegister<XRegister>(), &loop);
3312       break;
3313     }
3314 
3315     case TypeCheckKind::kClassHierarchyCheck: {
3316       // /* HeapReference<Class> */ temp = obj->klass_
3317       GenerateReferenceLoadTwoRegisters(instruction,
3318                                         temp_loc,
3319                                         obj_loc,
3320                                         class_offset,
3321                                         maybe_temp2_loc,
3322                                         kWithoutReadBarrier);
3323       // Walk over the class hierarchy to find a match.
3324       Riscv64Label loop;
3325       __ Bind(&loop);
3326       __ Beq(temp, cls.AsRegister<XRegister>(), &done);
3327       // /* HeapReference<Class> */ temp = temp->super_class_
3328       GenerateReferenceLoadOneRegister(instruction,
3329                                        temp_loc,
3330                                        super_offset,
3331                                        maybe_temp2_loc,
3332                                        kWithoutReadBarrier);
3333       // If the class reference currently in `temp` is null, jump to the slow path to throw the
3334       // exception. Otherwise, jump to the beginning of the loop.
3335       __ Bnez(temp, &loop);
3336       __ J(slow_path->GetEntryLabel());
3337       break;
3338     }
3339 
3340     case TypeCheckKind::kArrayObjectCheck: {
3341       // /* HeapReference<Class> */ temp = obj->klass_
3342       GenerateReferenceLoadTwoRegisters(instruction,
3343                                         temp_loc,
3344                                         obj_loc,
3345                                         class_offset,
3346                                         maybe_temp2_loc,
3347                                         kWithoutReadBarrier);
3348       // Do an exact check.
3349       __ Beq(temp, cls.AsRegister<XRegister>(), &done);
3350       // Otherwise, we need to check that the object's class is a non-primitive array.
3351       // /* HeapReference<Class> */ temp = temp->component_type_
3352       GenerateReferenceLoadOneRegister(instruction,
3353                                        temp_loc,
3354                                        component_offset,
3355                                        maybe_temp2_loc,
3356                                        kWithoutReadBarrier);
3357       // If the component type is null, jump to the slow path to throw the exception.
3358       __ Beqz(temp, slow_path->GetEntryLabel());
3359       // Otherwise, the object is indeed an array, further check that this component
3360       // type is not a primitive type.
3361       __ Loadhu(temp, temp, primitive_offset);
3362       static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
3363       __ Bnez(temp, slow_path->GetEntryLabel());
3364       break;
3365     }
3366 
3367     case TypeCheckKind::kUnresolvedCheck:
3368       // We always go into the type check slow path for the unresolved check case.
3369       // We cannot directly call the CheckCast runtime entry point
3370       // without resorting to a type checking slow path here (i.e. by
3371       // calling InvokeRuntime directly), as it would require to
3372       // assign fixed registers for the inputs of this HInstanceOf
3373       // instruction (following the runtime calling convention), which
3374       // might be cluttered by the potential first read barrier
3375       // emission at the beginning of this method.
3376       __ J(slow_path->GetEntryLabel());
3377       break;
3378 
3379     case TypeCheckKind::kInterfaceCheck: {
3380       // Avoid read barriers to improve performance of the fast path. We can not get false
3381       // positives by doing this. False negatives are handled by the slow path.
3382       // /* HeapReference<Class> */ temp = obj->klass_
3383       GenerateReferenceLoadTwoRegisters(instruction,
3384                                         temp_loc,
3385                                         obj_loc,
3386                                         class_offset,
3387                                         maybe_temp2_loc,
3388                                         kWithoutReadBarrier);
3389       // /* HeapReference<Class> */ temp = temp->iftable_
3390       GenerateReferenceLoadOneRegister(instruction,
3391                                        temp_loc,
3392                                        iftable_offset,
3393                                        maybe_temp2_loc,
3394                                        kWithoutReadBarrier);
3395       XRegister temp2 = maybe_temp2_loc.AsRegister<XRegister>();
3396       XRegister temp3 = maybe_temp3_loc.AsRegister<XRegister>();
3397       // Load the size of the `IfTable`. The `Class::iftable_` is never null.
3398       __ Loadw(temp2, temp, array_length_offset);
3399       // Loop through the iftable and check if any class matches.
3400       Riscv64Label loop;
3401       __ Bind(&loop);
3402       __ Beqz(temp2, slow_path->GetEntryLabel());
3403       __ Lwu(temp3, temp, object_array_data_offset);
3404       codegen_->MaybeUnpoisonHeapReference(temp3);
3405       // Go to next interface.
3406       __ Addi(temp, temp, 2 * kHeapReferenceSize);
3407       __ Addi(temp2, temp2, -2);
3408       // Compare the classes and continue the loop if they do not match.
3409       __ Bne(temp3, cls.AsRegister<XRegister>(), &loop);
3410       break;
3411     }
3412 
3413     case TypeCheckKind::kBitstringCheck: {
3414       // /* HeapReference<Class> */ temp = obj->klass_
3415       GenerateReferenceLoadTwoRegisters(instruction,
3416                                         temp_loc,
3417                                         obj_loc,
3418                                         class_offset,
3419                                         maybe_temp2_loc,
3420                                         kWithoutReadBarrier);
3421 
3422       GenerateBitstringTypeCheckCompare(instruction, temp);
3423       __ Bnez(temp, slow_path->GetEntryLabel());
3424       break;
3425     }
3426   }
3427 
3428   __ Bind(&done);
3429   __ Bind(slow_path->GetExitLabel());
3430 }
3431 
VisitClassTableGet(HClassTableGet * instruction)3432 void LocationsBuilderRISCV64::VisitClassTableGet(HClassTableGet* instruction) {
3433   LocationSummary* locations =
3434       new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
3435   locations->SetInAt(0, Location::RequiresRegister());
3436   locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3437 }
3438 
VisitClassTableGet(HClassTableGet * instruction)3439 void InstructionCodeGeneratorRISCV64::VisitClassTableGet(HClassTableGet* instruction) {
3440   LocationSummary* locations = instruction->GetLocations();
3441   XRegister in = locations->InAt(0).AsRegister<XRegister>();
3442   XRegister out = locations->Out().AsRegister<XRegister>();
3443   if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) {
3444     MemberOffset method_offset =
3445         mirror::Class::EmbeddedVTableEntryOffset(instruction->GetIndex(), kRiscv64PointerSize);
3446     __ Loadd(out, in, method_offset.SizeValue());
3447   } else {
3448     uint32_t method_offset = dchecked_integral_cast<uint32_t>(
3449         ImTable::OffsetOfElement(instruction->GetIndex(), kRiscv64PointerSize));
3450     __ Loadd(out, in, mirror::Class::ImtPtrOffset(kRiscv64PointerSize).Uint32Value());
3451     __ Loadd(out, out, method_offset);
3452   }
3453 }
3454 
GetExceptionTlsOffset()3455 static int32_t GetExceptionTlsOffset() {
3456   return Thread::ExceptionOffset<kRiscv64PointerSize>().Int32Value();
3457 }
3458 
VisitClearException(HClearException * instruction)3459 void LocationsBuilderRISCV64::VisitClearException(HClearException* instruction) {
3460   new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
3461 }
3462 
VisitClearException(HClearException * instruction)3463 void InstructionCodeGeneratorRISCV64::VisitClearException(
3464     [[maybe_unused]] HClearException* instruction) {
3465   __ Stored(Zero, TR, GetExceptionTlsOffset());
3466 }
3467 
VisitClinitCheck(HClinitCheck * instruction)3468 void LocationsBuilderRISCV64::VisitClinitCheck(HClinitCheck* instruction) {
3469   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
3470       instruction, LocationSummary::kCallOnSlowPath);
3471   locations->SetInAt(0, Location::RequiresRegister());
3472   if (instruction->HasUses()) {
3473     locations->SetOut(Location::SameAsFirstInput());
3474   }
3475   // Rely on the type initialization to save everything we need.
3476   locations->SetCustomSlowPathCallerSaves(OneRegInReferenceOutSaveEverythingCallerSaves());
3477 }
3478 
VisitClinitCheck(HClinitCheck * instruction)3479 void InstructionCodeGeneratorRISCV64::VisitClinitCheck(HClinitCheck* instruction) {
3480   // We assume the class is not null.
3481   SlowPathCodeRISCV64* slow_path = new (codegen_->GetScopedAllocator()) LoadClassSlowPathRISCV64(
3482       instruction->GetLoadClass(), instruction);
3483   codegen_->AddSlowPath(slow_path);
3484   GenerateClassInitializationCheck(slow_path,
3485                                    instruction->GetLocations()->InAt(0).AsRegister<XRegister>());
3486 }
3487 
VisitCompare(HCompare * instruction)3488 void LocationsBuilderRISCV64::VisitCompare(HCompare* instruction) {
3489   DataType::Type compare_type = instruction->GetComparisonType();
3490 
3491   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
3492 
3493   switch (compare_type) {
3494     case DataType::Type::kBool:
3495     case DataType::Type::kUint8:
3496     case DataType::Type::kInt8:
3497     case DataType::Type::kUint16:
3498     case DataType::Type::kInt16:
3499     case DataType::Type::kInt32:
3500     case DataType::Type::kUint32:
3501     case DataType::Type::kInt64:
3502     case DataType::Type::kUint64:
3503       locations->SetInAt(0, Location::RequiresRegister());
3504       locations->SetInAt(1, RegisterOrZeroBitPatternLocation(instruction->InputAt(1)));
3505       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3506       break;
3507 
3508     case DataType::Type::kFloat32:
3509     case DataType::Type::kFloat64:
3510       locations->SetInAt(0, Location::RequiresFpuRegister());
3511       locations->SetInAt(1, Location::RequiresFpuRegister());
3512       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3513       break;
3514 
3515     default:
3516       LOG(FATAL) << "Unexpected type for compare operation " << compare_type;
3517       UNREACHABLE();
3518   }
3519 }
3520 
VisitCompare(HCompare * instruction)3521 void InstructionCodeGeneratorRISCV64::VisitCompare(HCompare* instruction) {
3522   LocationSummary* locations = instruction->GetLocations();
3523   XRegister result = locations->Out().AsRegister<XRegister>();
3524   DataType::Type in_type = instruction->InputAt(0)->GetType();
3525   DataType::Type compare_type = instruction->GetComparisonType();
3526 
3527   //  0 if: left == right
3528   //  1 if: left  > right
3529   // -1 if: left  < right
3530   switch (compare_type) {
3531     case DataType::Type::kBool:
3532     case DataType::Type::kUint8:
3533     case DataType::Type::kInt8:
3534     case DataType::Type::kUint16:
3535     case DataType::Type::kInt16:
3536     case DataType::Type::kInt32:
3537     case DataType::Type::kInt64: {
3538       XRegister left = locations->InAt(0).AsRegister<XRegister>();
3539       XRegister right = InputXRegisterOrZero(locations->InAt(1));
3540       ScratchRegisterScope srs(GetAssembler());
3541       XRegister tmp = srs.AllocateXRegister();
3542       __ Slt(tmp, left, right);
3543       __ Slt(result, right, left);
3544       __ Sub(result, result, tmp);
3545       break;
3546     }
3547 
3548     case DataType::Type::kUint32:
3549     case DataType::Type::kUint64: {
3550       XRegister left = locations->InAt(0).AsRegister<XRegister>();
3551       XRegister right = InputXRegisterOrZero(locations->InAt(1));
3552       ScratchRegisterScope srs(GetAssembler());
3553       XRegister tmp = srs.AllocateXRegister();
3554       __ Sltu(tmp, left, right);
3555       __ Sltu(result, right, left);
3556       __ Sub(result, result, tmp);
3557       break;
3558     }
3559 
3560     case DataType::Type::kFloat32:
3561     case DataType::Type::kFloat64: {
3562       FRegister left = locations->InAt(0).AsFpuRegister<FRegister>();
3563       FRegister right = locations->InAt(1).AsFpuRegister<FRegister>();
3564       ScratchRegisterScope srs(GetAssembler());
3565       XRegister tmp = srs.AllocateXRegister();
3566       if (instruction->IsGtBias()) {
3567         // ((FLE l,r) ^ 1) - (FLT l,r); see `GenerateFpCondition()`.
3568         FLe(tmp, left, right, in_type);
3569         FLt(result, left, right, in_type);
3570         __ Xori(tmp, tmp, 1);
3571         __ Sub(result, tmp, result);
3572       } else {
3573         // ((FLE r,l) - 1) + (FLT r,l); see `GenerateFpCondition()`.
3574         FLe(tmp, right, left, in_type);
3575         FLt(result, right, left, in_type);
3576         __ Addi(tmp, tmp, -1);
3577         __ Add(result, result, tmp);
3578       }
3579       break;
3580     }
3581 
3582     default:
3583       LOG(FATAL) << "Unimplemented compare type " << in_type;
3584   }
3585 }
3586 
VisitConstructorFence(HConstructorFence * instruction)3587 void LocationsBuilderRISCV64::VisitConstructorFence(HConstructorFence* instruction) {
3588   instruction->SetLocations(nullptr);
3589 }
3590 
VisitConstructorFence(HConstructorFence * instruction)3591 void InstructionCodeGeneratorRISCV64::VisitConstructorFence(
3592     [[maybe_unused]] HConstructorFence* instruction) {
3593   codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
3594 }
3595 
VisitCurrentMethod(HCurrentMethod * instruction)3596 void LocationsBuilderRISCV64::VisitCurrentMethod(HCurrentMethod* instruction) {
3597   LocationSummary* locations =
3598       new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
3599   locations->SetOut(Location::RegisterLocation(kArtMethodRegister));
3600 }
3601 
VisitCurrentMethod(HCurrentMethod * instruction)3602 void InstructionCodeGeneratorRISCV64::VisitCurrentMethod(
3603     [[maybe_unused]] HCurrentMethod* instruction) {
3604   // Nothing to do, the method is already at its location.
3605 }
3606 
VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag * instruction)3607 void LocationsBuilderRISCV64::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* instruction) {
3608   LocationSummary* locations =
3609       new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
3610   locations->SetOut(Location::RequiresRegister());
3611 }
3612 
VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag * instruction)3613 void InstructionCodeGeneratorRISCV64::VisitShouldDeoptimizeFlag(
3614     HShouldDeoptimizeFlag* instruction) {
3615   __ Loadw(instruction->GetLocations()->Out().AsRegister<XRegister>(),
3616            SP,
3617            codegen_->GetStackOffsetOfShouldDeoptimizeFlag());
3618 }
3619 
VisitDeoptimize(HDeoptimize * instruction)3620 void LocationsBuilderRISCV64::VisitDeoptimize(HDeoptimize* instruction) {
3621   LocationSummary* locations = new (GetGraph()->GetAllocator())
3622       LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
3623   InvokeRuntimeCallingConvention calling_convention;
3624   RegisterSet caller_saves = RegisterSet::Empty();
3625   caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
3626   locations->SetCustomSlowPathCallerSaves(caller_saves);
3627   if (IsBooleanValueOrMaterializedCondition(instruction->InputAt(0))) {
3628     locations->SetInAt(0, Location::RequiresRegister());
3629   }
3630 }
3631 
VisitDeoptimize(HDeoptimize * instruction)3632 void InstructionCodeGeneratorRISCV64::VisitDeoptimize(HDeoptimize* instruction) {
3633   SlowPathCodeRISCV64* slow_path =
3634       deopt_slow_paths_.NewSlowPath<DeoptimizationSlowPathRISCV64>(instruction);
3635   GenerateTestAndBranch(instruction,
3636                         /* condition_input_index= */ 0,
3637                         slow_path->GetEntryLabel(),
3638                         /* false_target= */ nullptr);
3639 }
3640 
VisitDiv(HDiv * instruction)3641 void LocationsBuilderRISCV64::VisitDiv(HDiv* instruction) {
3642   LocationSummary* locations =
3643       new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
3644   switch (instruction->GetResultType()) {
3645     case DataType::Type::kInt32:
3646     case DataType::Type::kInt64:
3647       locations->SetInAt(0, Location::RequiresRegister());
3648       locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
3649       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3650       break;
3651 
3652     case DataType::Type::kFloat32:
3653     case DataType::Type::kFloat64:
3654       locations->SetInAt(0, Location::RequiresFpuRegister());
3655       locations->SetInAt(1, Location::RequiresFpuRegister());
3656       locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3657       break;
3658 
3659     default:
3660       LOG(FATAL) << "Unexpected div type " << instruction->GetResultType();
3661       UNREACHABLE();
3662   }
3663 }
3664 
VisitDiv(HDiv * instruction)3665 void InstructionCodeGeneratorRISCV64::VisitDiv(HDiv* instruction) {
3666   DataType::Type type = instruction->GetType();
3667   LocationSummary* locations = instruction->GetLocations();
3668 
3669   switch (type) {
3670     case DataType::Type::kInt32:
3671     case DataType::Type::kInt64:
3672       GenerateDivRemIntegral(instruction);
3673       break;
3674     case DataType::Type::kFloat32:
3675     case DataType::Type::kFloat64: {
3676       FRegister dst = locations->Out().AsFpuRegister<FRegister>();
3677       FRegister lhs = locations->InAt(0).AsFpuRegister<FRegister>();
3678       FRegister rhs = locations->InAt(1).AsFpuRegister<FRegister>();
3679       FDiv(dst, lhs, rhs, type);
3680       break;
3681     }
3682     default:
3683       LOG(FATAL) << "Unexpected div type " << type;
3684       UNREACHABLE();
3685   }
3686 }
3687 
VisitDivZeroCheck(HDivZeroCheck * instruction)3688 void LocationsBuilderRISCV64::VisitDivZeroCheck(HDivZeroCheck* instruction) {
3689   LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
3690   locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0)));
3691 }
3692 
VisitDivZeroCheck(HDivZeroCheck * instruction)3693 void InstructionCodeGeneratorRISCV64::VisitDivZeroCheck(HDivZeroCheck* instruction) {
3694   SlowPathCodeRISCV64* slow_path =
3695       new (codegen_->GetScopedAllocator()) DivZeroCheckSlowPathRISCV64(instruction);
3696   codegen_->AddSlowPath(slow_path);
3697   Location value = instruction->GetLocations()->InAt(0);
3698 
3699   DataType::Type type = instruction->GetType();
3700 
3701   if (!DataType::IsIntegralType(type)) {
3702     LOG(FATAL) << "Unexpected type " << type << " for DivZeroCheck.";
3703     UNREACHABLE();
3704   }
3705 
3706   if (value.IsConstant()) {
3707     int64_t divisor = codegen_->GetInt64ValueOf(value.GetConstant()->AsConstant());
3708     if (divisor == 0) {
3709       __ J(slow_path->GetEntryLabel());
3710     } else {
3711       // A division by a non-null constant is valid. We don't need to perform
3712       // any check, so simply fall through.
3713     }
3714   } else {
3715     __ Beqz(value.AsRegister<XRegister>(), slow_path->GetEntryLabel());
3716   }
3717 }
3718 
VisitDoubleConstant(HDoubleConstant * instruction)3719 void LocationsBuilderRISCV64::VisitDoubleConstant(HDoubleConstant* instruction) {
3720   LocationSummary* locations =
3721       new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
3722   locations->SetOut(Location::ConstantLocation(instruction));
3723 }
3724 
VisitDoubleConstant(HDoubleConstant * instruction)3725 void InstructionCodeGeneratorRISCV64::VisitDoubleConstant(
3726     [[maybe_unused]] HDoubleConstant* instruction) {
3727   // Will be generated at use site.
3728 }
3729 
VisitEqual(HEqual * instruction)3730 void LocationsBuilderRISCV64::VisitEqual(HEqual* instruction) {
3731   HandleCondition(instruction);
3732 }
3733 
VisitEqual(HEqual * instruction)3734 void InstructionCodeGeneratorRISCV64::VisitEqual(HEqual* instruction) {
3735   HandleCondition(instruction);
3736 }
3737 
VisitExit(HExit * instruction)3738 void LocationsBuilderRISCV64::VisitExit(HExit* instruction) {
3739   instruction->SetLocations(nullptr);
3740 }
3741 
VisitExit(HExit * instruction)3742 void InstructionCodeGeneratorRISCV64::VisitExit([[maybe_unused]] HExit* instruction) {}
3743 
VisitFloatConstant(HFloatConstant * instruction)3744 void LocationsBuilderRISCV64::VisitFloatConstant(HFloatConstant* instruction) {
3745   LocationSummary* locations =
3746       new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
3747   locations->SetOut(Location::ConstantLocation(instruction));
3748 }
3749 
VisitFloatConstant(HFloatConstant * instruction)3750 void InstructionCodeGeneratorRISCV64::VisitFloatConstant(
3751     [[maybe_unused]] HFloatConstant* instruction) {
3752   // Will be generated at use site.
3753 }
3754 
VisitGoto(HGoto * instruction)3755 void LocationsBuilderRISCV64::VisitGoto(HGoto* instruction) {
3756   instruction->SetLocations(nullptr);
3757 }
3758 
VisitGoto(HGoto * instruction)3759 void InstructionCodeGeneratorRISCV64::VisitGoto(HGoto* instruction) {
3760   HandleGoto(instruction, instruction->GetSuccessor());
3761 }
3762 
VisitGreaterThan(HGreaterThan * instruction)3763 void LocationsBuilderRISCV64::VisitGreaterThan(HGreaterThan* instruction) {
3764   HandleCondition(instruction);
3765 }
3766 
VisitGreaterThan(HGreaterThan * instruction)3767 void InstructionCodeGeneratorRISCV64::VisitGreaterThan(HGreaterThan* instruction) {
3768   HandleCondition(instruction);
3769 }
3770 
VisitGreaterThanOrEqual(HGreaterThanOrEqual * instruction)3771 void LocationsBuilderRISCV64::VisitGreaterThanOrEqual(HGreaterThanOrEqual* instruction) {
3772   HandleCondition(instruction);
3773 }
3774 
VisitGreaterThanOrEqual(HGreaterThanOrEqual * instruction)3775 void InstructionCodeGeneratorRISCV64::VisitGreaterThanOrEqual(HGreaterThanOrEqual* instruction) {
3776   HandleCondition(instruction);
3777 }
3778 
VisitIf(HIf * instruction)3779 void LocationsBuilderRISCV64::VisitIf(HIf* instruction) {
3780   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
3781   if (IsBooleanValueOrMaterializedCondition(instruction->InputAt(0))) {
3782     locations->SetInAt(0, Location::RequiresRegister());
3783     if (GetGraph()->IsCompilingBaseline() &&
3784         codegen_->GetCompilerOptions().ProfileBranches() &&
3785         !Runtime::Current()->IsAotCompiler()) {
3786       DCHECK(instruction->InputAt(0)->IsCondition());
3787       ProfilingInfo* info = GetGraph()->GetProfilingInfo();
3788       DCHECK(info != nullptr);
3789       BranchCache* cache = info->GetBranchCache(instruction->GetDexPc());
3790       if (cache != nullptr) {
3791         locations->AddTemp(Location::RequiresRegister());
3792       }
3793     }
3794   }
3795 }
3796 
VisitIf(HIf * instruction)3797 void InstructionCodeGeneratorRISCV64::VisitIf(HIf* instruction) {
3798   HBasicBlock* true_successor = instruction->IfTrueSuccessor();
3799   HBasicBlock* false_successor = instruction->IfFalseSuccessor();
3800   Riscv64Label* true_target = codegen_->GoesToNextBlock(instruction->GetBlock(), true_successor)
3801       ? nullptr
3802       : codegen_->GetLabelOf(true_successor);
3803   Riscv64Label* false_target = codegen_->GoesToNextBlock(instruction->GetBlock(), false_successor)
3804       ? nullptr
3805       : codegen_->GetLabelOf(false_successor);
3806   if (IsBooleanValueOrMaterializedCondition(instruction->InputAt(0))) {
3807     if (GetGraph()->IsCompilingBaseline() &&
3808         codegen_->GetCompilerOptions().ProfileBranches() &&
3809         !Runtime::Current()->IsAotCompiler()) {
3810       DCHECK(instruction->InputAt(0)->IsCondition());
3811       ProfilingInfo* info = GetGraph()->GetProfilingInfo();
3812       DCHECK(info != nullptr);
3813       BranchCache* cache = info->GetBranchCache(instruction->GetDexPc());
3814       // Currently, not all If branches are profiled.
3815       if (cache != nullptr) {
3816         uint64_t address =
3817             reinterpret_cast64<uint64_t>(cache) + BranchCache::FalseOffset().Int32Value();
3818         static_assert(
3819             BranchCache::TrueOffset().Int32Value() - BranchCache::FalseOffset().Int32Value() == 2,
3820             "Unexpected offsets for BranchCache");
3821         Riscv64Label done;
3822         XRegister condition = instruction->GetLocations()->InAt(0).AsRegister<XRegister>();
3823         XRegister temp = instruction->GetLocations()->GetTemp(0).AsRegister<XRegister>();
3824         __ LoadConst64(temp, address);
3825         __ Sh1Add(temp, condition, temp);
3826         ScratchRegisterScope srs(GetAssembler());
3827         XRegister counter = srs.AllocateXRegister();
3828         __ Loadhu(counter, temp, 0);
3829         __ Addi(counter, counter, 1);
3830         {
3831           ScratchRegisterScope srs2(GetAssembler());
3832           XRegister overflow = srs2.AllocateXRegister();
3833           __ Srli(overflow, counter, 16);
3834           __ Bnez(overflow, &done);
3835         }
3836         __ Storeh(counter, temp, 0);
3837         __ Bind(&done);
3838       }
3839     }
3840   }
3841   GenerateTestAndBranch(instruction, /* condition_input_index= */ 0, true_target, false_target);
3842 }
3843 
VisitInstanceFieldGet(HInstanceFieldGet * instruction)3844 void LocationsBuilderRISCV64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
3845   HandleFieldGet(instruction);
3846 }
3847 
VisitInstanceFieldGet(HInstanceFieldGet * instruction)3848 void InstructionCodeGeneratorRISCV64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
3849   HandleFieldGet(instruction, instruction->GetFieldInfo());
3850 }
3851 
VisitInstanceFieldSet(HInstanceFieldSet * instruction)3852 void LocationsBuilderRISCV64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
3853   HandleFieldSet(instruction);
3854 }
3855 
VisitInstanceFieldSet(HInstanceFieldSet * instruction)3856 void InstructionCodeGeneratorRISCV64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
3857   HandleFieldSet(instruction,
3858                  instruction->GetFieldInfo(),
3859                  instruction->GetValueCanBeNull(),
3860                  instruction->GetWriteBarrierKind());
3861 }
3862 
VisitInstanceOf(HInstanceOf * instruction)3863 void LocationsBuilderRISCV64::VisitInstanceOf(HInstanceOf* instruction) {
3864   LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
3865   TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
3866   bool baker_read_barrier_slow_path = false;
3867   switch (type_check_kind) {
3868     case TypeCheckKind::kExactCheck:
3869     case TypeCheckKind::kAbstractClassCheck:
3870     case TypeCheckKind::kClassHierarchyCheck:
3871     case TypeCheckKind::kArrayObjectCheck:
3872     case TypeCheckKind::kInterfaceCheck: {
3873       bool needs_read_barrier = codegen_->InstanceOfNeedsReadBarrier(instruction);
3874       call_kind = needs_read_barrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall;
3875       baker_read_barrier_slow_path = (kUseBakerReadBarrier && needs_read_barrier) &&
3876                                      (type_check_kind != TypeCheckKind::kInterfaceCheck);
3877       break;
3878     }
3879     case TypeCheckKind::kArrayCheck:
3880     case TypeCheckKind::kUnresolvedCheck:
3881       call_kind = LocationSummary::kCallOnSlowPath;
3882       break;
3883     case TypeCheckKind::kBitstringCheck:
3884       break;
3885   }
3886 
3887   LocationSummary* locations =
3888       new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
3889   if (baker_read_barrier_slow_path) {
3890     locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
3891   }
3892   locations->SetInAt(0, Location::RequiresRegister());
3893   if (type_check_kind == TypeCheckKind::kBitstringCheck) {
3894     locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)));
3895     locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)));
3896     locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)));
3897   } else {
3898     locations->SetInAt(1, Location::RequiresRegister());
3899   }
3900   // The output does overlap inputs.
3901   // Note that TypeCheckSlowPathRISCV64 uses this register too.
3902   locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
3903   locations->AddRegisterTemps(
3904       NumberOfInstanceOfTemps(codegen_->EmitReadBarrier(), type_check_kind));
3905 }
3906 
VisitInstanceOf(HInstanceOf * instruction)3907 void InstructionCodeGeneratorRISCV64::VisitInstanceOf(HInstanceOf* instruction) {
3908   TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
3909   LocationSummary* locations = instruction->GetLocations();
3910   Location obj_loc = locations->InAt(0);
3911   XRegister obj = obj_loc.AsRegister<XRegister>();
3912   Location cls = (type_check_kind == TypeCheckKind::kBitstringCheck)
3913       ? Location::NoLocation()
3914       : locations->InAt(1);
3915   Location out_loc = locations->Out();
3916   XRegister out = out_loc.AsRegister<XRegister>();
3917   const size_t num_temps = NumberOfInstanceOfTemps(codegen_->EmitReadBarrier(), type_check_kind);
3918   DCHECK_LE(num_temps, 1u);
3919   Location maybe_temp_loc = (num_temps >= 1) ? locations->GetTemp(0) : Location::NoLocation();
3920   const uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
3921   const uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
3922   const uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
3923   const uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
3924   const uint32_t iftable_offset = mirror::Class::IfTableOffset().Uint32Value();
3925   const uint32_t array_length_offset = mirror::Array::LengthOffset().Uint32Value();
3926   const uint32_t object_array_data_offset =
3927       mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value();
3928   Riscv64Label done;
3929   SlowPathCodeRISCV64* slow_path = nullptr;
3930 
3931   // Return 0 if `obj` is null.
3932   // Avoid this check if we know `obj` is not null.
3933   if (instruction->MustDoNullCheck()) {
3934     __ Mv(out, Zero);
3935     __ Beqz(obj, &done);
3936   }
3937 
3938   switch (type_check_kind) {
3939     case TypeCheckKind::kExactCheck: {
3940       ReadBarrierOption read_barrier_option =
3941           codegen_->ReadBarrierOptionForInstanceOf(instruction);
3942       // /* HeapReference<Class> */ out = obj->klass_
3943       GenerateReferenceLoadTwoRegisters(
3944           instruction, out_loc, obj_loc, class_offset, maybe_temp_loc, read_barrier_option);
3945       // Classes must be equal for the instanceof to succeed.
3946       __ Xor(out, out, cls.AsRegister<XRegister>());
3947       __ Seqz(out, out);
3948       break;
3949     }
3950 
3951     case TypeCheckKind::kAbstractClassCheck: {
3952       ReadBarrierOption read_barrier_option =
3953           codegen_->ReadBarrierOptionForInstanceOf(instruction);
3954       // /* HeapReference<Class> */ out = obj->klass_
3955       GenerateReferenceLoadTwoRegisters(
3956           instruction, out_loc, obj_loc, class_offset, maybe_temp_loc, read_barrier_option);
3957       // If the class is abstract, we eagerly fetch the super class of the
3958       // object to avoid doing a comparison we know will fail.
3959       Riscv64Label loop;
3960       __ Bind(&loop);
3961       // /* HeapReference<Class> */ out = out->super_class_
3962       GenerateReferenceLoadOneRegister(
3963           instruction, out_loc, super_offset, maybe_temp_loc, read_barrier_option);
3964       // If `out` is null, we use it for the result, and jump to `done`.
3965       __ Beqz(out, &done);
3966       __ Bne(out, cls.AsRegister<XRegister>(), &loop);
3967       __ LoadConst32(out, 1);
3968       break;
3969     }
3970 
3971     case TypeCheckKind::kClassHierarchyCheck: {
3972       ReadBarrierOption read_barrier_option =
3973           codegen_->ReadBarrierOptionForInstanceOf(instruction);
3974       // /* HeapReference<Class> */ out = obj->klass_
3975       GenerateReferenceLoadTwoRegisters(
3976           instruction, out_loc, obj_loc, class_offset, maybe_temp_loc, read_barrier_option);
3977       // Walk over the class hierarchy to find a match.
3978       Riscv64Label loop, success;
3979       __ Bind(&loop);
3980       __ Beq(out, cls.AsRegister<XRegister>(), &success);
3981       // /* HeapReference<Class> */ out = out->super_class_
3982       GenerateReferenceLoadOneRegister(
3983           instruction, out_loc, super_offset, maybe_temp_loc, read_barrier_option);
3984       __ Bnez(out, &loop);
3985       // If `out` is null, we use it for the result, and jump to `done`.
3986       __ J(&done);
3987       __ Bind(&success);
3988       __ LoadConst32(out, 1);
3989       break;
3990     }
3991 
3992     case TypeCheckKind::kArrayObjectCheck: {
3993       ReadBarrierOption read_barrier_option =
3994           codegen_->ReadBarrierOptionForInstanceOf(instruction);
3995       // FIXME(riscv64): We currently have marking entrypoints for 29 registers.
3996       // We need to either store entrypoint for register `N` in entry `N-A` where
3997       // `A` can be up to 5 (Zero, RA, SP, GP, TP are not valid registers for
3998       // marking), or define two more entrypoints, or request an additional temp
3999       // from the register allocator instead of using a scratch register.
4000       ScratchRegisterScope srs(GetAssembler());
4001       Location tmp = Location::RegisterLocation(srs.AllocateXRegister());
4002       // /* HeapReference<Class> */ tmp = obj->klass_
4003       GenerateReferenceLoadTwoRegisters(
4004           instruction, tmp, obj_loc, class_offset, maybe_temp_loc, read_barrier_option);
4005       // Do an exact check.
4006       __ LoadConst32(out, 1);
4007       __ Beq(tmp.AsRegister<XRegister>(), cls.AsRegister<XRegister>(), &done);
4008       // Otherwise, we need to check that the object's class is a non-primitive array.
4009       // /* HeapReference<Class> */ out = out->component_type_
4010       GenerateReferenceLoadTwoRegisters(
4011           instruction, out_loc, tmp, component_offset, maybe_temp_loc, read_barrier_option);
4012       // If `out` is null, we use it for the result, and jump to `done`.
4013       __ Beqz(out, &done);
4014       __ Loadhu(out, out, primitive_offset);
4015       static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
4016       __ Seqz(out, out);
4017       break;
4018     }
4019 
4020     case TypeCheckKind::kArrayCheck: {
4021       // No read barrier since the slow path will retry upon failure.
4022       // /* HeapReference<Class> */ out = obj->klass_
4023       GenerateReferenceLoadTwoRegisters(
4024           instruction, out_loc, obj_loc, class_offset, maybe_temp_loc, kWithoutReadBarrier);
4025       DCHECK(locations->OnlyCallsOnSlowPath());
4026       slow_path = new (codegen_->GetScopedAllocator())
4027           TypeCheckSlowPathRISCV64(instruction, /* is_fatal= */ false);
4028       codegen_->AddSlowPath(slow_path);
4029       __ Bne(out, cls.AsRegister<XRegister>(), slow_path->GetEntryLabel());
4030       __ LoadConst32(out, 1);
4031       break;
4032     }
4033 
4034     case TypeCheckKind::kInterfaceCheck: {
4035       if (codegen_->InstanceOfNeedsReadBarrier(instruction)) {
4036         DCHECK(locations->OnlyCallsOnSlowPath());
4037         slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathRISCV64(
4038             instruction, /* is_fatal= */ false);
4039         codegen_->AddSlowPath(slow_path);
4040         if (codegen_->EmitNonBakerReadBarrier()) {
4041           __ J(slow_path->GetEntryLabel());
4042           break;
4043         }
4044         // For Baker read barrier, take the slow path while marking.
4045         __ Loadw(out, TR, Thread::IsGcMarkingOffset<kRiscv64PointerSize>().Int32Value());
4046         __ Bnez(out, slow_path->GetEntryLabel());
4047       }
4048 
4049       // Fast-path without read barriers.
4050       ScratchRegisterScope srs(GetAssembler());
4051       XRegister temp = srs.AllocateXRegister();
4052       // /* HeapReference<Class> */ temp = obj->klass_
4053       __ Loadwu(temp, obj, class_offset);
4054       codegen_->MaybeUnpoisonHeapReference(temp);
4055       // /* HeapReference<Class> */ temp = temp->iftable_
4056       __ Loadwu(temp, temp, iftable_offset);
4057       codegen_->MaybeUnpoisonHeapReference(temp);
4058       // Load the size of the `IfTable`. The `Class::iftable_` is never null.
4059       __ Loadw(out, temp, array_length_offset);
4060       // Loop through the `IfTable` and check if any class matches.
4061       Riscv64Label loop;
4062       XRegister temp2 = srs.AllocateXRegister();
4063       __ Bind(&loop);
4064       __ Beqz(out, &done);  // If taken, the result in `out` is already 0 (false).
4065       __ Loadwu(temp2, temp, object_array_data_offset);
4066       codegen_->MaybeUnpoisonHeapReference(temp2);
4067       // Go to next interface.
4068       __ Addi(temp, temp, 2 * kHeapReferenceSize);
4069       __ Addi(out, out, -2);
4070       // Compare the classes and continue the loop if they do not match.
4071       __ Bne(cls.AsRegister<XRegister>(), temp2, &loop);
4072       __ LoadConst32(out, 1);
4073       break;
4074     }
4075 
4076     case TypeCheckKind::kUnresolvedCheck: {
4077       // Note that we indeed only call on slow path, but we always go
4078       // into the slow path for the unresolved check case.
4079       //
4080       // We cannot directly call the InstanceofNonTrivial runtime
4081       // entry point without resorting to a type checking slow path
4082       // here (i.e. by calling InvokeRuntime directly), as it would
4083       // require to assign fixed registers for the inputs of this
4084       // HInstanceOf instruction (following the runtime calling
4085       // convention), which might be cluttered by the potential first
4086       // read barrier emission at the beginning of this method.
4087       //
4088       // TODO: Introduce a new runtime entry point taking the object
4089       // to test (instead of its class) as argument, and let it deal
4090       // with the read barrier issues. This will let us refactor this
4091       // case of the `switch` code as it was previously (with a direct
4092       // call to the runtime not using a type checking slow path).
4093       // This should also be beneficial for the other cases above.
4094       DCHECK(locations->OnlyCallsOnSlowPath());
4095       slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathRISCV64(
4096           instruction, /* is_fatal= */ false);
4097       codegen_->AddSlowPath(slow_path);
4098       __ J(slow_path->GetEntryLabel());
4099       break;
4100     }
4101 
4102     case TypeCheckKind::kBitstringCheck: {
4103       // /* HeapReference<Class> */ temp = obj->klass_
4104       GenerateReferenceLoadTwoRegisters(
4105           instruction, out_loc, obj_loc, class_offset, maybe_temp_loc, kWithoutReadBarrier);
4106 
4107       GenerateBitstringTypeCheckCompare(instruction, out);
4108       __ Beqz(out, out);
4109       break;
4110     }
4111   }
4112 
4113   __ Bind(&done);
4114 
4115   if (slow_path != nullptr) {
4116     __ Bind(slow_path->GetExitLabel());
4117   }
4118 }
4119 
VisitIntConstant(HIntConstant * instruction)4120 void LocationsBuilderRISCV64::VisitIntConstant(HIntConstant* instruction) {
4121   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
4122   locations->SetOut(Location::ConstantLocation(instruction));
4123 }
4124 
VisitIntConstant(HIntConstant * instruction)4125 void InstructionCodeGeneratorRISCV64::VisitIntConstant([[maybe_unused]] HIntConstant* instruction) {
4126   // Will be generated at use site.
4127 }
4128 
VisitIntermediateAddress(HIntermediateAddress * instruction)4129 void LocationsBuilderRISCV64::VisitIntermediateAddress(HIntermediateAddress* instruction) {
4130   UNUSED(instruction);
4131   LOG(FATAL) << "Unimplemented";
4132 }
4133 
VisitIntermediateAddress(HIntermediateAddress * instruction)4134 void InstructionCodeGeneratorRISCV64::VisitIntermediateAddress(HIntermediateAddress* instruction) {
4135   UNUSED(instruction);
4136   LOG(FATAL) << "Unimplemented";
4137 }
4138 
VisitInvokeUnresolved(HInvokeUnresolved * instruction)4139 void LocationsBuilderRISCV64::VisitInvokeUnresolved(HInvokeUnresolved* instruction) {
4140   // The trampoline uses the same calling convention as dex calling conventions, except
4141   // instead of loading arg0/A0 with the target Method*, arg0/A0 will contain the method_idx.
4142   HandleInvoke(instruction);
4143 }
4144 
VisitInvokeUnresolved(HInvokeUnresolved * instruction)4145 void InstructionCodeGeneratorRISCV64::VisitInvokeUnresolved(HInvokeUnresolved* instruction) {
4146   codegen_->GenerateInvokeUnresolvedRuntimeCall(instruction);
4147 }
4148 
VisitInvokeInterface(HInvokeInterface * instruction)4149 void LocationsBuilderRISCV64::VisitInvokeInterface(HInvokeInterface* instruction) {
4150   HandleInvoke(instruction);
4151   // Use T0 as the hidden argument for `art_quick_imt_conflict_trampoline`.
4152   if (instruction->GetHiddenArgumentLoadKind() == MethodLoadKind::kRecursive) {
4153     instruction->GetLocations()->SetInAt(instruction->GetNumberOfArguments() - 1,
4154                                          Location::RegisterLocation(T0));
4155   } else {
4156     instruction->GetLocations()->AddTemp(Location::RegisterLocation(T0));
4157   }
4158 }
4159 
VisitInvokeInterface(HInvokeInterface * instruction)4160 void InstructionCodeGeneratorRISCV64::VisitInvokeInterface(HInvokeInterface* instruction) {
4161   LocationSummary* locations = instruction->GetLocations();
4162   XRegister temp = locations->GetTemp(0).AsRegister<XRegister>();
4163   XRegister receiver = locations->InAt(0).AsRegister<XRegister>();
4164   int32_t class_offset = mirror::Object::ClassOffset().Int32Value();
4165   Offset entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kRiscv64PointerSize);
4166 
4167   // /* HeapReference<Class> */ temp = receiver->klass_
4168   __ Loadwu(temp, receiver, class_offset);
4169   codegen_->MaybeRecordImplicitNullCheck(instruction);
4170   // Instead of simply (possibly) unpoisoning `temp` here, we should
4171   // emit a read barrier for the previous class reference load.
4172   // However this is not required in practice, as this is an
4173   // intermediate/temporary reference and because the current
4174   // concurrent copying collector keeps the from-space memory
4175   // intact/accessible until the end of the marking phase (the
4176   // concurrent copying collector may not in the future).
4177   codegen_->MaybeUnpoisonHeapReference(temp);
4178 
4179   // If we're compiling baseline, update the inline cache.
4180   codegen_->MaybeGenerateInlineCacheCheck(instruction, temp);
4181 
4182   // The register T0 is required to be used for the hidden argument in
4183   // `art_quick_imt_conflict_trampoline`.
4184   if (instruction->GetHiddenArgumentLoadKind() != MethodLoadKind::kRecursive &&
4185       instruction->GetHiddenArgumentLoadKind() != MethodLoadKind::kRuntimeCall) {
4186     Location hidden_reg = instruction->GetLocations()->GetTemp(1);
4187     // Load the resolved interface method in the hidden argument register T0.
4188     DCHECK_EQ(T0, hidden_reg.AsRegister<XRegister>());
4189     codegen_->LoadMethod(instruction->GetHiddenArgumentLoadKind(), hidden_reg, instruction);
4190   }
4191 
4192   __ Loadd(temp, temp, mirror::Class::ImtPtrOffset(kRiscv64PointerSize).Uint32Value());
4193   uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement(
4194       instruction->GetImtIndex(), kRiscv64PointerSize));
4195   // temp = temp->GetImtEntryAt(method_offset);
4196   __ Loadd(temp, temp, method_offset);
4197   if (instruction->GetHiddenArgumentLoadKind() == MethodLoadKind::kRuntimeCall) {
4198     // We pass the method from the IMT in case of a conflict. This will ensure
4199     // we go into the runtime to resolve the actual method.
4200     Location hidden_reg = instruction->GetLocations()->GetTemp(1);
4201     DCHECK_EQ(T0, hidden_reg.AsRegister<XRegister>());
4202     __ Mv(hidden_reg.AsRegister<XRegister>(), temp);
4203   }
4204   // RA = temp->GetEntryPoint();
4205   __ Loadd(RA, temp, entry_point.Int32Value());
4206 
4207   // RA();
4208   __ Jalr(RA);
4209   DCHECK(!codegen_->IsLeafMethod());
4210   codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
4211 }
4212 
VisitInvokeStaticOrDirect(HInvokeStaticOrDirect * instruction)4213 void LocationsBuilderRISCV64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* instruction) {
4214   // Explicit clinit checks triggered by static invokes must have been pruned by
4215   // art::PrepareForRegisterAllocation.
4216   DCHECK(!instruction->IsStaticWithExplicitClinitCheck());
4217 
4218   IntrinsicLocationsBuilderRISCV64 intrinsic(GetGraph()->GetAllocator(), codegen_);
4219   if (intrinsic.TryDispatch(instruction)) {
4220     return;
4221   }
4222 
4223   if (instruction->GetCodePtrLocation() == CodePtrLocation::kCallCriticalNative) {
4224     CriticalNativeCallingConventionVisitorRiscv64 calling_convention_visitor(
4225         /*for_register_allocation=*/ true);
4226     CodeGenerator::CreateCommonInvokeLocationSummary(instruction, &calling_convention_visitor);
4227   } else {
4228     HandleInvoke(instruction);
4229   }
4230 }
4231 
TryGenerateIntrinsicCode(HInvoke * invoke,CodeGeneratorRISCV64 * codegen)4232 static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorRISCV64* codegen) {
4233   if (invoke->GetLocations()->Intrinsified()) {
4234     IntrinsicCodeGeneratorRISCV64 intrinsic(codegen);
4235     intrinsic.Dispatch(invoke);
4236     return true;
4237   }
4238   return false;
4239 }
4240 
VisitInvokeStaticOrDirect(HInvokeStaticOrDirect * instruction)4241 void InstructionCodeGeneratorRISCV64::VisitInvokeStaticOrDirect(
4242     HInvokeStaticOrDirect* instruction) {
4243   // Explicit clinit checks triggered by static invokes must have been pruned by
4244   // art::PrepareForRegisterAllocation.
4245   DCHECK(!instruction->IsStaticWithExplicitClinitCheck());
4246 
4247   if (TryGenerateIntrinsicCode(instruction, codegen_)) {
4248     return;
4249   }
4250 
4251   LocationSummary* locations = instruction->GetLocations();
4252   codegen_->GenerateStaticOrDirectCall(
4253       instruction, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation());
4254 }
4255 
VisitInvokeVirtual(HInvokeVirtual * instruction)4256 void LocationsBuilderRISCV64::VisitInvokeVirtual(HInvokeVirtual* instruction) {
4257   IntrinsicLocationsBuilderRISCV64 intrinsic(GetGraph()->GetAllocator(), codegen_);
4258   if (intrinsic.TryDispatch(instruction)) {
4259     return;
4260   }
4261 
4262   HandleInvoke(instruction);
4263 }
4264 
VisitInvokeVirtual(HInvokeVirtual * instruction)4265 void InstructionCodeGeneratorRISCV64::VisitInvokeVirtual(HInvokeVirtual* instruction) {
4266   if (TryGenerateIntrinsicCode(instruction, codegen_)) {
4267     return;
4268   }
4269 
4270   codegen_->GenerateVirtualCall(instruction, instruction->GetLocations()->GetTemp(0));
4271   DCHECK(!codegen_->IsLeafMethod());
4272 }
4273 
VisitInvokePolymorphic(HInvokePolymorphic * instruction)4274 void LocationsBuilderRISCV64::VisitInvokePolymorphic(HInvokePolymorphic* instruction) {
4275   IntrinsicLocationsBuilderRISCV64 intrinsic(GetGraph()->GetAllocator(), codegen_);
4276   if (intrinsic.TryDispatch(instruction)) {
4277     return;
4278   }
4279   HandleInvoke(instruction);
4280 }
4281 
VisitInvokePolymorphic(HInvokePolymorphic * instruction)4282 void InstructionCodeGeneratorRISCV64::VisitInvokePolymorphic(HInvokePolymorphic* instruction) {
4283   if (TryGenerateIntrinsicCode(instruction, codegen_)) {
4284     return;
4285   }
4286   codegen_->GenerateInvokePolymorphicCall(instruction);
4287 }
4288 
VisitInvokeCustom(HInvokeCustom * instruction)4289 void LocationsBuilderRISCV64::VisitInvokeCustom(HInvokeCustom* instruction) {
4290   HandleInvoke(instruction);
4291 }
4292 
VisitInvokeCustom(HInvokeCustom * instruction)4293 void InstructionCodeGeneratorRISCV64::VisitInvokeCustom(HInvokeCustom* instruction) {
4294   codegen_->GenerateInvokeCustomCall(instruction);
4295 }
4296 
VisitLessThan(HLessThan * instruction)4297 void LocationsBuilderRISCV64::VisitLessThan(HLessThan* instruction) {
4298   HandleCondition(instruction);
4299 }
4300 
VisitLessThan(HLessThan * instruction)4301 void InstructionCodeGeneratorRISCV64::VisitLessThan(HLessThan* instruction) {
4302   HandleCondition(instruction);
4303 }
4304 
VisitLessThanOrEqual(HLessThanOrEqual * instruction)4305 void LocationsBuilderRISCV64::VisitLessThanOrEqual(HLessThanOrEqual* instruction) {
4306   HandleCondition(instruction);
4307 }
4308 
VisitLessThanOrEqual(HLessThanOrEqual * instruction)4309 void InstructionCodeGeneratorRISCV64::VisitLessThanOrEqual(HLessThanOrEqual* instruction) {
4310   HandleCondition(instruction);
4311 }
4312 
VisitLoadClass(HLoadClass * instruction)4313 void LocationsBuilderRISCV64::VisitLoadClass(HLoadClass* instruction) {
4314   HLoadClass::LoadKind load_kind = instruction->GetLoadKind();
4315   if (load_kind == HLoadClass::LoadKind::kRuntimeCall) {
4316     InvokeRuntimeCallingConvention calling_convention;
4317     Location loc = Location::RegisterLocation(calling_convention.GetRegisterAt(0));
4318     DCHECK_EQ(DataType::Type::kReference, instruction->GetType());
4319     DCHECK(loc.Equals(calling_convention.GetReturnLocation(DataType::Type::kReference)));
4320     CodeGenerator::CreateLoadClassRuntimeCallLocationSummary(instruction, loc, loc);
4321     return;
4322   }
4323   DCHECK_EQ(instruction->NeedsAccessCheck(),
4324             load_kind == HLoadClass::LoadKind::kBssEntryPublic ||
4325                 load_kind == HLoadClass::LoadKind::kBssEntryPackage);
4326 
4327   const bool requires_read_barrier = !instruction->IsInImage() && codegen_->EmitReadBarrier();
4328   LocationSummary::CallKind call_kind = (instruction->NeedsEnvironment() || requires_read_barrier)
4329       ? LocationSummary::kCallOnSlowPath
4330       : LocationSummary::kNoCall;
4331   LocationSummary* locations =
4332       new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
4333   if (kUseBakerReadBarrier && requires_read_barrier && !instruction->NeedsEnvironment()) {
4334     locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
4335   }
4336   if (load_kind == HLoadClass::LoadKind::kReferrersClass) {
4337     locations->SetInAt(0, Location::RequiresRegister());
4338   }
4339   locations->SetOut(Location::RequiresRegister());
4340   if (load_kind == HLoadClass::LoadKind::kBssEntry ||
4341       load_kind == HLoadClass::LoadKind::kBssEntryPublic ||
4342       load_kind == HLoadClass::LoadKind::kBssEntryPackage) {
4343     if (codegen_->EmitNonBakerReadBarrier()) {
4344       // For non-Baker read barriers we have a temp-clobbering call.
4345     } else {
4346       // Rely on the type resolution or initialization and marking to save everything we need.
4347       locations->SetCustomSlowPathCallerSaves(OneRegInReferenceOutSaveEverythingCallerSaves());
4348     }
4349   }
4350 }
4351 
4352 // NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
4353 // move.
VisitLoadClass(HLoadClass * instruction)4354 void InstructionCodeGeneratorRISCV64::VisitLoadClass(HLoadClass* instruction)
4355     NO_THREAD_SAFETY_ANALYSIS {
4356   HLoadClass::LoadKind load_kind = instruction->GetLoadKind();
4357   if (load_kind == HLoadClass::LoadKind::kRuntimeCall) {
4358     codegen_->GenerateLoadClassRuntimeCall(instruction);
4359     return;
4360   }
4361   DCHECK_EQ(instruction->NeedsAccessCheck(),
4362             load_kind == HLoadClass::LoadKind::kBssEntryPublic ||
4363                 load_kind == HLoadClass::LoadKind::kBssEntryPackage);
4364 
4365   LocationSummary* locations = instruction->GetLocations();
4366   Location out_loc = locations->Out();
4367   XRegister out = out_loc.AsRegister<XRegister>();
4368   const ReadBarrierOption read_barrier_option =
4369       instruction->IsInImage() ? kWithoutReadBarrier : codegen_->GetCompilerReadBarrierOption();
4370   bool generate_null_check = false;
4371   switch (load_kind) {
4372     case HLoadClass::LoadKind::kReferrersClass: {
4373       DCHECK(!instruction->CanCallRuntime());
4374       DCHECK(!instruction->MustGenerateClinitCheck());
4375       // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
4376       XRegister current_method = locations->InAt(0).AsRegister<XRegister>();
4377       codegen_->GenerateGcRootFieldLoad(instruction,
4378                                         out_loc,
4379                                         current_method,
4380                                         ArtMethod::DeclaringClassOffset().Int32Value(),
4381                                         read_barrier_option);
4382       break;
4383     }
4384     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: {
4385       DCHECK(codegen_->GetCompilerOptions().IsBootImage() ||
4386              codegen_->GetCompilerOptions().IsBootImageExtension());
4387       DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
4388       CodeGeneratorRISCV64::PcRelativePatchInfo* info_high =
4389           codegen_->NewBootImageTypePatch(instruction->GetDexFile(), instruction->GetTypeIndex());
4390       codegen_->EmitPcRelativeAuipcPlaceholder(info_high, out);
4391       CodeGeneratorRISCV64::PcRelativePatchInfo* info_low =
4392           codegen_->NewBootImageTypePatch(
4393               instruction->GetDexFile(), instruction->GetTypeIndex(), info_high);
4394       codegen_->EmitPcRelativeAddiPlaceholder(info_low, out, out);
4395       break;
4396     }
4397     case HLoadClass::LoadKind::kBootImageRelRo: {
4398       DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
4399       uint32_t boot_image_offset = codegen_->GetBootImageOffset(instruction);
4400       codegen_->LoadBootImageRelRoEntry(out, boot_image_offset);
4401       break;
4402     }
4403     case HLoadClass::LoadKind::kAppImageRelRo: {
4404       DCHECK(codegen_->GetCompilerOptions().IsAppImage());
4405       DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
4406       CodeGeneratorRISCV64::PcRelativePatchInfo* info_high =
4407           codegen_->NewAppImageTypePatch(instruction->GetDexFile(), instruction->GetTypeIndex());
4408       codegen_->EmitPcRelativeAuipcPlaceholder(info_high, out);
4409       CodeGeneratorRISCV64::PcRelativePatchInfo* info_low =
4410           codegen_->NewAppImageTypePatch(
4411               instruction->GetDexFile(), instruction->GetTypeIndex(), info_high);
4412       codegen_->EmitPcRelativeLwuPlaceholder(info_low, out, out);
4413       break;
4414     }
4415     case HLoadClass::LoadKind::kBssEntry:
4416     case HLoadClass::LoadKind::kBssEntryPublic:
4417     case HLoadClass::LoadKind::kBssEntryPackage: {
4418       CodeGeneratorRISCV64::PcRelativePatchInfo* bss_info_high =
4419           codegen_->NewTypeBssEntryPatch(instruction);
4420       codegen_->EmitPcRelativeAuipcPlaceholder(bss_info_high, out);
4421       CodeGeneratorRISCV64::PcRelativePatchInfo* info_low = codegen_->NewTypeBssEntryPatch(
4422           instruction, bss_info_high);
4423       codegen_->GenerateGcRootFieldLoad(instruction,
4424                                         out_loc,
4425                                         out,
4426                                         /* offset= */ kLinkTimeOffsetPlaceholderLow,
4427                                         read_barrier_option,
4428                                         &info_low->label);
4429       generate_null_check = true;
4430       break;
4431     }
4432     case HLoadClass::LoadKind::kJitBootImageAddress: {
4433       DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
4434       uint32_t address = reinterpret_cast32<uint32_t>(instruction->GetClass().Get());
4435       DCHECK_NE(address, 0u);
4436       __ Loadwu(out, codegen_->DeduplicateBootImageAddressLiteral(address));
4437       break;
4438     }
4439     case HLoadClass::LoadKind::kJitTableAddress:
4440       __ Loadwu(out, codegen_->DeduplicateJitClassLiteral(instruction->GetDexFile(),
4441                                                           instruction->GetTypeIndex(),
4442                                                           instruction->GetClass()));
4443       codegen_->GenerateGcRootFieldLoad(
4444           instruction, out_loc, out, /* offset= */ 0, read_barrier_option);
4445       break;
4446     case HLoadClass::LoadKind::kRuntimeCall:
4447     case HLoadClass::LoadKind::kInvalid:
4448       LOG(FATAL) << "UNREACHABLE";
4449       UNREACHABLE();
4450   }
4451 
4452   if (generate_null_check || instruction->MustGenerateClinitCheck()) {
4453     DCHECK(instruction->CanCallRuntime());
4454     SlowPathCodeRISCV64* slow_path =
4455         new (codegen_->GetScopedAllocator()) LoadClassSlowPathRISCV64(instruction, instruction);
4456     codegen_->AddSlowPath(slow_path);
4457     if (generate_null_check) {
4458       __ Beqz(out, slow_path->GetEntryLabel());
4459     }
4460     if (instruction->MustGenerateClinitCheck()) {
4461       GenerateClassInitializationCheck(slow_path, out);
4462     } else {
4463       __ Bind(slow_path->GetExitLabel());
4464     }
4465   }
4466 }
4467 
VisitLoadException(HLoadException * instruction)4468 void LocationsBuilderRISCV64::VisitLoadException(HLoadException* instruction) {
4469   LocationSummary* locations =
4470       new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
4471   locations->SetOut(Location::RequiresRegister());
4472 }
4473 
VisitLoadException(HLoadException * instruction)4474 void InstructionCodeGeneratorRISCV64::VisitLoadException(HLoadException* instruction) {
4475   XRegister out = instruction->GetLocations()->Out().AsRegister<XRegister>();
4476   __ Loadwu(out, TR, GetExceptionTlsOffset());
4477 }
4478 
VisitLoadMethodHandle(HLoadMethodHandle * instruction)4479 void LocationsBuilderRISCV64::VisitLoadMethodHandle(HLoadMethodHandle* instruction) {
4480   InvokeRuntimeCallingConvention calling_convention;
4481   Location loc = Location::RegisterLocation(calling_convention.GetRegisterAt(0));
4482   CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(instruction, loc, loc);
4483 }
4484 
VisitLoadMethodHandle(HLoadMethodHandle * instruction)4485 void InstructionCodeGeneratorRISCV64::VisitLoadMethodHandle(HLoadMethodHandle* instruction) {
4486   codegen_->GenerateLoadMethodHandleRuntimeCall(instruction);
4487 }
4488 
VisitLoadMethodType(HLoadMethodType * instruction)4489 void LocationsBuilderRISCV64::VisitLoadMethodType(HLoadMethodType* instruction) {
4490   InvokeRuntimeCallingConvention calling_convention;
4491   Location loc = Location::RegisterLocation(calling_convention.GetRegisterAt(0));
4492   CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(instruction, loc, loc);
4493 }
4494 
VisitLoadMethodType(HLoadMethodType * instruction)4495 void InstructionCodeGeneratorRISCV64::VisitLoadMethodType(HLoadMethodType* instruction) {
4496   codegen_->GenerateLoadMethodTypeRuntimeCall(instruction);
4497 }
4498 
VisitLoadString(HLoadString * instruction)4499 void LocationsBuilderRISCV64::VisitLoadString(HLoadString* instruction) {
4500   HLoadString::LoadKind load_kind = instruction->GetLoadKind();
4501   LocationSummary::CallKind call_kind = codegen_->GetLoadStringCallKind(instruction);
4502   LocationSummary* locations =
4503       new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
4504   if (load_kind == HLoadString::LoadKind::kRuntimeCall) {
4505     InvokeRuntimeCallingConvention calling_convention;
4506     DCHECK_EQ(DataType::Type::kReference, instruction->GetType());
4507     locations->SetOut(calling_convention.GetReturnLocation(DataType::Type::kReference));
4508   } else {
4509     locations->SetOut(Location::RequiresRegister());
4510     if (load_kind == HLoadString::LoadKind::kBssEntry) {
4511       if (codegen_->EmitNonBakerReadBarrier()) {
4512         // For non-Baker read barriers we have a temp-clobbering call.
4513       } else {
4514         // Rely on the pResolveString and marking to save everything we need.
4515         locations->SetCustomSlowPathCallerSaves(OneRegInReferenceOutSaveEverythingCallerSaves());
4516       }
4517     }
4518   }
4519 }
4520 
4521 // NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
4522 // move.
VisitLoadString(HLoadString * instruction)4523 void InstructionCodeGeneratorRISCV64::VisitLoadString(HLoadString* instruction)
4524     NO_THREAD_SAFETY_ANALYSIS {
4525   HLoadString::LoadKind load_kind = instruction->GetLoadKind();
4526   LocationSummary* locations = instruction->GetLocations();
4527   Location out_loc = locations->Out();
4528   XRegister out = out_loc.AsRegister<XRegister>();
4529 
4530   switch (load_kind) {
4531     case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
4532       DCHECK(codegen_->GetCompilerOptions().IsBootImage() ||
4533              codegen_->GetCompilerOptions().IsBootImageExtension());
4534       CodeGeneratorRISCV64::PcRelativePatchInfo* info_high = codegen_->NewBootImageStringPatch(
4535           instruction->GetDexFile(), instruction->GetStringIndex());
4536       codegen_->EmitPcRelativeAuipcPlaceholder(info_high, out);
4537       CodeGeneratorRISCV64::PcRelativePatchInfo* info_low = codegen_->NewBootImageStringPatch(
4538           instruction->GetDexFile(), instruction->GetStringIndex(), info_high);
4539       codegen_->EmitPcRelativeAddiPlaceholder(info_low, out, out);
4540       return;
4541     }
4542     case HLoadString::LoadKind::kBootImageRelRo: {
4543       DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
4544       uint32_t boot_image_offset = codegen_->GetBootImageOffset(instruction);
4545       codegen_->LoadBootImageRelRoEntry(out, boot_image_offset);
4546       return;
4547     }
4548     case HLoadString::LoadKind::kBssEntry: {
4549       CodeGeneratorRISCV64::PcRelativePatchInfo* info_high = codegen_->NewStringBssEntryPatch(
4550           instruction->GetDexFile(), instruction->GetStringIndex());
4551       codegen_->EmitPcRelativeAuipcPlaceholder(info_high, out);
4552       CodeGeneratorRISCV64::PcRelativePatchInfo* info_low = codegen_->NewStringBssEntryPatch(
4553           instruction->GetDexFile(), instruction->GetStringIndex(), info_high);
4554       codegen_->GenerateGcRootFieldLoad(instruction,
4555                                         out_loc,
4556                                         out,
4557                                         /* offset= */ kLinkTimeOffsetPlaceholderLow,
4558                                         codegen_->GetCompilerReadBarrierOption(),
4559                                         &info_low->label);
4560       SlowPathCodeRISCV64* slow_path =
4561           new (codegen_->GetScopedAllocator()) LoadStringSlowPathRISCV64(instruction);
4562       codegen_->AddSlowPath(slow_path);
4563       __ Beqz(out, slow_path->GetEntryLabel());
4564       __ Bind(slow_path->GetExitLabel());
4565       return;
4566     }
4567     case HLoadString::LoadKind::kJitBootImageAddress: {
4568       uint32_t address = reinterpret_cast32<uint32_t>(instruction->GetString().Get());
4569       DCHECK_NE(address, 0u);
4570       __ Loadwu(out, codegen_->DeduplicateBootImageAddressLiteral(address));
4571       return;
4572     }
4573     case HLoadString::LoadKind::kJitTableAddress:
4574       __ Loadwu(
4575           out,
4576           codegen_->DeduplicateJitStringLiteral(
4577               instruction->GetDexFile(), instruction->GetStringIndex(), instruction->GetString()));
4578       codegen_->GenerateGcRootFieldLoad(
4579           instruction, out_loc, out, 0, codegen_->GetCompilerReadBarrierOption());
4580       return;
4581     default:
4582       break;
4583   }
4584 
4585   DCHECK(load_kind == HLoadString::LoadKind::kRuntimeCall);
4586   InvokeRuntimeCallingConvention calling_convention;
4587   DCHECK(calling_convention.GetReturnLocation(DataType::Type::kReference).Equals(out_loc));
4588   __ LoadConst32(calling_convention.GetRegisterAt(0), instruction->GetStringIndex().index_);
4589   codegen_->InvokeRuntime(kQuickResolveString, instruction, instruction->GetDexPc());
4590   CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
4591 }
4592 
VisitLongConstant(HLongConstant * instruction)4593 void LocationsBuilderRISCV64::VisitLongConstant(HLongConstant* instruction) {
4594   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
4595   locations->SetOut(Location::ConstantLocation(instruction));
4596 }
4597 
VisitLongConstant(HLongConstant * instruction)4598 void InstructionCodeGeneratorRISCV64::VisitLongConstant(
4599     [[maybe_unused]] HLongConstant* instruction) {
4600   // Will be generated at use site.
4601 }
4602 
VisitMax(HMax * instruction)4603 void LocationsBuilderRISCV64::VisitMax(HMax* instruction) {
4604   HandleBinaryOp(instruction);
4605 }
4606 
VisitMax(HMax * instruction)4607 void InstructionCodeGeneratorRISCV64::VisitMax(HMax* instruction) {
4608   HandleBinaryOp(instruction);
4609 }
4610 
VisitMemoryBarrier(HMemoryBarrier * instruction)4611 void LocationsBuilderRISCV64::VisitMemoryBarrier(HMemoryBarrier* instruction) {
4612   instruction->SetLocations(nullptr);
4613 }
4614 
VisitMemoryBarrier(HMemoryBarrier * instruction)4615 void InstructionCodeGeneratorRISCV64::VisitMemoryBarrier(HMemoryBarrier* instruction) {
4616   codegen_->GenerateMemoryBarrier(instruction->GetBarrierKind());
4617 }
4618 
VisitMethodEntryHook(HMethodEntryHook * instruction)4619 void LocationsBuilderRISCV64::VisitMethodEntryHook(HMethodEntryHook* instruction) {
4620   new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
4621 }
4622 
VisitMethodEntryHook(HMethodEntryHook * instruction)4623 void InstructionCodeGeneratorRISCV64::VisitMethodEntryHook(HMethodEntryHook* instruction) {
4624   DCHECK(codegen_->GetCompilerOptions().IsJitCompiler() && GetGraph()->IsDebuggable());
4625   DCHECK(codegen_->RequiresCurrentMethod());
4626   GenerateMethodEntryExitHook(instruction);
4627 }
4628 
VisitMethodExitHook(HMethodExitHook * instruction)4629 void LocationsBuilderRISCV64::VisitMethodExitHook(HMethodExitHook* instruction) {
4630   LocationSummary* locations = new (GetGraph()->GetAllocator())
4631       LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
4632   DataType::Type return_type = instruction->InputAt(0)->GetType();
4633   locations->SetInAt(0, Riscv64ReturnLocation(return_type));
4634 }
4635 
VisitMethodExitHook(HMethodExitHook * instruction)4636 void InstructionCodeGeneratorRISCV64::VisitMethodExitHook(HMethodExitHook* instruction) {
4637   DCHECK(codegen_->GetCompilerOptions().IsJitCompiler() && GetGraph()->IsDebuggable());
4638   DCHECK(codegen_->RequiresCurrentMethod());
4639   GenerateMethodEntryExitHook(instruction);
4640 }
4641 
VisitMin(HMin * instruction)4642 void LocationsBuilderRISCV64::VisitMin(HMin* instruction) {
4643   HandleBinaryOp(instruction);
4644 }
4645 
VisitMin(HMin * instruction)4646 void InstructionCodeGeneratorRISCV64::VisitMin(HMin* instruction) {
4647   HandleBinaryOp(instruction);
4648 }
4649 
VisitMonitorOperation(HMonitorOperation * instruction)4650 void LocationsBuilderRISCV64::VisitMonitorOperation(HMonitorOperation* instruction) {
4651   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
4652       instruction, LocationSummary::kCallOnMainOnly);
4653   InvokeRuntimeCallingConvention calling_convention;
4654   locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
4655 }
4656 
VisitMonitorOperation(HMonitorOperation * instruction)4657 void InstructionCodeGeneratorRISCV64::VisitMonitorOperation(HMonitorOperation* instruction) {
4658   codegen_->InvokeRuntime(instruction->IsEnter() ? kQuickLockObject : kQuickUnlockObject,
4659                           instruction,
4660                           instruction->GetDexPc());
4661   if (instruction->IsEnter()) {
4662     CheckEntrypointTypes<kQuickLockObject, void, mirror::Object*>();
4663   } else {
4664     CheckEntrypointTypes<kQuickUnlockObject, void, mirror::Object*>();
4665   }
4666 }
4667 
VisitMul(HMul * instruction)4668 void LocationsBuilderRISCV64::VisitMul(HMul* instruction) {
4669   LocationSummary* locations =
4670       new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
4671   switch (instruction->GetResultType()) {
4672     case DataType::Type::kInt32:
4673     case DataType::Type::kInt64:
4674       locations->SetInAt(0, Location::RequiresRegister());
4675       locations->SetInAt(1, Location::RequiresRegister());
4676       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4677       break;
4678 
4679     case DataType::Type::kFloat32:
4680     case DataType::Type::kFloat64:
4681       locations->SetInAt(0, Location::RequiresFpuRegister());
4682       locations->SetInAt(1, Location::RequiresFpuRegister());
4683       locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4684       break;
4685 
4686     default:
4687       LOG(FATAL) << "Unexpected mul type " << instruction->GetResultType();
4688   }
4689 }
4690 
VisitMul(HMul * instruction)4691 void InstructionCodeGeneratorRISCV64::VisitMul(HMul* instruction) {
4692   LocationSummary* locations = instruction->GetLocations();
4693   switch (instruction->GetResultType()) {
4694     case DataType::Type::kInt32:
4695       __ Mulw(locations->Out().AsRegister<XRegister>(),
4696               locations->InAt(0).AsRegister<XRegister>(),
4697               locations->InAt(1).AsRegister<XRegister>());
4698       break;
4699 
4700     case DataType::Type::kInt64:
4701       __ Mul(locations->Out().AsRegister<XRegister>(),
4702              locations->InAt(0).AsRegister<XRegister>(),
4703              locations->InAt(1).AsRegister<XRegister>());
4704       break;
4705 
4706     case DataType::Type::kFloat32:
4707     case DataType::Type::kFloat64:
4708       FMul(locations->Out().AsFpuRegister<FRegister>(),
4709            locations->InAt(0).AsFpuRegister<FRegister>(),
4710            locations->InAt(1).AsFpuRegister<FRegister>(),
4711            instruction->GetResultType());
4712       break;
4713 
4714     default:
4715       LOG(FATAL) << "Unexpected mul type " << instruction->GetResultType();
4716   }
4717 }
4718 
VisitNeg(HNeg * instruction)4719 void LocationsBuilderRISCV64::VisitNeg(HNeg* instruction) {
4720   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
4721   switch (instruction->GetResultType()) {
4722     case DataType::Type::kInt32:
4723     case DataType::Type::kInt64:
4724       locations->SetInAt(0, Location::RequiresRegister());
4725       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4726       break;
4727 
4728     case DataType::Type::kFloat32:
4729     case DataType::Type::kFloat64:
4730       locations->SetInAt(0, Location::RequiresFpuRegister());
4731       locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4732       break;
4733 
4734     default:
4735       LOG(FATAL) << "Unexpected neg type " << instruction->GetResultType();
4736       UNREACHABLE();
4737   }
4738 }
4739 
VisitNeg(HNeg * instruction)4740 void InstructionCodeGeneratorRISCV64::VisitNeg(HNeg* instruction) {
4741   LocationSummary* locations = instruction->GetLocations();
4742   switch (instruction->GetResultType()) {
4743     case DataType::Type::kInt32:
4744       __ NegW(locations->Out().AsRegister<XRegister>(), locations->InAt(0).AsRegister<XRegister>());
4745       break;
4746 
4747     case DataType::Type::kInt64:
4748       __ Neg(locations->Out().AsRegister<XRegister>(), locations->InAt(0).AsRegister<XRegister>());
4749       break;
4750 
4751     case DataType::Type::kFloat32:
4752     case DataType::Type::kFloat64:
4753       FNeg(locations->Out().AsFpuRegister<FRegister>(),
4754            locations->InAt(0).AsFpuRegister<FRegister>(),
4755            instruction->GetResultType());
4756       break;
4757 
4758     default:
4759       LOG(FATAL) << "Unexpected neg type " << instruction->GetResultType();
4760       UNREACHABLE();
4761   }
4762 }
4763 
VisitNewArray(HNewArray * instruction)4764 void LocationsBuilderRISCV64::VisitNewArray(HNewArray* instruction) {
4765   LocationSummary* locations = new (GetGraph()->GetAllocator())
4766       LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
4767   InvokeRuntimeCallingConvention calling_convention;
4768   locations->SetOut(calling_convention.GetReturnLocation(DataType::Type::kReference));
4769   locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
4770   locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
4771 }
4772 
VisitNewArray(HNewArray * instruction)4773 void InstructionCodeGeneratorRISCV64::VisitNewArray(HNewArray* instruction) {
4774   QuickEntrypointEnum entrypoint = CodeGenerator::GetArrayAllocationEntrypoint(instruction);
4775   codegen_->InvokeRuntime(entrypoint, instruction, instruction->GetDexPc());
4776   CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>();
4777   DCHECK(!codegen_->IsLeafMethod());
4778 }
4779 
VisitNewInstance(HNewInstance * instruction)4780 void LocationsBuilderRISCV64::VisitNewInstance(HNewInstance* instruction) {
4781   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
4782       instruction, LocationSummary::kCallOnMainOnly);
4783   InvokeRuntimeCallingConvention calling_convention;
4784   locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
4785   locations->SetOut(calling_convention.GetReturnLocation(DataType::Type::kReference));
4786 }
4787 
VisitNewInstance(HNewInstance * instruction)4788 void InstructionCodeGeneratorRISCV64::VisitNewInstance(HNewInstance* instruction) {
4789   codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
4790   CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
4791 }
4792 
VisitNop(HNop * instruction)4793 void LocationsBuilderRISCV64::VisitNop(HNop* instruction) {
4794   new (GetGraph()->GetAllocator()) LocationSummary(instruction);
4795 }
4796 
VisitNop(HNop * instruction)4797 void InstructionCodeGeneratorRISCV64::VisitNop([[maybe_unused]] HNop* instruction) {
4798   // The environment recording already happened in CodeGenerator::Compile.
4799 }
4800 
VisitNot(HNot * instruction)4801 void LocationsBuilderRISCV64::VisitNot(HNot* instruction) {
4802   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
4803   locations->SetInAt(0, Location::RequiresRegister());
4804   locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4805 }
4806 
VisitNot(HNot * instruction)4807 void InstructionCodeGeneratorRISCV64::VisitNot(HNot* instruction) {
4808   LocationSummary* locations = instruction->GetLocations();
4809   switch (instruction->GetResultType()) {
4810     case DataType::Type::kInt32:
4811     case DataType::Type::kInt64:
4812       __ Not(locations->Out().AsRegister<XRegister>(), locations->InAt(0).AsRegister<XRegister>());
4813       break;
4814 
4815     default:
4816       LOG(FATAL) << "Unexpected type for not operation " << instruction->GetResultType();
4817       UNREACHABLE();
4818   }
4819 }
4820 
VisitNotEqual(HNotEqual * instruction)4821 void LocationsBuilderRISCV64::VisitNotEqual(HNotEqual* instruction) {
4822   HandleCondition(instruction);
4823 }
4824 
VisitNotEqual(HNotEqual * instruction)4825 void InstructionCodeGeneratorRISCV64::VisitNotEqual(HNotEqual* instruction) {
4826   HandleCondition(instruction);
4827 }
4828 
VisitNullConstant(HNullConstant * instruction)4829 void LocationsBuilderRISCV64::VisitNullConstant(HNullConstant* instruction) {
4830   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
4831   locations->SetOut(Location::ConstantLocation(instruction));
4832 }
4833 
VisitNullConstant(HNullConstant * instruction)4834 void InstructionCodeGeneratorRISCV64::VisitNullConstant(
4835     [[maybe_unused]] HNullConstant* instruction) {
4836   // Will be generated at use site.
4837 }
4838 
VisitNullCheck(HNullCheck * instruction)4839 void LocationsBuilderRISCV64::VisitNullCheck(HNullCheck* instruction) {
4840   LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
4841   locations->SetInAt(0, Location::RequiresRegister());
4842 }
4843 
VisitNullCheck(HNullCheck * instruction)4844 void InstructionCodeGeneratorRISCV64::VisitNullCheck(HNullCheck* instruction) {
4845   codegen_->GenerateNullCheck(instruction);
4846 }
4847 
VisitOr(HOr * instruction)4848 void LocationsBuilderRISCV64::VisitOr(HOr* instruction) {
4849   HandleBinaryOp(instruction);
4850 }
4851 
VisitOr(HOr * instruction)4852 void InstructionCodeGeneratorRISCV64::VisitOr(HOr* instruction) {
4853   HandleBinaryOp(instruction);
4854 }
4855 
VisitPackedSwitch(HPackedSwitch * instruction)4856 void LocationsBuilderRISCV64::VisitPackedSwitch(HPackedSwitch* instruction) {
4857   LocationSummary* locations =
4858       new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
4859   locations->SetInAt(0, Location::RequiresRegister());
4860 }
4861 
VisitPackedSwitch(HPackedSwitch * instruction)4862 void InstructionCodeGeneratorRISCV64::VisitPackedSwitch(HPackedSwitch* instruction) {
4863   int32_t lower_bound = instruction->GetStartValue();
4864   uint32_t num_entries = instruction->GetNumEntries();
4865   LocationSummary* locations = instruction->GetLocations();
4866   XRegister value = locations->InAt(0).AsRegister<XRegister>();
4867   HBasicBlock* switch_block = instruction->GetBlock();
4868   HBasicBlock* default_block = instruction->GetDefaultBlock();
4869 
4870   // Prepare a temporary register and an adjusted zero-based value.
4871   ScratchRegisterScope srs(GetAssembler());
4872   XRegister temp = srs.AllocateXRegister();
4873   XRegister adjusted = value;
4874   if (lower_bound != 0) {
4875     adjusted = temp;
4876     __ AddConst32(temp, value, -lower_bound);
4877   }
4878 
4879   // Jump to the default block if the index is out of the packed switch value range.
4880   // Note: We could save one instruction for `num_entries == 1` with BNEZ but the
4881   // `HInstructionBuilder` transforms that case to an `HIf`, so let's keep the code simple.
4882   CHECK_NE(num_entries, 0u);  // `HInstructionBuilder` creates a `HGoto` for empty packed-switch.
4883   {
4884     ScratchRegisterScope srs2(GetAssembler());
4885     XRegister temp2 = srs2.AllocateXRegister();
4886     __ LoadConst32(temp2, num_entries);
4887     __ Bgeu(adjusted, temp2, codegen_->GetLabelOf(default_block));  // Can clobber `TMP` if taken.
4888   }
4889 
4890   if (num_entries >= kPackedSwitchCompareJumpThreshold) {
4891     GenTableBasedPackedSwitch(adjusted, temp, num_entries, switch_block);
4892   } else {
4893     GenPackedSwitchWithCompares(adjusted, temp, num_entries, switch_block);
4894   }
4895 }
4896 
VisitParallelMove(HParallelMove * instruction)4897 void LocationsBuilderRISCV64::VisitParallelMove([[maybe_unused]] HParallelMove* instruction) {
4898   LOG(FATAL) << "Unreachable";
4899 }
4900 
VisitParallelMove(HParallelMove * instruction)4901 void InstructionCodeGeneratorRISCV64::VisitParallelMove(HParallelMove* instruction) {
4902   if (instruction->GetNext()->IsSuspendCheck() &&
4903       instruction->GetBlock()->GetLoopInformation() != nullptr) {
4904     HSuspendCheck* suspend_check = instruction->GetNext()->AsSuspendCheck();
4905     // The back edge will generate the suspend check.
4906     codegen_->ClearSpillSlotsFromLoopPhisInStackMap(suspend_check, instruction);
4907   }
4908 
4909   codegen_->GetMoveResolver()->EmitNativeCode(instruction);
4910 }
4911 
VisitParameterValue(HParameterValue * instruction)4912 void LocationsBuilderRISCV64::VisitParameterValue(HParameterValue* instruction) {
4913   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
4914   Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
4915   if (location.IsStackSlot()) {
4916     location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
4917   } else if (location.IsDoubleStackSlot()) {
4918     location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
4919   }
4920   locations->SetOut(location);
4921 }
4922 
VisitParameterValue(HParameterValue * instruction)4923 void InstructionCodeGeneratorRISCV64::VisitParameterValue(
4924     [[maybe_unused]] HParameterValue* instruction) {
4925   // Nothing to do, the parameter is already at its location.
4926 }
4927 
VisitPhi(HPhi * instruction)4928 void LocationsBuilderRISCV64::VisitPhi(HPhi* instruction) {
4929   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
4930   for (size_t i = 0, e = locations->GetInputCount(); i < e; ++i) {
4931     locations->SetInAt(i, Location::Any());
4932   }
4933   locations->SetOut(Location::Any());
4934 }
4935 
VisitPhi(HPhi * instruction)4936 void InstructionCodeGeneratorRISCV64::VisitPhi([[maybe_unused]] HPhi* instruction) {
4937   LOG(FATAL) << "Unreachable";
4938 }
4939 
VisitRem(HRem * instruction)4940 void LocationsBuilderRISCV64::VisitRem(HRem* instruction) {
4941   DataType::Type type = instruction->GetResultType();
4942   LocationSummary::CallKind call_kind =
4943       DataType::IsFloatingPointType(type) ? LocationSummary::kCallOnMainOnly
4944                                           : LocationSummary::kNoCall;
4945   LocationSummary* locations =
4946       new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
4947 
4948   switch (type) {
4949     case DataType::Type::kInt32:
4950     case DataType::Type::kInt64:
4951       locations->SetInAt(0, Location::RequiresRegister());
4952       locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
4953       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4954       break;
4955 
4956     case DataType::Type::kFloat32:
4957     case DataType::Type::kFloat64: {
4958       InvokeRuntimeCallingConvention calling_convention;
4959       locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0)));
4960       locations->SetInAt(1, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(1)));
4961       locations->SetOut(calling_convention.GetReturnLocation(type));
4962       break;
4963     }
4964 
4965     default:
4966       LOG(FATAL) << "Unexpected rem type " << type;
4967       UNREACHABLE();
4968   }
4969 }
4970 
VisitRem(HRem * instruction)4971 void InstructionCodeGeneratorRISCV64::VisitRem(HRem* instruction) {
4972   DataType::Type type = instruction->GetType();
4973 
4974   switch (type) {
4975     case DataType::Type::kInt32:
4976     case DataType::Type::kInt64:
4977       GenerateDivRemIntegral(instruction);
4978       break;
4979 
4980     case DataType::Type::kFloat32:
4981     case DataType::Type::kFloat64: {
4982       QuickEntrypointEnum entrypoint =
4983           (type == DataType::Type::kFloat32) ? kQuickFmodf : kQuickFmod;
4984       codegen_->InvokeRuntime(entrypoint, instruction, instruction->GetDexPc());
4985       if (type == DataType::Type::kFloat32) {
4986         CheckEntrypointTypes<kQuickFmodf, float, float, float>();
4987       } else {
4988         CheckEntrypointTypes<kQuickFmod, double, double, double>();
4989       }
4990       break;
4991     }
4992     default:
4993       LOG(FATAL) << "Unexpected rem type " << type;
4994       UNREACHABLE();
4995   }
4996 }
4997 
VisitReturn(HReturn * instruction)4998 void LocationsBuilderRISCV64::VisitReturn(HReturn* instruction) {
4999   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
5000   DataType::Type return_type = instruction->InputAt(0)->GetType();
5001   DCHECK_NE(return_type, DataType::Type::kVoid);
5002   locations->SetInAt(0, Riscv64ReturnLocation(return_type));
5003 }
5004 
VisitReturn(HReturn * instruction)5005 void InstructionCodeGeneratorRISCV64::VisitReturn(HReturn* instruction) {
5006   if (GetGraph()->IsCompilingOsr()) {
5007     // To simplify callers of an OSR method, we put a floating point return value
5008     // in both floating point and core return registers.
5009     DataType::Type type = instruction->InputAt(0)->GetType();
5010     if (DataType::IsFloatingPointType(type)) {
5011       FMvX(A0, FA0, type);
5012     }
5013   }
5014   codegen_->GenerateFrameExit();
5015 }
5016 
VisitReturnVoid(HReturnVoid * instruction)5017 void LocationsBuilderRISCV64::VisitReturnVoid(HReturnVoid* instruction) {
5018   instruction->SetLocations(nullptr);
5019 }
5020 
VisitReturnVoid(HReturnVoid * instruction)5021 void InstructionCodeGeneratorRISCV64::VisitReturnVoid([[maybe_unused]] HReturnVoid* instruction) {
5022   codegen_->GenerateFrameExit();
5023 }
5024 
VisitRol(HRol * instruction)5025 void LocationsBuilderRISCV64::VisitRol(HRol* instruction) {
5026   HandleShift(instruction);
5027 }
5028 
VisitRol(HRol * instruction)5029 void InstructionCodeGeneratorRISCV64::VisitRol(HRol* instruction) {
5030   HandleShift(instruction);
5031 }
5032 
VisitRor(HRor * instruction)5033 void LocationsBuilderRISCV64::VisitRor(HRor* instruction) {
5034   HandleShift(instruction);
5035 }
5036 
VisitRor(HRor * instruction)5037 void InstructionCodeGeneratorRISCV64::VisitRor(HRor* instruction) {
5038   HandleShift(instruction);
5039 }
5040 
VisitShl(HShl * instruction)5041 void LocationsBuilderRISCV64::VisitShl(HShl* instruction) {
5042   HandleShift(instruction);
5043 }
5044 
VisitShl(HShl * instruction)5045 void InstructionCodeGeneratorRISCV64::VisitShl(HShl* instruction) {
5046   HandleShift(instruction);
5047 }
5048 
VisitShr(HShr * instruction)5049 void LocationsBuilderRISCV64::VisitShr(HShr* instruction) {
5050   HandleShift(instruction);
5051 }
5052 
VisitShr(HShr * instruction)5053 void InstructionCodeGeneratorRISCV64::VisitShr(HShr* instruction) {
5054   HandleShift(instruction);
5055 }
5056 
VisitStaticFieldGet(HStaticFieldGet * instruction)5057 void LocationsBuilderRISCV64::VisitStaticFieldGet(HStaticFieldGet* instruction) {
5058   HandleFieldGet(instruction);
5059 }
5060 
VisitStaticFieldGet(HStaticFieldGet * instruction)5061 void InstructionCodeGeneratorRISCV64::VisitStaticFieldGet(HStaticFieldGet* instruction) {
5062   HandleFieldGet(instruction, instruction->GetFieldInfo());
5063 }
5064 
VisitStaticFieldSet(HStaticFieldSet * instruction)5065 void LocationsBuilderRISCV64::VisitStaticFieldSet(HStaticFieldSet* instruction) {
5066   HandleFieldSet(instruction);
5067 }
5068 
VisitStaticFieldSet(HStaticFieldSet * instruction)5069 void InstructionCodeGeneratorRISCV64::VisitStaticFieldSet(HStaticFieldSet* instruction) {
5070   HandleFieldSet(instruction,
5071                  instruction->GetFieldInfo(),
5072                  instruction->GetValueCanBeNull(),
5073                  instruction->GetWriteBarrierKind());
5074 }
5075 
VisitStringBuilderAppend(HStringBuilderAppend * instruction)5076 void LocationsBuilderRISCV64::VisitStringBuilderAppend(HStringBuilderAppend* instruction) {
5077   codegen_->CreateStringBuilderAppendLocations(instruction, Location::RegisterLocation(A0));
5078 }
5079 
VisitStringBuilderAppend(HStringBuilderAppend * instruction)5080 void InstructionCodeGeneratorRISCV64::VisitStringBuilderAppend(HStringBuilderAppend* instruction) {
5081   __ LoadConst32(A0, instruction->GetFormat()->GetValue());
5082   codegen_->InvokeRuntime(kQuickStringBuilderAppend, instruction, instruction->GetDexPc());
5083 }
5084 
VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet * instruction)5085 void LocationsBuilderRISCV64::VisitUnresolvedInstanceFieldGet(
5086     HUnresolvedInstanceFieldGet* instruction) {
5087   FieldAccessCallingConventionRISCV64 calling_convention;
5088   codegen_->CreateUnresolvedFieldLocationSummary(
5089       instruction, instruction->GetFieldType(), calling_convention);
5090 }
5091 
VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet * instruction)5092 void InstructionCodeGeneratorRISCV64::VisitUnresolvedInstanceFieldGet(
5093     HUnresolvedInstanceFieldGet* instruction) {
5094   FieldAccessCallingConventionRISCV64 calling_convention;
5095   codegen_->GenerateUnresolvedFieldAccess(instruction,
5096                                           instruction->GetFieldType(),
5097                                           instruction->GetFieldIndex(),
5098                                           instruction->GetDexPc(),
5099                                           calling_convention);
5100 }
5101 
VisitUnresolvedInstanceFieldSet(HUnresolvedInstanceFieldSet * instruction)5102 void LocationsBuilderRISCV64::VisitUnresolvedInstanceFieldSet(
5103     HUnresolvedInstanceFieldSet* instruction) {
5104   FieldAccessCallingConventionRISCV64 calling_convention;
5105   codegen_->CreateUnresolvedFieldLocationSummary(
5106       instruction, instruction->GetFieldType(), calling_convention);
5107 }
5108 
VisitUnresolvedInstanceFieldSet(HUnresolvedInstanceFieldSet * instruction)5109 void InstructionCodeGeneratorRISCV64::VisitUnresolvedInstanceFieldSet(
5110     HUnresolvedInstanceFieldSet* instruction) {
5111   FieldAccessCallingConventionRISCV64 calling_convention;
5112   codegen_->GenerateUnresolvedFieldAccess(instruction,
5113                                           instruction->GetFieldType(),
5114                                           instruction->GetFieldIndex(),
5115                                           instruction->GetDexPc(),
5116                                           calling_convention);
5117 }
5118 
VisitUnresolvedStaticFieldGet(HUnresolvedStaticFieldGet * instruction)5119 void LocationsBuilderRISCV64::VisitUnresolvedStaticFieldGet(
5120     HUnresolvedStaticFieldGet* instruction) {
5121   FieldAccessCallingConventionRISCV64 calling_convention;
5122   codegen_->CreateUnresolvedFieldLocationSummary(
5123       instruction, instruction->GetFieldType(), calling_convention);
5124 }
5125 
VisitUnresolvedStaticFieldGet(HUnresolvedStaticFieldGet * instruction)5126 void InstructionCodeGeneratorRISCV64::VisitUnresolvedStaticFieldGet(
5127     HUnresolvedStaticFieldGet* instruction) {
5128   FieldAccessCallingConventionRISCV64 calling_convention;
5129   codegen_->GenerateUnresolvedFieldAccess(instruction,
5130                                           instruction->GetFieldType(),
5131                                           instruction->GetFieldIndex(),
5132                                           instruction->GetDexPc(),
5133                                           calling_convention);
5134 }
5135 
VisitUnresolvedStaticFieldSet(HUnresolvedStaticFieldSet * instruction)5136 void LocationsBuilderRISCV64::VisitUnresolvedStaticFieldSet(
5137     HUnresolvedStaticFieldSet* instruction) {
5138   FieldAccessCallingConventionRISCV64 calling_convention;
5139   codegen_->CreateUnresolvedFieldLocationSummary(
5140       instruction, instruction->GetFieldType(), calling_convention);
5141 }
5142 
VisitUnresolvedStaticFieldSet(HUnresolvedStaticFieldSet * instruction)5143 void InstructionCodeGeneratorRISCV64::VisitUnresolvedStaticFieldSet(
5144     HUnresolvedStaticFieldSet* instruction) {
5145   FieldAccessCallingConventionRISCV64 calling_convention;
5146   codegen_->GenerateUnresolvedFieldAccess(instruction,
5147                                           instruction->GetFieldType(),
5148                                           instruction->GetFieldIndex(),
5149                                           instruction->GetDexPc(),
5150                                           calling_convention);
5151 }
5152 
VisitSelect(HSelect * instruction)5153 void LocationsBuilderRISCV64::VisitSelect(HSelect* instruction) {
5154   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
5155   if (DataType::IsFloatingPointType(instruction->GetType())) {
5156     locations->SetInAt(0, FpuRegisterOrZeroBitPatternLocation(instruction->GetFalseValue()));
5157     locations->SetInAt(1, FpuRegisterOrZeroBitPatternLocation(instruction->GetTrueValue()));
5158     locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
5159     if (!locations->InAt(0).IsConstant() && !locations->InAt(1).IsConstant()) {
5160       locations->AddTemp(Location::RequiresRegister());
5161     }
5162   }  else {
5163     locations->SetInAt(0, RegisterOrZeroBitPatternLocation(instruction->GetFalseValue()));
5164     locations->SetInAt(1, RegisterOrZeroBitPatternLocation(instruction->GetTrueValue()));
5165     locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
5166   }
5167 
5168   if (IsBooleanValueOrMaterializedCondition(instruction->GetCondition())) {
5169     locations->SetInAt(2, Location::RequiresRegister());
5170   }
5171 }
5172 
VisitSelect(HSelect * instruction)5173 void InstructionCodeGeneratorRISCV64::VisitSelect(HSelect* instruction) {
5174   LocationSummary* locations = instruction->GetLocations();
5175   HInstruction* cond = instruction->GetCondition();
5176   ScratchRegisterScope srs(GetAssembler());
5177   XRegister tmp = srs.AllocateXRegister();
5178   if (!IsBooleanValueOrMaterializedCondition(cond)) {
5179     DataType::Type cond_type = cond->InputAt(0)->GetType();
5180     IfCondition if_cond = cond->AsCondition()->GetCondition();
5181     if (DataType::IsFloatingPointType(cond_type)) {
5182       GenerateFpCondition(if_cond,
5183                           cond->AsCondition()->IsGtBias(),
5184                           cond_type,
5185                           cond->GetLocations(),
5186                           /*label=*/ nullptr,
5187                           tmp,
5188                           /*to_all_bits=*/ true);
5189     } else {
5190       GenerateIntLongCondition(if_cond, cond->GetLocations(), tmp, /*to_all_bits=*/ true);
5191     }
5192   } else {
5193     // TODO(riscv64): Remove the normalizing SNEZ when we can ensure that booleans
5194     // have only values 0 and 1. b/279302742
5195     __ Snez(tmp, locations->InAt(2).AsRegister<XRegister>());
5196     __ Neg(tmp, tmp);
5197   }
5198 
5199   XRegister true_reg, false_reg, xor_reg, out_reg;
5200   DataType::Type type = instruction->GetType();
5201   if (DataType::IsFloatingPointType(type)) {
5202     if (locations->InAt(0).IsConstant()) {
5203       DCHECK(locations->InAt(0).GetConstant()->IsZeroBitPattern());
5204       false_reg = Zero;
5205     } else {
5206       false_reg = srs.AllocateXRegister();
5207       FMvX(false_reg, locations->InAt(0).AsFpuRegister<FRegister>(), type);
5208     }
5209     if (locations->InAt(1).IsConstant()) {
5210       DCHECK(locations->InAt(1).GetConstant()->IsZeroBitPattern());
5211       true_reg = Zero;
5212     } else {
5213       true_reg = (false_reg == Zero) ? srs.AllocateXRegister()
5214                                      : locations->GetTemp(0).AsRegister<XRegister>();
5215       FMvX(true_reg, locations->InAt(1).AsFpuRegister<FRegister>(), type);
5216     }
5217     // We can clobber the "true value" with the XOR result.
5218     // Note: The XOR is not emitted if `true_reg == Zero`, see below.
5219     xor_reg = true_reg;
5220     out_reg = tmp;
5221   } else {
5222     false_reg = InputXRegisterOrZero(locations->InAt(0));
5223     true_reg = InputXRegisterOrZero(locations->InAt(1));
5224     xor_reg = srs.AllocateXRegister();
5225     out_reg = locations->Out().AsRegister<XRegister>();
5226   }
5227 
5228   // We use a branch-free implementation of `HSelect`.
5229   // With `tmp` initialized to 0 for `false` and -1 for `true`:
5230   //     xor xor_reg, false_reg, true_reg
5231   //     and tmp, tmp, xor_reg
5232   //     xor out_reg, tmp, false_reg
5233   if (false_reg == Zero) {
5234     xor_reg = true_reg;
5235   } else if (true_reg == Zero) {
5236     xor_reg = false_reg;
5237   } else {
5238     DCHECK_NE(xor_reg, Zero);
5239     __ Xor(xor_reg, false_reg, true_reg);
5240   }
5241   __ And(tmp, tmp, xor_reg);
5242   __ Xor(out_reg, tmp, false_reg);
5243 
5244   if (type == DataType::Type::kFloat64) {
5245     __ FMvDX(locations->Out().AsFpuRegister<FRegister>(), out_reg);
5246   } else if (type == DataType::Type::kFloat32) {
5247     __ FMvWX(locations->Out().AsFpuRegister<FRegister>(), out_reg);
5248   }
5249 }
5250 
VisitSub(HSub * instruction)5251 void LocationsBuilderRISCV64::VisitSub(HSub* instruction) {
5252   HandleBinaryOp(instruction);
5253 }
5254 
VisitSub(HSub * instruction)5255 void InstructionCodeGeneratorRISCV64::VisitSub(HSub* instruction) {
5256   HandleBinaryOp(instruction);
5257 }
5258 
VisitSuspendCheck(HSuspendCheck * instruction)5259 void LocationsBuilderRISCV64::VisitSuspendCheck(HSuspendCheck* instruction) {
5260   LocationSummary* locations = new (GetGraph()->GetAllocator())
5261       LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
5262   // In suspend check slow path, usually there are no caller-save registers at all.
5263   // If SIMD instructions are present, however, we force spilling all live SIMD
5264   // registers in full width (since the runtime only saves/restores lower part).
5265   locations->SetCustomSlowPathCallerSaves(GetGraph()->HasSIMD() ? RegisterSet::AllFpu() :
5266                                                                   RegisterSet::Empty());
5267 }
5268 
VisitSuspendCheck(HSuspendCheck * instruction)5269 void InstructionCodeGeneratorRISCV64::VisitSuspendCheck(HSuspendCheck* instruction) {
5270   HBasicBlock* block = instruction->GetBlock();
5271   if (block->GetLoopInformation() != nullptr) {
5272     DCHECK(block->GetLoopInformation()->GetSuspendCheck() == instruction);
5273     // The back edge will generate the suspend check.
5274     return;
5275   }
5276   if (block->IsEntryBlock() && instruction->GetNext()->IsGoto()) {
5277     // The goto will generate the suspend check.
5278     return;
5279   }
5280   GenerateSuspendCheck(instruction, nullptr);
5281 }
5282 
VisitThrow(HThrow * instruction)5283 void LocationsBuilderRISCV64::VisitThrow(HThrow* instruction) {
5284   LocationSummary* locations = new (GetGraph()->GetAllocator())
5285       LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
5286   InvokeRuntimeCallingConvention calling_convention;
5287   locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
5288 }
5289 
VisitThrow(HThrow * instruction)5290 void InstructionCodeGeneratorRISCV64::VisitThrow(HThrow* instruction) {
5291   codegen_->InvokeRuntime(kQuickDeliverException, instruction, instruction->GetDexPc());
5292   CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
5293 }
5294 
VisitTryBoundary(HTryBoundary * instruction)5295 void LocationsBuilderRISCV64::VisitTryBoundary(HTryBoundary* instruction) {
5296   instruction->SetLocations(nullptr);
5297 }
5298 
VisitTryBoundary(HTryBoundary * instruction)5299 void InstructionCodeGeneratorRISCV64::VisitTryBoundary(HTryBoundary* instruction) {
5300   HBasicBlock* successor = instruction->GetNormalFlowSuccessor();
5301   if (!successor->IsExitBlock()) {
5302     HandleGoto(instruction, successor);
5303   }
5304 }
5305 
VisitTypeConversion(HTypeConversion * instruction)5306 void LocationsBuilderRISCV64::VisitTypeConversion(HTypeConversion* instruction) {
5307   DataType::Type input_type = instruction->GetInputType();
5308   DataType::Type result_type = instruction->GetResultType();
5309   DCHECK(!DataType::IsTypeConversionImplicit(input_type, result_type))
5310       << input_type << " -> " << result_type;
5311 
5312   if ((input_type == DataType::Type::kReference) || (input_type == DataType::Type::kVoid) ||
5313       (result_type == DataType::Type::kReference) || (result_type == DataType::Type::kVoid)) {
5314     LOG(FATAL) << "Unexpected type conversion from " << input_type << " to " << result_type;
5315   }
5316 
5317   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
5318 
5319   if (DataType::IsFloatingPointType(input_type)) {
5320     locations->SetInAt(0, Location::RequiresFpuRegister());
5321   } else {
5322     locations->SetInAt(0, Location::RequiresRegister());
5323   }
5324 
5325   if (DataType::IsFloatingPointType(result_type)) {
5326     locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
5327   } else {
5328     locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5329   }
5330 }
5331 
VisitTypeConversion(HTypeConversion * instruction)5332 void InstructionCodeGeneratorRISCV64::VisitTypeConversion(HTypeConversion* instruction) {
5333   LocationSummary* locations = instruction->GetLocations();
5334   DataType::Type result_type = instruction->GetResultType();
5335   DataType::Type input_type = instruction->GetInputType();
5336 
5337   DCHECK(!DataType::IsTypeConversionImplicit(input_type, result_type))
5338       << input_type << " -> " << result_type;
5339 
5340   if (DataType::IsIntegralType(result_type) && DataType::IsIntegralType(input_type)) {
5341     XRegister dst = locations->Out().AsRegister<XRegister>();
5342     XRegister src = locations->InAt(0).AsRegister<XRegister>();
5343     switch (result_type) {
5344       case DataType::Type::kUint8:
5345         __ ZextB(dst, src);
5346         break;
5347       case DataType::Type::kInt8:
5348         __ SextB(dst, src);
5349         break;
5350       case DataType::Type::kUint16:
5351         __ ZextH(dst, src);
5352         break;
5353       case DataType::Type::kInt16:
5354         __ SextH(dst, src);
5355         break;
5356       case DataType::Type::kInt32:
5357       case DataType::Type::kInt64:
5358         // Sign-extend 32-bit int into bits 32 through 63 for int-to-long and long-to-int
5359         // conversions, except when the input and output registers are the same and we are not
5360         // converting longs to shorter types. In these cases, do nothing.
5361         if ((input_type == DataType::Type::kInt64) || (dst != src)) {
5362           __ Addiw(dst, src, 0);
5363         }
5364         break;
5365 
5366       default:
5367         LOG(FATAL) << "Unexpected type conversion from " << input_type
5368                    << " to " << result_type;
5369         UNREACHABLE();
5370     }
5371   } else if (DataType::IsFloatingPointType(result_type) && DataType::IsIntegralType(input_type)) {
5372     FRegister dst = locations->Out().AsFpuRegister<FRegister>();
5373     XRegister src = locations->InAt(0).AsRegister<XRegister>();
5374     if (input_type == DataType::Type::kInt64) {
5375       if (result_type == DataType::Type::kFloat32) {
5376         __ FCvtSL(dst, src, FPRoundingMode::kRNE);
5377       } else {
5378         __ FCvtDL(dst, src, FPRoundingMode::kRNE);
5379       }
5380     } else {
5381       if (result_type == DataType::Type::kFloat32) {
5382         __ FCvtSW(dst, src, FPRoundingMode::kRNE);
5383       } else {
5384         __ FCvtDW(dst, src);  // No rounding.
5385       }
5386     }
5387   } else if (DataType::IsIntegralType(result_type) && DataType::IsFloatingPointType(input_type)) {
5388     CHECK(result_type == DataType::Type::kInt32 || result_type == DataType::Type::kInt64);
5389     XRegister dst = locations->Out().AsRegister<XRegister>();
5390     FRegister src = locations->InAt(0).AsFpuRegister<FRegister>();
5391     if (result_type == DataType::Type::kInt64) {
5392       if (input_type == DataType::Type::kFloat32) {
5393         __ FCvtLS(dst, src, FPRoundingMode::kRTZ);
5394       } else {
5395         __ FCvtLD(dst, src, FPRoundingMode::kRTZ);
5396       }
5397     } else {
5398       if (input_type == DataType::Type::kFloat32) {
5399         __ FCvtWS(dst, src, FPRoundingMode::kRTZ);
5400       } else {
5401         __ FCvtWD(dst, src, FPRoundingMode::kRTZ);
5402       }
5403     }
5404     // For NaN inputs we need to return 0.
5405     ScratchRegisterScope srs(GetAssembler());
5406     XRegister tmp = srs.AllocateXRegister();
5407     FClass(tmp, src, input_type);
5408     __ Sltiu(tmp, tmp, kFClassNaNMinValue);  // 0 for NaN, 1 otherwise.
5409     __ Neg(tmp, tmp);  // 0 for NaN, -1 otherwise.
5410     __ And(dst, dst, tmp);  // Cleared for NaN.
5411   } else if (DataType::IsFloatingPointType(result_type) &&
5412              DataType::IsFloatingPointType(input_type)) {
5413     FRegister dst = locations->Out().AsFpuRegister<FRegister>();
5414     FRegister src = locations->InAt(0).AsFpuRegister<FRegister>();
5415     if (result_type == DataType::Type::kFloat32) {
5416       __ FCvtSD(dst, src);
5417     } else {
5418       __ FCvtDS(dst, src);
5419     }
5420   } else {
5421     LOG(FATAL) << "Unexpected or unimplemented type conversion from " << input_type
5422                 << " to " << result_type;
5423     UNREACHABLE();
5424   }
5425 }
5426 
VisitUShr(HUShr * instruction)5427 void LocationsBuilderRISCV64::VisitUShr(HUShr* instruction) {
5428   HandleShift(instruction);
5429 }
5430 
VisitUShr(HUShr * instruction)5431 void InstructionCodeGeneratorRISCV64::VisitUShr(HUShr* instruction) {
5432   HandleShift(instruction);
5433 }
5434 
VisitXor(HXor * instruction)5435 void LocationsBuilderRISCV64::VisitXor(HXor* instruction) {
5436   HandleBinaryOp(instruction);
5437 }
5438 
VisitXor(HXor * instruction)5439 void InstructionCodeGeneratorRISCV64::VisitXor(HXor* instruction) {
5440   HandleBinaryOp(instruction);
5441 }
5442 
VisitRiscv64ShiftAdd(HRiscv64ShiftAdd * instruction)5443 void LocationsBuilderRISCV64::VisitRiscv64ShiftAdd(HRiscv64ShiftAdd* instruction) {
5444   DCHECK(codegen_->GetInstructionSetFeatures().HasZba());
5445   DCHECK_EQ(instruction->GetType(), DataType::Type::kInt64)
5446       << "Unexpected ShiftAdd type: " << instruction->GetType();
5447 
5448   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
5449   locations->SetInAt(0, Location::RequiresRegister());
5450   locations->SetInAt(1, Location::RequiresRegister());
5451   locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5452 }
5453 
VisitRiscv64ShiftAdd(HRiscv64ShiftAdd * instruction)5454 void InstructionCodeGeneratorRISCV64::VisitRiscv64ShiftAdd(HRiscv64ShiftAdd* instruction) {
5455   DCHECK_EQ(instruction->GetType(), DataType::Type::kInt64)
5456       << "Unexpected ShiftAdd type: " << instruction->GetType();
5457   LocationSummary* locations = instruction->GetLocations();
5458   XRegister first = locations->InAt(0).AsRegister<XRegister>();
5459   XRegister second = locations->InAt(1).AsRegister<XRegister>();
5460   XRegister dest = locations->Out().AsRegister<XRegister>();
5461 
5462   switch (instruction->GetDistance()) {
5463     case 1:
5464       __ Sh1Add(dest, first, second);
5465       break;
5466     case 2:
5467       __ Sh2Add(dest, first, second);
5468       break;
5469     case 3:
5470       __ Sh3Add(dest, first, second);
5471       break;
5472     default:
5473       LOG(FATAL) << "Unexpected distance of ShiftAdd: " << instruction->GetDistance();
5474       UNREACHABLE();
5475   }
5476 }
5477 
VisitBitwiseNegatedRight(HBitwiseNegatedRight * instruction)5478 void LocationsBuilderRISCV64::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) {
5479   DCHECK(codegen_->GetInstructionSetFeatures().HasZbb());
5480   DCHECK(DataType::IsIntegralType(instruction->GetType())) << instruction->GetType();
5481 
5482   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
5483   locations->SetInAt(0, Location::RequiresRegister());
5484   locations->SetInAt(1, Location::RequiresRegister());
5485   locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5486 }
5487 
VisitBitwiseNegatedRight(HBitwiseNegatedRight * instruction)5488 void InstructionCodeGeneratorRISCV64::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) {
5489   LocationSummary* locations = instruction->GetLocations();
5490   XRegister lhs = locations->InAt(0).AsRegister<XRegister>();
5491   XRegister rhs = locations->InAt(1).AsRegister<XRegister>();
5492   XRegister dst = locations->Out().AsRegister<XRegister>();
5493 
5494   switch (instruction->GetOpKind()) {
5495     case HInstruction::kAnd:
5496       __ Andn(dst, lhs, rhs);
5497       break;
5498     case HInstruction::kOr:
5499       __ Orn(dst, lhs, rhs);
5500       break;
5501     case HInstruction::kXor:
5502       __ Xnor(dst, lhs, rhs);
5503       break;
5504     default:
5505       LOG(FATAL) << "Unreachable";
5506   }
5507 }
5508 
VisitVecReplicateScalar(HVecReplicateScalar * instruction)5509 void LocationsBuilderRISCV64::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
5510   UNUSED(instruction);
5511   LOG(FATAL) << "Unimplemented";
5512 }
5513 
VisitVecReplicateScalar(HVecReplicateScalar * instruction)5514 void InstructionCodeGeneratorRISCV64::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
5515   UNUSED(instruction);
5516   LOG(FATAL) << "Unimplemented";
5517 }
5518 
VisitVecExtractScalar(HVecExtractScalar * instruction)5519 void LocationsBuilderRISCV64::VisitVecExtractScalar(HVecExtractScalar* instruction) {
5520   UNUSED(instruction);
5521   LOG(FATAL) << "Unimplemented";
5522 }
5523 
VisitVecExtractScalar(HVecExtractScalar * instruction)5524 void InstructionCodeGeneratorRISCV64::VisitVecExtractScalar(HVecExtractScalar* instruction) {
5525   UNUSED(instruction);
5526   LOG(FATAL) << "Unimplemented";
5527 }
5528 
VisitVecReduce(HVecReduce * instruction)5529 void LocationsBuilderRISCV64::VisitVecReduce(HVecReduce* instruction) {
5530   UNUSED(instruction);
5531   LOG(FATAL) << "Unimplemented";
5532 }
5533 
VisitVecReduce(HVecReduce * instruction)5534 void InstructionCodeGeneratorRISCV64::VisitVecReduce(HVecReduce* instruction) {
5535   UNUSED(instruction);
5536   LOG(FATAL) << "Unimplemented";
5537 }
5538 
VisitVecCnv(HVecCnv * instruction)5539 void LocationsBuilderRISCV64::VisitVecCnv(HVecCnv* instruction) {
5540   UNUSED(instruction);
5541   LOG(FATAL) << "Unimplemented";
5542 }
5543 
VisitVecCnv(HVecCnv * instruction)5544 void InstructionCodeGeneratorRISCV64::VisitVecCnv(HVecCnv* instruction) {
5545   UNUSED(instruction);
5546   LOG(FATAL) << "Unimplemented";
5547 }
5548 
VisitVecNeg(HVecNeg * instruction)5549 void LocationsBuilderRISCV64::VisitVecNeg(HVecNeg* instruction) {
5550   UNUSED(instruction);
5551   LOG(FATAL) << "Unimplemented";
5552 }
5553 
VisitVecNeg(HVecNeg * instruction)5554 void InstructionCodeGeneratorRISCV64::VisitVecNeg(HVecNeg* instruction) {
5555   UNUSED(instruction);
5556   LOG(FATAL) << "Unimplemented";
5557 }
5558 
VisitVecAbs(HVecAbs * instruction)5559 void LocationsBuilderRISCV64::VisitVecAbs(HVecAbs* instruction) {
5560   UNUSED(instruction);
5561   LOG(FATAL) << "Unimplemented";
5562 }
5563 
VisitVecAbs(HVecAbs * instruction)5564 void InstructionCodeGeneratorRISCV64::VisitVecAbs(HVecAbs* instruction) {
5565   UNUSED(instruction);
5566   LOG(FATAL) << "Unimplemented";
5567 }
5568 
VisitVecNot(HVecNot * instruction)5569 void LocationsBuilderRISCV64::VisitVecNot(HVecNot* instruction) {
5570   UNUSED(instruction);
5571   LOG(FATAL) << "Unimplemented";
5572 }
5573 
VisitVecNot(HVecNot * instruction)5574 void InstructionCodeGeneratorRISCV64::VisitVecNot(HVecNot* instruction) {
5575   UNUSED(instruction);
5576   LOG(FATAL) << "Unimplemented";
5577 }
5578 
VisitVecAdd(HVecAdd * instruction)5579 void LocationsBuilderRISCV64::VisitVecAdd(HVecAdd* instruction) {
5580   UNUSED(instruction);
5581   LOG(FATAL) << "Unimplemented";
5582 }
5583 
VisitVecAdd(HVecAdd * instruction)5584 void InstructionCodeGeneratorRISCV64::VisitVecAdd(HVecAdd* instruction) {
5585   UNUSED(instruction);
5586   LOG(FATAL) << "Unimplemented";
5587 }
5588 
VisitVecHalvingAdd(HVecHalvingAdd * instruction)5589 void LocationsBuilderRISCV64::VisitVecHalvingAdd(HVecHalvingAdd* instruction) {
5590   UNUSED(instruction);
5591   LOG(FATAL) << "Unimplemented";
5592 }
5593 
VisitVecHalvingAdd(HVecHalvingAdd * instruction)5594 void InstructionCodeGeneratorRISCV64::VisitVecHalvingAdd(HVecHalvingAdd* instruction) {
5595   UNUSED(instruction);
5596   LOG(FATAL) << "Unimplemented";
5597 }
5598 
VisitVecSub(HVecSub * instruction)5599 void LocationsBuilderRISCV64::VisitVecSub(HVecSub* instruction) {
5600   UNUSED(instruction);
5601   LOG(FATAL) << "Unimplemented";
5602 }
5603 
VisitVecSub(HVecSub * instruction)5604 void InstructionCodeGeneratorRISCV64::VisitVecSub(HVecSub* instruction) {
5605   UNUSED(instruction);
5606   LOG(FATAL) << "Unimplemented";
5607 }
5608 
VisitVecMul(HVecMul * instruction)5609 void LocationsBuilderRISCV64::VisitVecMul(HVecMul* instruction) {
5610   UNUSED(instruction);
5611   LOG(FATAL) << "Unimplemented";
5612 }
5613 
VisitVecMul(HVecMul * instruction)5614 void InstructionCodeGeneratorRISCV64::VisitVecMul(HVecMul* instruction) {
5615   UNUSED(instruction);
5616   LOG(FATAL) << "Unimplemented";
5617 }
5618 
VisitVecDiv(HVecDiv * instruction)5619 void LocationsBuilderRISCV64::VisitVecDiv(HVecDiv* instruction) {
5620   UNUSED(instruction);
5621   LOG(FATAL) << "Unimplemented";
5622 }
5623 
VisitVecDiv(HVecDiv * instruction)5624 void InstructionCodeGeneratorRISCV64::VisitVecDiv(HVecDiv* instruction) {
5625   UNUSED(instruction);
5626   LOG(FATAL) << "Unimplemented";
5627 }
5628 
VisitVecMin(HVecMin * instruction)5629 void LocationsBuilderRISCV64::VisitVecMin(HVecMin* instruction) {
5630   UNUSED(instruction);
5631   LOG(FATAL) << "Unimplemented";
5632 }
5633 
VisitVecMin(HVecMin * instruction)5634 void InstructionCodeGeneratorRISCV64::VisitVecMin(HVecMin* instruction) {
5635   UNUSED(instruction);
5636   LOG(FATAL) << "Unimplemented";
5637 }
5638 
VisitVecMax(HVecMax * instruction)5639 void LocationsBuilderRISCV64::VisitVecMax(HVecMax* instruction) {
5640   UNUSED(instruction);
5641   LOG(FATAL) << "Unimplemented";
5642 }
5643 
VisitVecMax(HVecMax * instruction)5644 void InstructionCodeGeneratorRISCV64::VisitVecMax(HVecMax* instruction) {
5645   UNUSED(instruction);
5646   LOG(FATAL) << "Unimplemented";
5647 }
5648 
VisitVecAnd(HVecAnd * instruction)5649 void LocationsBuilderRISCV64::VisitVecAnd(HVecAnd* instruction) {
5650   UNUSED(instruction);
5651   LOG(FATAL) << "Unimplemented";
5652 }
5653 
VisitVecAnd(HVecAnd * instruction)5654 void InstructionCodeGeneratorRISCV64::VisitVecAnd(HVecAnd* instruction) {
5655   UNUSED(instruction);
5656   LOG(FATAL) << "Unimplemented";
5657 }
5658 
VisitVecAndNot(HVecAndNot * instruction)5659 void LocationsBuilderRISCV64::VisitVecAndNot(HVecAndNot* instruction) {
5660   UNUSED(instruction);
5661   LOG(FATAL) << "Unimplemented";
5662 }
5663 
VisitVecAndNot(HVecAndNot * instruction)5664 void InstructionCodeGeneratorRISCV64::VisitVecAndNot(HVecAndNot* instruction) {
5665   UNUSED(instruction);
5666   LOG(FATAL) << "Unimplemented";
5667 }
5668 
VisitVecOr(HVecOr * instruction)5669 void LocationsBuilderRISCV64::VisitVecOr(HVecOr* instruction) {
5670   UNUSED(instruction);
5671   LOG(FATAL) << "Unimplemented";
5672 }
5673 
VisitVecOr(HVecOr * instruction)5674 void InstructionCodeGeneratorRISCV64::VisitVecOr(HVecOr* instruction) {
5675   UNUSED(instruction);
5676   LOG(FATAL) << "Unimplemented";
5677 }
5678 
VisitVecXor(HVecXor * instruction)5679 void LocationsBuilderRISCV64::VisitVecXor(HVecXor* instruction) {
5680   UNUSED(instruction);
5681   LOG(FATAL) << "Unimplemented";
5682 }
5683 
VisitVecXor(HVecXor * instruction)5684 void InstructionCodeGeneratorRISCV64::VisitVecXor(HVecXor* instruction) {
5685   UNUSED(instruction);
5686   LOG(FATAL) << "Unimplemented";
5687 }
5688 
VisitVecSaturationAdd(HVecSaturationAdd * instruction)5689 void LocationsBuilderRISCV64::VisitVecSaturationAdd(HVecSaturationAdd* instruction) {
5690   UNUSED(instruction);
5691   LOG(FATAL) << "Unimplemented";
5692 }
5693 
VisitVecSaturationAdd(HVecSaturationAdd * instruction)5694 void InstructionCodeGeneratorRISCV64::VisitVecSaturationAdd(HVecSaturationAdd* instruction) {
5695   UNUSED(instruction);
5696   LOG(FATAL) << "Unimplemented";
5697 }
5698 
VisitVecSaturationSub(HVecSaturationSub * instruction)5699 void LocationsBuilderRISCV64::VisitVecSaturationSub(HVecSaturationSub* instruction) {
5700   UNUSED(instruction);
5701   LOG(FATAL) << "Unimplemented";
5702 }
5703 
VisitVecSaturationSub(HVecSaturationSub * instruction)5704 void InstructionCodeGeneratorRISCV64::VisitVecSaturationSub(HVecSaturationSub* instruction) {
5705   UNUSED(instruction);
5706   LOG(FATAL) << "Unimplemented";
5707 }
5708 
VisitVecShl(HVecShl * instruction)5709 void LocationsBuilderRISCV64::VisitVecShl(HVecShl* instruction) {
5710   UNUSED(instruction);
5711   LOG(FATAL) << "Unimplemented";
5712 }
5713 
VisitVecShl(HVecShl * instruction)5714 void InstructionCodeGeneratorRISCV64::VisitVecShl(HVecShl* instruction) {
5715   UNUSED(instruction);
5716   LOG(FATAL) << "Unimplemented";
5717 }
5718 
VisitVecShr(HVecShr * instruction)5719 void LocationsBuilderRISCV64::VisitVecShr(HVecShr* instruction) {
5720   UNUSED(instruction);
5721   LOG(FATAL) << "Unimplemented";
5722 }
5723 
VisitVecShr(HVecShr * instruction)5724 void InstructionCodeGeneratorRISCV64::VisitVecShr(HVecShr* instruction) {
5725   UNUSED(instruction);
5726   LOG(FATAL) << "Unimplemented";
5727 }
5728 
VisitVecUShr(HVecUShr * instruction)5729 void LocationsBuilderRISCV64::VisitVecUShr(HVecUShr* instruction) {
5730   UNUSED(instruction);
5731   LOG(FATAL) << "Unimplemented";
5732 }
5733 
VisitVecUShr(HVecUShr * instruction)5734 void InstructionCodeGeneratorRISCV64::VisitVecUShr(HVecUShr* instruction) {
5735   UNUSED(instruction);
5736   LOG(FATAL) << "Unimplemented";
5737 }
5738 
VisitVecSetScalars(HVecSetScalars * instruction)5739 void LocationsBuilderRISCV64::VisitVecSetScalars(HVecSetScalars* instruction) {
5740   UNUSED(instruction);
5741   LOG(FATAL) << "Unimplemented";
5742 }
5743 
VisitVecSetScalars(HVecSetScalars * instruction)5744 void InstructionCodeGeneratorRISCV64::VisitVecSetScalars(HVecSetScalars* instruction) {
5745   UNUSED(instruction);
5746   LOG(FATAL) << "Unimplemented";
5747 }
5748 
VisitVecMultiplyAccumulate(HVecMultiplyAccumulate * instruction)5749 void LocationsBuilderRISCV64::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instruction) {
5750   UNUSED(instruction);
5751   LOG(FATAL) << "Unimplemented";
5752 }
5753 
VisitVecMultiplyAccumulate(HVecMultiplyAccumulate * instruction)5754 void InstructionCodeGeneratorRISCV64::VisitVecMultiplyAccumulate(
5755     HVecMultiplyAccumulate* instruction) {
5756   UNUSED(instruction);
5757   LOG(FATAL) << "Unimplemented";
5758 }
5759 
VisitVecSADAccumulate(HVecSADAccumulate * instruction)5760 void LocationsBuilderRISCV64::VisitVecSADAccumulate(HVecSADAccumulate* instruction) {
5761   UNUSED(instruction);
5762   LOG(FATAL) << "Unimplemented";
5763 }
5764 
VisitVecSADAccumulate(HVecSADAccumulate * instruction)5765 void InstructionCodeGeneratorRISCV64::VisitVecSADAccumulate(HVecSADAccumulate* instruction) {
5766   UNUSED(instruction);
5767   LOG(FATAL) << "Unimplemented";
5768 }
5769 
VisitVecDotProd(HVecDotProd * instruction)5770 void LocationsBuilderRISCV64::VisitVecDotProd(HVecDotProd* instruction) {
5771   UNUSED(instruction);
5772   LOG(FATAL) << "Unimplemented";
5773 }
5774 
VisitVecDotProd(HVecDotProd * instruction)5775 void InstructionCodeGeneratorRISCV64::VisitVecDotProd(HVecDotProd* instruction) {
5776   UNUSED(instruction);
5777   LOG(FATAL) << "Unimplemented";
5778 }
5779 
VisitVecLoad(HVecLoad * instruction)5780 void LocationsBuilderRISCV64::VisitVecLoad(HVecLoad* instruction) {
5781   UNUSED(instruction);
5782   LOG(FATAL) << "Unimplemented";
5783 }
5784 
VisitVecLoad(HVecLoad * instruction)5785 void InstructionCodeGeneratorRISCV64::VisitVecLoad(HVecLoad* instruction) {
5786   UNUSED(instruction);
5787   LOG(FATAL) << "Unimplemented";
5788 }
5789 
VisitVecStore(HVecStore * instruction)5790 void LocationsBuilderRISCV64::VisitVecStore(HVecStore* instruction) {
5791   UNUSED(instruction);
5792   LOG(FATAL) << "Unimplemented";
5793 }
5794 
VisitVecStore(HVecStore * instruction)5795 void InstructionCodeGeneratorRISCV64::VisitVecStore(HVecStore* instruction) {
5796   UNUSED(instruction);
5797   LOG(FATAL) << "Unimplemented";
5798 }
5799 
VisitVecPredSetAll(HVecPredSetAll * instruction)5800 void LocationsBuilderRISCV64::VisitVecPredSetAll(HVecPredSetAll* instruction) {
5801   UNUSED(instruction);
5802   LOG(FATAL) << "Unimplemented";
5803 }
5804 
VisitVecPredSetAll(HVecPredSetAll * instruction)5805 void InstructionCodeGeneratorRISCV64::VisitVecPredSetAll(HVecPredSetAll* instruction) {
5806   UNUSED(instruction);
5807   LOG(FATAL) << "Unimplemented";
5808 }
5809 
VisitVecPredWhile(HVecPredWhile * instruction)5810 void LocationsBuilderRISCV64::VisitVecPredWhile(HVecPredWhile* instruction) {
5811   UNUSED(instruction);
5812   LOG(FATAL) << "Unimplemented";
5813 }
5814 
VisitVecPredWhile(HVecPredWhile * instruction)5815 void InstructionCodeGeneratorRISCV64::VisitVecPredWhile(HVecPredWhile* instruction) {
5816   UNUSED(instruction);
5817   LOG(FATAL) << "Unimplemented";
5818 }
5819 
VisitVecPredToBoolean(HVecPredToBoolean * instruction)5820 void LocationsBuilderRISCV64::VisitVecPredToBoolean(HVecPredToBoolean* instruction) {
5821   UNUSED(instruction);
5822   LOG(FATAL) << "Unimplemented";
5823 }
5824 
VisitVecPredToBoolean(HVecPredToBoolean * instruction)5825 void InstructionCodeGeneratorRISCV64::VisitVecPredToBoolean(HVecPredToBoolean* instruction) {
5826   UNUSED(instruction);
5827   LOG(FATAL) << "Unimplemented";
5828 }
5829 
VisitVecEqual(HVecEqual * instruction)5830 void LocationsBuilderRISCV64::VisitVecEqual(HVecEqual* instruction) {
5831   UNUSED(instruction);
5832   LOG(FATAL) << "Unimplemented";
5833 }
5834 
VisitVecEqual(HVecEqual * instruction)5835 void InstructionCodeGeneratorRISCV64::VisitVecEqual(HVecEqual* instruction) {
5836   UNUSED(instruction);
5837   LOG(FATAL) << "Unimplemented";
5838 }
5839 
VisitVecNotEqual(HVecNotEqual * instruction)5840 void LocationsBuilderRISCV64::VisitVecNotEqual(HVecNotEqual* instruction) {
5841   UNUSED(instruction);
5842   LOG(FATAL) << "Unimplemented";
5843 }
5844 
VisitVecNotEqual(HVecNotEqual * instruction)5845 void InstructionCodeGeneratorRISCV64::VisitVecNotEqual(HVecNotEqual* instruction) {
5846   UNUSED(instruction);
5847   LOG(FATAL) << "Unimplemented";
5848 }
5849 
VisitVecLessThan(HVecLessThan * instruction)5850 void LocationsBuilderRISCV64::VisitVecLessThan(HVecLessThan* instruction) {
5851   UNUSED(instruction);
5852   LOG(FATAL) << "Unimplemented";
5853 }
5854 
VisitVecLessThan(HVecLessThan * instruction)5855 void InstructionCodeGeneratorRISCV64::VisitVecLessThan(HVecLessThan* instruction) {
5856   UNUSED(instruction);
5857   LOG(FATAL) << "Unimplemented";
5858 }
5859 
VisitVecLessThanOrEqual(HVecLessThanOrEqual * instruction)5860 void LocationsBuilderRISCV64::VisitVecLessThanOrEqual(HVecLessThanOrEqual* instruction) {
5861   UNUSED(instruction);
5862   LOG(FATAL) << "Unimplemented";
5863 }
5864 
VisitVecLessThanOrEqual(HVecLessThanOrEqual * instruction)5865 void InstructionCodeGeneratorRISCV64::VisitVecLessThanOrEqual(HVecLessThanOrEqual* instruction) {
5866   UNUSED(instruction);
5867   LOG(FATAL) << "Unimplemented";
5868 }
5869 
VisitVecGreaterThan(HVecGreaterThan * instruction)5870 void LocationsBuilderRISCV64::VisitVecGreaterThan(HVecGreaterThan* instruction) {
5871   UNUSED(instruction);
5872   LOG(FATAL) << "Unimplemented";
5873 }
5874 
VisitVecGreaterThan(HVecGreaterThan * instruction)5875 void InstructionCodeGeneratorRISCV64::VisitVecGreaterThan(HVecGreaterThan* instruction) {
5876   UNUSED(instruction);
5877   LOG(FATAL) << "Unimplemented";
5878 }
5879 
VisitVecGreaterThanOrEqual(HVecGreaterThanOrEqual * instruction)5880 void LocationsBuilderRISCV64::VisitVecGreaterThanOrEqual(HVecGreaterThanOrEqual* instruction) {
5881   UNUSED(instruction);
5882   LOG(FATAL) << "Unimplemented";
5883 }
5884 
VisitVecGreaterThanOrEqual(HVecGreaterThanOrEqual * instruction)5885 void InstructionCodeGeneratorRISCV64::VisitVecGreaterThanOrEqual(
5886     HVecGreaterThanOrEqual* instruction) {
5887   UNUSED(instruction);
5888   LOG(FATAL) << "Unimplemented";
5889 }
5890 
VisitVecBelow(HVecBelow * instruction)5891 void LocationsBuilderRISCV64::VisitVecBelow(HVecBelow* instruction) {
5892   UNUSED(instruction);
5893   LOG(FATAL) << "Unimplemented";
5894 }
5895 
VisitVecBelow(HVecBelow * instruction)5896 void InstructionCodeGeneratorRISCV64::VisitVecBelow(HVecBelow* instruction) {
5897   UNUSED(instruction);
5898   LOG(FATAL) << "Unimplemented";
5899 }
5900 
VisitVecBelowOrEqual(HVecBelowOrEqual * instruction)5901 void LocationsBuilderRISCV64::VisitVecBelowOrEqual(HVecBelowOrEqual* instruction) {
5902   UNUSED(instruction);
5903   LOG(FATAL) << "Unimplemented";
5904 }
5905 
VisitVecBelowOrEqual(HVecBelowOrEqual * instruction)5906 void InstructionCodeGeneratorRISCV64::VisitVecBelowOrEqual(HVecBelowOrEqual* instruction) {
5907   UNUSED(instruction);
5908   LOG(FATAL) << "Unimplemented";
5909 }
5910 
VisitVecAbove(HVecAbove * instruction)5911 void LocationsBuilderRISCV64::VisitVecAbove(HVecAbove* instruction) {
5912   UNUSED(instruction);
5913   LOG(FATAL) << "Unimplemented";
5914 }
5915 
VisitVecAbove(HVecAbove * instruction)5916 void InstructionCodeGeneratorRISCV64::VisitVecAbove(HVecAbove* instruction) {
5917   UNUSED(instruction);
5918   LOG(FATAL) << "Unimplemented";
5919 }
5920 
VisitVecAboveOrEqual(HVecAboveOrEqual * instruction)5921 void LocationsBuilderRISCV64::VisitVecAboveOrEqual(HVecAboveOrEqual* instruction) {
5922   UNUSED(instruction);
5923   LOG(FATAL) << "Unimplemented";
5924 }
5925 
VisitVecAboveOrEqual(HVecAboveOrEqual * instruction)5926 void InstructionCodeGeneratorRISCV64::VisitVecAboveOrEqual(HVecAboveOrEqual* instruction) {
5927   UNUSED(instruction);
5928   LOG(FATAL) << "Unimplemented";
5929 }
5930 
VisitVecPredNot(HVecPredNot * instruction)5931 void LocationsBuilderRISCV64::VisitVecPredNot(HVecPredNot* instruction) {
5932   UNUSED(instruction);
5933   LOG(FATAL) << "Unimplemented";
5934 }
5935 
VisitVecPredNot(HVecPredNot * instruction)5936 void InstructionCodeGeneratorRISCV64::VisitVecPredNot(HVecPredNot* instruction) {
5937   UNUSED(instruction);
5938   LOG(FATAL) << "Unimplemented";
5939 }
5940 
5941 namespace detail {
5942 
5943 // Mark which intrinsics we don't have handcrafted code for.
5944 template <Intrinsics T>
5945 struct IsUnimplemented {
5946   bool is_unimplemented = false;
5947 };
5948 
5949 #define TRUE_OVERRIDE(Name)                     \
5950   template <>                                   \
5951   struct IsUnimplemented<Intrinsics::k##Name> { \
5952     bool is_unimplemented = true;               \
5953   };
5954 UNIMPLEMENTED_INTRINSIC_LIST_RISCV64(TRUE_OVERRIDE)
5955 #undef TRUE_OVERRIDE
5956 
5957 static constexpr bool kIsIntrinsicUnimplemented[] = {
5958     false,  // kNone
5959 #define IS_UNIMPLEMENTED(Intrinsic, ...) \
5960     IsUnimplemented<Intrinsics::k##Intrinsic>().is_unimplemented,
5961     ART_INTRINSICS_LIST(IS_UNIMPLEMENTED)
5962 #undef IS_UNIMPLEMENTED
5963 };
5964 
5965 }  // namespace detail
5966 
CodeGeneratorRISCV64(HGraph * graph,const CompilerOptions & compiler_options,OptimizingCompilerStats * stats)5967 CodeGeneratorRISCV64::CodeGeneratorRISCV64(HGraph* graph,
5968                                            const CompilerOptions& compiler_options,
5969                                            OptimizingCompilerStats* stats)
5970     : CodeGenerator(graph,
5971                     kNumberOfXRegisters,
5972                     kNumberOfFRegisters,
5973                     /*number_of_register_pairs=*/ 0u,
5974                     ComputeRegisterMask(kCoreCalleeSaves, arraysize(kCoreCalleeSaves)),
5975                     ComputeRegisterMask(kFpuCalleeSaves, arraysize(kFpuCalleeSaves)),
5976                     compiler_options,
5977                     stats,
5978                     ArrayRef<const bool>(detail::kIsIntrinsicUnimplemented)),
5979       assembler_(graph->GetAllocator(),
5980                  compiler_options.GetInstructionSetFeatures()->AsRiscv64InstructionSetFeatures()),
5981       location_builder_(graph, this),
5982       instruction_visitor_(graph, this),
5983       block_labels_(nullptr),
5984       move_resolver_(graph->GetAllocator(), this),
5985       uint32_literals_(std::less<uint32_t>(),
5986                        graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
5987       uint64_literals_(std::less<uint64_t>(),
5988                        graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
5989       boot_image_method_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
5990       app_image_method_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
5991       method_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
5992       boot_image_type_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
5993       app_image_type_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
5994       type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
5995       public_type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
5996       package_type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
5997       boot_image_string_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
5998       string_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
5999       boot_image_jni_entrypoint_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
6000       boot_image_other_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
6001       jit_string_patches_(StringReferenceValueComparator(),
6002                           graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
6003       jit_class_patches_(TypeReferenceValueComparator(),
6004                          graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)) {
6005   // Always mark the RA register to be saved.
6006   AddAllocatedRegister(Location::RegisterLocation(RA));
6007 }
6008 
MaybeIncrementHotness(HSuspendCheck * suspend_check,bool is_frame_entry)6009 void CodeGeneratorRISCV64::MaybeIncrementHotness(HSuspendCheck* suspend_check,
6010                                                  bool is_frame_entry) {
6011   if (GetCompilerOptions().CountHotnessInCompiledCode()) {
6012     ScratchRegisterScope srs(GetAssembler());
6013     XRegister method = is_frame_entry ? kArtMethodRegister : srs.AllocateXRegister();
6014     if (!is_frame_entry) {
6015       __ Loadd(method, SP, 0);
6016     }
6017     XRegister counter = srs.AllocateXRegister();
6018     __ Loadhu(counter, method, ArtMethod::HotnessCountOffset().Int32Value());
6019     Riscv64Label done;
6020     DCHECK_EQ(0u, interpreter::kNterpHotnessValue);
6021     __ Beqz(counter, &done);  // Can clobber `TMP` if taken.
6022     __ Addi(counter, counter, -1);
6023     // We may not have another scratch register available for `Storeh`()`,
6024     // so we must use the `Sh()` function directly.
6025     static_assert(IsInt<12>(ArtMethod::HotnessCountOffset().Int32Value()));
6026     __ Sh(counter, method, ArtMethod::HotnessCountOffset().Int32Value());
6027     __ Bind(&done);
6028   }
6029 
6030   if (GetGraph()->IsCompilingBaseline() &&
6031       GetGraph()->IsUsefulOptimizing() &&
6032       !Runtime::Current()->IsAotCompiler()) {
6033     ProfilingInfo* info = GetGraph()->GetProfilingInfo();
6034     DCHECK(info != nullptr);
6035     DCHECK(!HasEmptyFrame());
6036     uint64_t address = reinterpret_cast64<uint64_t>(info) +
6037                        ProfilingInfo::BaselineHotnessCountOffset().SizeValue();
6038     auto [base_address, imm12] = SplitJitAddress(address);
6039     ScratchRegisterScope srs(GetAssembler());
6040     XRegister counter = srs.AllocateXRegister();
6041     XRegister tmp = RA;
6042     __ LoadConst64(tmp, base_address);
6043     SlowPathCodeRISCV64* slow_path =
6044         new (GetScopedAllocator()) CompileOptimizedSlowPathRISCV64(suspend_check, tmp, imm12);
6045     AddSlowPath(slow_path);
6046     __ Lhu(counter, tmp, imm12);
6047     __ Beqz(counter, slow_path->GetEntryLabel());  // Can clobber `TMP` if taken.
6048     __ Addi(counter, counter, -1);
6049     __ Sh(counter, tmp, imm12);
6050     __ Bind(slow_path->GetExitLabel());
6051   }
6052 }
6053 
CanUseImplicitSuspendCheck() const6054 bool CodeGeneratorRISCV64::CanUseImplicitSuspendCheck() const {
6055   // TODO(riscv64): Implement implicit suspend checks to reduce code size.
6056   return false;
6057 }
6058 
GenerateMemoryBarrier(MemBarrierKind kind)6059 void CodeGeneratorRISCV64::GenerateMemoryBarrier(MemBarrierKind kind) {
6060   switch (kind) {
6061     case MemBarrierKind::kAnyAny:
6062       __ Fence(/*pred=*/ kFenceRead | kFenceWrite, /*succ=*/ kFenceRead | kFenceWrite);
6063       break;
6064     case MemBarrierKind::kAnyStore:
6065       __ Fence(/*pred=*/ kFenceRead | kFenceWrite, /*succ=*/ kFenceWrite);
6066       break;
6067     case MemBarrierKind::kLoadAny:
6068       __ Fence(/*pred=*/ kFenceRead, /*succ=*/ kFenceRead | kFenceWrite);
6069       break;
6070     case MemBarrierKind::kStoreStore:
6071       __ Fence(/*pred=*/ kFenceWrite, /*succ=*/ kFenceWrite);
6072       break;
6073 
6074     default:
6075       LOG(FATAL) << "Unexpected memory barrier " << kind;
6076       UNREACHABLE();
6077   }
6078 }
6079 
GenerateFrameEntry()6080 void CodeGeneratorRISCV64::GenerateFrameEntry() {
6081   // Check if we need to generate the clinit check. We will jump to the
6082   // resolution stub if the class is not initialized and the executing thread is
6083   // not the thread initializing it.
6084   // We do this before constructing the frame to get the correct stack trace if
6085   // an exception is thrown.
6086   if (GetCompilerOptions().ShouldCompileWithClinitCheck(GetGraph()->GetArtMethod())) {
6087     Riscv64Label resolution;
6088     Riscv64Label memory_barrier;
6089 
6090     ScratchRegisterScope srs(GetAssembler());
6091     XRegister tmp = srs.AllocateXRegister();
6092     XRegister tmp2 = srs.AllocateXRegister();
6093 
6094     // We don't emit a read barrier here to save on code size. We rely on the
6095     // resolution trampoline to do a clinit check before re-entering this code.
6096     __ Loadwu(tmp2, kArtMethodRegister, ArtMethod::DeclaringClassOffset().Int32Value());
6097 
6098     // We shall load the full 32-bit status word with sign-extension and compare as unsigned
6099     // to sign-extended shifted status values. This yields the same comparison as loading and
6100     // materializing unsigned but the constant is materialized with a single LUI instruction.
6101     __ Loadw(tmp, tmp2, mirror::Class::StatusOffset().SizeValue());  // Sign-extended.
6102 
6103     // Check if we're visibly initialized.
6104     __ Li(tmp2, ShiftedSignExtendedClassStatusValue<ClassStatus::kVisiblyInitialized>());
6105     __ Bgeu(tmp, tmp2, &frame_entry_label_);  // Can clobber `TMP` if taken.
6106 
6107     // Check if we're initialized and jump to code that does a memory barrier if so.
6108     __ Li(tmp2, ShiftedSignExtendedClassStatusValue<ClassStatus::kInitialized>());
6109     __ Bgeu(tmp, tmp2, &memory_barrier);  // Can clobber `TMP` if taken.
6110 
6111     // Check if we're initializing and the thread initializing is the one
6112     // executing the code.
6113     __ Li(tmp2, ShiftedSignExtendedClassStatusValue<ClassStatus::kInitializing>());
6114     __ Bltu(tmp, tmp2, &resolution);  // Can clobber `TMP` if taken.
6115 
6116     __ Loadwu(tmp2, kArtMethodRegister, ArtMethod::DeclaringClassOffset().Int32Value());
6117     __ Loadw(tmp, tmp2, mirror::Class::ClinitThreadIdOffset().Int32Value());
6118     __ Loadw(tmp2, TR, Thread::TidOffset<kRiscv64PointerSize>().Int32Value());
6119     __ Beq(tmp, tmp2, &frame_entry_label_);
6120     __ Bind(&resolution);
6121 
6122     // Jump to the resolution stub.
6123     ThreadOffset64 entrypoint_offset =
6124         GetThreadOffset<kRiscv64PointerSize>(kQuickQuickResolutionTrampoline);
6125     __ Loadd(tmp, TR, entrypoint_offset.Int32Value());
6126     __ Jr(tmp);
6127 
6128     __ Bind(&memory_barrier);
6129     GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
6130   }
6131   __ Bind(&frame_entry_label_);
6132 
6133   bool do_overflow_check =
6134       FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kRiscv64) || !IsLeafMethod();
6135 
6136   if (do_overflow_check) {
6137     DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks());
6138     __ Loadw(
6139         Zero, SP, -static_cast<int32_t>(GetStackOverflowReservedBytes(InstructionSet::kRiscv64)));
6140     RecordPcInfo(nullptr, 0);
6141   }
6142 
6143   if (!HasEmptyFrame()) {
6144     // Make sure the frame size isn't unreasonably large.
6145     DCHECK_LE(GetFrameSize(), GetMaximumFrameSize());
6146 
6147     // Spill callee-saved registers.
6148 
6149     uint32_t frame_size = GetFrameSize();
6150 
6151     IncreaseFrame(frame_size);
6152 
6153     uint32_t offset = frame_size;
6154     for (size_t i = arraysize(kCoreCalleeSaves); i != 0; ) {
6155       --i;
6156       XRegister reg = kCoreCalleeSaves[i];
6157       if (allocated_registers_.ContainsCoreRegister(reg)) {
6158         offset -= kRiscv64DoublewordSize;
6159         __ Stored(reg, SP, offset);
6160         __ cfi().RelOffset(dwarf::Reg::Riscv64Core(reg), offset);
6161       }
6162     }
6163 
6164     for (size_t i = arraysize(kFpuCalleeSaves); i != 0; ) {
6165       --i;
6166       FRegister reg = kFpuCalleeSaves[i];
6167       if (allocated_registers_.ContainsFloatingPointRegister(reg)) {
6168         offset -= kRiscv64DoublewordSize;
6169         __ FStored(reg, SP, offset);
6170         __ cfi().RelOffset(dwarf::Reg::Riscv64Fp(reg), offset);
6171       }
6172     }
6173 
6174     // Save the current method if we need it. Note that we do not
6175     // do this in HCurrentMethod, as the instruction might have been removed
6176     // in the SSA graph.
6177     if (RequiresCurrentMethod()) {
6178       __ Stored(kArtMethodRegister, SP, 0);
6179     }
6180 
6181     if (GetGraph()->HasShouldDeoptimizeFlag()) {
6182       // Initialize should_deoptimize flag to 0.
6183       __ Storew(Zero, SP, GetStackOffsetOfShouldDeoptimizeFlag());
6184     }
6185   }
6186   MaybeIncrementHotness(/* suspend_check= */ nullptr, /*is_frame_entry=*/ true);
6187 }
6188 
GenerateFrameExit()6189 void CodeGeneratorRISCV64::GenerateFrameExit() {
6190   __ cfi().RememberState();
6191 
6192   if (!HasEmptyFrame()) {
6193     // Restore callee-saved registers.
6194 
6195     // For better instruction scheduling restore RA before other registers.
6196     uint32_t offset = GetFrameSize();
6197     for (size_t i = arraysize(kCoreCalleeSaves); i != 0; ) {
6198       --i;
6199       XRegister reg = kCoreCalleeSaves[i];
6200       if (allocated_registers_.ContainsCoreRegister(reg)) {
6201         offset -= kRiscv64DoublewordSize;
6202         __ Loadd(reg, SP, offset);
6203         __ cfi().Restore(dwarf::Reg::Riscv64Core(reg));
6204       }
6205     }
6206 
6207     for (size_t i = arraysize(kFpuCalleeSaves); i != 0; ) {
6208       --i;
6209       FRegister reg = kFpuCalleeSaves[i];
6210       if (allocated_registers_.ContainsFloatingPointRegister(reg)) {
6211         offset -= kRiscv64DoublewordSize;
6212         __ FLoadd(reg, SP, offset);
6213         __ cfi().Restore(dwarf::Reg::Riscv64Fp(reg));
6214       }
6215     }
6216 
6217     DecreaseFrame(GetFrameSize());
6218   }
6219 
6220   __ Jr(RA);
6221 
6222   __ cfi().RestoreState();
6223   __ cfi().DefCFAOffset(GetFrameSize());
6224 }
6225 
Bind(HBasicBlock * block)6226 void CodeGeneratorRISCV64::Bind(HBasicBlock* block) { __ Bind(GetLabelOf(block)); }
6227 
MoveConstant(Location destination,int32_t value)6228 void CodeGeneratorRISCV64::MoveConstant(Location destination, int32_t value) {
6229   DCHECK(destination.IsRegister());
6230   __ LoadConst32(destination.AsRegister<XRegister>(), value);
6231 }
6232 
MoveLocation(Location destination,Location source,DataType::Type dst_type)6233 void CodeGeneratorRISCV64::MoveLocation(Location destination,
6234                                         Location source,
6235                                         DataType::Type dst_type) {
6236   if (source.Equals(destination)) {
6237     return;
6238   }
6239 
6240   // A valid move type can always be inferred from the destination and source locations.
6241   // When moving from and to a register, the `dst_type` can be used to generate 32-bit instead
6242   // of 64-bit moves but it's generally OK to use 64-bit moves for 32-bit values in registers.
6243   bool unspecified_type = (dst_type == DataType::Type::kVoid);
6244   // TODO(riscv64): Is the destination type known in all cases?
6245   // TODO(riscv64): Can unspecified `dst_type` move 32-bit GPR to FPR without NaN-boxing?
6246   CHECK(!unspecified_type);
6247 
6248   if (destination.IsRegister() || destination.IsFpuRegister()) {
6249     if (unspecified_type) {
6250       HConstant* src_cst = source.IsConstant() ? source.GetConstant() : nullptr;
6251       if (source.IsStackSlot() ||
6252           (src_cst != nullptr &&
6253            (src_cst->IsIntConstant() || src_cst->IsFloatConstant() || src_cst->IsNullConstant()))) {
6254         // For stack slots and 32-bit constants, a 32-bit type is appropriate.
6255         dst_type = destination.IsRegister() ? DataType::Type::kInt32 : DataType::Type::kFloat32;
6256       } else {
6257         // If the source is a double stack slot or a 64-bit constant, a 64-bit type
6258         // is appropriate. Else the source is a register, and since the type has not
6259         // been specified, we chose a 64-bit type to force a 64-bit move.
6260         dst_type = destination.IsRegister() ? DataType::Type::kInt64 : DataType::Type::kFloat64;
6261       }
6262     }
6263     DCHECK((destination.IsFpuRegister() && DataType::IsFloatingPointType(dst_type)) ||
6264            (destination.IsRegister() && !DataType::IsFloatingPointType(dst_type)));
6265 
6266     if (source.IsStackSlot() || source.IsDoubleStackSlot()) {
6267       // Move to GPR/FPR from stack
6268       if (DataType::IsFloatingPointType(dst_type)) {
6269         if (DataType::Is64BitType(dst_type)) {
6270           __ FLoadd(destination.AsFpuRegister<FRegister>(), SP, source.GetStackIndex());
6271         } else {
6272           __ FLoadw(destination.AsFpuRegister<FRegister>(), SP, source.GetStackIndex());
6273         }
6274       } else {
6275         if (DataType::Is64BitType(dst_type)) {
6276           __ Loadd(destination.AsRegister<XRegister>(), SP, source.GetStackIndex());
6277         } else if (dst_type == DataType::Type::kReference) {
6278           __ Loadwu(destination.AsRegister<XRegister>(), SP, source.GetStackIndex());
6279         } else {
6280           __ Loadw(destination.AsRegister<XRegister>(), SP, source.GetStackIndex());
6281         }
6282       }
6283     } else if (source.IsConstant()) {
6284       // Move to GPR/FPR from constant
6285       // TODO(riscv64): Consider using literals for difficult-to-materialize 64-bit constants.
6286       int64_t value = GetInt64ValueOf(source.GetConstant()->AsConstant());
6287       ScratchRegisterScope srs(GetAssembler());
6288       XRegister gpr = DataType::IsFloatingPointType(dst_type)
6289           ? srs.AllocateXRegister()
6290           : destination.AsRegister<XRegister>();
6291       if (DataType::IsFloatingPointType(dst_type) && value == 0) {
6292         gpr = Zero;  // Note: The scratch register allocated above shall not be used.
6293       } else {
6294         // Note: For `float` we load the sign-extended value here as it can sometimes yield
6295         // a shorter instruction sequence. The higher 32 bits shall be ignored during the
6296         // transfer to FP reg and the result shall be correctly NaN-boxed.
6297         __ LoadConst64(gpr, value);
6298       }
6299       if (dst_type == DataType::Type::kFloat32) {
6300         __ FMvWX(destination.AsFpuRegister<FRegister>(), gpr);
6301       } else if (dst_type == DataType::Type::kFloat64) {
6302         __ FMvDX(destination.AsFpuRegister<FRegister>(), gpr);
6303       }
6304     } else if (source.IsRegister()) {
6305       if (destination.IsRegister()) {
6306         // Move to GPR from GPR
6307         __ Mv(destination.AsRegister<XRegister>(), source.AsRegister<XRegister>());
6308       } else {
6309         DCHECK(destination.IsFpuRegister());
6310         if (DataType::Is64BitType(dst_type)) {
6311           __ FMvDX(destination.AsFpuRegister<FRegister>(), source.AsRegister<XRegister>());
6312         } else {
6313           __ FMvWX(destination.AsFpuRegister<FRegister>(), source.AsRegister<XRegister>());
6314         }
6315       }
6316     } else if (source.IsFpuRegister()) {
6317       if (destination.IsFpuRegister()) {
6318         if (GetGraph()->HasSIMD()) {
6319           LOG(FATAL) << "Vector extension is unsupported";
6320           UNREACHABLE();
6321         } else {
6322           // Move to FPR from FPR
6323           if (dst_type == DataType::Type::kFloat32) {
6324             __ FMvS(destination.AsFpuRegister<FRegister>(), source.AsFpuRegister<FRegister>());
6325           } else {
6326             DCHECK_EQ(dst_type, DataType::Type::kFloat64);
6327             __ FMvD(destination.AsFpuRegister<FRegister>(), source.AsFpuRegister<FRegister>());
6328           }
6329         }
6330       } else {
6331         DCHECK(destination.IsRegister());
6332         if (DataType::Is64BitType(dst_type)) {
6333           __ FMvXD(destination.AsRegister<XRegister>(), source.AsFpuRegister<FRegister>());
6334         } else {
6335           __ FMvXW(destination.AsRegister<XRegister>(), source.AsFpuRegister<FRegister>());
6336         }
6337       }
6338     }
6339   } else if (destination.IsSIMDStackSlot()) {
6340     LOG(FATAL) << "SIMD is unsupported";
6341     UNREACHABLE();
6342   } else {  // The destination is not a register. It must be a stack slot.
6343     DCHECK(destination.IsStackSlot() || destination.IsDoubleStackSlot());
6344     if (source.IsRegister() || source.IsFpuRegister()) {
6345       if (unspecified_type) {
6346         if (source.IsRegister()) {
6347           dst_type = destination.IsStackSlot() ? DataType::Type::kInt32 : DataType::Type::kInt64;
6348         } else {
6349           dst_type =
6350               destination.IsStackSlot() ? DataType::Type::kFloat32 : DataType::Type::kFloat64;
6351         }
6352       }
6353       DCHECK_EQ(source.IsFpuRegister(), DataType::IsFloatingPointType(dst_type));
6354       // For direct @CriticalNative calls, we need to sign-extend narrow integral args
6355       // to 64 bits, so widening integral values is allowed. Narrowing is forbidden.
6356       DCHECK_IMPLIES(DataType::IsFloatingPointType(dst_type) || destination.IsStackSlot(),
6357                      destination.IsDoubleStackSlot() == DataType::Is64BitType(dst_type));
6358       // Move to stack from GPR/FPR
6359       if (destination.IsDoubleStackSlot()) {
6360         if (source.IsRegister()) {
6361           __ Stored(source.AsRegister<XRegister>(), SP, destination.GetStackIndex());
6362         } else {
6363           __ FStored(source.AsFpuRegister<FRegister>(), SP, destination.GetStackIndex());
6364         }
6365       } else {
6366         if (source.IsRegister()) {
6367           __ Storew(source.AsRegister<XRegister>(), SP, destination.GetStackIndex());
6368         } else {
6369           __ FStorew(source.AsFpuRegister<FRegister>(), SP, destination.GetStackIndex());
6370         }
6371       }
6372     } else if (source.IsConstant()) {
6373       // Move to stack from constant
6374       int64_t value = GetInt64ValueOf(source.GetConstant());
6375       ScratchRegisterScope srs(GetAssembler());
6376       XRegister gpr = (value != 0) ? srs.AllocateXRegister() : Zero;
6377       if (value != 0) {
6378         __ LoadConst64(gpr, value);
6379       }
6380       if (destination.IsStackSlot()) {
6381         __ Storew(gpr, SP, destination.GetStackIndex());
6382       } else {
6383         DCHECK(destination.IsDoubleStackSlot());
6384         __ Stored(gpr, SP, destination.GetStackIndex());
6385       }
6386     } else {
6387       DCHECK(source.IsStackSlot() || source.IsDoubleStackSlot());
6388       // For direct @CriticalNative calls, we need to sign-extend narrow integral args
6389       // to 64 bits, so widening move is allowed. Narrowing move is forbidden.
6390       DCHECK_IMPLIES(destination.IsStackSlot(), source.IsStackSlot());
6391       // Move to stack from stack
6392       ScratchRegisterScope srs(GetAssembler());
6393       XRegister tmp = srs.AllocateXRegister();
6394       if (source.IsStackSlot()) {
6395         __ Loadw(tmp, SP, source.GetStackIndex());
6396       } else {
6397         __ Loadd(tmp, SP, source.GetStackIndex());
6398       }
6399       if (destination.IsStackSlot()) {
6400         __ Storew(tmp, SP, destination.GetStackIndex());
6401       } else {
6402         __ Stored(tmp, SP, destination.GetStackIndex());
6403       }
6404     }
6405   }
6406 }
6407 
AddLocationAsTemp(Location location,LocationSummary * locations)6408 void CodeGeneratorRISCV64::AddLocationAsTemp(Location location, LocationSummary* locations) {
6409   if (location.IsRegister()) {
6410     locations->AddTemp(location);
6411   } else {
6412     UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location;
6413   }
6414 }
6415 
SetupBlockedRegisters() const6416 void CodeGeneratorRISCV64::SetupBlockedRegisters() const {
6417   // ZERO, GP, SP, RA, TP and TR(S1) are reserved and can't be allocated.
6418   blocked_core_registers_[Zero] = true;
6419   blocked_core_registers_[GP] = true;
6420   blocked_core_registers_[SP] = true;
6421   blocked_core_registers_[RA] = true;
6422   blocked_core_registers_[TP] = true;
6423   blocked_core_registers_[TR] = true;  // ART Thread register.
6424 
6425   // TMP(T6), TMP2(T5) and FTMP(FT11) are used as temporary/scratch registers.
6426   blocked_core_registers_[TMP] = true;
6427   blocked_core_registers_[TMP2] = true;
6428   blocked_fpu_registers_[FTMP] = true;
6429 
6430   if (GetGraph()->IsDebuggable()) {
6431     // Stubs do not save callee-save floating point registers. If the graph
6432     // is debuggable, we need to deal with these registers differently. For
6433     // now, just block them.
6434     for (size_t i = 0; i < arraysize(kFpuCalleeSaves); ++i) {
6435       blocked_fpu_registers_[kFpuCalleeSaves[i]] = true;
6436     }
6437   }
6438 }
6439 
SaveCoreRegister(size_t stack_index,uint32_t reg_id)6440 size_t CodeGeneratorRISCV64::SaveCoreRegister(size_t stack_index, uint32_t reg_id) {
6441   __ Stored(XRegister(reg_id), SP, stack_index);
6442   return kRiscv64DoublewordSize;
6443 }
6444 
RestoreCoreRegister(size_t stack_index,uint32_t reg_id)6445 size_t CodeGeneratorRISCV64::RestoreCoreRegister(size_t stack_index, uint32_t reg_id) {
6446   __ Loadd(XRegister(reg_id), SP, stack_index);
6447   return kRiscv64DoublewordSize;
6448 }
6449 
SaveFloatingPointRegister(size_t stack_index,uint32_t reg_id)6450 size_t CodeGeneratorRISCV64::SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
6451   if (GetGraph()->HasSIMD()) {
6452     // TODO(riscv64): RISC-V vector extension.
6453     UNIMPLEMENTED(FATAL) << "Vector extension is unsupported";
6454     UNREACHABLE();
6455   }
6456   __ FStored(FRegister(reg_id), SP, stack_index);
6457   return kRiscv64FloatRegSizeInBytes;
6458 }
6459 
RestoreFloatingPointRegister(size_t stack_index,uint32_t reg_id)6460 size_t CodeGeneratorRISCV64::RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
6461   if (GetGraph()->HasSIMD()) {
6462     // TODO(riscv64): RISC-V vector extension.
6463     UNIMPLEMENTED(FATAL) << "Vector extension is unsupported";
6464     UNREACHABLE();
6465   }
6466   __ FLoadd(FRegister(reg_id), SP, stack_index);
6467   return kRiscv64FloatRegSizeInBytes;
6468 }
6469 
DumpCoreRegister(std::ostream & stream,int reg) const6470 void CodeGeneratorRISCV64::DumpCoreRegister(std::ostream& stream, int reg) const {
6471   stream << XRegister(reg);
6472 }
6473 
DumpFloatingPointRegister(std::ostream & stream,int reg) const6474 void CodeGeneratorRISCV64::DumpFloatingPointRegister(std::ostream& stream, int reg) const {
6475   stream << FRegister(reg);
6476 }
6477 
GetInstructionSetFeatures() const6478 const Riscv64InstructionSetFeatures& CodeGeneratorRISCV64::GetInstructionSetFeatures() const {
6479   return *GetCompilerOptions().GetInstructionSetFeatures()->AsRiscv64InstructionSetFeatures();
6480 }
6481 
Finalize()6482 void CodeGeneratorRISCV64::Finalize() {
6483   // Ensure that we fix up branches and literal loads and emit the literal pool.
6484   __ FinalizeCode();
6485 
6486   // Adjust native pc offsets in stack maps.
6487   StackMapStream* stack_map_stream = GetStackMapStream();
6488   for (size_t i = 0, num = stack_map_stream->GetNumberOfStackMaps(); i != num; ++i) {
6489     uint32_t old_position = stack_map_stream->GetStackMapNativePcOffset(i);
6490     uint32_t new_position = __ GetAdjustedPosition(old_position);
6491     DCHECK_GE(new_position, old_position);
6492     stack_map_stream->SetStackMapNativePcOffset(i, new_position);
6493   }
6494 
6495   // Adjust pc offsets for the disassembly information.
6496   if (disasm_info_ != nullptr) {
6497     GeneratedCodeInterval* frame_entry_interval = disasm_info_->GetFrameEntryInterval();
6498     frame_entry_interval->start = __ GetAdjustedPosition(frame_entry_interval->start);
6499     frame_entry_interval->end = __ GetAdjustedPosition(frame_entry_interval->end);
6500     for (auto& entry : *disasm_info_->GetInstructionIntervals()) {
6501       entry.second.start = __ GetAdjustedPosition(entry.second.start);
6502       entry.second.end = __ GetAdjustedPosition(entry.second.end);
6503     }
6504     for (auto& entry : *disasm_info_->GetSlowPathIntervals()) {
6505       entry.code_interval.start = __ GetAdjustedPosition(entry.code_interval.start);
6506       entry.code_interval.end = __ GetAdjustedPosition(entry.code_interval.end);
6507     }
6508   }
6509 }
6510 
6511 // Generate code to invoke a runtime entry point.
InvokeRuntime(QuickEntrypointEnum entrypoint,HInstruction * instruction,uint32_t dex_pc,SlowPathCode * slow_path)6512 void CodeGeneratorRISCV64::InvokeRuntime(QuickEntrypointEnum entrypoint,
6513                                          HInstruction* instruction,
6514                                          uint32_t dex_pc,
6515                                          SlowPathCode* slow_path) {
6516   ValidateInvokeRuntime(entrypoint, instruction, slow_path);
6517 
6518   ThreadOffset64 entrypoint_offset = GetThreadOffset<kRiscv64PointerSize>(entrypoint);
6519 
6520   // TODO(riscv64): Reduce code size for AOT by using shared trampolines for slow path
6521   // runtime calls across the entire oat file.
6522   __ Loadd(RA, TR, entrypoint_offset.Int32Value());
6523   __ Jalr(RA);
6524   if (EntrypointRequiresStackMap(entrypoint)) {
6525     RecordPcInfo(instruction, dex_pc, slow_path);
6526   }
6527 }
6528 
6529 // Generate code to invoke a runtime entry point, but do not record
6530 // PC-related information in a stack map.
InvokeRuntimeWithoutRecordingPcInfo(int32_t entry_point_offset,HInstruction * instruction,SlowPathCode * slow_path)6531 void CodeGeneratorRISCV64::InvokeRuntimeWithoutRecordingPcInfo(int32_t entry_point_offset,
6532                                                                HInstruction* instruction,
6533                                                                SlowPathCode* slow_path) {
6534   ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction, slow_path);
6535   __ Loadd(RA, TR, entry_point_offset);
6536   __ Jalr(RA);
6537 }
6538 
IncreaseFrame(size_t adjustment)6539 void CodeGeneratorRISCV64::IncreaseFrame(size_t adjustment) {
6540   int32_t adjustment32 = dchecked_integral_cast<int32_t>(adjustment);
6541   __ AddConst64(SP, SP, -adjustment32);
6542   GetAssembler()->cfi().AdjustCFAOffset(adjustment32);
6543 }
6544 
DecreaseFrame(size_t adjustment)6545 void CodeGeneratorRISCV64::DecreaseFrame(size_t adjustment) {
6546   int32_t adjustment32 = dchecked_integral_cast<int32_t>(adjustment);
6547   __ AddConst64(SP, SP, adjustment32);
6548   GetAssembler()->cfi().AdjustCFAOffset(-adjustment32);
6549 }
6550 
GenerateNop()6551 void CodeGeneratorRISCV64::GenerateNop() {
6552   __ Nop();
6553 }
6554 
GenerateImplicitNullCheck(HNullCheck * instruction)6555 void CodeGeneratorRISCV64::GenerateImplicitNullCheck(HNullCheck* instruction) {
6556   if (CanMoveNullCheckToUser(instruction)) {
6557     return;
6558   }
6559   Location obj = instruction->GetLocations()->InAt(0);
6560 
6561   __ Lw(Zero, obj.AsRegister<XRegister>(), 0);
6562   RecordPcInfo(instruction, instruction->GetDexPc());
6563 }
6564 
GenerateExplicitNullCheck(HNullCheck * instruction)6565 void CodeGeneratorRISCV64::GenerateExplicitNullCheck(HNullCheck* instruction) {
6566   SlowPathCodeRISCV64* slow_path = new (GetScopedAllocator()) NullCheckSlowPathRISCV64(instruction);
6567   AddSlowPath(slow_path);
6568 
6569   Location obj = instruction->GetLocations()->InAt(0);
6570 
6571   __ Beqz(obj.AsRegister<XRegister>(), slow_path->GetEntryLabel());
6572 }
6573 
GetSupportedLoadStringKind(HLoadString::LoadKind desired_string_load_kind)6574 HLoadString::LoadKind CodeGeneratorRISCV64::GetSupportedLoadStringKind(
6575     HLoadString::LoadKind desired_string_load_kind) {
6576   switch (desired_string_load_kind) {
6577     case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
6578     case HLoadString::LoadKind::kBootImageRelRo:
6579     case HLoadString::LoadKind::kBssEntry:
6580       DCHECK(!Runtime::Current()->UseJitCompilation());
6581       break;
6582     case HLoadString::LoadKind::kJitBootImageAddress:
6583     case HLoadString::LoadKind::kJitTableAddress:
6584       DCHECK(Runtime::Current()->UseJitCompilation());
6585       break;
6586     case HLoadString::LoadKind::kRuntimeCall:
6587       break;
6588   }
6589   return desired_string_load_kind;
6590 }
6591 
GetSupportedLoadClassKind(HLoadClass::LoadKind desired_class_load_kind)6592 HLoadClass::LoadKind CodeGeneratorRISCV64::GetSupportedLoadClassKind(
6593     HLoadClass::LoadKind desired_class_load_kind) {
6594   switch (desired_class_load_kind) {
6595     case HLoadClass::LoadKind::kInvalid:
6596       LOG(FATAL) << "UNREACHABLE";
6597       UNREACHABLE();
6598     case HLoadClass::LoadKind::kReferrersClass:
6599       break;
6600     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
6601     case HLoadClass::LoadKind::kBootImageRelRo:
6602     case HLoadClass::LoadKind::kAppImageRelRo:
6603     case HLoadClass::LoadKind::kBssEntry:
6604     case HLoadClass::LoadKind::kBssEntryPublic:
6605     case HLoadClass::LoadKind::kBssEntryPackage:
6606       DCHECK(!Runtime::Current()->UseJitCompilation());
6607       break;
6608     case HLoadClass::LoadKind::kJitBootImageAddress:
6609     case HLoadClass::LoadKind::kJitTableAddress:
6610       DCHECK(Runtime::Current()->UseJitCompilation());
6611       break;
6612     case HLoadClass::LoadKind::kRuntimeCall:
6613       break;
6614   }
6615   return desired_class_load_kind;
6616 }
6617 
GetSupportedInvokeStaticOrDirectDispatch(const HInvokeStaticOrDirect::DispatchInfo & desired_dispatch_info,ArtMethod * method)6618 HInvokeStaticOrDirect::DispatchInfo CodeGeneratorRISCV64::GetSupportedInvokeStaticOrDirectDispatch(
6619     const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info, ArtMethod* method) {
6620   UNUSED(method);
6621   // On RISCV64 we support all dispatch types.
6622   return desired_dispatch_info;
6623 }
6624 
NewBootImageIntrinsicPatch(uint32_t intrinsic_data,const PcRelativePatchInfo * info_high)6625 CodeGeneratorRISCV64::PcRelativePatchInfo* CodeGeneratorRISCV64::NewBootImageIntrinsicPatch(
6626     uint32_t intrinsic_data, const PcRelativePatchInfo* info_high) {
6627   return NewPcRelativePatch(
6628       /* dex_file= */ nullptr, intrinsic_data, info_high, &boot_image_other_patches_);
6629 }
6630 
NewBootImageRelRoPatch(uint32_t boot_image_offset,const PcRelativePatchInfo * info_high)6631 CodeGeneratorRISCV64::PcRelativePatchInfo* CodeGeneratorRISCV64::NewBootImageRelRoPatch(
6632     uint32_t boot_image_offset, const PcRelativePatchInfo* info_high) {
6633   return NewPcRelativePatch(
6634       /* dex_file= */ nullptr, boot_image_offset, info_high, &boot_image_other_patches_);
6635 }
6636 
NewBootImageMethodPatch(MethodReference target_method,const PcRelativePatchInfo * info_high)6637 CodeGeneratorRISCV64::PcRelativePatchInfo* CodeGeneratorRISCV64::NewBootImageMethodPatch(
6638     MethodReference target_method, const PcRelativePatchInfo* info_high) {
6639   return NewPcRelativePatch(
6640       target_method.dex_file, target_method.index, info_high, &boot_image_method_patches_);
6641 }
6642 
NewAppImageMethodPatch(MethodReference target_method,const PcRelativePatchInfo * info_high)6643 CodeGeneratorRISCV64::PcRelativePatchInfo* CodeGeneratorRISCV64::NewAppImageMethodPatch(
6644     MethodReference target_method, const PcRelativePatchInfo* info_high) {
6645   return NewPcRelativePatch(
6646       target_method.dex_file, target_method.index, info_high, &app_image_method_patches_);
6647 }
6648 
NewMethodBssEntryPatch(MethodReference target_method,const PcRelativePatchInfo * info_high)6649 CodeGeneratorRISCV64::PcRelativePatchInfo* CodeGeneratorRISCV64::NewMethodBssEntryPatch(
6650     MethodReference target_method, const PcRelativePatchInfo* info_high) {
6651   return NewPcRelativePatch(
6652       target_method.dex_file, target_method.index, info_high, &method_bss_entry_patches_);
6653 }
6654 
NewBootImageTypePatch(const DexFile & dex_file,dex::TypeIndex type_index,const PcRelativePatchInfo * info_high)6655 CodeGeneratorRISCV64::PcRelativePatchInfo* CodeGeneratorRISCV64::NewBootImageTypePatch(
6656     const DexFile& dex_file, dex::TypeIndex type_index, const PcRelativePatchInfo* info_high) {
6657   return NewPcRelativePatch(&dex_file, type_index.index_, info_high, &boot_image_type_patches_);
6658 }
6659 
NewAppImageTypePatch(const DexFile & dex_file,dex::TypeIndex type_index,const PcRelativePatchInfo * info_high)6660 CodeGeneratorRISCV64::PcRelativePatchInfo* CodeGeneratorRISCV64::NewAppImageTypePatch(
6661     const DexFile& dex_file, dex::TypeIndex type_index, const PcRelativePatchInfo* info_high) {
6662   return NewPcRelativePatch(&dex_file, type_index.index_, info_high, &app_image_type_patches_);
6663 }
6664 
NewBootImageJniEntrypointPatch(MethodReference target_method,const PcRelativePatchInfo * info_high)6665 CodeGeneratorRISCV64::PcRelativePatchInfo* CodeGeneratorRISCV64::NewBootImageJniEntrypointPatch(
6666     MethodReference target_method, const PcRelativePatchInfo* info_high) {
6667   return NewPcRelativePatch(
6668       target_method.dex_file, target_method.index, info_high, &boot_image_jni_entrypoint_patches_);
6669 }
6670 
NewTypeBssEntryPatch(HLoadClass * load_class,const PcRelativePatchInfo * info_high)6671 CodeGeneratorRISCV64::PcRelativePatchInfo* CodeGeneratorRISCV64::NewTypeBssEntryPatch(
6672     HLoadClass* load_class,
6673     const PcRelativePatchInfo* info_high) {
6674   const DexFile& dex_file = load_class->GetDexFile();
6675   dex::TypeIndex type_index = load_class->GetTypeIndex();
6676   ArenaDeque<PcRelativePatchInfo>* patches = nullptr;
6677   switch (load_class->GetLoadKind()) {
6678     case HLoadClass::LoadKind::kBssEntry:
6679       patches = &type_bss_entry_patches_;
6680       break;
6681     case HLoadClass::LoadKind::kBssEntryPublic:
6682       patches = &public_type_bss_entry_patches_;
6683       break;
6684     case HLoadClass::LoadKind::kBssEntryPackage:
6685       patches = &package_type_bss_entry_patches_;
6686       break;
6687     default:
6688       LOG(FATAL) << "Unexpected load kind: " << load_class->GetLoadKind();
6689       UNREACHABLE();
6690   }
6691   return NewPcRelativePatch(&dex_file, type_index.index_, info_high, patches);
6692 }
6693 
NewBootImageStringPatch(const DexFile & dex_file,dex::StringIndex string_index,const PcRelativePatchInfo * info_high)6694 CodeGeneratorRISCV64::PcRelativePatchInfo* CodeGeneratorRISCV64::NewBootImageStringPatch(
6695     const DexFile& dex_file, dex::StringIndex string_index, const PcRelativePatchInfo* info_high) {
6696   return NewPcRelativePatch(&dex_file, string_index.index_, info_high, &boot_image_string_patches_);
6697 }
6698 
NewStringBssEntryPatch(const DexFile & dex_file,dex::StringIndex string_index,const PcRelativePatchInfo * info_high)6699 CodeGeneratorRISCV64::PcRelativePatchInfo* CodeGeneratorRISCV64::NewStringBssEntryPatch(
6700     const DexFile& dex_file, dex::StringIndex string_index, const PcRelativePatchInfo* info_high) {
6701   return NewPcRelativePatch(&dex_file, string_index.index_, info_high, &string_bss_entry_patches_);
6702 }
6703 
NewPcRelativePatch(const DexFile * dex_file,uint32_t offset_or_index,const PcRelativePatchInfo * info_high,ArenaDeque<PcRelativePatchInfo> * patches)6704 CodeGeneratorRISCV64::PcRelativePatchInfo* CodeGeneratorRISCV64::NewPcRelativePatch(
6705     const DexFile* dex_file,
6706     uint32_t offset_or_index,
6707     const PcRelativePatchInfo* info_high,
6708     ArenaDeque<PcRelativePatchInfo>* patches) {
6709   patches->emplace_back(dex_file, offset_or_index, info_high);
6710   return &patches->back();
6711 }
6712 
DeduplicateUint32Literal(uint32_t value)6713 Literal* CodeGeneratorRISCV64::DeduplicateUint32Literal(uint32_t value) {
6714   return uint32_literals_.GetOrCreate(value,
6715                                       [this, value]() { return __ NewLiteral<uint32_t>(value); });
6716 }
6717 
DeduplicateUint64Literal(uint64_t value)6718 Literal* CodeGeneratorRISCV64::DeduplicateUint64Literal(uint64_t value) {
6719   return uint64_literals_.GetOrCreate(value,
6720                                       [this, value]() { return __ NewLiteral<uint64_t>(value); });
6721 }
6722 
DeduplicateBootImageAddressLiteral(uint64_t address)6723 Literal* CodeGeneratorRISCV64::DeduplicateBootImageAddressLiteral(uint64_t address) {
6724   return DeduplicateUint32Literal(dchecked_integral_cast<uint32_t>(address));
6725 }
6726 
DeduplicateJitStringLiteral(const DexFile & dex_file,dex::StringIndex string_index,Handle<mirror::String> handle)6727 Literal* CodeGeneratorRISCV64::DeduplicateJitStringLiteral(const DexFile& dex_file,
6728                                                            dex::StringIndex string_index,
6729                                                            Handle<mirror::String> handle) {
6730   ReserveJitStringRoot(StringReference(&dex_file, string_index), handle);
6731   return jit_string_patches_.GetOrCreate(
6732       StringReference(&dex_file, string_index),
6733       [this]() { return __ NewLiteral<uint32_t>(/* value= */ 0u); });
6734 }
6735 
DeduplicateJitClassLiteral(const DexFile & dex_file,dex::TypeIndex type_index,Handle<mirror::Class> handle)6736 Literal* CodeGeneratorRISCV64::DeduplicateJitClassLiteral(const DexFile& dex_file,
6737                                                           dex::TypeIndex type_index,
6738                                                           Handle<mirror::Class> handle) {
6739   ReserveJitClassRoot(TypeReference(&dex_file, type_index), handle);
6740   return jit_class_patches_.GetOrCreate(
6741       TypeReference(&dex_file, type_index),
6742       [this]() { return __ NewLiteral<uint32_t>(/* value= */ 0u); });
6743 }
6744 
PatchJitRootUse(uint8_t * code,const uint8_t * roots_data,const Literal * literal,uint64_t index_in_table) const6745 void CodeGeneratorRISCV64::PatchJitRootUse(uint8_t* code,
6746                                           const uint8_t* roots_data,
6747                                           const Literal* literal,
6748                                           uint64_t index_in_table) const {
6749   uint32_t literal_offset = GetAssembler().GetLabelLocation(literal->GetLabel());
6750   uintptr_t address =
6751       reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>);
6752   reinterpret_cast<uint32_t*>(code + literal_offset)[0] = dchecked_integral_cast<uint32_t>(address);
6753 }
6754 
EmitJitRootPatches(uint8_t * code,const uint8_t * roots_data)6755 void CodeGeneratorRISCV64::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) {
6756   for (const auto& entry : jit_string_patches_) {
6757     const StringReference& string_reference = entry.first;
6758     Literal* table_entry_literal = entry.second;
6759     uint64_t index_in_table = GetJitStringRootIndex(string_reference);
6760     PatchJitRootUse(code, roots_data, table_entry_literal, index_in_table);
6761   }
6762   for (const auto& entry : jit_class_patches_) {
6763     const TypeReference& type_reference = entry.first;
6764     Literal* table_entry_literal = entry.second;
6765     uint64_t index_in_table = GetJitClassRootIndex(type_reference);
6766     PatchJitRootUse(code, roots_data, table_entry_literal, index_in_table);
6767   }
6768 }
6769 
EmitPcRelativeAuipcPlaceholder(PcRelativePatchInfo * info_high,XRegister out)6770 void CodeGeneratorRISCV64::EmitPcRelativeAuipcPlaceholder(PcRelativePatchInfo* info_high,
6771                                                           XRegister out) {
6772   DCHECK(info_high->pc_insn_label == &info_high->label);
6773   __ Bind(&info_high->label);
6774   __ Auipc(out, /*imm20=*/ kLinkTimeOffsetPlaceholderHigh);
6775 }
6776 
EmitPcRelativeAddiPlaceholder(PcRelativePatchInfo * info_low,XRegister rd,XRegister rs1)6777 void CodeGeneratorRISCV64::EmitPcRelativeAddiPlaceholder(PcRelativePatchInfo* info_low,
6778                                                          XRegister rd,
6779                                                          XRegister rs1) {
6780   DCHECK(info_low->pc_insn_label != &info_low->label);
6781   __ Bind(&info_low->label);
6782   __ Addi(rd, rs1, /*imm12=*/ kLinkTimeOffsetPlaceholderLow);
6783 }
6784 
EmitPcRelativeLwuPlaceholder(PcRelativePatchInfo * info_low,XRegister rd,XRegister rs1)6785 void CodeGeneratorRISCV64::EmitPcRelativeLwuPlaceholder(PcRelativePatchInfo* info_low,
6786                                                         XRegister rd,
6787                                                         XRegister rs1) {
6788   DCHECK(info_low->pc_insn_label != &info_low->label);
6789   __ Bind(&info_low->label);
6790   __ Lwu(rd, rs1, /*offset=*/ kLinkTimeOffsetPlaceholderLow);
6791 }
6792 
EmitPcRelativeLdPlaceholder(PcRelativePatchInfo * info_low,XRegister rd,XRegister rs1)6793 void CodeGeneratorRISCV64::EmitPcRelativeLdPlaceholder(PcRelativePatchInfo* info_low,
6794                                                        XRegister rd,
6795                                                        XRegister rs1) {
6796   DCHECK(info_low->pc_insn_label != &info_low->label);
6797   __ Bind(&info_low->label);
6798   __ Ld(rd, rs1, /*offset=*/ kLinkTimeOffsetPlaceholderLow);
6799 }
6800 
6801 template <linker::LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)>
EmitPcRelativeLinkerPatches(const ArenaDeque<PcRelativePatchInfo> & infos,ArenaVector<linker::LinkerPatch> * linker_patches)6802 inline void CodeGeneratorRISCV64::EmitPcRelativeLinkerPatches(
6803     const ArenaDeque<PcRelativePatchInfo>& infos,
6804     ArenaVector<linker::LinkerPatch>* linker_patches) {
6805   for (const PcRelativePatchInfo& info : infos) {
6806     linker_patches->push_back(Factory(__ GetLabelLocation(&info.label),
6807                                       info.target_dex_file,
6808                                       __ GetLabelLocation(info.pc_insn_label),
6809                                       info.offset_or_index));
6810   }
6811 }
6812 
6813 template <linker::LinkerPatch (*Factory)(size_t, uint32_t, uint32_t)>
NoDexFileAdapter(size_t literal_offset,const DexFile * target_dex_file,uint32_t pc_insn_offset,uint32_t boot_image_offset)6814 linker::LinkerPatch NoDexFileAdapter(size_t literal_offset,
6815                                      const DexFile* target_dex_file,
6816                                      uint32_t pc_insn_offset,
6817                                      uint32_t boot_image_offset) {
6818   DCHECK(target_dex_file == nullptr);  // Unused for these patches, should be null.
6819   return Factory(literal_offset, pc_insn_offset, boot_image_offset);
6820 }
6821 
EmitLinkerPatches(ArenaVector<linker::LinkerPatch> * linker_patches)6822 void CodeGeneratorRISCV64::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* linker_patches) {
6823   DCHECK(linker_patches->empty());
6824   size_t size =
6825       boot_image_method_patches_.size() +
6826       app_image_method_patches_.size() +
6827       method_bss_entry_patches_.size() +
6828       boot_image_type_patches_.size() +
6829       app_image_type_patches_.size() +
6830       type_bss_entry_patches_.size() +
6831       public_type_bss_entry_patches_.size() +
6832       package_type_bss_entry_patches_.size() +
6833       boot_image_string_patches_.size() +
6834       string_bss_entry_patches_.size() +
6835       boot_image_jni_entrypoint_patches_.size() +
6836       boot_image_other_patches_.size();
6837   linker_patches->reserve(size);
6838   if (GetCompilerOptions().IsBootImage() || GetCompilerOptions().IsBootImageExtension()) {
6839     EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeMethodPatch>(
6840         boot_image_method_patches_, linker_patches);
6841     EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeTypePatch>(
6842         boot_image_type_patches_, linker_patches);
6843     EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeStringPatch>(
6844         boot_image_string_patches_, linker_patches);
6845   } else {
6846     DCHECK(boot_image_method_patches_.empty());
6847     DCHECK(boot_image_type_patches_.empty());
6848     DCHECK(boot_image_string_patches_.empty());
6849   }
6850   DCHECK_IMPLIES(!GetCompilerOptions().IsAppImage(), app_image_method_patches_.empty());
6851   DCHECK_IMPLIES(!GetCompilerOptions().IsAppImage(), app_image_type_patches_.empty());
6852   if (GetCompilerOptions().IsBootImage()) {
6853     EmitPcRelativeLinkerPatches<NoDexFileAdapter<linker::LinkerPatch::IntrinsicReferencePatch>>(
6854         boot_image_other_patches_, linker_patches);
6855   } else {
6856     EmitPcRelativeLinkerPatches<NoDexFileAdapter<linker::LinkerPatch::BootImageRelRoPatch>>(
6857         boot_image_other_patches_, linker_patches);
6858     EmitPcRelativeLinkerPatches<linker::LinkerPatch::MethodAppImageRelRoPatch>(
6859         app_image_method_patches_, linker_patches);
6860     EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeAppImageRelRoPatch>(
6861         app_image_type_patches_, linker_patches);
6862   }
6863   EmitPcRelativeLinkerPatches<linker::LinkerPatch::MethodBssEntryPatch>(
6864       method_bss_entry_patches_, linker_patches);
6865   EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeBssEntryPatch>(
6866       type_bss_entry_patches_, linker_patches);
6867   EmitPcRelativeLinkerPatches<linker::LinkerPatch::PublicTypeBssEntryPatch>(
6868       public_type_bss_entry_patches_, linker_patches);
6869   EmitPcRelativeLinkerPatches<linker::LinkerPatch::PackageTypeBssEntryPatch>(
6870       package_type_bss_entry_patches_, linker_patches);
6871   EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringBssEntryPatch>(
6872       string_bss_entry_patches_, linker_patches);
6873   EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeJniEntrypointPatch>(
6874       boot_image_jni_entrypoint_patches_, linker_patches);
6875   DCHECK_EQ(size, linker_patches->size());
6876 }
6877 
LoadTypeForBootImageIntrinsic(XRegister dest,TypeReference target_type)6878 void CodeGeneratorRISCV64::LoadTypeForBootImageIntrinsic(XRegister dest,
6879                                                          TypeReference target_type) {
6880   // Load the type the same way as for HLoadClass::LoadKind::kBootImageLinkTimePcRelative.
6881   DCHECK(GetCompilerOptions().IsBootImage() || GetCompilerOptions().IsBootImageExtension());
6882   PcRelativePatchInfo* info_high =
6883       NewBootImageTypePatch(*target_type.dex_file, target_type.TypeIndex());
6884   EmitPcRelativeAuipcPlaceholder(info_high, dest);
6885   PcRelativePatchInfo* info_low =
6886       NewBootImageTypePatch(*target_type.dex_file, target_type.TypeIndex(), info_high);
6887   EmitPcRelativeAddiPlaceholder(info_low, dest, dest);
6888 }
6889 
LoadBootImageRelRoEntry(XRegister dest,uint32_t boot_image_offset)6890 void CodeGeneratorRISCV64::LoadBootImageRelRoEntry(XRegister dest, uint32_t boot_image_offset) {
6891   PcRelativePatchInfo* info_high = NewBootImageRelRoPatch(boot_image_offset);
6892   EmitPcRelativeAuipcPlaceholder(info_high, dest);
6893   PcRelativePatchInfo* info_low = NewBootImageRelRoPatch(boot_image_offset, info_high);
6894   // Note: Boot image is in the low 4GiB and the entry is always 32-bit, so emit a 32-bit load.
6895   EmitPcRelativeLwuPlaceholder(info_low, dest, dest);
6896 }
6897 
LoadBootImageAddress(XRegister dest,uint32_t boot_image_reference)6898 void CodeGeneratorRISCV64::LoadBootImageAddress(XRegister dest, uint32_t boot_image_reference) {
6899   if (GetCompilerOptions().IsBootImage()) {
6900     PcRelativePatchInfo* info_high = NewBootImageIntrinsicPatch(boot_image_reference);
6901     EmitPcRelativeAuipcPlaceholder(info_high, dest);
6902     PcRelativePatchInfo* info_low = NewBootImageIntrinsicPatch(boot_image_reference, info_high);
6903     EmitPcRelativeAddiPlaceholder(info_low, dest, dest);
6904   } else if (GetCompilerOptions().GetCompilePic()) {
6905     LoadBootImageRelRoEntry(dest, boot_image_reference);
6906   } else {
6907     DCHECK(GetCompilerOptions().IsJitCompiler());
6908     gc::Heap* heap = Runtime::Current()->GetHeap();
6909     DCHECK(!heap->GetBootImageSpaces().empty());
6910     const uint8_t* address = heap->GetBootImageSpaces()[0]->Begin() + boot_image_reference;
6911     // Note: Boot image is in the low 4GiB (usually the low 2GiB, requiring just LUI+ADDI).
6912     // We may not have an available scratch register for `LoadConst64()` but it never
6913     // emits better code than `Li()` for 32-bit unsigned constants anyway.
6914     __ Li(dest, reinterpret_cast32<uint32_t>(address));
6915   }
6916 }
6917 
LoadIntrinsicDeclaringClass(XRegister dest,HInvoke * invoke)6918 void CodeGeneratorRISCV64::LoadIntrinsicDeclaringClass(XRegister dest, HInvoke* invoke) {
6919   DCHECK_NE(invoke->GetIntrinsic(), Intrinsics::kNone);
6920   if (GetCompilerOptions().IsBootImage()) {
6921     MethodReference target_method = invoke->GetResolvedMethodReference();
6922     dex::TypeIndex type_idx = target_method.dex_file->GetMethodId(target_method.index).class_idx_;
6923     LoadTypeForBootImageIntrinsic(dest, TypeReference(target_method.dex_file, type_idx));
6924   } else {
6925     uint32_t boot_image_offset = GetBootImageOffsetOfIntrinsicDeclaringClass(invoke);
6926     LoadBootImageAddress(dest, boot_image_offset);
6927   }
6928 }
6929 
LoadClassRootForIntrinsic(XRegister dest,ClassRoot class_root)6930 void CodeGeneratorRISCV64::LoadClassRootForIntrinsic(XRegister dest, ClassRoot class_root) {
6931   if (GetCompilerOptions().IsBootImage()) {
6932     ScopedObjectAccess soa(Thread::Current());
6933     ObjPtr<mirror::Class> klass = GetClassRoot(class_root);
6934     TypeReference target_type(&klass->GetDexFile(), klass->GetDexTypeIndex());
6935     LoadTypeForBootImageIntrinsic(dest, target_type);
6936   } else {
6937     uint32_t boot_image_offset = GetBootImageOffset(class_root);
6938     LoadBootImageAddress(dest, boot_image_offset);
6939   }
6940 }
6941 
LoadMethod(MethodLoadKind load_kind,Location temp,HInvoke * invoke)6942 void CodeGeneratorRISCV64::LoadMethod(MethodLoadKind load_kind, Location temp, HInvoke* invoke) {
6943   switch (load_kind) {
6944     case MethodLoadKind::kBootImageLinkTimePcRelative: {
6945       DCHECK(GetCompilerOptions().IsBootImage() || GetCompilerOptions().IsBootImageExtension());
6946       CodeGeneratorRISCV64::PcRelativePatchInfo* info_high =
6947           NewBootImageMethodPatch(invoke->GetResolvedMethodReference());
6948       EmitPcRelativeAuipcPlaceholder(info_high, temp.AsRegister<XRegister>());
6949       CodeGeneratorRISCV64::PcRelativePatchInfo* info_low =
6950           NewBootImageMethodPatch(invoke->GetResolvedMethodReference(), info_high);
6951       EmitPcRelativeAddiPlaceholder(
6952           info_low, temp.AsRegister<XRegister>(), temp.AsRegister<XRegister>());
6953       break;
6954     }
6955     case MethodLoadKind::kBootImageRelRo: {
6956       uint32_t boot_image_offset = GetBootImageOffset(invoke);
6957       LoadBootImageRelRoEntry(temp.AsRegister<XRegister>(), boot_image_offset);
6958       break;
6959     }
6960     case MethodLoadKind::kAppImageRelRo: {
6961       DCHECK(GetCompilerOptions().IsAppImage());
6962       PcRelativePatchInfo* info_high =
6963           NewAppImageMethodPatch(invoke->GetResolvedMethodReference());
6964       EmitPcRelativeAuipcPlaceholder(info_high, temp.AsRegister<XRegister>());
6965       PcRelativePatchInfo* info_low =
6966           NewAppImageMethodPatch(invoke->GetResolvedMethodReference(), info_high);
6967       EmitPcRelativeLwuPlaceholder(
6968           info_low, temp.AsRegister<XRegister>(), temp.AsRegister<XRegister>());
6969       break;
6970     }
6971     case MethodLoadKind::kBssEntry: {
6972       PcRelativePatchInfo* info_high = NewMethodBssEntryPatch(invoke->GetMethodReference());
6973       EmitPcRelativeAuipcPlaceholder(info_high, temp.AsRegister<XRegister>());
6974       PcRelativePatchInfo* info_low =
6975           NewMethodBssEntryPatch(invoke->GetMethodReference(), info_high);
6976       EmitPcRelativeLdPlaceholder(
6977           info_low, temp.AsRegister<XRegister>(), temp.AsRegister<XRegister>());
6978       break;
6979     }
6980     case MethodLoadKind::kJitDirectAddress: {
6981       __ LoadConst64(temp.AsRegister<XRegister>(),
6982                      reinterpret_cast<uint64_t>(invoke->GetResolvedMethod()));
6983       break;
6984     }
6985     case MethodLoadKind::kRuntimeCall: {
6986       // Test situation, don't do anything.
6987       break;
6988     }
6989     default: {
6990       LOG(FATAL) << "Load kind should have already been handled " << load_kind;
6991       UNREACHABLE();
6992     }
6993   }
6994 }
6995 
GenerateStaticOrDirectCall(HInvokeStaticOrDirect * invoke,Location temp,SlowPathCode * slow_path)6996 void CodeGeneratorRISCV64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke,
6997                                                       Location temp,
6998                                                       SlowPathCode* slow_path) {
6999   // All registers are assumed to be correctly set up per the calling convention.
7000   Location callee_method = temp;  // For all kinds except kRecursive, callee will be in temp.
7001 
7002   switch (invoke->GetMethodLoadKind()) {
7003     case MethodLoadKind::kStringInit: {
7004       // temp = thread->string_init_entrypoint
7005       uint32_t offset =
7006           GetThreadOffset<kRiscv64PointerSize>(invoke->GetStringInitEntryPoint()).Int32Value();
7007       __ Loadd(temp.AsRegister<XRegister>(), TR, offset);
7008       break;
7009     }
7010     case MethodLoadKind::kRecursive:
7011       callee_method = invoke->GetLocations()->InAt(invoke->GetCurrentMethodIndex());
7012       break;
7013     case MethodLoadKind::kRuntimeCall:
7014       GenerateInvokeStaticOrDirectRuntimeCall(invoke, temp, slow_path);
7015       return;  // No code pointer retrieval; the runtime performs the call directly.
7016     case MethodLoadKind::kBootImageLinkTimePcRelative:
7017       DCHECK(GetCompilerOptions().IsBootImage() || GetCompilerOptions().IsBootImageExtension());
7018       if (invoke->GetCodePtrLocation() == CodePtrLocation::kCallCriticalNative) {
7019         // Do not materialize the method pointer, load directly the entrypoint.
7020         CodeGeneratorRISCV64::PcRelativePatchInfo* info_high =
7021             NewBootImageJniEntrypointPatch(invoke->GetResolvedMethodReference());
7022         EmitPcRelativeAuipcPlaceholder(info_high, RA);
7023         CodeGeneratorRISCV64::PcRelativePatchInfo* info_low =
7024             NewBootImageJniEntrypointPatch(invoke->GetResolvedMethodReference(), info_high);
7025         EmitPcRelativeLdPlaceholder(info_low, RA, RA);
7026         break;
7027       }
7028       FALLTHROUGH_INTENDED;
7029     default:
7030       LoadMethod(invoke->GetMethodLoadKind(), temp, invoke);
7031       break;
7032   }
7033 
7034   switch (invoke->GetCodePtrLocation()) {
7035     case CodePtrLocation::kCallSelf:
7036       DCHECK(!GetGraph()->HasShouldDeoptimizeFlag());
7037       __ Jal(&frame_entry_label_);
7038       RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
7039       break;
7040     case CodePtrLocation::kCallArtMethod:
7041       // RA = callee_method->entry_point_from_quick_compiled_code_;
7042       __ Loadd(RA,
7043                callee_method.AsRegister<XRegister>(),
7044                ArtMethod::EntryPointFromQuickCompiledCodeOffset(kRiscv64PointerSize).Int32Value());
7045       // RA()
7046       __ Jalr(RA);
7047       RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
7048       break;
7049     case CodePtrLocation::kCallCriticalNative: {
7050       size_t out_frame_size =
7051           PrepareCriticalNativeCall<CriticalNativeCallingConventionVisitorRiscv64,
7052                                     kNativeStackAlignment,
7053                                     GetCriticalNativeDirectCallFrameSize>(invoke);
7054       if (invoke->GetMethodLoadKind() == MethodLoadKind::kBootImageLinkTimePcRelative) {
7055         // Entrypoint is already loaded in RA.
7056       } else {
7057         // RA = callee_method->ptr_sized_fields_.data_;  // EntryPointFromJni
7058         MemberOffset offset = ArtMethod::EntryPointFromJniOffset(kRiscv64PointerSize);
7059         __ Loadd(RA, callee_method.AsRegister<XRegister>(), offset.Int32Value());
7060       }
7061       __ Jalr(RA);
7062       RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
7063       // The result is returned the same way in native ABI and managed ABI. No result conversion is
7064       // needed, see comments in `Riscv64JniCallingConvention::RequiresSmallResultTypeExtension()`.
7065       if (out_frame_size != 0u) {
7066         DecreaseFrame(out_frame_size);
7067       }
7068       break;
7069     }
7070   }
7071 
7072   DCHECK(!IsLeafMethod());
7073 }
7074 
MaybeGenerateInlineCacheCheck(HInstruction * instruction,XRegister klass)7075 void CodeGeneratorRISCV64::MaybeGenerateInlineCacheCheck(HInstruction* instruction,
7076                                                          XRegister klass) {
7077   if (ProfilingInfoBuilder::IsInlineCacheUseful(instruction->AsInvoke(), this)) {
7078     ProfilingInfo* info = GetGraph()->GetProfilingInfo();
7079     DCHECK(info != nullptr);
7080     InlineCache* cache = ProfilingInfoBuilder::GetInlineCache(
7081         info, GetCompilerOptions(), instruction->AsInvoke());
7082     if (cache != nullptr) {
7083       uint64_t address = reinterpret_cast64<uint64_t>(cache);
7084       Riscv64Label done;
7085       // The `art_quick_update_inline_cache` expects the inline cache in T5.
7086       XRegister ic_reg = T5;
7087       ScratchRegisterScope srs(GetAssembler());
7088       DCHECK_EQ(srs.AvailableXRegisters(), 2u);
7089       srs.ExcludeXRegister(ic_reg);
7090       DCHECK_EQ(srs.AvailableXRegisters(), 1u);
7091       __ LoadConst64(ic_reg, address);
7092       {
7093         ScratchRegisterScope srs2(GetAssembler());
7094         XRegister tmp = srs2.AllocateXRegister();
7095         __ Loadd(tmp, ic_reg, InlineCache::ClassesOffset().Int32Value());
7096         // Fast path for a monomorphic cache.
7097         __ Beq(klass, tmp, &done);
7098       }
7099       InvokeRuntime(kQuickUpdateInlineCache, instruction, instruction->GetDexPc());
7100       __ Bind(&done);
7101     } else {
7102       // This is unexpected, but we don't guarantee stable compilation across
7103       // JIT runs so just warn about it.
7104       ScopedObjectAccess soa(Thread::Current());
7105       LOG(WARNING) << "Missing inline cache for " << GetGraph()->GetArtMethod()->PrettyMethod();
7106     }
7107   }
7108 }
7109 
GenerateVirtualCall(HInvokeVirtual * invoke,Location temp_location,SlowPathCode * slow_path)7110 void CodeGeneratorRISCV64::GenerateVirtualCall(HInvokeVirtual* invoke,
7111                                                Location temp_location,
7112                                                SlowPathCode* slow_path) {
7113   // Use the calling convention instead of the location of the receiver, as
7114   // intrinsics may have put the receiver in a different register. In the intrinsics
7115   // slow path, the arguments have been moved to the right place, so here we are
7116   // guaranteed that the receiver is the first register of the calling convention.
7117   InvokeDexCallingConvention calling_convention;
7118   XRegister receiver = calling_convention.GetRegisterAt(0);
7119   XRegister temp = temp_location.AsRegister<XRegister>();
7120   MemberOffset method_offset =
7121       mirror::Class::EmbeddedVTableEntryOffset(invoke->GetVTableIndex(), kRiscv64PointerSize);
7122   MemberOffset class_offset = mirror::Object::ClassOffset();
7123   Offset entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kRiscv64PointerSize);
7124 
7125   // temp = object->GetClass();
7126   __ Loadwu(temp, receiver, class_offset.Int32Value());
7127   MaybeRecordImplicitNullCheck(invoke);
7128   // Instead of simply (possibly) unpoisoning `temp` here, we should
7129   // emit a read barrier for the previous class reference load.
7130   // However this is not required in practice, as this is an
7131   // intermediate/temporary reference and because the current
7132   // concurrent copying collector keeps the from-space memory
7133   // intact/accessible until the end of the marking phase (the
7134   // concurrent copying collector may not in the future).
7135   MaybeUnpoisonHeapReference(temp);
7136 
7137   // If we're compiling baseline, update the inline cache.
7138   MaybeGenerateInlineCacheCheck(invoke, temp);
7139 
7140   // temp = temp->GetMethodAt(method_offset);
7141   __ Loadd(temp, temp, method_offset.Int32Value());
7142   // RA = temp->GetEntryPoint();
7143   __ Loadd(RA, temp, entry_point.Int32Value());
7144   // RA();
7145   __ Jalr(RA);
7146   RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
7147 }
7148 
MoveFromReturnRegister(Location trg,DataType::Type type)7149 void CodeGeneratorRISCV64::MoveFromReturnRegister(Location trg, DataType::Type type) {
7150   if (!trg.IsValid()) {
7151     DCHECK_EQ(type, DataType::Type::kVoid);
7152     return;
7153   }
7154 
7155   DCHECK_NE(type, DataType::Type::kVoid);
7156 
7157   if (DataType::IsIntegralType(type) || type == DataType::Type::kReference) {
7158     XRegister trg_reg = trg.AsRegister<XRegister>();
7159     XRegister res_reg = Riscv64ReturnLocation(type).AsRegister<XRegister>();
7160     if (trg_reg != res_reg) {
7161       __ Mv(trg_reg, res_reg);
7162     }
7163   } else {
7164     FRegister trg_reg = trg.AsFpuRegister<FRegister>();
7165     FRegister res_reg = Riscv64ReturnLocation(type).AsFpuRegister<FRegister>();
7166     if (trg_reg != res_reg) {
7167       __ FMvD(trg_reg, res_reg);  // 64-bit move is OK also for `float`.
7168     }
7169   }
7170 }
7171 
PoisonHeapReference(XRegister reg)7172 void CodeGeneratorRISCV64::PoisonHeapReference(XRegister reg) {
7173   __ Sub(reg, Zero, reg);  // Negate the ref.
7174   __ ZextW(reg, reg);      // Zero-extend the 32-bit ref.
7175 }
7176 
UnpoisonHeapReference(XRegister reg)7177 void CodeGeneratorRISCV64::UnpoisonHeapReference(XRegister reg) {
7178   __ Sub(reg, Zero, reg);  // Negate the ref.
7179   __ ZextW(reg, reg);      // Zero-extend the 32-bit ref.
7180 }
7181 
MaybePoisonHeapReference(XRegister reg)7182 void CodeGeneratorRISCV64::MaybePoisonHeapReference(XRegister reg) {
7183   if (kPoisonHeapReferences) {
7184     PoisonHeapReference(reg);
7185   }
7186 }
7187 
MaybeUnpoisonHeapReference(XRegister reg)7188 void CodeGeneratorRISCV64::MaybeUnpoisonHeapReference(XRegister reg) {
7189   if (kPoisonHeapReferences) {
7190     UnpoisonHeapReference(reg);
7191   }
7192 }
7193 
SwapLocations(Location loc1,Location loc2,DataType::Type type)7194 void CodeGeneratorRISCV64::SwapLocations(Location loc1, Location loc2, DataType::Type type) {
7195   DCHECK(!loc1.IsConstant());
7196   DCHECK(!loc2.IsConstant());
7197 
7198   if (loc1.Equals(loc2)) {
7199     return;
7200   }
7201 
7202   bool is_slot1 = loc1.IsStackSlot() || loc1.IsDoubleStackSlot();
7203   bool is_slot2 = loc2.IsStackSlot() || loc2.IsDoubleStackSlot();
7204   bool is_simd1 = loc1.IsSIMDStackSlot();
7205   bool is_simd2 = loc2.IsSIMDStackSlot();
7206   bool is_fp_reg1 = loc1.IsFpuRegister();
7207   bool is_fp_reg2 = loc2.IsFpuRegister();
7208 
7209   if ((is_slot1 != is_slot2) ||
7210       (loc2.IsRegister() && loc1.IsRegister()) ||
7211       (is_fp_reg2 && is_fp_reg1)) {
7212     if ((is_fp_reg2 && is_fp_reg1) && GetGraph()->HasSIMD()) {
7213       LOG(FATAL) << "Unsupported";
7214       UNREACHABLE();
7215     }
7216     ScratchRegisterScope srs(GetAssembler());
7217     Location tmp = (is_fp_reg2 || is_fp_reg1)
7218         ? Location::FpuRegisterLocation(srs.AllocateFRegister())
7219         : Location::RegisterLocation(srs.AllocateXRegister());
7220     MoveLocation(tmp, loc1, type);
7221     MoveLocation(loc1, loc2, type);
7222     MoveLocation(loc2, tmp, type);
7223   } else if (is_slot1 && is_slot2) {
7224     move_resolver_.Exchange(loc1.GetStackIndex(), loc2.GetStackIndex(), loc1.IsDoubleStackSlot());
7225   } else if (is_simd1 && is_simd2) {
7226     // TODO(riscv64): Add VECTOR/SIMD later.
7227     UNIMPLEMENTED(FATAL) << "Vector extension is unsupported";
7228   } else if ((is_fp_reg1 && is_simd2) || (is_fp_reg2 && is_simd1)) {
7229     // TODO(riscv64): Add VECTOR/SIMD later.
7230     UNIMPLEMENTED(FATAL) << "Vector extension is unsupported";
7231   } else {
7232     LOG(FATAL) << "Unimplemented swap between locations " << loc1 << " and " << loc2;
7233   }
7234 }
7235 
7236 }  // namespace riscv64
7237 }  // namespace art
7238