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