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