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->IsRor());
2335
2336 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
2337 DataType::Type type = instruction->GetResultType();
2338 switch (type) {
2339 case DataType::Type::kInt32:
2340 case DataType::Type::kInt64: {
2341 locations->SetInAt(0, Location::RequiresRegister());
2342 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
2343 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2344 break;
2345 }
2346 default:
2347 LOG(FATAL) << "Unexpected shift type " << type;
2348 UNREACHABLE();
2349 }
2350 }
2351
HandleShift(HBinaryOperation * instruction)2352 void InstructionCodeGeneratorRISCV64::HandleShift(HBinaryOperation* instruction) {
2353 DCHECK(instruction->IsShl() ||
2354 instruction->IsShr() ||
2355 instruction->IsUShr() ||
2356 instruction->IsRor());
2357 LocationSummary* locations = instruction->GetLocations();
2358 DataType::Type type = instruction->GetType();
2359
2360 switch (type) {
2361 case DataType::Type::kInt32:
2362 case DataType::Type::kInt64: {
2363 XRegister rd = locations->Out().AsRegister<XRegister>();
2364 XRegister rs1 = locations->InAt(0).AsRegister<XRegister>();
2365 Location rs2_location = locations->InAt(1);
2366
2367 if (rs2_location.IsConstant()) {
2368 int64_t imm = CodeGenerator::GetInt64ValueOf(rs2_location.GetConstant());
2369 uint32_t shamt =
2370 imm & (type == DataType::Type::kInt32 ? kMaxIntShiftDistance : kMaxLongShiftDistance);
2371
2372 if (shamt == 0) {
2373 if (rd != rs1) {
2374 __ Mv(rd, rs1);
2375 }
2376 } else if (type == DataType::Type::kInt32) {
2377 if (instruction->IsShl()) {
2378 __ Slliw(rd, rs1, shamt);
2379 } else if (instruction->IsShr()) {
2380 __ Sraiw(rd, rs1, shamt);
2381 } else if (instruction->IsUShr()) {
2382 __ Srliw(rd, rs1, shamt);
2383 } else {
2384 DCHECK(instruction->IsRor());
2385 __ Roriw(rd, rs1, shamt);
2386 }
2387 } else {
2388 if (instruction->IsShl()) {
2389 __ Slli(rd, rs1, shamt);
2390 } else if (instruction->IsShr()) {
2391 __ Srai(rd, rs1, shamt);
2392 } else if (instruction->IsUShr()) {
2393 __ Srli(rd, rs1, shamt);
2394 } else {
2395 DCHECK(instruction->IsRor());
2396 __ Rori(rd, rs1, shamt);
2397 }
2398 }
2399 } else {
2400 XRegister rs2 = rs2_location.AsRegister<XRegister>();
2401 if (type == DataType::Type::kInt32) {
2402 if (instruction->IsShl()) {
2403 __ Sllw(rd, rs1, rs2);
2404 } else if (instruction->IsShr()) {
2405 __ Sraw(rd, rs1, rs2);
2406 } else if (instruction->IsUShr()) {
2407 __ Srlw(rd, rs1, rs2);
2408 } else {
2409 DCHECK(instruction->IsRor());
2410 __ Rorw(rd, rs1, rs2);
2411 }
2412 } else {
2413 if (instruction->IsShl()) {
2414 __ Sll(rd, rs1, rs2);
2415 } else if (instruction->IsShr()) {
2416 __ Sra(rd, rs1, rs2);
2417 } else if (instruction->IsUShr()) {
2418 __ Srl(rd, rs1, rs2);
2419 } else {
2420 DCHECK(instruction->IsRor());
2421 __ Ror(rd, rs1, rs2);
2422 }
2423 }
2424 }
2425 break;
2426 }
2427 default:
2428 LOG(FATAL) << "Unexpected shift operation type " << type;
2429 }
2430 }
2431
MaybeMarkGCCard(XRegister object,XRegister value,bool value_can_be_null)2432 void CodeGeneratorRISCV64::MaybeMarkGCCard(XRegister object,
2433 XRegister value,
2434 bool value_can_be_null) {
2435 Riscv64Label done;
2436 if (value_can_be_null) {
2437 __ Beqz(value, &done);
2438 }
2439 MarkGCCard(object);
2440 __ Bind(&done);
2441 }
2442
MarkGCCard(XRegister object)2443 void CodeGeneratorRISCV64::MarkGCCard(XRegister object) {
2444 ScratchRegisterScope srs(GetAssembler());
2445 XRegister card = srs.AllocateXRegister();
2446 XRegister temp = srs.AllocateXRegister();
2447 // Load the address of the card table into `card`.
2448 __ Loadd(card, TR, Thread::CardTableOffset<kRiscv64PointerSize>().Int32Value());
2449
2450 // Calculate the address of the card corresponding to `object`.
2451 __ Srli(temp, object, gc::accounting::CardTable::kCardShift);
2452 __ Add(temp, card, temp);
2453 // Write the `art::gc::accounting::CardTable::kCardDirty` value into the
2454 // `object`'s card.
2455 //
2456 // Register `card` contains the address of the card table. Note that the card
2457 // table's base is biased during its creation so that it always starts at an
2458 // address whose least-significant byte is equal to `kCardDirty` (see
2459 // art::gc::accounting::CardTable::Create). Therefore the SB instruction
2460 // below writes the `kCardDirty` (byte) value into the `object`'s card
2461 // (located at `card + object >> kCardShift`).
2462 //
2463 // This dual use of the value in register `card` (1. to calculate the location
2464 // of the card to mark; and 2. to load the `kCardDirty` value) saves a load
2465 // (no need to explicitly load `kCardDirty` as an immediate value).
2466 __ Sb(card, temp, 0); // No scratch register left for `Storeb()`.
2467 }
2468
CheckGCCardIsValid(XRegister object)2469 void CodeGeneratorRISCV64::CheckGCCardIsValid(XRegister object) {
2470 Riscv64Label done;
2471 ScratchRegisterScope srs(GetAssembler());
2472 XRegister card = srs.AllocateXRegister();
2473 XRegister temp = srs.AllocateXRegister();
2474 // Load the address of the card table into `card`.
2475 __ Loadd(card, TR, Thread::CardTableOffset<kRiscv64PointerSize>().Int32Value());
2476
2477 // Calculate the address of the card corresponding to `object`.
2478 __ Srli(temp, object, gc::accounting::CardTable::kCardShift);
2479 __ Add(temp, card, temp);
2480 // assert (!clean || !self->is_gc_marking)
2481 __ Lb(temp, temp, 0);
2482 static_assert(gc::accounting::CardTable::kCardClean == 0);
2483 __ Bnez(temp, &done);
2484 __ Loadw(temp, TR, Thread::IsGcMarkingOffset<kRiscv64PointerSize>().Int32Value());
2485 __ Beqz(temp, &done);
2486 __ Unimp();
2487 __ Bind(&done);
2488 }
2489
HandleFieldSet(HInstruction * instruction)2490 void LocationsBuilderRISCV64::HandleFieldSet(HInstruction* instruction) {
2491 LocationSummary* locations =
2492 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
2493 locations->SetInAt(0, Location::RequiresRegister());
2494 locations->SetInAt(1, ValueLocationForStore(instruction->InputAt(1)));
2495 }
2496
HandleFieldSet(HInstruction * instruction,const FieldInfo & field_info,bool value_can_be_null,WriteBarrierKind write_barrier_kind)2497 void InstructionCodeGeneratorRISCV64::HandleFieldSet(HInstruction* instruction,
2498 const FieldInfo& field_info,
2499 bool value_can_be_null,
2500 WriteBarrierKind write_barrier_kind) {
2501 DataType::Type type = field_info.GetFieldType();
2502 LocationSummary* locations = instruction->GetLocations();
2503 XRegister obj = locations->InAt(0).AsRegister<XRegister>();
2504 Location value = locations->InAt(1);
2505 DCHECK_IMPLIES(value.IsConstant(), IsZeroBitPattern(value.GetConstant()));
2506 bool is_volatile = field_info.IsVolatile();
2507 uint32_t offset = field_info.GetFieldOffset().Uint32Value();
2508
2509 if (is_volatile) {
2510 StoreSeqCst(value, obj, offset, type, instruction);
2511 } else {
2512 Store(value, obj, offset, type);
2513 codegen_->MaybeRecordImplicitNullCheck(instruction);
2514 }
2515
2516 bool needs_write_barrier =
2517 codegen_->StoreNeedsWriteBarrier(type, instruction->InputAt(1), write_barrier_kind);
2518 if (needs_write_barrier) {
2519 if (value.IsConstant()) {
2520 DCHECK_EQ(write_barrier_kind, WriteBarrierKind::kEmitBeingReliedOn);
2521 codegen_->MarkGCCard(obj);
2522 } else {
2523 codegen_->MaybeMarkGCCard(
2524 obj,
2525 value.AsRegister<XRegister>(),
2526 value_can_be_null && write_barrier_kind == WriteBarrierKind::kEmitNotBeingReliedOn);
2527 }
2528 } else if (codegen_->ShouldCheckGCCard(type, instruction->InputAt(1), write_barrier_kind)) {
2529 codegen_->CheckGCCardIsValid(obj);
2530 }
2531 }
2532
HandleFieldGet(HInstruction * instruction)2533 void LocationsBuilderRISCV64::HandleFieldGet(HInstruction* instruction) {
2534 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
2535
2536 bool object_field_get_with_read_barrier =
2537 (instruction->GetType() == DataType::Type::kReference) && codegen_->EmitReadBarrier();
2538 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
2539 instruction,
2540 object_field_get_with_read_barrier
2541 ? LocationSummary::kCallOnSlowPath
2542 : LocationSummary::kNoCall);
2543
2544 // Input for object receiver.
2545 locations->SetInAt(0, Location::RequiresRegister());
2546
2547 if (DataType::IsFloatingPointType(instruction->GetType())) {
2548 locations->SetOut(Location::RequiresFpuRegister());
2549 } else {
2550 // The output overlaps for an object field get when read barriers
2551 // are enabled: we do not want the load to overwrite the object's
2552 // location, as we need it to emit the read barrier.
2553 locations->SetOut(
2554 Location::RequiresRegister(),
2555 object_field_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap);
2556 }
2557
2558 if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
2559 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
2560 // We need a temporary register for the read barrier marking slow
2561 // path in CodeGeneratorRISCV64::GenerateFieldLoadWithBakerReadBarrier.
2562 locations->AddTemp(Location::RequiresRegister());
2563 }
2564 }
2565
HandleFieldGet(HInstruction * instruction,const FieldInfo & field_info)2566 void InstructionCodeGeneratorRISCV64::HandleFieldGet(HInstruction* instruction,
2567 const FieldInfo& field_info) {
2568 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
2569 DCHECK_EQ(DataType::Size(field_info.GetFieldType()), DataType::Size(instruction->GetType()));
2570 DataType::Type type = instruction->GetType();
2571 LocationSummary* locations = instruction->GetLocations();
2572 Location obj_loc = locations->InAt(0);
2573 XRegister obj = obj_loc.AsRegister<XRegister>();
2574 Location dst_loc = locations->Out();
2575 bool is_volatile = field_info.IsVolatile();
2576 uint32_t offset = field_info.GetFieldOffset().Uint32Value();
2577
2578 if (is_volatile) {
2579 codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
2580 }
2581
2582 if (type == DataType::Type::kReference && codegen_->EmitBakerReadBarrier()) {
2583 // /* HeapReference<Object> */ dst = *(obj + offset)
2584 Location temp_loc = locations->GetTemp(0);
2585 // Note that a potential implicit null check is handled in this
2586 // CodeGeneratorRISCV64::GenerateFieldLoadWithBakerReadBarrier call.
2587 codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction,
2588 dst_loc,
2589 obj,
2590 offset,
2591 temp_loc,
2592 /* needs_null_check= */ true);
2593 } else {
2594 Load(dst_loc, obj, offset, type);
2595 codegen_->MaybeRecordImplicitNullCheck(instruction);
2596 }
2597
2598 if (is_volatile) {
2599 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
2600 }
2601
2602 if (type == DataType::Type::kReference && !codegen_->EmitBakerReadBarrier()) {
2603 // If read barriers are enabled, emit read barriers other than
2604 // Baker's using a slow path (and also unpoison the loaded
2605 // reference, if heap poisoning is enabled).
2606 codegen_->MaybeGenerateReadBarrierSlow(instruction, dst_loc, dst_loc, obj_loc, offset);
2607 }
2608 }
2609
GenerateMethodEntryExitHook(HInstruction * instruction)2610 void InstructionCodeGeneratorRISCV64::GenerateMethodEntryExitHook(HInstruction* instruction) {
2611 SlowPathCodeRISCV64* slow_path =
2612 new (codegen_->GetScopedAllocator()) MethodEntryExitHooksSlowPathRISCV64(instruction);
2613 codegen_->AddSlowPath(slow_path);
2614
2615 ScratchRegisterScope temps(GetAssembler());
2616 XRegister tmp = temps.AllocateXRegister();
2617
2618 if (instruction->IsMethodExitHook()) {
2619 // Check if we are required to check if the caller needs a deoptimization. Strictly speaking it
2620 // would be sufficient to check if CheckCallerForDeopt bit is set. Though it is faster to check
2621 // if it is just non-zero. kCHA bit isn't used in debuggable runtimes as cha optimization is
2622 // disabled in debuggable runtime. The other bit is used when this method itself requires a
2623 // deoptimization due to redefinition. So it is safe to just check for non-zero value here.
2624 __ Loadwu(tmp, SP, codegen_->GetStackOffsetOfShouldDeoptimizeFlag());
2625 __ Bnez(tmp, slow_path->GetEntryLabel());
2626 }
2627
2628 uint64_t hook_offset = instruction->IsMethodExitHook() ?
2629 instrumentation::Instrumentation::HaveMethodExitListenersOffset().SizeValue() :
2630 instrumentation::Instrumentation::HaveMethodEntryListenersOffset().SizeValue();
2631 auto [base_hook_address, hook_imm12] = SplitJitAddress(
2632 reinterpret_cast64<uint64_t>(Runtime::Current()->GetInstrumentation()) + hook_offset);
2633 __ LoadConst64(tmp, base_hook_address);
2634 __ Lbu(tmp, tmp, hook_imm12);
2635 // Check if there are any method entry / exit listeners. If no, continue.
2636 __ Beqz(tmp, slow_path->GetExitLabel());
2637 // Check if there are any slow (jvmti / trace with thread cpu time) method entry / exit listeners.
2638 // If yes, just take the slow path.
2639 static_assert(instrumentation::Instrumentation::kFastTraceListeners == 1u);
2640 __ Addi(tmp, tmp, -1);
2641 __ Bnez(tmp, slow_path->GetEntryLabel());
2642
2643 // Check if there is place in the buffer to store a new entry, if no, take the slow path.
2644 int32_t trace_buffer_index_offset =
2645 Thread::TraceBufferIndexOffset<kRiscv64PointerSize>().Int32Value();
2646 __ Loadd(tmp, TR, trace_buffer_index_offset);
2647 __ Addi(tmp, tmp, -dchecked_integral_cast<int32_t>(kNumEntriesForWallClock));
2648 __ Bltz(tmp, slow_path->GetEntryLabel());
2649
2650 // Update the index in the `Thread`.
2651 __ Stored(tmp, TR, trace_buffer_index_offset);
2652
2653 // Allocate second core scratch register. We can no longer use `Stored()`
2654 // and similar macro instructions because there is no core scratch register left.
2655 XRegister tmp2 = temps.AllocateXRegister();
2656
2657 // Calculate the entry address in the buffer.
2658 // /*addr*/ tmp = TR->GetMethodTraceBuffer() + sizeof(void*) * /*index*/ tmp;
2659 __ Loadd(tmp2, TR, Thread::TraceBufferPtrOffset<kRiscv64PointerSize>().SizeValue());
2660 __ Sh3Add(tmp, tmp, tmp2);
2661
2662 // Record method pointer and trace action.
2663 __ Ld(tmp2, SP, 0);
2664 // Use last two bits to encode trace method action. For MethodEntry it is 0
2665 // so no need to set the bits since they are 0 already.
2666 DCHECK_GE(ArtMethod::Alignment(kRuntimePointerSize), static_cast<size_t>(4));
2667 static_assert(enum_cast<int32_t>(TraceAction::kTraceMethodEnter) == 0);
2668 static_assert(enum_cast<int32_t>(TraceAction::kTraceMethodExit) == 1);
2669 if (instruction->IsMethodExitHook()) {
2670 __ Ori(tmp2, tmp2, enum_cast<int32_t>(TraceAction::kTraceMethodExit));
2671 }
2672 static_assert(IsInt<12>(kMethodOffsetInBytes)); // No free scratch register for `Stored()`.
2673 __ Sd(tmp2, tmp, kMethodOffsetInBytes);
2674
2675 // Record the timestamp.
2676 __ RdTime(tmp2);
2677 static_assert(IsInt<12>(kTimestampOffsetInBytes)); // No free scratch register for `Stored()`.
2678 __ Sd(tmp2, tmp, kTimestampOffsetInBytes);
2679
2680 __ Bind(slow_path->GetExitLabel());
2681 }
2682
VisitAbove(HAbove * instruction)2683 void LocationsBuilderRISCV64::VisitAbove(HAbove* instruction) {
2684 HandleCondition(instruction);
2685 }
2686
VisitAbove(HAbove * instruction)2687 void InstructionCodeGeneratorRISCV64::VisitAbove(HAbove* instruction) {
2688 HandleCondition(instruction);
2689 }
2690
VisitAboveOrEqual(HAboveOrEqual * instruction)2691 void LocationsBuilderRISCV64::VisitAboveOrEqual(HAboveOrEqual* instruction) {
2692 HandleCondition(instruction);
2693 }
2694
VisitAboveOrEqual(HAboveOrEqual * instruction)2695 void InstructionCodeGeneratorRISCV64::VisitAboveOrEqual(HAboveOrEqual* instruction) {
2696 HandleCondition(instruction);
2697 }
2698
VisitAbs(HAbs * abs)2699 void LocationsBuilderRISCV64::VisitAbs(HAbs* abs) {
2700 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(abs);
2701 switch (abs->GetResultType()) {
2702 case DataType::Type::kInt32:
2703 case DataType::Type::kInt64:
2704 locations->SetInAt(0, Location::RequiresRegister());
2705 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2706 break;
2707 case DataType::Type::kFloat32:
2708 case DataType::Type::kFloat64:
2709 locations->SetInAt(0, Location::RequiresFpuRegister());
2710 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2711 break;
2712 default:
2713 LOG(FATAL) << "Unexpected abs type " << abs->GetResultType();
2714 }
2715 }
2716
VisitAbs(HAbs * abs)2717 void InstructionCodeGeneratorRISCV64::VisitAbs(HAbs* abs) {
2718 LocationSummary* locations = abs->GetLocations();
2719 switch (abs->GetResultType()) {
2720 case DataType::Type::kInt32: {
2721 XRegister in = locations->InAt(0).AsRegister<XRegister>();
2722 XRegister out = locations->Out().AsRegister<XRegister>();
2723 ScratchRegisterScope srs(GetAssembler());
2724 XRegister tmp = srs.AllocateXRegister();
2725 __ Sraiw(tmp, in, 31);
2726 __ Xor(out, in, tmp);
2727 __ Subw(out, out, tmp);
2728 break;
2729 }
2730 case DataType::Type::kInt64: {
2731 XRegister in = locations->InAt(0).AsRegister<XRegister>();
2732 XRegister out = locations->Out().AsRegister<XRegister>();
2733 ScratchRegisterScope srs(GetAssembler());
2734 XRegister tmp = srs.AllocateXRegister();
2735 __ Srai(tmp, in, 63);
2736 __ Xor(out, in, tmp);
2737 __ Sub(out, out, tmp);
2738 break;
2739 }
2740 case DataType::Type::kFloat32:
2741 case DataType::Type::kFloat64:
2742 FAbs(locations->Out().AsFpuRegister<FRegister>(),
2743 locations->InAt(0).AsFpuRegister<FRegister>(),
2744 abs->GetResultType());
2745 break;
2746 default:
2747 LOG(FATAL) << "Unexpected abs type " << abs->GetResultType();
2748 }
2749 }
2750
VisitAdd(HAdd * instruction)2751 void LocationsBuilderRISCV64::VisitAdd(HAdd* instruction) {
2752 HandleBinaryOp(instruction);
2753 }
2754
VisitAdd(HAdd * instruction)2755 void InstructionCodeGeneratorRISCV64::VisitAdd(HAdd* instruction) {
2756 HandleBinaryOp(instruction);
2757 }
2758
VisitAnd(HAnd * instruction)2759 void LocationsBuilderRISCV64::VisitAnd(HAnd* instruction) {
2760 HandleBinaryOp(instruction);
2761 }
2762
VisitAnd(HAnd * instruction)2763 void InstructionCodeGeneratorRISCV64::VisitAnd(HAnd* instruction) {
2764 HandleBinaryOp(instruction);
2765 }
2766
VisitArrayGet(HArrayGet * instruction)2767 void LocationsBuilderRISCV64::VisitArrayGet(HArrayGet* instruction) {
2768 DataType::Type type = instruction->GetType();
2769 bool object_array_get_with_read_barrier =
2770 (type == DataType::Type::kReference) && codegen_->EmitReadBarrier();
2771 LocationSummary* locations = new (GetGraph()->GetAllocator())
2772 LocationSummary(instruction,
2773 object_array_get_with_read_barrier ? LocationSummary::kCallOnSlowPath :
2774 LocationSummary::kNoCall);
2775 locations->SetInAt(0, Location::RequiresRegister());
2776 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
2777 if (DataType::IsFloatingPointType(type)) {
2778 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2779 } else {
2780 // The output overlaps in the case of an object array get with
2781 // read barriers enabled: we do not want the move to overwrite the
2782 // array's location, as we need it to emit the read barrier.
2783 locations->SetOut(
2784 Location::RequiresRegister(),
2785 object_array_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap);
2786 }
2787 if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
2788 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
2789 // We need a temporary register for the read barrier marking slow
2790 // path in CodeGeneratorRISCV64::GenerateArrayLoadWithBakerReadBarrier.
2791 locations->AddTemp(Location::RequiresRegister());
2792 }
2793 }
2794
VisitArrayGet(HArrayGet * instruction)2795 void InstructionCodeGeneratorRISCV64::VisitArrayGet(HArrayGet* instruction) {
2796 LocationSummary* locations = instruction->GetLocations();
2797 Location obj_loc = locations->InAt(0);
2798 XRegister obj = obj_loc.AsRegister<XRegister>();
2799 Location out_loc = locations->Out();
2800 Location index = locations->InAt(1);
2801 uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
2802 DataType::Type type = instruction->GetType();
2803 const bool maybe_compressed_char_at =
2804 mirror::kUseStringCompression && instruction->IsStringCharAt();
2805
2806 Riscv64Label string_char_at_done;
2807 if (maybe_compressed_char_at) {
2808 DCHECK_EQ(type, DataType::Type::kUint16);
2809 uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
2810 Riscv64Label uncompressed_load;
2811 {
2812 ScratchRegisterScope srs(GetAssembler());
2813 XRegister tmp = srs.AllocateXRegister();
2814 __ Loadw(tmp, obj, count_offset);
2815 codegen_->MaybeRecordImplicitNullCheck(instruction);
2816 __ Andi(tmp, tmp, 0x1);
2817 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
2818 "Expecting 0=compressed, 1=uncompressed");
2819 __ Bnez(tmp, &uncompressed_load);
2820 }
2821 XRegister out = out_loc.AsRegister<XRegister>();
2822 if (index.IsConstant()) {
2823 int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue();
2824 __ Loadbu(out, obj, data_offset + const_index);
2825 } else {
2826 __ Add(out, obj, index.AsRegister<XRegister>());
2827 __ Loadbu(out, out, data_offset);
2828 }
2829 __ J(&string_char_at_done);
2830 __ Bind(&uncompressed_load);
2831 }
2832
2833 if (type == DataType::Type::kReference && codegen_->EmitBakerReadBarrier()) {
2834 static_assert(
2835 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
2836 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
2837 // /* HeapReference<Object> */ out =
2838 // *(obj + data_offset + index * sizeof(HeapReference<Object>))
2839 // Note that a potential implicit null check could be handled in these
2840 // `CodeGeneratorRISCV64::Generate{Array,Field}LoadWithBakerReadBarrier()` calls
2841 // but we currently do not support implicit null checks on `HArrayGet`.
2842 DCHECK(!instruction->CanDoImplicitNullCheckOn(instruction->InputAt(0)));
2843 Location temp = locations->GetTemp(0);
2844 if (index.IsConstant()) {
2845 // Array load with a constant index can be treated as a field load.
2846 static constexpr size_t shift = DataType::SizeShift(DataType::Type::kReference);
2847 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << shift) + data_offset;
2848 codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction,
2849 out_loc,
2850 obj,
2851 offset,
2852 temp,
2853 /* needs_null_check= */ false);
2854 } else {
2855 codegen_->GenerateArrayLoadWithBakerReadBarrier(instruction,
2856 out_loc,
2857 obj,
2858 data_offset,
2859 index,
2860 temp,
2861 /* needs_null_check= */ false);
2862 }
2863 } else if (index.IsConstant()) {
2864 int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue();
2865 int32_t offset = data_offset + (const_index << DataType::SizeShift(type));
2866 Load(out_loc, obj, offset, type);
2867 if (!maybe_compressed_char_at) {
2868 codegen_->MaybeRecordImplicitNullCheck(instruction);
2869 }
2870 if (type == DataType::Type::kReference) {
2871 DCHECK(!codegen_->EmitBakerReadBarrier());
2872 // If read barriers are enabled, emit read barriers other than Baker's using
2873 // a slow path (and also unpoison the loaded reference, if heap poisoning is enabled).
2874 codegen_->MaybeGenerateReadBarrierSlow(instruction, out_loc, out_loc, obj_loc, offset);
2875 }
2876 } else {
2877 ScratchRegisterScope srs(GetAssembler());
2878 XRegister tmp = srs.AllocateXRegister();
2879 ShNAdd(tmp, index.AsRegister<XRegister>(), obj, type);
2880 Load(out_loc, tmp, data_offset, type);
2881 if (!maybe_compressed_char_at) {
2882 codegen_->MaybeRecordImplicitNullCheck(instruction);
2883 }
2884 if (type == DataType::Type::kReference) {
2885 DCHECK(!codegen_->EmitBakerReadBarrier());
2886 // If read barriers are enabled, emit read barriers other than Baker's using
2887 // a slow path (and also unpoison the loaded reference, if heap poisoning is enabled).
2888 codegen_->MaybeGenerateReadBarrierSlow(
2889 instruction, out_loc, out_loc, obj_loc, data_offset, index);
2890 }
2891 }
2892
2893 if (maybe_compressed_char_at) {
2894 __ Bind(&string_char_at_done);
2895 }
2896 }
2897
VisitArrayLength(HArrayLength * instruction)2898 void LocationsBuilderRISCV64::VisitArrayLength(HArrayLength* instruction) {
2899 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
2900 locations->SetInAt(0, Location::RequiresRegister());
2901 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2902 }
2903
VisitArrayLength(HArrayLength * instruction)2904 void InstructionCodeGeneratorRISCV64::VisitArrayLength(HArrayLength* instruction) {
2905 LocationSummary* locations = instruction->GetLocations();
2906 uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction);
2907 XRegister obj = locations->InAt(0).AsRegister<XRegister>();
2908 XRegister out = locations->Out().AsRegister<XRegister>();
2909 __ Loadwu(out, obj, offset); // Unsigned for string length; does not matter for other arrays.
2910 codegen_->MaybeRecordImplicitNullCheck(instruction);
2911 // Mask out compression flag from String's array length.
2912 if (mirror::kUseStringCompression && instruction->IsStringLength()) {
2913 __ Srli(out, out, 1u);
2914 }
2915 }
2916
VisitArraySet(HArraySet * instruction)2917 void LocationsBuilderRISCV64::VisitArraySet(HArraySet* instruction) {
2918 bool needs_type_check = instruction->NeedsTypeCheck();
2919 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
2920 instruction,
2921 needs_type_check ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall);
2922 locations->SetInAt(0, Location::RequiresRegister());
2923 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
2924 locations->SetInAt(2, ValueLocationForStore(instruction->GetValue()));
2925 if (kPoisonHeapReferences &&
2926 instruction->GetComponentType() == DataType::Type::kReference &&
2927 !locations->InAt(1).IsConstant() &&
2928 !locations->InAt(2).IsConstant()) {
2929 locations->AddTemp(Location::RequiresRegister());
2930 }
2931 }
2932
VisitArraySet(HArraySet * instruction)2933 void InstructionCodeGeneratorRISCV64::VisitArraySet(HArraySet* instruction) {
2934 LocationSummary* locations = instruction->GetLocations();
2935 XRegister array = locations->InAt(0).AsRegister<XRegister>();
2936 Location index = locations->InAt(1);
2937 Location value = locations->InAt(2);
2938 DataType::Type value_type = instruction->GetComponentType();
2939 bool needs_type_check = instruction->NeedsTypeCheck();
2940 const WriteBarrierKind write_barrier_kind = instruction->GetWriteBarrierKind();
2941 bool needs_write_barrier =
2942 codegen_->StoreNeedsWriteBarrier(value_type, instruction->GetValue(), write_barrier_kind);
2943 size_t data_offset = mirror::Array::DataOffset(DataType::Size(value_type)).Uint32Value();
2944 SlowPathCodeRISCV64* slow_path = nullptr;
2945
2946 if (needs_write_barrier) {
2947 DCHECK_EQ(value_type, DataType::Type::kReference);
2948 DCHECK_IMPLIES(value.IsConstant(), value.GetConstant()->IsArithmeticZero());
2949 const bool storing_constant_zero = value.IsConstant();
2950 // The WriteBarrierKind::kEmitNotBeingReliedOn case is able to skip the write barrier when its
2951 // value is null (without an extra CompareAndBranchIfZero since we already checked if the
2952 // value is null for the type check).
2953 bool skip_marking_gc_card = false;
2954 Riscv64Label skip_writing_card;
2955 if (!storing_constant_zero) {
2956 Riscv64Label do_store;
2957
2958 bool can_value_be_null = instruction->GetValueCanBeNull();
2959 skip_marking_gc_card =
2960 can_value_be_null && write_barrier_kind == WriteBarrierKind::kEmitNotBeingReliedOn;
2961 if (can_value_be_null) {
2962 if (skip_marking_gc_card) {
2963 __ Beqz(value.AsRegister<XRegister>(), &skip_writing_card);
2964 } else {
2965 __ Beqz(value.AsRegister<XRegister>(), &do_store);
2966 }
2967 }
2968
2969 if (needs_type_check) {
2970 slow_path = new (codegen_->GetScopedAllocator()) ArraySetSlowPathRISCV64(instruction);
2971 codegen_->AddSlowPath(slow_path);
2972
2973 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
2974 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
2975 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
2976
2977 ScratchRegisterScope srs(GetAssembler());
2978 XRegister temp1 = srs.AllocateXRegister();
2979 XRegister temp2 = srs.AllocateXRegister();
2980
2981 // Note that when read barriers are enabled, the type checks are performed
2982 // without read barriers. This is fine, even in the case where a class object
2983 // is in the from-space after the flip, as a comparison involving such a type
2984 // would not produce a false positive; it may of course produce a false
2985 // negative, in which case we would take the ArraySet slow path.
2986
2987 // /* HeapReference<Class> */ temp1 = array->klass_
2988 __ Loadwu(temp1, array, class_offset);
2989 codegen_->MaybeRecordImplicitNullCheck(instruction);
2990 codegen_->MaybeUnpoisonHeapReference(temp1);
2991
2992 // /* HeapReference<Class> */ temp2 = temp1->component_type_
2993 __ Loadwu(temp2, temp1, component_offset);
2994 // /* HeapReference<Class> */ temp1 = value->klass_
2995 __ Loadwu(temp1, value.AsRegister<XRegister>(), class_offset);
2996 // If heap poisoning is enabled, no need to unpoison `temp1`
2997 // nor `temp2`, as we are comparing two poisoned references.
2998 if (instruction->StaticTypeOfArrayIsObjectArray()) {
2999 Riscv64Label do_put;
3000 __ Beq(temp1, temp2, &do_put);
3001 // If heap poisoning is enabled, the `temp2` reference has
3002 // not been unpoisoned yet; unpoison it now.
3003 codegen_->MaybeUnpoisonHeapReference(temp2);
3004
3005 // /* HeapReference<Class> */ temp1 = temp2->super_class_
3006 __ Loadwu(temp1, temp2, super_offset);
3007 // If heap poisoning is enabled, no need to unpoison
3008 // `temp1`, as we are comparing against null below.
3009 __ Bnez(temp1, slow_path->GetEntryLabel());
3010 __ Bind(&do_put);
3011 } else {
3012 __ Bne(temp1, temp2, slow_path->GetEntryLabel());
3013 }
3014 }
3015
3016 if (can_value_be_null && !skip_marking_gc_card) {
3017 DCHECK(do_store.IsLinked());
3018 __ Bind(&do_store);
3019 }
3020 }
3021
3022 DCHECK_NE(write_barrier_kind, WriteBarrierKind::kDontEmit);
3023 DCHECK_IMPLIES(storing_constant_zero,
3024 write_barrier_kind == WriteBarrierKind::kEmitBeingReliedOn);
3025 codegen_->MarkGCCard(array);
3026
3027 if (skip_marking_gc_card) {
3028 // Note that we don't check that the GC card is valid as it can be correctly clean.
3029 DCHECK(skip_writing_card.IsLinked());
3030 __ Bind(&skip_writing_card);
3031 }
3032 } else if (codegen_->ShouldCheckGCCard(value_type, instruction->GetValue(), write_barrier_kind)) {
3033 codegen_->CheckGCCardIsValid(array);
3034 }
3035
3036 if (index.IsConstant()) {
3037 int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue();
3038 int32_t offset = data_offset + (const_index << DataType::SizeShift(value_type));
3039 Store(value, array, offset, value_type);
3040 } else {
3041 ScratchRegisterScope srs(GetAssembler());
3042 // Heap poisoning needs two scratch registers in `Store()`, except for null constants.
3043 XRegister tmp =
3044 (kPoisonHeapReferences && value_type == DataType::Type::kReference && !value.IsConstant())
3045 ? locations->GetTemp(0).AsRegister<XRegister>()
3046 : srs.AllocateXRegister();
3047 ShNAdd(tmp, index.AsRegister<XRegister>(), array, value_type);
3048 Store(value, tmp, data_offset, value_type);
3049 }
3050 // There must be no instructions between the `Store()` and the `MaybeRecordImplicitNullCheck()`.
3051 // We can avoid this if the type check makes the null check unconditionally.
3052 DCHECK_IMPLIES(needs_type_check, needs_write_barrier);
3053 if (!(needs_type_check && !instruction->GetValueCanBeNull())) {
3054 codegen_->MaybeRecordImplicitNullCheck(instruction);
3055 }
3056
3057 if (slow_path != nullptr) {
3058 __ Bind(slow_path->GetExitLabel());
3059 }
3060 }
3061
VisitBelow(HBelow * instruction)3062 void LocationsBuilderRISCV64::VisitBelow(HBelow* instruction) {
3063 HandleCondition(instruction);
3064 }
3065
VisitBelow(HBelow * instruction)3066 void InstructionCodeGeneratorRISCV64::VisitBelow(HBelow* instruction) {
3067 HandleCondition(instruction);
3068 }
3069
VisitBelowOrEqual(HBelowOrEqual * instruction)3070 void LocationsBuilderRISCV64::VisitBelowOrEqual(HBelowOrEqual* instruction) {
3071 HandleCondition(instruction);
3072 }
3073
VisitBelowOrEqual(HBelowOrEqual * instruction)3074 void InstructionCodeGeneratorRISCV64::VisitBelowOrEqual(HBelowOrEqual* instruction) {
3075 HandleCondition(instruction);
3076 }
3077
VisitBooleanNot(HBooleanNot * instruction)3078 void LocationsBuilderRISCV64::VisitBooleanNot(HBooleanNot* instruction) {
3079 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
3080 locations->SetInAt(0, Location::RequiresRegister());
3081 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3082 }
3083
VisitBooleanNot(HBooleanNot * instruction)3084 void InstructionCodeGeneratorRISCV64::VisitBooleanNot(HBooleanNot* instruction) {
3085 LocationSummary* locations = instruction->GetLocations();
3086 __ Xori(locations->Out().AsRegister<XRegister>(), locations->InAt(0).AsRegister<XRegister>(), 1);
3087 }
3088
VisitBoundsCheck(HBoundsCheck * instruction)3089 void LocationsBuilderRISCV64::VisitBoundsCheck(HBoundsCheck* instruction) {
3090 RegisterSet caller_saves = RegisterSet::Empty();
3091 InvokeRuntimeCallingConvention calling_convention;
3092 caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
3093 caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
3094 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction, caller_saves);
3095
3096 HInstruction* index = instruction->InputAt(0);
3097 HInstruction* length = instruction->InputAt(1);
3098
3099 bool const_index = false;
3100 bool const_length = false;
3101
3102 if (length->IsConstant()) {
3103 if (index->IsConstant()) {
3104 const_index = true;
3105 const_length = true;
3106 } else {
3107 int32_t length_value = length->AsIntConstant()->GetValue();
3108 if (length_value == 0 || length_value == 1) {
3109 const_length = true;
3110 }
3111 }
3112 } else if (index->IsConstant()) {
3113 int32_t index_value = index->AsIntConstant()->GetValue();
3114 if (index_value <= 0) {
3115 const_index = true;
3116 }
3117 }
3118
3119 locations->SetInAt(
3120 0,
3121 const_index ? Location::ConstantLocation(index) : Location::RequiresRegister());
3122 locations->SetInAt(
3123 1,
3124 const_length ? Location::ConstantLocation(length) : Location::RequiresRegister());
3125 }
3126
VisitBoundsCheck(HBoundsCheck * instruction)3127 void InstructionCodeGeneratorRISCV64::VisitBoundsCheck(HBoundsCheck* instruction) {
3128 LocationSummary* locations = instruction->GetLocations();
3129 Location index_loc = locations->InAt(0);
3130 Location length_loc = locations->InAt(1);
3131
3132 if (length_loc.IsConstant()) {
3133 int32_t length = length_loc.GetConstant()->AsIntConstant()->GetValue();
3134 if (index_loc.IsConstant()) {
3135 int32_t index = index_loc.GetConstant()->AsIntConstant()->GetValue();
3136 if (index < 0 || index >= length) {
3137 BoundsCheckSlowPathRISCV64* slow_path =
3138 new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathRISCV64(instruction);
3139 codegen_->AddSlowPath(slow_path);
3140 __ J(slow_path->GetEntryLabel());
3141 } else {
3142 // Nothing to be done.
3143 }
3144 return;
3145 }
3146
3147 BoundsCheckSlowPathRISCV64* slow_path =
3148 new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathRISCV64(instruction);
3149 codegen_->AddSlowPath(slow_path);
3150 XRegister index = index_loc.AsRegister<XRegister>();
3151 if (length == 0) {
3152 __ J(slow_path->GetEntryLabel());
3153 } else {
3154 DCHECK_EQ(length, 1);
3155 __ Bnez(index, slow_path->GetEntryLabel());
3156 }
3157 } else {
3158 XRegister length = length_loc.AsRegister<XRegister>();
3159 BoundsCheckSlowPathRISCV64* slow_path =
3160 new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathRISCV64(instruction);
3161 codegen_->AddSlowPath(slow_path);
3162 if (index_loc.IsConstant()) {
3163 int32_t index = index_loc.GetConstant()->AsIntConstant()->GetValue();
3164 if (index < 0) {
3165 __ J(slow_path->GetEntryLabel());
3166 } else {
3167 DCHECK_EQ(index, 0);
3168 __ Blez(length, slow_path->GetEntryLabel());
3169 }
3170 } else {
3171 XRegister index = index_loc.AsRegister<XRegister>();
3172 __ Bgeu(index, length, slow_path->GetEntryLabel());
3173 }
3174 }
3175 }
3176
VisitBoundType(HBoundType * instruction)3177 void LocationsBuilderRISCV64::VisitBoundType([[maybe_unused]] HBoundType* instruction) {
3178 // Nothing to do, this should be removed during prepare for register allocator.
3179 LOG(FATAL) << "Unreachable";
3180 }
3181
VisitBoundType(HBoundType * instruction)3182 void InstructionCodeGeneratorRISCV64::VisitBoundType([[maybe_unused]] HBoundType* instruction) {
3183 // Nothing to do, this should be removed during prepare for register allocator.
3184 LOG(FATAL) << "Unreachable";
3185 }
3186
3187 // Temp is used for read barrier.
NumberOfInstanceOfTemps(bool emit_read_barrier,TypeCheckKind type_check_kind)3188 static size_t NumberOfInstanceOfTemps(bool emit_read_barrier, TypeCheckKind type_check_kind) {
3189 if (emit_read_barrier &&
3190 (kUseBakerReadBarrier ||
3191 type_check_kind == TypeCheckKind::kAbstractClassCheck ||
3192 type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
3193 type_check_kind == TypeCheckKind::kArrayObjectCheck)) {
3194 return 1;
3195 }
3196 return 0;
3197 }
3198
3199 // Interface case has 3 temps, one for holding the number of interfaces, one for the current
3200 // interface pointer, one for loading the current interface.
3201 // 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)3202 static size_t NumberOfCheckCastTemps(bool emit_read_barrier, TypeCheckKind type_check_kind) {
3203 if (type_check_kind == TypeCheckKind::kInterfaceCheck) {
3204 return 3;
3205 }
3206 return 1 + NumberOfInstanceOfTemps(emit_read_barrier, type_check_kind);
3207 }
3208
VisitCheckCast(HCheckCast * instruction)3209 void LocationsBuilderRISCV64::VisitCheckCast(HCheckCast* instruction) {
3210 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
3211 LocationSummary::CallKind call_kind = codegen_->GetCheckCastCallKind(instruction);
3212 LocationSummary* locations =
3213 new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
3214 locations->SetInAt(0, Location::RequiresRegister());
3215 if (type_check_kind == TypeCheckKind::kBitstringCheck) {
3216 locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)));
3217 locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)));
3218 locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)));
3219 } else {
3220 locations->SetInAt(1, Location::RequiresRegister());
3221 }
3222 locations->AddRegisterTemps(NumberOfCheckCastTemps(codegen_->EmitReadBarrier(), type_check_kind));
3223 }
3224
VisitCheckCast(HCheckCast * instruction)3225 void InstructionCodeGeneratorRISCV64::VisitCheckCast(HCheckCast* instruction) {
3226 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
3227 LocationSummary* locations = instruction->GetLocations();
3228 Location obj_loc = locations->InAt(0);
3229 XRegister obj = obj_loc.AsRegister<XRegister>();
3230 Location cls = (type_check_kind == TypeCheckKind::kBitstringCheck)
3231 ? Location::NoLocation()
3232 : locations->InAt(1);
3233 Location temp_loc = locations->GetTemp(0);
3234 XRegister temp = temp_loc.AsRegister<XRegister>();
3235 const size_t num_temps = NumberOfCheckCastTemps(codegen_->EmitReadBarrier(), type_check_kind);
3236 DCHECK_GE(num_temps, 1u);
3237 DCHECK_LE(num_temps, 3u);
3238 Location maybe_temp2_loc = (num_temps >= 2) ? locations->GetTemp(1) : Location::NoLocation();
3239 Location maybe_temp3_loc = (num_temps >= 3) ? locations->GetTemp(2) : Location::NoLocation();
3240 const uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
3241 const uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
3242 const uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
3243 const uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
3244 const uint32_t iftable_offset = mirror::Class::IfTableOffset().Uint32Value();
3245 const uint32_t array_length_offset = mirror::Array::LengthOffset().Uint32Value();
3246 const uint32_t object_array_data_offset =
3247 mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value();
3248 Riscv64Label done;
3249
3250 bool is_type_check_slow_path_fatal = codegen_->IsTypeCheckSlowPathFatal(instruction);
3251 SlowPathCodeRISCV64* slow_path =
3252 new (codegen_->GetScopedAllocator()) TypeCheckSlowPathRISCV64(
3253 instruction, is_type_check_slow_path_fatal);
3254 codegen_->AddSlowPath(slow_path);
3255
3256 // Avoid this check if we know `obj` is not null.
3257 if (instruction->MustDoNullCheck()) {
3258 __ Beqz(obj, &done);
3259 }
3260
3261 switch (type_check_kind) {
3262 case TypeCheckKind::kExactCheck:
3263 case TypeCheckKind::kArrayCheck: {
3264 // /* HeapReference<Class> */ temp = obj->klass_
3265 GenerateReferenceLoadTwoRegisters(instruction,
3266 temp_loc,
3267 obj_loc,
3268 class_offset,
3269 maybe_temp2_loc,
3270 kWithoutReadBarrier);
3271 // Jump to slow path for throwing the exception or doing a
3272 // more involved array check.
3273 __ Bne(temp, cls.AsRegister<XRegister>(), slow_path->GetEntryLabel());
3274 break;
3275 }
3276
3277 case TypeCheckKind::kAbstractClassCheck: {
3278 // /* HeapReference<Class> */ temp = obj->klass_
3279 GenerateReferenceLoadTwoRegisters(instruction,
3280 temp_loc,
3281 obj_loc,
3282 class_offset,
3283 maybe_temp2_loc,
3284 kWithoutReadBarrier);
3285 // If the class is abstract, we eagerly fetch the super class of the
3286 // object to avoid doing a comparison we know will fail.
3287 Riscv64Label loop;
3288 __ Bind(&loop);
3289 // /* HeapReference<Class> */ temp = temp->super_class_
3290 GenerateReferenceLoadOneRegister(instruction,
3291 temp_loc,
3292 super_offset,
3293 maybe_temp2_loc,
3294 kWithoutReadBarrier);
3295 // If the class reference currently in `temp` is null, jump to the slow path to throw the
3296 // exception.
3297 __ Beqz(temp, slow_path->GetEntryLabel());
3298 // Otherwise, compare the classes.
3299 __ Bne(temp, cls.AsRegister<XRegister>(), &loop);
3300 break;
3301 }
3302
3303 case TypeCheckKind::kClassHierarchyCheck: {
3304 // /* HeapReference<Class> */ temp = obj->klass_
3305 GenerateReferenceLoadTwoRegisters(instruction,
3306 temp_loc,
3307 obj_loc,
3308 class_offset,
3309 maybe_temp2_loc,
3310 kWithoutReadBarrier);
3311 // Walk over the class hierarchy to find a match.
3312 Riscv64Label loop;
3313 __ Bind(&loop);
3314 __ Beq(temp, cls.AsRegister<XRegister>(), &done);
3315 // /* HeapReference<Class> */ temp = temp->super_class_
3316 GenerateReferenceLoadOneRegister(instruction,
3317 temp_loc,
3318 super_offset,
3319 maybe_temp2_loc,
3320 kWithoutReadBarrier);
3321 // If the class reference currently in `temp` is null, jump to the slow path to throw the
3322 // exception. Otherwise, jump to the beginning of the loop.
3323 __ Bnez(temp, &loop);
3324 __ J(slow_path->GetEntryLabel());
3325 break;
3326 }
3327
3328 case TypeCheckKind::kArrayObjectCheck: {
3329 // /* HeapReference<Class> */ temp = obj->klass_
3330 GenerateReferenceLoadTwoRegisters(instruction,
3331 temp_loc,
3332 obj_loc,
3333 class_offset,
3334 maybe_temp2_loc,
3335 kWithoutReadBarrier);
3336 // Do an exact check.
3337 __ Beq(temp, cls.AsRegister<XRegister>(), &done);
3338 // Otherwise, we need to check that the object's class is a non-primitive array.
3339 // /* HeapReference<Class> */ temp = temp->component_type_
3340 GenerateReferenceLoadOneRegister(instruction,
3341 temp_loc,
3342 component_offset,
3343 maybe_temp2_loc,
3344 kWithoutReadBarrier);
3345 // If the component type is null, jump to the slow path to throw the exception.
3346 __ Beqz(temp, slow_path->GetEntryLabel());
3347 // Otherwise, the object is indeed an array, further check that this component
3348 // type is not a primitive type.
3349 __ Loadhu(temp, temp, primitive_offset);
3350 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
3351 __ Bnez(temp, slow_path->GetEntryLabel());
3352 break;
3353 }
3354
3355 case TypeCheckKind::kUnresolvedCheck:
3356 // We always go into the type check slow path for the unresolved check case.
3357 // We cannot directly call the CheckCast runtime entry point
3358 // without resorting to a type checking slow path here (i.e. by
3359 // calling InvokeRuntime directly), as it would require to
3360 // assign fixed registers for the inputs of this HInstanceOf
3361 // instruction (following the runtime calling convention), which
3362 // might be cluttered by the potential first read barrier
3363 // emission at the beginning of this method.
3364 __ J(slow_path->GetEntryLabel());
3365 break;
3366
3367 case TypeCheckKind::kInterfaceCheck: {
3368 // Avoid read barriers to improve performance of the fast path. We can not get false
3369 // positives by doing this. False negatives are handled by the slow path.
3370 // /* HeapReference<Class> */ temp = obj->klass_
3371 GenerateReferenceLoadTwoRegisters(instruction,
3372 temp_loc,
3373 obj_loc,
3374 class_offset,
3375 maybe_temp2_loc,
3376 kWithoutReadBarrier);
3377 // /* HeapReference<Class> */ temp = temp->iftable_
3378 GenerateReferenceLoadOneRegister(instruction,
3379 temp_loc,
3380 iftable_offset,
3381 maybe_temp2_loc,
3382 kWithoutReadBarrier);
3383 XRegister temp2 = maybe_temp2_loc.AsRegister<XRegister>();
3384 XRegister temp3 = maybe_temp3_loc.AsRegister<XRegister>();
3385 // Load the size of the `IfTable`. The `Class::iftable_` is never null.
3386 __ Loadw(temp2, temp, array_length_offset);
3387 // Loop through the iftable and check if any class matches.
3388 Riscv64Label loop;
3389 __ Bind(&loop);
3390 __ Beqz(temp2, slow_path->GetEntryLabel());
3391 __ Lwu(temp3, temp, object_array_data_offset);
3392 codegen_->MaybeUnpoisonHeapReference(temp3);
3393 // Go to next interface.
3394 __ Addi(temp, temp, 2 * kHeapReferenceSize);
3395 __ Addi(temp2, temp2, -2);
3396 // Compare the classes and continue the loop if they do not match.
3397 __ Bne(temp3, cls.AsRegister<XRegister>(), &loop);
3398 break;
3399 }
3400
3401 case TypeCheckKind::kBitstringCheck: {
3402 // /* HeapReference<Class> */ temp = obj->klass_
3403 GenerateReferenceLoadTwoRegisters(instruction,
3404 temp_loc,
3405 obj_loc,
3406 class_offset,
3407 maybe_temp2_loc,
3408 kWithoutReadBarrier);
3409
3410 GenerateBitstringTypeCheckCompare(instruction, temp);
3411 __ Bnez(temp, slow_path->GetEntryLabel());
3412 break;
3413 }
3414 }
3415
3416 __ Bind(&done);
3417 __ Bind(slow_path->GetExitLabel());
3418 }
3419
VisitClassTableGet(HClassTableGet * instruction)3420 void LocationsBuilderRISCV64::VisitClassTableGet(HClassTableGet* instruction) {
3421 LocationSummary* locations =
3422 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
3423 locations->SetInAt(0, Location::RequiresRegister());
3424 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3425 }
3426
VisitClassTableGet(HClassTableGet * instruction)3427 void InstructionCodeGeneratorRISCV64::VisitClassTableGet(HClassTableGet* instruction) {
3428 LocationSummary* locations = instruction->GetLocations();
3429 XRegister in = locations->InAt(0).AsRegister<XRegister>();
3430 XRegister out = locations->Out().AsRegister<XRegister>();
3431 if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) {
3432 MemberOffset method_offset =
3433 mirror::Class::EmbeddedVTableEntryOffset(instruction->GetIndex(), kRiscv64PointerSize);
3434 __ Loadd(out, in, method_offset.SizeValue());
3435 } else {
3436 uint32_t method_offset = dchecked_integral_cast<uint32_t>(
3437 ImTable::OffsetOfElement(instruction->GetIndex(), kRiscv64PointerSize));
3438 __ Loadd(out, in, mirror::Class::ImtPtrOffset(kRiscv64PointerSize).Uint32Value());
3439 __ Loadd(out, out, method_offset);
3440 }
3441 }
3442
GetExceptionTlsOffset()3443 static int32_t GetExceptionTlsOffset() {
3444 return Thread::ExceptionOffset<kRiscv64PointerSize>().Int32Value();
3445 }
3446
VisitClearException(HClearException * instruction)3447 void LocationsBuilderRISCV64::VisitClearException(HClearException* instruction) {
3448 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
3449 }
3450
VisitClearException(HClearException * instruction)3451 void InstructionCodeGeneratorRISCV64::VisitClearException(
3452 [[maybe_unused]] HClearException* instruction) {
3453 __ Stored(Zero, TR, GetExceptionTlsOffset());
3454 }
3455
VisitClinitCheck(HClinitCheck * instruction)3456 void LocationsBuilderRISCV64::VisitClinitCheck(HClinitCheck* instruction) {
3457 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
3458 instruction, LocationSummary::kCallOnSlowPath);
3459 locations->SetInAt(0, Location::RequiresRegister());
3460 if (instruction->HasUses()) {
3461 locations->SetOut(Location::SameAsFirstInput());
3462 }
3463 // Rely on the type initialization to save everything we need.
3464 locations->SetCustomSlowPathCallerSaves(OneRegInReferenceOutSaveEverythingCallerSaves());
3465 }
3466
VisitClinitCheck(HClinitCheck * instruction)3467 void InstructionCodeGeneratorRISCV64::VisitClinitCheck(HClinitCheck* instruction) {
3468 // We assume the class is not null.
3469 SlowPathCodeRISCV64* slow_path = new (codegen_->GetScopedAllocator()) LoadClassSlowPathRISCV64(
3470 instruction->GetLoadClass(), instruction);
3471 codegen_->AddSlowPath(slow_path);
3472 GenerateClassInitializationCheck(slow_path,
3473 instruction->GetLocations()->InAt(0).AsRegister<XRegister>());
3474 }
3475
VisitCompare(HCompare * instruction)3476 void LocationsBuilderRISCV64::VisitCompare(HCompare* instruction) {
3477 DataType::Type in_type = instruction->InputAt(0)->GetType();
3478
3479 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
3480
3481 switch (in_type) {
3482 case DataType::Type::kBool:
3483 case DataType::Type::kUint8:
3484 case DataType::Type::kInt8:
3485 case DataType::Type::kUint16:
3486 case DataType::Type::kInt16:
3487 case DataType::Type::kInt32:
3488 case DataType::Type::kInt64:
3489 locations->SetInAt(0, Location::RequiresRegister());
3490 locations->SetInAt(1, RegisterOrZeroBitPatternLocation(instruction->InputAt(1)));
3491 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3492 break;
3493
3494 case DataType::Type::kFloat32:
3495 case DataType::Type::kFloat64:
3496 locations->SetInAt(0, Location::RequiresFpuRegister());
3497 locations->SetInAt(1, Location::RequiresFpuRegister());
3498 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3499 break;
3500
3501 default:
3502 LOG(FATAL) << "Unexpected type for compare operation " << in_type;
3503 UNREACHABLE();
3504 }
3505 }
3506
VisitCompare(HCompare * instruction)3507 void InstructionCodeGeneratorRISCV64::VisitCompare(HCompare* instruction) {
3508 LocationSummary* locations = instruction->GetLocations();
3509 XRegister result = locations->Out().AsRegister<XRegister>();
3510 DataType::Type in_type = instruction->InputAt(0)->GetType();
3511
3512 // 0 if: left == right
3513 // 1 if: left > right
3514 // -1 if: left < right
3515 switch (in_type) {
3516 case DataType::Type::kBool:
3517 case DataType::Type::kUint8:
3518 case DataType::Type::kInt8:
3519 case DataType::Type::kUint16:
3520 case DataType::Type::kInt16:
3521 case DataType::Type::kInt32:
3522 case DataType::Type::kInt64: {
3523 XRegister left = locations->InAt(0).AsRegister<XRegister>();
3524 XRegister right = InputXRegisterOrZero(locations->InAt(1));
3525 ScratchRegisterScope srs(GetAssembler());
3526 XRegister tmp = srs.AllocateXRegister();
3527 __ Slt(tmp, left, right);
3528 __ Slt(result, right, left);
3529 __ Sub(result, result, tmp);
3530 break;
3531 }
3532
3533 case DataType::Type::kFloat32:
3534 case DataType::Type::kFloat64: {
3535 FRegister left = locations->InAt(0).AsFpuRegister<FRegister>();
3536 FRegister right = locations->InAt(1).AsFpuRegister<FRegister>();
3537 ScratchRegisterScope srs(GetAssembler());
3538 XRegister tmp = srs.AllocateXRegister();
3539 if (instruction->IsGtBias()) {
3540 // ((FLE l,r) ^ 1) - (FLT l,r); see `GenerateFpCondition()`.
3541 FLe(tmp, left, right, in_type);
3542 FLt(result, left, right, in_type);
3543 __ Xori(tmp, tmp, 1);
3544 __ Sub(result, tmp, result);
3545 } else {
3546 // ((FLE r,l) - 1) + (FLT r,l); see `GenerateFpCondition()`.
3547 FLe(tmp, right, left, in_type);
3548 FLt(result, right, left, in_type);
3549 __ Addi(tmp, tmp, -1);
3550 __ Add(result, result, tmp);
3551 }
3552 break;
3553 }
3554
3555 default:
3556 LOG(FATAL) << "Unimplemented compare type " << in_type;
3557 }
3558 }
3559
VisitConstructorFence(HConstructorFence * instruction)3560 void LocationsBuilderRISCV64::VisitConstructorFence(HConstructorFence* instruction) {
3561 instruction->SetLocations(nullptr);
3562 }
3563
VisitConstructorFence(HConstructorFence * instruction)3564 void InstructionCodeGeneratorRISCV64::VisitConstructorFence(
3565 [[maybe_unused]] HConstructorFence* instruction) {
3566 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
3567 }
3568
VisitCurrentMethod(HCurrentMethod * instruction)3569 void LocationsBuilderRISCV64::VisitCurrentMethod(HCurrentMethod* instruction) {
3570 LocationSummary* locations =
3571 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
3572 locations->SetOut(Location::RegisterLocation(kArtMethodRegister));
3573 }
3574
VisitCurrentMethod(HCurrentMethod * instruction)3575 void InstructionCodeGeneratorRISCV64::VisitCurrentMethod(
3576 [[maybe_unused]] HCurrentMethod* instruction) {
3577 // Nothing to do, the method is already at its location.
3578 }
3579
VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag * instruction)3580 void LocationsBuilderRISCV64::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* instruction) {
3581 LocationSummary* locations =
3582 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
3583 locations->SetOut(Location::RequiresRegister());
3584 }
3585
VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag * instruction)3586 void InstructionCodeGeneratorRISCV64::VisitShouldDeoptimizeFlag(
3587 HShouldDeoptimizeFlag* instruction) {
3588 __ Loadw(instruction->GetLocations()->Out().AsRegister<XRegister>(),
3589 SP,
3590 codegen_->GetStackOffsetOfShouldDeoptimizeFlag());
3591 }
3592
VisitDeoptimize(HDeoptimize * instruction)3593 void LocationsBuilderRISCV64::VisitDeoptimize(HDeoptimize* instruction) {
3594 LocationSummary* locations = new (GetGraph()->GetAllocator())
3595 LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
3596 InvokeRuntimeCallingConvention calling_convention;
3597 RegisterSet caller_saves = RegisterSet::Empty();
3598 caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
3599 locations->SetCustomSlowPathCallerSaves(caller_saves);
3600 if (IsBooleanValueOrMaterializedCondition(instruction->InputAt(0))) {
3601 locations->SetInAt(0, Location::RequiresRegister());
3602 }
3603 }
3604
VisitDeoptimize(HDeoptimize * instruction)3605 void InstructionCodeGeneratorRISCV64::VisitDeoptimize(HDeoptimize* instruction) {
3606 SlowPathCodeRISCV64* slow_path =
3607 deopt_slow_paths_.NewSlowPath<DeoptimizationSlowPathRISCV64>(instruction);
3608 GenerateTestAndBranch(instruction,
3609 /* condition_input_index= */ 0,
3610 slow_path->GetEntryLabel(),
3611 /* false_target= */ nullptr);
3612 }
3613
VisitDiv(HDiv * instruction)3614 void LocationsBuilderRISCV64::VisitDiv(HDiv* instruction) {
3615 LocationSummary* locations =
3616 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
3617 switch (instruction->GetResultType()) {
3618 case DataType::Type::kInt32:
3619 case DataType::Type::kInt64:
3620 locations->SetInAt(0, Location::RequiresRegister());
3621 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
3622 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3623 break;
3624
3625 case DataType::Type::kFloat32:
3626 case DataType::Type::kFloat64:
3627 locations->SetInAt(0, Location::RequiresFpuRegister());
3628 locations->SetInAt(1, Location::RequiresFpuRegister());
3629 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3630 break;
3631
3632 default:
3633 LOG(FATAL) << "Unexpected div type " << instruction->GetResultType();
3634 UNREACHABLE();
3635 }
3636 }
3637
VisitDiv(HDiv * instruction)3638 void InstructionCodeGeneratorRISCV64::VisitDiv(HDiv* instruction) {
3639 DataType::Type type = instruction->GetType();
3640 LocationSummary* locations = instruction->GetLocations();
3641
3642 switch (type) {
3643 case DataType::Type::kInt32:
3644 case DataType::Type::kInt64:
3645 GenerateDivRemIntegral(instruction);
3646 break;
3647 case DataType::Type::kFloat32:
3648 case DataType::Type::kFloat64: {
3649 FRegister dst = locations->Out().AsFpuRegister<FRegister>();
3650 FRegister lhs = locations->InAt(0).AsFpuRegister<FRegister>();
3651 FRegister rhs = locations->InAt(1).AsFpuRegister<FRegister>();
3652 FDiv(dst, lhs, rhs, type);
3653 break;
3654 }
3655 default:
3656 LOG(FATAL) << "Unexpected div type " << type;
3657 UNREACHABLE();
3658 }
3659 }
3660
VisitDivZeroCheck(HDivZeroCheck * instruction)3661 void LocationsBuilderRISCV64::VisitDivZeroCheck(HDivZeroCheck* instruction) {
3662 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
3663 locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0)));
3664 }
3665
VisitDivZeroCheck(HDivZeroCheck * instruction)3666 void InstructionCodeGeneratorRISCV64::VisitDivZeroCheck(HDivZeroCheck* instruction) {
3667 SlowPathCodeRISCV64* slow_path =
3668 new (codegen_->GetScopedAllocator()) DivZeroCheckSlowPathRISCV64(instruction);
3669 codegen_->AddSlowPath(slow_path);
3670 Location value = instruction->GetLocations()->InAt(0);
3671
3672 DataType::Type type = instruction->GetType();
3673
3674 if (!DataType::IsIntegralType(type)) {
3675 LOG(FATAL) << "Unexpected type " << type << " for DivZeroCheck.";
3676 UNREACHABLE();
3677 }
3678
3679 if (value.IsConstant()) {
3680 int64_t divisor = codegen_->GetInt64ValueOf(value.GetConstant()->AsConstant());
3681 if (divisor == 0) {
3682 __ J(slow_path->GetEntryLabel());
3683 } else {
3684 // A division by a non-null constant is valid. We don't need to perform
3685 // any check, so simply fall through.
3686 }
3687 } else {
3688 __ Beqz(value.AsRegister<XRegister>(), slow_path->GetEntryLabel());
3689 }
3690 }
3691
VisitDoubleConstant(HDoubleConstant * instruction)3692 void LocationsBuilderRISCV64::VisitDoubleConstant(HDoubleConstant* instruction) {
3693 LocationSummary* locations =
3694 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
3695 locations->SetOut(Location::ConstantLocation(instruction));
3696 }
3697
VisitDoubleConstant(HDoubleConstant * instruction)3698 void InstructionCodeGeneratorRISCV64::VisitDoubleConstant(
3699 [[maybe_unused]] HDoubleConstant* instruction) {
3700 // Will be generated at use site.
3701 }
3702
VisitEqual(HEqual * instruction)3703 void LocationsBuilderRISCV64::VisitEqual(HEqual* instruction) {
3704 HandleCondition(instruction);
3705 }
3706
VisitEqual(HEqual * instruction)3707 void InstructionCodeGeneratorRISCV64::VisitEqual(HEqual* instruction) {
3708 HandleCondition(instruction);
3709 }
3710
VisitExit(HExit * instruction)3711 void LocationsBuilderRISCV64::VisitExit(HExit* instruction) {
3712 instruction->SetLocations(nullptr);
3713 }
3714
VisitExit(HExit * instruction)3715 void InstructionCodeGeneratorRISCV64::VisitExit([[maybe_unused]] HExit* instruction) {}
3716
VisitFloatConstant(HFloatConstant * instruction)3717 void LocationsBuilderRISCV64::VisitFloatConstant(HFloatConstant* instruction) {
3718 LocationSummary* locations =
3719 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
3720 locations->SetOut(Location::ConstantLocation(instruction));
3721 }
3722
VisitFloatConstant(HFloatConstant * instruction)3723 void InstructionCodeGeneratorRISCV64::VisitFloatConstant(
3724 [[maybe_unused]] HFloatConstant* instruction) {
3725 // Will be generated at use site.
3726 }
3727
VisitGoto(HGoto * instruction)3728 void LocationsBuilderRISCV64::VisitGoto(HGoto* instruction) {
3729 instruction->SetLocations(nullptr);
3730 }
3731
VisitGoto(HGoto * instruction)3732 void InstructionCodeGeneratorRISCV64::VisitGoto(HGoto* instruction) {
3733 HandleGoto(instruction, instruction->GetSuccessor());
3734 }
3735
VisitGreaterThan(HGreaterThan * instruction)3736 void LocationsBuilderRISCV64::VisitGreaterThan(HGreaterThan* instruction) {
3737 HandleCondition(instruction);
3738 }
3739
VisitGreaterThan(HGreaterThan * instruction)3740 void InstructionCodeGeneratorRISCV64::VisitGreaterThan(HGreaterThan* instruction) {
3741 HandleCondition(instruction);
3742 }
3743
VisitGreaterThanOrEqual(HGreaterThanOrEqual * instruction)3744 void LocationsBuilderRISCV64::VisitGreaterThanOrEqual(HGreaterThanOrEqual* instruction) {
3745 HandleCondition(instruction);
3746 }
3747
VisitGreaterThanOrEqual(HGreaterThanOrEqual * instruction)3748 void InstructionCodeGeneratorRISCV64::VisitGreaterThanOrEqual(HGreaterThanOrEqual* instruction) {
3749 HandleCondition(instruction);
3750 }
3751
VisitIf(HIf * instruction)3752 void LocationsBuilderRISCV64::VisitIf(HIf* instruction) {
3753 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
3754 if (IsBooleanValueOrMaterializedCondition(instruction->InputAt(0))) {
3755 locations->SetInAt(0, Location::RequiresRegister());
3756 if (GetGraph()->IsCompilingBaseline() &&
3757 codegen_->GetCompilerOptions().ProfileBranches() &&
3758 !Runtime::Current()->IsAotCompiler()) {
3759 DCHECK(instruction->InputAt(0)->IsCondition());
3760 ProfilingInfo* info = GetGraph()->GetProfilingInfo();
3761 DCHECK(info != nullptr);
3762 BranchCache* cache = info->GetBranchCache(instruction->GetDexPc());
3763 if (cache != nullptr) {
3764 locations->AddTemp(Location::RequiresRegister());
3765 }
3766 }
3767 }
3768 }
3769
VisitIf(HIf * instruction)3770 void InstructionCodeGeneratorRISCV64::VisitIf(HIf* instruction) {
3771 HBasicBlock* true_successor = instruction->IfTrueSuccessor();
3772 HBasicBlock* false_successor = instruction->IfFalseSuccessor();
3773 Riscv64Label* true_target = codegen_->GoesToNextBlock(instruction->GetBlock(), true_successor)
3774 ? nullptr
3775 : codegen_->GetLabelOf(true_successor);
3776 Riscv64Label* false_target = codegen_->GoesToNextBlock(instruction->GetBlock(), false_successor)
3777 ? nullptr
3778 : codegen_->GetLabelOf(false_successor);
3779 if (IsBooleanValueOrMaterializedCondition(instruction->InputAt(0))) {
3780 if (GetGraph()->IsCompilingBaseline() &&
3781 codegen_->GetCompilerOptions().ProfileBranches() &&
3782 !Runtime::Current()->IsAotCompiler()) {
3783 DCHECK(instruction->InputAt(0)->IsCondition());
3784 ProfilingInfo* info = GetGraph()->GetProfilingInfo();
3785 DCHECK(info != nullptr);
3786 BranchCache* cache = info->GetBranchCache(instruction->GetDexPc());
3787 // Currently, not all If branches are profiled.
3788 if (cache != nullptr) {
3789 uint64_t address =
3790 reinterpret_cast64<uint64_t>(cache) + BranchCache::FalseOffset().Int32Value();
3791 static_assert(
3792 BranchCache::TrueOffset().Int32Value() - BranchCache::FalseOffset().Int32Value() == 2,
3793 "Unexpected offsets for BranchCache");
3794 Riscv64Label done;
3795 XRegister condition = instruction->GetLocations()->InAt(0).AsRegister<XRegister>();
3796 XRegister temp = instruction->GetLocations()->GetTemp(0).AsRegister<XRegister>();
3797 __ LoadConst64(temp, address);
3798 __ Sh1Add(temp, condition, temp);
3799 ScratchRegisterScope srs(GetAssembler());
3800 XRegister counter = srs.AllocateXRegister();
3801 __ Loadhu(counter, temp, 0);
3802 __ Addi(counter, counter, 1);
3803 {
3804 ScratchRegisterScope srs2(GetAssembler());
3805 XRegister overflow = srs2.AllocateXRegister();
3806 __ Srli(overflow, counter, 16);
3807 __ Bnez(overflow, &done);
3808 }
3809 __ Storeh(counter, temp, 0);
3810 __ Bind(&done);
3811 }
3812 }
3813 }
3814 GenerateTestAndBranch(instruction, /* condition_input_index= */ 0, true_target, false_target);
3815 }
3816
VisitInstanceFieldGet(HInstanceFieldGet * instruction)3817 void LocationsBuilderRISCV64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
3818 HandleFieldGet(instruction);
3819 }
3820
VisitInstanceFieldGet(HInstanceFieldGet * instruction)3821 void InstructionCodeGeneratorRISCV64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
3822 HandleFieldGet(instruction, instruction->GetFieldInfo());
3823 }
3824
VisitInstanceFieldSet(HInstanceFieldSet * instruction)3825 void LocationsBuilderRISCV64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
3826 HandleFieldSet(instruction);
3827 }
3828
VisitInstanceFieldSet(HInstanceFieldSet * instruction)3829 void InstructionCodeGeneratorRISCV64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
3830 HandleFieldSet(instruction,
3831 instruction->GetFieldInfo(),
3832 instruction->GetValueCanBeNull(),
3833 instruction->GetWriteBarrierKind());
3834 }
3835
VisitInstanceOf(HInstanceOf * instruction)3836 void LocationsBuilderRISCV64::VisitInstanceOf(HInstanceOf* instruction) {
3837 LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
3838 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
3839 bool baker_read_barrier_slow_path = false;
3840 switch (type_check_kind) {
3841 case TypeCheckKind::kExactCheck:
3842 case TypeCheckKind::kAbstractClassCheck:
3843 case TypeCheckKind::kClassHierarchyCheck:
3844 case TypeCheckKind::kArrayObjectCheck:
3845 case TypeCheckKind::kInterfaceCheck: {
3846 bool needs_read_barrier = codegen_->InstanceOfNeedsReadBarrier(instruction);
3847 call_kind = needs_read_barrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall;
3848 baker_read_barrier_slow_path = (kUseBakerReadBarrier && needs_read_barrier) &&
3849 (type_check_kind != TypeCheckKind::kInterfaceCheck);
3850 break;
3851 }
3852 case TypeCheckKind::kArrayCheck:
3853 case TypeCheckKind::kUnresolvedCheck:
3854 call_kind = LocationSummary::kCallOnSlowPath;
3855 break;
3856 case TypeCheckKind::kBitstringCheck:
3857 break;
3858 }
3859
3860 LocationSummary* locations =
3861 new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
3862 if (baker_read_barrier_slow_path) {
3863 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
3864 }
3865 locations->SetInAt(0, Location::RequiresRegister());
3866 if (type_check_kind == TypeCheckKind::kBitstringCheck) {
3867 locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)));
3868 locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)));
3869 locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)));
3870 } else {
3871 locations->SetInAt(1, Location::RequiresRegister());
3872 }
3873 // The output does overlap inputs.
3874 // Note that TypeCheckSlowPathRISCV64 uses this register too.
3875 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
3876 locations->AddRegisterTemps(
3877 NumberOfInstanceOfTemps(codegen_->EmitReadBarrier(), type_check_kind));
3878 }
3879
VisitInstanceOf(HInstanceOf * instruction)3880 void InstructionCodeGeneratorRISCV64::VisitInstanceOf(HInstanceOf* instruction) {
3881 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
3882 LocationSummary* locations = instruction->GetLocations();
3883 Location obj_loc = locations->InAt(0);
3884 XRegister obj = obj_loc.AsRegister<XRegister>();
3885 Location cls = (type_check_kind == TypeCheckKind::kBitstringCheck)
3886 ? Location::NoLocation()
3887 : locations->InAt(1);
3888 Location out_loc = locations->Out();
3889 XRegister out = out_loc.AsRegister<XRegister>();
3890 const size_t num_temps = NumberOfInstanceOfTemps(codegen_->EmitReadBarrier(), type_check_kind);
3891 DCHECK_LE(num_temps, 1u);
3892 Location maybe_temp_loc = (num_temps >= 1) ? locations->GetTemp(0) : Location::NoLocation();
3893 const uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
3894 const uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
3895 const uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
3896 const uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
3897 const uint32_t iftable_offset = mirror::Class::IfTableOffset().Uint32Value();
3898 const uint32_t array_length_offset = mirror::Array::LengthOffset().Uint32Value();
3899 const uint32_t object_array_data_offset =
3900 mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value();
3901 Riscv64Label done;
3902 SlowPathCodeRISCV64* slow_path = nullptr;
3903
3904 // Return 0 if `obj` is null.
3905 // Avoid this check if we know `obj` is not null.
3906 if (instruction->MustDoNullCheck()) {
3907 __ Mv(out, Zero);
3908 __ Beqz(obj, &done);
3909 }
3910
3911 switch (type_check_kind) {
3912 case TypeCheckKind::kExactCheck: {
3913 ReadBarrierOption read_barrier_option =
3914 codegen_->ReadBarrierOptionForInstanceOf(instruction);
3915 // /* HeapReference<Class> */ out = obj->klass_
3916 GenerateReferenceLoadTwoRegisters(
3917 instruction, out_loc, obj_loc, class_offset, maybe_temp_loc, read_barrier_option);
3918 // Classes must be equal for the instanceof to succeed.
3919 __ Xor(out, out, cls.AsRegister<XRegister>());
3920 __ Seqz(out, out);
3921 break;
3922 }
3923
3924 case TypeCheckKind::kAbstractClassCheck: {
3925 ReadBarrierOption read_barrier_option =
3926 codegen_->ReadBarrierOptionForInstanceOf(instruction);
3927 // /* HeapReference<Class> */ out = obj->klass_
3928 GenerateReferenceLoadTwoRegisters(
3929 instruction, out_loc, obj_loc, class_offset, maybe_temp_loc, read_barrier_option);
3930 // If the class is abstract, we eagerly fetch the super class of the
3931 // object to avoid doing a comparison we know will fail.
3932 Riscv64Label loop;
3933 __ Bind(&loop);
3934 // /* HeapReference<Class> */ out = out->super_class_
3935 GenerateReferenceLoadOneRegister(
3936 instruction, out_loc, super_offset, maybe_temp_loc, read_barrier_option);
3937 // If `out` is null, we use it for the result, and jump to `done`.
3938 __ Beqz(out, &done);
3939 __ Bne(out, cls.AsRegister<XRegister>(), &loop);
3940 __ LoadConst32(out, 1);
3941 break;
3942 }
3943
3944 case TypeCheckKind::kClassHierarchyCheck: {
3945 ReadBarrierOption read_barrier_option =
3946 codegen_->ReadBarrierOptionForInstanceOf(instruction);
3947 // /* HeapReference<Class> */ out = obj->klass_
3948 GenerateReferenceLoadTwoRegisters(
3949 instruction, out_loc, obj_loc, class_offset, maybe_temp_loc, read_barrier_option);
3950 // Walk over the class hierarchy to find a match.
3951 Riscv64Label loop, success;
3952 __ Bind(&loop);
3953 __ Beq(out, cls.AsRegister<XRegister>(), &success);
3954 // /* HeapReference<Class> */ out = out->super_class_
3955 GenerateReferenceLoadOneRegister(
3956 instruction, out_loc, super_offset, maybe_temp_loc, read_barrier_option);
3957 __ Bnez(out, &loop);
3958 // If `out` is null, we use it for the result, and jump to `done`.
3959 __ J(&done);
3960 __ Bind(&success);
3961 __ LoadConst32(out, 1);
3962 break;
3963 }
3964
3965 case TypeCheckKind::kArrayObjectCheck: {
3966 ReadBarrierOption read_barrier_option =
3967 codegen_->ReadBarrierOptionForInstanceOf(instruction);
3968 // FIXME(riscv64): We currently have marking entrypoints for 29 registers.
3969 // We need to either store entrypoint for register `N` in entry `N-A` where
3970 // `A` can be up to 5 (Zero, RA, SP, GP, TP are not valid registers for
3971 // marking), or define two more entrypoints, or request an additional temp
3972 // from the register allocator instead of using a scratch register.
3973 ScratchRegisterScope srs(GetAssembler());
3974 Location tmp = Location::RegisterLocation(srs.AllocateXRegister());
3975 // /* HeapReference<Class> */ tmp = obj->klass_
3976 GenerateReferenceLoadTwoRegisters(
3977 instruction, tmp, obj_loc, class_offset, maybe_temp_loc, read_barrier_option);
3978 // Do an exact check.
3979 __ LoadConst32(out, 1);
3980 __ Beq(tmp.AsRegister<XRegister>(), cls.AsRegister<XRegister>(), &done);
3981 // Otherwise, we need to check that the object's class is a non-primitive array.
3982 // /* HeapReference<Class> */ out = out->component_type_
3983 GenerateReferenceLoadTwoRegisters(
3984 instruction, out_loc, tmp, component_offset, maybe_temp_loc, read_barrier_option);
3985 // If `out` is null, we use it for the result, and jump to `done`.
3986 __ Beqz(out, &done);
3987 __ Loadhu(out, out, primitive_offset);
3988 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
3989 __ Seqz(out, out);
3990 break;
3991 }
3992
3993 case TypeCheckKind::kArrayCheck: {
3994 // No read barrier since the slow path will retry upon failure.
3995 // /* HeapReference<Class> */ out = obj->klass_
3996 GenerateReferenceLoadTwoRegisters(
3997 instruction, out_loc, obj_loc, class_offset, maybe_temp_loc, kWithoutReadBarrier);
3998 DCHECK(locations->OnlyCallsOnSlowPath());
3999 slow_path = new (codegen_->GetScopedAllocator())
4000 TypeCheckSlowPathRISCV64(instruction, /* is_fatal= */ false);
4001 codegen_->AddSlowPath(slow_path);
4002 __ Bne(out, cls.AsRegister<XRegister>(), slow_path->GetEntryLabel());
4003 __ LoadConst32(out, 1);
4004 break;
4005 }
4006
4007 case TypeCheckKind::kInterfaceCheck: {
4008 if (codegen_->InstanceOfNeedsReadBarrier(instruction)) {
4009 DCHECK(locations->OnlyCallsOnSlowPath());
4010 slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathRISCV64(
4011 instruction, /* is_fatal= */ false);
4012 codegen_->AddSlowPath(slow_path);
4013 if (codegen_->EmitNonBakerReadBarrier()) {
4014 __ J(slow_path->GetEntryLabel());
4015 break;
4016 }
4017 // For Baker read barrier, take the slow path while marking.
4018 __ Loadw(out, TR, Thread::IsGcMarkingOffset<kRiscv64PointerSize>().Int32Value());
4019 __ Bnez(out, slow_path->GetEntryLabel());
4020 }
4021
4022 // Fast-path without read barriers.
4023 ScratchRegisterScope srs(GetAssembler());
4024 XRegister temp = srs.AllocateXRegister();
4025 // /* HeapReference<Class> */ temp = obj->klass_
4026 __ Loadwu(temp, obj, class_offset);
4027 codegen_->MaybeUnpoisonHeapReference(temp);
4028 // /* HeapReference<Class> */ temp = temp->iftable_
4029 __ Loadwu(temp, temp, iftable_offset);
4030 codegen_->MaybeUnpoisonHeapReference(temp);
4031 // Load the size of the `IfTable`. The `Class::iftable_` is never null.
4032 __ Loadw(out, temp, array_length_offset);
4033 // Loop through the `IfTable` and check if any class matches.
4034 Riscv64Label loop;
4035 XRegister temp2 = srs.AllocateXRegister();
4036 __ Bind(&loop);
4037 __ Beqz(out, &done); // If taken, the result in `out` is already 0 (false).
4038 __ Loadwu(temp2, temp, object_array_data_offset);
4039 codegen_->MaybeUnpoisonHeapReference(temp2);
4040 // Go to next interface.
4041 __ Addi(temp, temp, 2 * kHeapReferenceSize);
4042 __ Addi(out, out, -2);
4043 // Compare the classes and continue the loop if they do not match.
4044 __ Bne(cls.AsRegister<XRegister>(), temp2, &loop);
4045 __ LoadConst32(out, 1);
4046 break;
4047 }
4048
4049 case TypeCheckKind::kUnresolvedCheck: {
4050 // Note that we indeed only call on slow path, but we always go
4051 // into the slow path for the unresolved check case.
4052 //
4053 // We cannot directly call the InstanceofNonTrivial runtime
4054 // entry point without resorting to a type checking slow path
4055 // here (i.e. by calling InvokeRuntime directly), as it would
4056 // require to assign fixed registers for the inputs of this
4057 // HInstanceOf instruction (following the runtime calling
4058 // convention), which might be cluttered by the potential first
4059 // read barrier emission at the beginning of this method.
4060 //
4061 // TODO: Introduce a new runtime entry point taking the object
4062 // to test (instead of its class) as argument, and let it deal
4063 // with the read barrier issues. This will let us refactor this
4064 // case of the `switch` code as it was previously (with a direct
4065 // call to the runtime not using a type checking slow path).
4066 // This should also be beneficial for the other cases above.
4067 DCHECK(locations->OnlyCallsOnSlowPath());
4068 slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathRISCV64(
4069 instruction, /* is_fatal= */ false);
4070 codegen_->AddSlowPath(slow_path);
4071 __ J(slow_path->GetEntryLabel());
4072 break;
4073 }
4074
4075 case TypeCheckKind::kBitstringCheck: {
4076 // /* HeapReference<Class> */ temp = obj->klass_
4077 GenerateReferenceLoadTwoRegisters(
4078 instruction, out_loc, obj_loc, class_offset, maybe_temp_loc, kWithoutReadBarrier);
4079
4080 GenerateBitstringTypeCheckCompare(instruction, out);
4081 __ Beqz(out, out);
4082 break;
4083 }
4084 }
4085
4086 __ Bind(&done);
4087
4088 if (slow_path != nullptr) {
4089 __ Bind(slow_path->GetExitLabel());
4090 }
4091 }
4092
VisitIntConstant(HIntConstant * instruction)4093 void LocationsBuilderRISCV64::VisitIntConstant(HIntConstant* instruction) {
4094 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
4095 locations->SetOut(Location::ConstantLocation(instruction));
4096 }
4097
VisitIntConstant(HIntConstant * instruction)4098 void InstructionCodeGeneratorRISCV64::VisitIntConstant([[maybe_unused]] HIntConstant* instruction) {
4099 // Will be generated at use site.
4100 }
4101
VisitIntermediateAddress(HIntermediateAddress * instruction)4102 void LocationsBuilderRISCV64::VisitIntermediateAddress(HIntermediateAddress* instruction) {
4103 UNUSED(instruction);
4104 LOG(FATAL) << "Unimplemented";
4105 }
4106
VisitIntermediateAddress(HIntermediateAddress * instruction)4107 void InstructionCodeGeneratorRISCV64::VisitIntermediateAddress(HIntermediateAddress* instruction) {
4108 UNUSED(instruction);
4109 LOG(FATAL) << "Unimplemented";
4110 }
4111
VisitInvokeUnresolved(HInvokeUnresolved * instruction)4112 void LocationsBuilderRISCV64::VisitInvokeUnresolved(HInvokeUnresolved* instruction) {
4113 // The trampoline uses the same calling convention as dex calling conventions, except
4114 // instead of loading arg0/A0 with the target Method*, arg0/A0 will contain the method_idx.
4115 HandleInvoke(instruction);
4116 }
4117
VisitInvokeUnresolved(HInvokeUnresolved * instruction)4118 void InstructionCodeGeneratorRISCV64::VisitInvokeUnresolved(HInvokeUnresolved* instruction) {
4119 codegen_->GenerateInvokeUnresolvedRuntimeCall(instruction);
4120 }
4121
VisitInvokeInterface(HInvokeInterface * instruction)4122 void LocationsBuilderRISCV64::VisitInvokeInterface(HInvokeInterface* instruction) {
4123 HandleInvoke(instruction);
4124 // Use T0 as the hidden argument for `art_quick_imt_conflict_trampoline`.
4125 if (instruction->GetHiddenArgumentLoadKind() == MethodLoadKind::kRecursive) {
4126 instruction->GetLocations()->SetInAt(instruction->GetNumberOfArguments() - 1,
4127 Location::RegisterLocation(T0));
4128 } else {
4129 instruction->GetLocations()->AddTemp(Location::RegisterLocation(T0));
4130 }
4131 }
4132
VisitInvokeInterface(HInvokeInterface * instruction)4133 void InstructionCodeGeneratorRISCV64::VisitInvokeInterface(HInvokeInterface* instruction) {
4134 LocationSummary* locations = instruction->GetLocations();
4135 XRegister temp = locations->GetTemp(0).AsRegister<XRegister>();
4136 XRegister receiver = locations->InAt(0).AsRegister<XRegister>();
4137 int32_t class_offset = mirror::Object::ClassOffset().Int32Value();
4138 Offset entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kRiscv64PointerSize);
4139
4140 // /* HeapReference<Class> */ temp = receiver->klass_
4141 __ Loadwu(temp, receiver, class_offset);
4142 codegen_->MaybeRecordImplicitNullCheck(instruction);
4143 // Instead of simply (possibly) unpoisoning `temp` here, we should
4144 // emit a read barrier for the previous class reference load.
4145 // However this is not required in practice, as this is an
4146 // intermediate/temporary reference and because the current
4147 // concurrent copying collector keeps the from-space memory
4148 // intact/accessible until the end of the marking phase (the
4149 // concurrent copying collector may not in the future).
4150 codegen_->MaybeUnpoisonHeapReference(temp);
4151
4152 // If we're compiling baseline, update the inline cache.
4153 codegen_->MaybeGenerateInlineCacheCheck(instruction, temp);
4154
4155 // The register T0 is required to be used for the hidden argument in
4156 // `art_quick_imt_conflict_trampoline`.
4157 if (instruction->GetHiddenArgumentLoadKind() != MethodLoadKind::kRecursive &&
4158 instruction->GetHiddenArgumentLoadKind() != MethodLoadKind::kRuntimeCall) {
4159 Location hidden_reg = instruction->GetLocations()->GetTemp(1);
4160 // Load the resolved interface method in the hidden argument register T0.
4161 DCHECK_EQ(T0, hidden_reg.AsRegister<XRegister>());
4162 codegen_->LoadMethod(instruction->GetHiddenArgumentLoadKind(), hidden_reg, instruction);
4163 }
4164
4165 __ Loadd(temp, temp, mirror::Class::ImtPtrOffset(kRiscv64PointerSize).Uint32Value());
4166 uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement(
4167 instruction->GetImtIndex(), kRiscv64PointerSize));
4168 // temp = temp->GetImtEntryAt(method_offset);
4169 __ Loadd(temp, temp, method_offset);
4170 if (instruction->GetHiddenArgumentLoadKind() == MethodLoadKind::kRuntimeCall) {
4171 // We pass the method from the IMT in case of a conflict. This will ensure
4172 // we go into the runtime to resolve the actual method.
4173 Location hidden_reg = instruction->GetLocations()->GetTemp(1);
4174 DCHECK_EQ(T0, hidden_reg.AsRegister<XRegister>());
4175 __ Mv(hidden_reg.AsRegister<XRegister>(), temp);
4176 }
4177 // RA = temp->GetEntryPoint();
4178 __ Loadd(RA, temp, entry_point.Int32Value());
4179
4180 // RA();
4181 __ Jalr(RA);
4182 DCHECK(!codegen_->IsLeafMethod());
4183 codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
4184 }
4185
VisitInvokeStaticOrDirect(HInvokeStaticOrDirect * instruction)4186 void LocationsBuilderRISCV64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* instruction) {
4187 // Explicit clinit checks triggered by static invokes must have been pruned by
4188 // art::PrepareForRegisterAllocation.
4189 DCHECK(!instruction->IsStaticWithExplicitClinitCheck());
4190
4191 IntrinsicLocationsBuilderRISCV64 intrinsic(GetGraph()->GetAllocator(), codegen_);
4192 if (intrinsic.TryDispatch(instruction)) {
4193 return;
4194 }
4195
4196 if (instruction->GetCodePtrLocation() == CodePtrLocation::kCallCriticalNative) {
4197 CriticalNativeCallingConventionVisitorRiscv64 calling_convention_visitor(
4198 /*for_register_allocation=*/ true);
4199 CodeGenerator::CreateCommonInvokeLocationSummary(instruction, &calling_convention_visitor);
4200 } else {
4201 HandleInvoke(instruction);
4202 }
4203 }
4204
TryGenerateIntrinsicCode(HInvoke * invoke,CodeGeneratorRISCV64 * codegen)4205 static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorRISCV64* codegen) {
4206 if (invoke->GetLocations()->Intrinsified()) {
4207 IntrinsicCodeGeneratorRISCV64 intrinsic(codegen);
4208 intrinsic.Dispatch(invoke);
4209 return true;
4210 }
4211 return false;
4212 }
4213
VisitInvokeStaticOrDirect(HInvokeStaticOrDirect * instruction)4214 void InstructionCodeGeneratorRISCV64::VisitInvokeStaticOrDirect(
4215 HInvokeStaticOrDirect* instruction) {
4216 // Explicit clinit checks triggered by static invokes must have been pruned by
4217 // art::PrepareForRegisterAllocation.
4218 DCHECK(!instruction->IsStaticWithExplicitClinitCheck());
4219
4220 if (TryGenerateIntrinsicCode(instruction, codegen_)) {
4221 return;
4222 }
4223
4224 LocationSummary* locations = instruction->GetLocations();
4225 codegen_->GenerateStaticOrDirectCall(
4226 instruction, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation());
4227 }
4228
VisitInvokeVirtual(HInvokeVirtual * instruction)4229 void LocationsBuilderRISCV64::VisitInvokeVirtual(HInvokeVirtual* instruction) {
4230 IntrinsicLocationsBuilderRISCV64 intrinsic(GetGraph()->GetAllocator(), codegen_);
4231 if (intrinsic.TryDispatch(instruction)) {
4232 return;
4233 }
4234
4235 HandleInvoke(instruction);
4236 }
4237
VisitInvokeVirtual(HInvokeVirtual * instruction)4238 void InstructionCodeGeneratorRISCV64::VisitInvokeVirtual(HInvokeVirtual* instruction) {
4239 if (TryGenerateIntrinsicCode(instruction, codegen_)) {
4240 return;
4241 }
4242
4243 codegen_->GenerateVirtualCall(instruction, instruction->GetLocations()->GetTemp(0));
4244 DCHECK(!codegen_->IsLeafMethod());
4245 }
4246
VisitInvokePolymorphic(HInvokePolymorphic * instruction)4247 void LocationsBuilderRISCV64::VisitInvokePolymorphic(HInvokePolymorphic* instruction) {
4248 IntrinsicLocationsBuilderRISCV64 intrinsic(GetGraph()->GetAllocator(), codegen_);
4249 if (intrinsic.TryDispatch(instruction)) {
4250 return;
4251 }
4252 HandleInvoke(instruction);
4253 }
4254
VisitInvokePolymorphic(HInvokePolymorphic * instruction)4255 void InstructionCodeGeneratorRISCV64::VisitInvokePolymorphic(HInvokePolymorphic* instruction) {
4256 if (TryGenerateIntrinsicCode(instruction, codegen_)) {
4257 return;
4258 }
4259 codegen_->GenerateInvokePolymorphicCall(instruction);
4260 }
4261
VisitInvokeCustom(HInvokeCustom * instruction)4262 void LocationsBuilderRISCV64::VisitInvokeCustom(HInvokeCustom* instruction) {
4263 HandleInvoke(instruction);
4264 }
4265
VisitInvokeCustom(HInvokeCustom * instruction)4266 void InstructionCodeGeneratorRISCV64::VisitInvokeCustom(HInvokeCustom* instruction) {
4267 codegen_->GenerateInvokeCustomCall(instruction);
4268 }
4269
VisitLessThan(HLessThan * instruction)4270 void LocationsBuilderRISCV64::VisitLessThan(HLessThan* instruction) {
4271 HandleCondition(instruction);
4272 }
4273
VisitLessThan(HLessThan * instruction)4274 void InstructionCodeGeneratorRISCV64::VisitLessThan(HLessThan* instruction) {
4275 HandleCondition(instruction);
4276 }
4277
VisitLessThanOrEqual(HLessThanOrEqual * instruction)4278 void LocationsBuilderRISCV64::VisitLessThanOrEqual(HLessThanOrEqual* instruction) {
4279 HandleCondition(instruction);
4280 }
4281
VisitLessThanOrEqual(HLessThanOrEqual * instruction)4282 void InstructionCodeGeneratorRISCV64::VisitLessThanOrEqual(HLessThanOrEqual* instruction) {
4283 HandleCondition(instruction);
4284 }
4285
VisitLoadClass(HLoadClass * instruction)4286 void LocationsBuilderRISCV64::VisitLoadClass(HLoadClass* instruction) {
4287 HLoadClass::LoadKind load_kind = instruction->GetLoadKind();
4288 if (load_kind == HLoadClass::LoadKind::kRuntimeCall) {
4289 InvokeRuntimeCallingConvention calling_convention;
4290 Location loc = Location::RegisterLocation(calling_convention.GetRegisterAt(0));
4291 DCHECK_EQ(DataType::Type::kReference, instruction->GetType());
4292 DCHECK(loc.Equals(calling_convention.GetReturnLocation(DataType::Type::kReference)));
4293 CodeGenerator::CreateLoadClassRuntimeCallLocationSummary(instruction, loc, loc);
4294 return;
4295 }
4296 DCHECK_EQ(instruction->NeedsAccessCheck(),
4297 load_kind == HLoadClass::LoadKind::kBssEntryPublic ||
4298 load_kind == HLoadClass::LoadKind::kBssEntryPackage);
4299
4300 const bool requires_read_barrier = !instruction->IsInImage() && codegen_->EmitReadBarrier();
4301 LocationSummary::CallKind call_kind = (instruction->NeedsEnvironment() || requires_read_barrier)
4302 ? LocationSummary::kCallOnSlowPath
4303 : LocationSummary::kNoCall;
4304 LocationSummary* locations =
4305 new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
4306 if (kUseBakerReadBarrier && requires_read_barrier && !instruction->NeedsEnvironment()) {
4307 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
4308 }
4309 if (load_kind == HLoadClass::LoadKind::kReferrersClass) {
4310 locations->SetInAt(0, Location::RequiresRegister());
4311 }
4312 locations->SetOut(Location::RequiresRegister());
4313 if (load_kind == HLoadClass::LoadKind::kBssEntry ||
4314 load_kind == HLoadClass::LoadKind::kBssEntryPublic ||
4315 load_kind == HLoadClass::LoadKind::kBssEntryPackage) {
4316 if (codegen_->EmitNonBakerReadBarrier()) {
4317 // For non-Baker read barriers we have a temp-clobbering call.
4318 } else {
4319 // Rely on the type resolution or initialization and marking to save everything we need.
4320 locations->SetCustomSlowPathCallerSaves(OneRegInReferenceOutSaveEverythingCallerSaves());
4321 }
4322 }
4323 }
4324
4325 // NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
4326 // move.
VisitLoadClass(HLoadClass * instruction)4327 void InstructionCodeGeneratorRISCV64::VisitLoadClass(HLoadClass* instruction)
4328 NO_THREAD_SAFETY_ANALYSIS {
4329 HLoadClass::LoadKind load_kind = instruction->GetLoadKind();
4330 if (load_kind == HLoadClass::LoadKind::kRuntimeCall) {
4331 codegen_->GenerateLoadClassRuntimeCall(instruction);
4332 return;
4333 }
4334 DCHECK_EQ(instruction->NeedsAccessCheck(),
4335 load_kind == HLoadClass::LoadKind::kBssEntryPublic ||
4336 load_kind == HLoadClass::LoadKind::kBssEntryPackage);
4337
4338 LocationSummary* locations = instruction->GetLocations();
4339 Location out_loc = locations->Out();
4340 XRegister out = out_loc.AsRegister<XRegister>();
4341 const ReadBarrierOption read_barrier_option =
4342 instruction->IsInImage() ? kWithoutReadBarrier : codegen_->GetCompilerReadBarrierOption();
4343 bool generate_null_check = false;
4344 switch (load_kind) {
4345 case HLoadClass::LoadKind::kReferrersClass: {
4346 DCHECK(!instruction->CanCallRuntime());
4347 DCHECK(!instruction->MustGenerateClinitCheck());
4348 // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
4349 XRegister current_method = locations->InAt(0).AsRegister<XRegister>();
4350 codegen_->GenerateGcRootFieldLoad(instruction,
4351 out_loc,
4352 current_method,
4353 ArtMethod::DeclaringClassOffset().Int32Value(),
4354 read_barrier_option);
4355 break;
4356 }
4357 case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: {
4358 DCHECK(codegen_->GetCompilerOptions().IsBootImage() ||
4359 codegen_->GetCompilerOptions().IsBootImageExtension());
4360 DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
4361 CodeGeneratorRISCV64::PcRelativePatchInfo* info_high =
4362 codegen_->NewBootImageTypePatch(instruction->GetDexFile(), instruction->GetTypeIndex());
4363 codegen_->EmitPcRelativeAuipcPlaceholder(info_high, out);
4364 CodeGeneratorRISCV64::PcRelativePatchInfo* info_low =
4365 codegen_->NewBootImageTypePatch(
4366 instruction->GetDexFile(), instruction->GetTypeIndex(), info_high);
4367 codegen_->EmitPcRelativeAddiPlaceholder(info_low, out, out);
4368 break;
4369 }
4370 case HLoadClass::LoadKind::kBootImageRelRo: {
4371 DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
4372 uint32_t boot_image_offset = codegen_->GetBootImageOffset(instruction);
4373 codegen_->LoadBootImageRelRoEntry(out, boot_image_offset);
4374 break;
4375 }
4376 case HLoadClass::LoadKind::kAppImageRelRo: {
4377 DCHECK(codegen_->GetCompilerOptions().IsAppImage());
4378 DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
4379 CodeGeneratorRISCV64::PcRelativePatchInfo* info_high =
4380 codegen_->NewAppImageTypePatch(instruction->GetDexFile(), instruction->GetTypeIndex());
4381 codegen_->EmitPcRelativeAuipcPlaceholder(info_high, out);
4382 CodeGeneratorRISCV64::PcRelativePatchInfo* info_low =
4383 codegen_->NewAppImageTypePatch(
4384 instruction->GetDexFile(), instruction->GetTypeIndex(), info_high);
4385 codegen_->EmitPcRelativeLwuPlaceholder(info_low, out, out);
4386 break;
4387 }
4388 case HLoadClass::LoadKind::kBssEntry:
4389 case HLoadClass::LoadKind::kBssEntryPublic:
4390 case HLoadClass::LoadKind::kBssEntryPackage: {
4391 CodeGeneratorRISCV64::PcRelativePatchInfo* bss_info_high =
4392 codegen_->NewTypeBssEntryPatch(instruction);
4393 codegen_->EmitPcRelativeAuipcPlaceholder(bss_info_high, out);
4394 CodeGeneratorRISCV64::PcRelativePatchInfo* info_low = codegen_->NewTypeBssEntryPatch(
4395 instruction, bss_info_high);
4396 codegen_->GenerateGcRootFieldLoad(instruction,
4397 out_loc,
4398 out,
4399 /* offset= */ kLinkTimeOffsetPlaceholderLow,
4400 read_barrier_option,
4401 &info_low->label);
4402 generate_null_check = true;
4403 break;
4404 }
4405 case HLoadClass::LoadKind::kJitBootImageAddress: {
4406 DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
4407 uint32_t address = reinterpret_cast32<uint32_t>(instruction->GetClass().Get());
4408 DCHECK_NE(address, 0u);
4409 __ Loadwu(out, codegen_->DeduplicateBootImageAddressLiteral(address));
4410 break;
4411 }
4412 case HLoadClass::LoadKind::kJitTableAddress:
4413 __ Loadwu(out, codegen_->DeduplicateJitClassLiteral(instruction->GetDexFile(),
4414 instruction->GetTypeIndex(),
4415 instruction->GetClass()));
4416 codegen_->GenerateGcRootFieldLoad(
4417 instruction, out_loc, out, /* offset= */ 0, read_barrier_option);
4418 break;
4419 case HLoadClass::LoadKind::kRuntimeCall:
4420 case HLoadClass::LoadKind::kInvalid:
4421 LOG(FATAL) << "UNREACHABLE";
4422 UNREACHABLE();
4423 }
4424
4425 if (generate_null_check || instruction->MustGenerateClinitCheck()) {
4426 DCHECK(instruction->CanCallRuntime());
4427 SlowPathCodeRISCV64* slow_path =
4428 new (codegen_->GetScopedAllocator()) LoadClassSlowPathRISCV64(instruction, instruction);
4429 codegen_->AddSlowPath(slow_path);
4430 if (generate_null_check) {
4431 __ Beqz(out, slow_path->GetEntryLabel());
4432 }
4433 if (instruction->MustGenerateClinitCheck()) {
4434 GenerateClassInitializationCheck(slow_path, out);
4435 } else {
4436 __ Bind(slow_path->GetExitLabel());
4437 }
4438 }
4439 }
4440
VisitLoadException(HLoadException * instruction)4441 void LocationsBuilderRISCV64::VisitLoadException(HLoadException* instruction) {
4442 LocationSummary* locations =
4443 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
4444 locations->SetOut(Location::RequiresRegister());
4445 }
4446
VisitLoadException(HLoadException * instruction)4447 void InstructionCodeGeneratorRISCV64::VisitLoadException(HLoadException* instruction) {
4448 XRegister out = instruction->GetLocations()->Out().AsRegister<XRegister>();
4449 __ Loadwu(out, TR, GetExceptionTlsOffset());
4450 }
4451
VisitLoadMethodHandle(HLoadMethodHandle * instruction)4452 void LocationsBuilderRISCV64::VisitLoadMethodHandle(HLoadMethodHandle* instruction) {
4453 InvokeRuntimeCallingConvention calling_convention;
4454 Location loc = Location::RegisterLocation(calling_convention.GetRegisterAt(0));
4455 CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(instruction, loc, loc);
4456 }
4457
VisitLoadMethodHandle(HLoadMethodHandle * instruction)4458 void InstructionCodeGeneratorRISCV64::VisitLoadMethodHandle(HLoadMethodHandle* instruction) {
4459 codegen_->GenerateLoadMethodHandleRuntimeCall(instruction);
4460 }
4461
VisitLoadMethodType(HLoadMethodType * instruction)4462 void LocationsBuilderRISCV64::VisitLoadMethodType(HLoadMethodType* instruction) {
4463 InvokeRuntimeCallingConvention calling_convention;
4464 Location loc = Location::RegisterLocation(calling_convention.GetRegisterAt(0));
4465 CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(instruction, loc, loc);
4466 }
4467
VisitLoadMethodType(HLoadMethodType * instruction)4468 void InstructionCodeGeneratorRISCV64::VisitLoadMethodType(HLoadMethodType* instruction) {
4469 codegen_->GenerateLoadMethodTypeRuntimeCall(instruction);
4470 }
4471
VisitLoadString(HLoadString * instruction)4472 void LocationsBuilderRISCV64::VisitLoadString(HLoadString* instruction) {
4473 HLoadString::LoadKind load_kind = instruction->GetLoadKind();
4474 LocationSummary::CallKind call_kind = codegen_->GetLoadStringCallKind(instruction);
4475 LocationSummary* locations =
4476 new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
4477 if (load_kind == HLoadString::LoadKind::kRuntimeCall) {
4478 InvokeRuntimeCallingConvention calling_convention;
4479 DCHECK_EQ(DataType::Type::kReference, instruction->GetType());
4480 locations->SetOut(calling_convention.GetReturnLocation(DataType::Type::kReference));
4481 } else {
4482 locations->SetOut(Location::RequiresRegister());
4483 if (load_kind == HLoadString::LoadKind::kBssEntry) {
4484 if (codegen_->EmitNonBakerReadBarrier()) {
4485 // For non-Baker read barriers we have a temp-clobbering call.
4486 } else {
4487 // Rely on the pResolveString and marking to save everything we need.
4488 locations->SetCustomSlowPathCallerSaves(OneRegInReferenceOutSaveEverythingCallerSaves());
4489 }
4490 }
4491 }
4492 }
4493
4494 // NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
4495 // move.
VisitLoadString(HLoadString * instruction)4496 void InstructionCodeGeneratorRISCV64::VisitLoadString(HLoadString* instruction)
4497 NO_THREAD_SAFETY_ANALYSIS {
4498 HLoadString::LoadKind load_kind = instruction->GetLoadKind();
4499 LocationSummary* locations = instruction->GetLocations();
4500 Location out_loc = locations->Out();
4501 XRegister out = out_loc.AsRegister<XRegister>();
4502
4503 switch (load_kind) {
4504 case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
4505 DCHECK(codegen_->GetCompilerOptions().IsBootImage() ||
4506 codegen_->GetCompilerOptions().IsBootImageExtension());
4507 CodeGeneratorRISCV64::PcRelativePatchInfo* info_high = codegen_->NewBootImageStringPatch(
4508 instruction->GetDexFile(), instruction->GetStringIndex());
4509 codegen_->EmitPcRelativeAuipcPlaceholder(info_high, out);
4510 CodeGeneratorRISCV64::PcRelativePatchInfo* info_low = codegen_->NewBootImageStringPatch(
4511 instruction->GetDexFile(), instruction->GetStringIndex(), info_high);
4512 codegen_->EmitPcRelativeAddiPlaceholder(info_low, out, out);
4513 return;
4514 }
4515 case HLoadString::LoadKind::kBootImageRelRo: {
4516 DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
4517 uint32_t boot_image_offset = codegen_->GetBootImageOffset(instruction);
4518 codegen_->LoadBootImageRelRoEntry(out, boot_image_offset);
4519 return;
4520 }
4521 case HLoadString::LoadKind::kBssEntry: {
4522 CodeGeneratorRISCV64::PcRelativePatchInfo* info_high = codegen_->NewStringBssEntryPatch(
4523 instruction->GetDexFile(), instruction->GetStringIndex());
4524 codegen_->EmitPcRelativeAuipcPlaceholder(info_high, out);
4525 CodeGeneratorRISCV64::PcRelativePatchInfo* info_low = codegen_->NewStringBssEntryPatch(
4526 instruction->GetDexFile(), instruction->GetStringIndex(), info_high);
4527 codegen_->GenerateGcRootFieldLoad(instruction,
4528 out_loc,
4529 out,
4530 /* offset= */ kLinkTimeOffsetPlaceholderLow,
4531 codegen_->GetCompilerReadBarrierOption(),
4532 &info_low->label);
4533 SlowPathCodeRISCV64* slow_path =
4534 new (codegen_->GetScopedAllocator()) LoadStringSlowPathRISCV64(instruction);
4535 codegen_->AddSlowPath(slow_path);
4536 __ Beqz(out, slow_path->GetEntryLabel());
4537 __ Bind(slow_path->GetExitLabel());
4538 return;
4539 }
4540 case HLoadString::LoadKind::kJitBootImageAddress: {
4541 uint32_t address = reinterpret_cast32<uint32_t>(instruction->GetString().Get());
4542 DCHECK_NE(address, 0u);
4543 __ Loadwu(out, codegen_->DeduplicateBootImageAddressLiteral(address));
4544 return;
4545 }
4546 case HLoadString::LoadKind::kJitTableAddress:
4547 __ Loadwu(
4548 out,
4549 codegen_->DeduplicateJitStringLiteral(
4550 instruction->GetDexFile(), instruction->GetStringIndex(), instruction->GetString()));
4551 codegen_->GenerateGcRootFieldLoad(
4552 instruction, out_loc, out, 0, codegen_->GetCompilerReadBarrierOption());
4553 return;
4554 default:
4555 break;
4556 }
4557
4558 DCHECK(load_kind == HLoadString::LoadKind::kRuntimeCall);
4559 InvokeRuntimeCallingConvention calling_convention;
4560 DCHECK(calling_convention.GetReturnLocation(DataType::Type::kReference).Equals(out_loc));
4561 __ LoadConst32(calling_convention.GetRegisterAt(0), instruction->GetStringIndex().index_);
4562 codegen_->InvokeRuntime(kQuickResolveString, instruction, instruction->GetDexPc());
4563 CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
4564 }
4565
VisitLongConstant(HLongConstant * instruction)4566 void LocationsBuilderRISCV64::VisitLongConstant(HLongConstant* instruction) {
4567 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
4568 locations->SetOut(Location::ConstantLocation(instruction));
4569 }
4570
VisitLongConstant(HLongConstant * instruction)4571 void InstructionCodeGeneratorRISCV64::VisitLongConstant(
4572 [[maybe_unused]] HLongConstant* instruction) {
4573 // Will be generated at use site.
4574 }
4575
VisitMax(HMax * instruction)4576 void LocationsBuilderRISCV64::VisitMax(HMax* instruction) {
4577 HandleBinaryOp(instruction);
4578 }
4579
VisitMax(HMax * instruction)4580 void InstructionCodeGeneratorRISCV64::VisitMax(HMax* instruction) {
4581 HandleBinaryOp(instruction);
4582 }
4583
VisitMemoryBarrier(HMemoryBarrier * instruction)4584 void LocationsBuilderRISCV64::VisitMemoryBarrier(HMemoryBarrier* instruction) {
4585 instruction->SetLocations(nullptr);
4586 }
4587
VisitMemoryBarrier(HMemoryBarrier * instruction)4588 void InstructionCodeGeneratorRISCV64::VisitMemoryBarrier(HMemoryBarrier* instruction) {
4589 codegen_->GenerateMemoryBarrier(instruction->GetBarrierKind());
4590 }
4591
VisitMethodEntryHook(HMethodEntryHook * instruction)4592 void LocationsBuilderRISCV64::VisitMethodEntryHook(HMethodEntryHook* instruction) {
4593 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
4594 }
4595
VisitMethodEntryHook(HMethodEntryHook * instruction)4596 void InstructionCodeGeneratorRISCV64::VisitMethodEntryHook(HMethodEntryHook* instruction) {
4597 DCHECK(codegen_->GetCompilerOptions().IsJitCompiler() && GetGraph()->IsDebuggable());
4598 DCHECK(codegen_->RequiresCurrentMethod());
4599 GenerateMethodEntryExitHook(instruction);
4600 }
4601
VisitMethodExitHook(HMethodExitHook * instruction)4602 void LocationsBuilderRISCV64::VisitMethodExitHook(HMethodExitHook* instruction) {
4603 LocationSummary* locations = new (GetGraph()->GetAllocator())
4604 LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
4605 DataType::Type return_type = instruction->InputAt(0)->GetType();
4606 locations->SetInAt(0, Riscv64ReturnLocation(return_type));
4607 }
4608
VisitMethodExitHook(HMethodExitHook * instruction)4609 void InstructionCodeGeneratorRISCV64::VisitMethodExitHook(HMethodExitHook* instruction) {
4610 DCHECK(codegen_->GetCompilerOptions().IsJitCompiler() && GetGraph()->IsDebuggable());
4611 DCHECK(codegen_->RequiresCurrentMethod());
4612 GenerateMethodEntryExitHook(instruction);
4613 }
4614
VisitMin(HMin * instruction)4615 void LocationsBuilderRISCV64::VisitMin(HMin* instruction) {
4616 HandleBinaryOp(instruction);
4617 }
4618
VisitMin(HMin * instruction)4619 void InstructionCodeGeneratorRISCV64::VisitMin(HMin* instruction) {
4620 HandleBinaryOp(instruction);
4621 }
4622
VisitMonitorOperation(HMonitorOperation * instruction)4623 void LocationsBuilderRISCV64::VisitMonitorOperation(HMonitorOperation* instruction) {
4624 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
4625 instruction, LocationSummary::kCallOnMainOnly);
4626 InvokeRuntimeCallingConvention calling_convention;
4627 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
4628 }
4629
VisitMonitorOperation(HMonitorOperation * instruction)4630 void InstructionCodeGeneratorRISCV64::VisitMonitorOperation(HMonitorOperation* instruction) {
4631 codegen_->InvokeRuntime(instruction->IsEnter() ? kQuickLockObject : kQuickUnlockObject,
4632 instruction,
4633 instruction->GetDexPc());
4634 if (instruction->IsEnter()) {
4635 CheckEntrypointTypes<kQuickLockObject, void, mirror::Object*>();
4636 } else {
4637 CheckEntrypointTypes<kQuickUnlockObject, void, mirror::Object*>();
4638 }
4639 }
4640
VisitMul(HMul * instruction)4641 void LocationsBuilderRISCV64::VisitMul(HMul* instruction) {
4642 LocationSummary* locations =
4643 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
4644 switch (instruction->GetResultType()) {
4645 case DataType::Type::kInt32:
4646 case DataType::Type::kInt64:
4647 locations->SetInAt(0, Location::RequiresRegister());
4648 locations->SetInAt(1, Location::RequiresRegister());
4649 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4650 break;
4651
4652 case DataType::Type::kFloat32:
4653 case DataType::Type::kFloat64:
4654 locations->SetInAt(0, Location::RequiresFpuRegister());
4655 locations->SetInAt(1, Location::RequiresFpuRegister());
4656 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4657 break;
4658
4659 default:
4660 LOG(FATAL) << "Unexpected mul type " << instruction->GetResultType();
4661 }
4662 }
4663
VisitMul(HMul * instruction)4664 void InstructionCodeGeneratorRISCV64::VisitMul(HMul* instruction) {
4665 LocationSummary* locations = instruction->GetLocations();
4666 switch (instruction->GetResultType()) {
4667 case DataType::Type::kInt32:
4668 __ Mulw(locations->Out().AsRegister<XRegister>(),
4669 locations->InAt(0).AsRegister<XRegister>(),
4670 locations->InAt(1).AsRegister<XRegister>());
4671 break;
4672
4673 case DataType::Type::kInt64:
4674 __ Mul(locations->Out().AsRegister<XRegister>(),
4675 locations->InAt(0).AsRegister<XRegister>(),
4676 locations->InAt(1).AsRegister<XRegister>());
4677 break;
4678
4679 case DataType::Type::kFloat32:
4680 case DataType::Type::kFloat64:
4681 FMul(locations->Out().AsFpuRegister<FRegister>(),
4682 locations->InAt(0).AsFpuRegister<FRegister>(),
4683 locations->InAt(1).AsFpuRegister<FRegister>(),
4684 instruction->GetResultType());
4685 break;
4686
4687 default:
4688 LOG(FATAL) << "Unexpected mul type " << instruction->GetResultType();
4689 }
4690 }
4691
VisitNeg(HNeg * instruction)4692 void LocationsBuilderRISCV64::VisitNeg(HNeg* instruction) {
4693 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
4694 switch (instruction->GetResultType()) {
4695 case DataType::Type::kInt32:
4696 case DataType::Type::kInt64:
4697 locations->SetInAt(0, Location::RequiresRegister());
4698 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4699 break;
4700
4701 case DataType::Type::kFloat32:
4702 case DataType::Type::kFloat64:
4703 locations->SetInAt(0, Location::RequiresFpuRegister());
4704 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4705 break;
4706
4707 default:
4708 LOG(FATAL) << "Unexpected neg type " << instruction->GetResultType();
4709 UNREACHABLE();
4710 }
4711 }
4712
VisitNeg(HNeg * instruction)4713 void InstructionCodeGeneratorRISCV64::VisitNeg(HNeg* instruction) {
4714 LocationSummary* locations = instruction->GetLocations();
4715 switch (instruction->GetResultType()) {
4716 case DataType::Type::kInt32:
4717 __ NegW(locations->Out().AsRegister<XRegister>(), locations->InAt(0).AsRegister<XRegister>());
4718 break;
4719
4720 case DataType::Type::kInt64:
4721 __ Neg(locations->Out().AsRegister<XRegister>(), locations->InAt(0).AsRegister<XRegister>());
4722 break;
4723
4724 case DataType::Type::kFloat32:
4725 case DataType::Type::kFloat64:
4726 FNeg(locations->Out().AsFpuRegister<FRegister>(),
4727 locations->InAt(0).AsFpuRegister<FRegister>(),
4728 instruction->GetResultType());
4729 break;
4730
4731 default:
4732 LOG(FATAL) << "Unexpected neg type " << instruction->GetResultType();
4733 UNREACHABLE();
4734 }
4735 }
4736
VisitNewArray(HNewArray * instruction)4737 void LocationsBuilderRISCV64::VisitNewArray(HNewArray* instruction) {
4738 LocationSummary* locations = new (GetGraph()->GetAllocator())
4739 LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
4740 InvokeRuntimeCallingConvention calling_convention;
4741 locations->SetOut(calling_convention.GetReturnLocation(DataType::Type::kReference));
4742 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
4743 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
4744 }
4745
VisitNewArray(HNewArray * instruction)4746 void InstructionCodeGeneratorRISCV64::VisitNewArray(HNewArray* instruction) {
4747 QuickEntrypointEnum entrypoint = CodeGenerator::GetArrayAllocationEntrypoint(instruction);
4748 codegen_->InvokeRuntime(entrypoint, instruction, instruction->GetDexPc());
4749 CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>();
4750 DCHECK(!codegen_->IsLeafMethod());
4751 }
4752
VisitNewInstance(HNewInstance * instruction)4753 void LocationsBuilderRISCV64::VisitNewInstance(HNewInstance* instruction) {
4754 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
4755 instruction, LocationSummary::kCallOnMainOnly);
4756 InvokeRuntimeCallingConvention calling_convention;
4757 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
4758 locations->SetOut(calling_convention.GetReturnLocation(DataType::Type::kReference));
4759 }
4760
VisitNewInstance(HNewInstance * instruction)4761 void InstructionCodeGeneratorRISCV64::VisitNewInstance(HNewInstance* instruction) {
4762 codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
4763 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
4764 }
4765
VisitNop(HNop * instruction)4766 void LocationsBuilderRISCV64::VisitNop(HNop* instruction) {
4767 new (GetGraph()->GetAllocator()) LocationSummary(instruction);
4768 }
4769
VisitNop(HNop * instruction)4770 void InstructionCodeGeneratorRISCV64::VisitNop([[maybe_unused]] HNop* instruction) {
4771 // The environment recording already happened in CodeGenerator::Compile.
4772 }
4773
VisitNot(HNot * instruction)4774 void LocationsBuilderRISCV64::VisitNot(HNot* instruction) {
4775 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
4776 locations->SetInAt(0, Location::RequiresRegister());
4777 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4778 }
4779
VisitNot(HNot * instruction)4780 void InstructionCodeGeneratorRISCV64::VisitNot(HNot* instruction) {
4781 LocationSummary* locations = instruction->GetLocations();
4782 switch (instruction->GetResultType()) {
4783 case DataType::Type::kInt32:
4784 case DataType::Type::kInt64:
4785 __ Not(locations->Out().AsRegister<XRegister>(), locations->InAt(0).AsRegister<XRegister>());
4786 break;
4787
4788 default:
4789 LOG(FATAL) << "Unexpected type for not operation " << instruction->GetResultType();
4790 UNREACHABLE();
4791 }
4792 }
4793
VisitNotEqual(HNotEqual * instruction)4794 void LocationsBuilderRISCV64::VisitNotEqual(HNotEqual* instruction) {
4795 HandleCondition(instruction);
4796 }
4797
VisitNotEqual(HNotEqual * instruction)4798 void InstructionCodeGeneratorRISCV64::VisitNotEqual(HNotEqual* instruction) {
4799 HandleCondition(instruction);
4800 }
4801
VisitNullConstant(HNullConstant * instruction)4802 void LocationsBuilderRISCV64::VisitNullConstant(HNullConstant* instruction) {
4803 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
4804 locations->SetOut(Location::ConstantLocation(instruction));
4805 }
4806
VisitNullConstant(HNullConstant * instruction)4807 void InstructionCodeGeneratorRISCV64::VisitNullConstant(
4808 [[maybe_unused]] HNullConstant* instruction) {
4809 // Will be generated at use site.
4810 }
4811
VisitNullCheck(HNullCheck * instruction)4812 void LocationsBuilderRISCV64::VisitNullCheck(HNullCheck* instruction) {
4813 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
4814 locations->SetInAt(0, Location::RequiresRegister());
4815 }
4816
VisitNullCheck(HNullCheck * instruction)4817 void InstructionCodeGeneratorRISCV64::VisitNullCheck(HNullCheck* instruction) {
4818 codegen_->GenerateNullCheck(instruction);
4819 }
4820
VisitOr(HOr * instruction)4821 void LocationsBuilderRISCV64::VisitOr(HOr* instruction) {
4822 HandleBinaryOp(instruction);
4823 }
4824
VisitOr(HOr * instruction)4825 void InstructionCodeGeneratorRISCV64::VisitOr(HOr* instruction) {
4826 HandleBinaryOp(instruction);
4827 }
4828
VisitPackedSwitch(HPackedSwitch * instruction)4829 void LocationsBuilderRISCV64::VisitPackedSwitch(HPackedSwitch* instruction) {
4830 LocationSummary* locations =
4831 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
4832 locations->SetInAt(0, Location::RequiresRegister());
4833 }
4834
VisitPackedSwitch(HPackedSwitch * instruction)4835 void InstructionCodeGeneratorRISCV64::VisitPackedSwitch(HPackedSwitch* instruction) {
4836 int32_t lower_bound = instruction->GetStartValue();
4837 uint32_t num_entries = instruction->GetNumEntries();
4838 LocationSummary* locations = instruction->GetLocations();
4839 XRegister value = locations->InAt(0).AsRegister<XRegister>();
4840 HBasicBlock* switch_block = instruction->GetBlock();
4841 HBasicBlock* default_block = instruction->GetDefaultBlock();
4842
4843 // Prepare a temporary register and an adjusted zero-based value.
4844 ScratchRegisterScope srs(GetAssembler());
4845 XRegister temp = srs.AllocateXRegister();
4846 XRegister adjusted = value;
4847 if (lower_bound != 0) {
4848 adjusted = temp;
4849 __ AddConst32(temp, value, -lower_bound);
4850 }
4851
4852 // Jump to the default block if the index is out of the packed switch value range.
4853 // Note: We could save one instruction for `num_entries == 1` with BNEZ but the
4854 // `HInstructionBuilder` transforms that case to an `HIf`, so let's keep the code simple.
4855 CHECK_NE(num_entries, 0u); // `HInstructionBuilder` creates a `HGoto` for empty packed-switch.
4856 {
4857 ScratchRegisterScope srs2(GetAssembler());
4858 XRegister temp2 = srs2.AllocateXRegister();
4859 __ LoadConst32(temp2, num_entries);
4860 __ Bgeu(adjusted, temp2, codegen_->GetLabelOf(default_block)); // Can clobber `TMP` if taken.
4861 }
4862
4863 if (num_entries >= kPackedSwitchCompareJumpThreshold) {
4864 GenTableBasedPackedSwitch(adjusted, temp, num_entries, switch_block);
4865 } else {
4866 GenPackedSwitchWithCompares(adjusted, temp, num_entries, switch_block);
4867 }
4868 }
4869
VisitParallelMove(HParallelMove * instruction)4870 void LocationsBuilderRISCV64::VisitParallelMove([[maybe_unused]] HParallelMove* instruction) {
4871 LOG(FATAL) << "Unreachable";
4872 }
4873
VisitParallelMove(HParallelMove * instruction)4874 void InstructionCodeGeneratorRISCV64::VisitParallelMove(HParallelMove* instruction) {
4875 if (instruction->GetNext()->IsSuspendCheck() &&
4876 instruction->GetBlock()->GetLoopInformation() != nullptr) {
4877 HSuspendCheck* suspend_check = instruction->GetNext()->AsSuspendCheck();
4878 // The back edge will generate the suspend check.
4879 codegen_->ClearSpillSlotsFromLoopPhisInStackMap(suspend_check, instruction);
4880 }
4881
4882 codegen_->GetMoveResolver()->EmitNativeCode(instruction);
4883 }
4884
VisitParameterValue(HParameterValue * instruction)4885 void LocationsBuilderRISCV64::VisitParameterValue(HParameterValue* instruction) {
4886 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
4887 Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
4888 if (location.IsStackSlot()) {
4889 location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
4890 } else if (location.IsDoubleStackSlot()) {
4891 location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
4892 }
4893 locations->SetOut(location);
4894 }
4895
VisitParameterValue(HParameterValue * instruction)4896 void InstructionCodeGeneratorRISCV64::VisitParameterValue(
4897 [[maybe_unused]] HParameterValue* instruction) {
4898 // Nothing to do, the parameter is already at its location.
4899 }
4900
VisitPhi(HPhi * instruction)4901 void LocationsBuilderRISCV64::VisitPhi(HPhi* instruction) {
4902 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
4903 for (size_t i = 0, e = locations->GetInputCount(); i < e; ++i) {
4904 locations->SetInAt(i, Location::Any());
4905 }
4906 locations->SetOut(Location::Any());
4907 }
4908
VisitPhi(HPhi * instruction)4909 void InstructionCodeGeneratorRISCV64::VisitPhi([[maybe_unused]] HPhi* instruction) {
4910 LOG(FATAL) << "Unreachable";
4911 }
4912
VisitRem(HRem * instruction)4913 void LocationsBuilderRISCV64::VisitRem(HRem* instruction) {
4914 DataType::Type type = instruction->GetResultType();
4915 LocationSummary::CallKind call_kind =
4916 DataType::IsFloatingPointType(type) ? LocationSummary::kCallOnMainOnly
4917 : LocationSummary::kNoCall;
4918 LocationSummary* locations =
4919 new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
4920
4921 switch (type) {
4922 case DataType::Type::kInt32:
4923 case DataType::Type::kInt64:
4924 locations->SetInAt(0, Location::RequiresRegister());
4925 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
4926 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4927 break;
4928
4929 case DataType::Type::kFloat32:
4930 case DataType::Type::kFloat64: {
4931 InvokeRuntimeCallingConvention calling_convention;
4932 locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0)));
4933 locations->SetInAt(1, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(1)));
4934 locations->SetOut(calling_convention.GetReturnLocation(type));
4935 break;
4936 }
4937
4938 default:
4939 LOG(FATAL) << "Unexpected rem type " << type;
4940 UNREACHABLE();
4941 }
4942 }
4943
VisitRem(HRem * instruction)4944 void InstructionCodeGeneratorRISCV64::VisitRem(HRem* instruction) {
4945 DataType::Type type = instruction->GetType();
4946
4947 switch (type) {
4948 case DataType::Type::kInt32:
4949 case DataType::Type::kInt64:
4950 GenerateDivRemIntegral(instruction);
4951 break;
4952
4953 case DataType::Type::kFloat32:
4954 case DataType::Type::kFloat64: {
4955 QuickEntrypointEnum entrypoint =
4956 (type == DataType::Type::kFloat32) ? kQuickFmodf : kQuickFmod;
4957 codegen_->InvokeRuntime(entrypoint, instruction, instruction->GetDexPc());
4958 if (type == DataType::Type::kFloat32) {
4959 CheckEntrypointTypes<kQuickFmodf, float, float, float>();
4960 } else {
4961 CheckEntrypointTypes<kQuickFmod, double, double, double>();
4962 }
4963 break;
4964 }
4965 default:
4966 LOG(FATAL) << "Unexpected rem type " << type;
4967 UNREACHABLE();
4968 }
4969 }
4970
VisitReturn(HReturn * instruction)4971 void LocationsBuilderRISCV64::VisitReturn(HReturn* instruction) {
4972 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
4973 DataType::Type return_type = instruction->InputAt(0)->GetType();
4974 DCHECK_NE(return_type, DataType::Type::kVoid);
4975 locations->SetInAt(0, Riscv64ReturnLocation(return_type));
4976 }
4977
VisitReturn(HReturn * instruction)4978 void InstructionCodeGeneratorRISCV64::VisitReturn(HReturn* instruction) {
4979 if (GetGraph()->IsCompilingOsr()) {
4980 // To simplify callers of an OSR method, we put a floating point return value
4981 // in both floating point and core return registers.
4982 DataType::Type type = instruction->InputAt(0)->GetType();
4983 if (DataType::IsFloatingPointType(type)) {
4984 FMvX(A0, FA0, type);
4985 }
4986 }
4987 codegen_->GenerateFrameExit();
4988 }
4989
VisitReturnVoid(HReturnVoid * instruction)4990 void LocationsBuilderRISCV64::VisitReturnVoid(HReturnVoid* instruction) {
4991 instruction->SetLocations(nullptr);
4992 }
4993
VisitReturnVoid(HReturnVoid * instruction)4994 void InstructionCodeGeneratorRISCV64::VisitReturnVoid([[maybe_unused]] HReturnVoid* instruction) {
4995 codegen_->GenerateFrameExit();
4996 }
4997
VisitRor(HRor * instruction)4998 void LocationsBuilderRISCV64::VisitRor(HRor* instruction) {
4999 HandleShift(instruction);
5000 }
5001
VisitRor(HRor * instruction)5002 void InstructionCodeGeneratorRISCV64::VisitRor(HRor* instruction) {
5003 HandleShift(instruction);
5004 }
5005
VisitShl(HShl * instruction)5006 void LocationsBuilderRISCV64::VisitShl(HShl* instruction) {
5007 HandleShift(instruction);
5008 }
5009
VisitShl(HShl * instruction)5010 void InstructionCodeGeneratorRISCV64::VisitShl(HShl* instruction) {
5011 HandleShift(instruction);
5012 }
5013
VisitShr(HShr * instruction)5014 void LocationsBuilderRISCV64::VisitShr(HShr* instruction) {
5015 HandleShift(instruction);
5016 }
5017
VisitShr(HShr * instruction)5018 void InstructionCodeGeneratorRISCV64::VisitShr(HShr* instruction) {
5019 HandleShift(instruction);
5020 }
5021
VisitStaticFieldGet(HStaticFieldGet * instruction)5022 void LocationsBuilderRISCV64::VisitStaticFieldGet(HStaticFieldGet* instruction) {
5023 HandleFieldGet(instruction);
5024 }
5025
VisitStaticFieldGet(HStaticFieldGet * instruction)5026 void InstructionCodeGeneratorRISCV64::VisitStaticFieldGet(HStaticFieldGet* instruction) {
5027 HandleFieldGet(instruction, instruction->GetFieldInfo());
5028 }
5029
VisitStaticFieldSet(HStaticFieldSet * instruction)5030 void LocationsBuilderRISCV64::VisitStaticFieldSet(HStaticFieldSet* instruction) {
5031 HandleFieldSet(instruction);
5032 }
5033
VisitStaticFieldSet(HStaticFieldSet * instruction)5034 void InstructionCodeGeneratorRISCV64::VisitStaticFieldSet(HStaticFieldSet* instruction) {
5035 HandleFieldSet(instruction,
5036 instruction->GetFieldInfo(),
5037 instruction->GetValueCanBeNull(),
5038 instruction->GetWriteBarrierKind());
5039 }
5040
VisitStringBuilderAppend(HStringBuilderAppend * instruction)5041 void LocationsBuilderRISCV64::VisitStringBuilderAppend(HStringBuilderAppend* instruction) {
5042 codegen_->CreateStringBuilderAppendLocations(instruction, Location::RegisterLocation(A0));
5043 }
5044
VisitStringBuilderAppend(HStringBuilderAppend * instruction)5045 void InstructionCodeGeneratorRISCV64::VisitStringBuilderAppend(HStringBuilderAppend* instruction) {
5046 __ LoadConst32(A0, instruction->GetFormat()->GetValue());
5047 codegen_->InvokeRuntime(kQuickStringBuilderAppend, instruction, instruction->GetDexPc());
5048 }
5049
VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet * instruction)5050 void LocationsBuilderRISCV64::VisitUnresolvedInstanceFieldGet(
5051 HUnresolvedInstanceFieldGet* instruction) {
5052 FieldAccessCallingConventionRISCV64 calling_convention;
5053 codegen_->CreateUnresolvedFieldLocationSummary(
5054 instruction, instruction->GetFieldType(), calling_convention);
5055 }
5056
VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet * instruction)5057 void InstructionCodeGeneratorRISCV64::VisitUnresolvedInstanceFieldGet(
5058 HUnresolvedInstanceFieldGet* instruction) {
5059 FieldAccessCallingConventionRISCV64 calling_convention;
5060 codegen_->GenerateUnresolvedFieldAccess(instruction,
5061 instruction->GetFieldType(),
5062 instruction->GetFieldIndex(),
5063 instruction->GetDexPc(),
5064 calling_convention);
5065 }
5066
VisitUnresolvedInstanceFieldSet(HUnresolvedInstanceFieldSet * instruction)5067 void LocationsBuilderRISCV64::VisitUnresolvedInstanceFieldSet(
5068 HUnresolvedInstanceFieldSet* instruction) {
5069 FieldAccessCallingConventionRISCV64 calling_convention;
5070 codegen_->CreateUnresolvedFieldLocationSummary(
5071 instruction, instruction->GetFieldType(), calling_convention);
5072 }
5073
VisitUnresolvedInstanceFieldSet(HUnresolvedInstanceFieldSet * instruction)5074 void InstructionCodeGeneratorRISCV64::VisitUnresolvedInstanceFieldSet(
5075 HUnresolvedInstanceFieldSet* instruction) {
5076 FieldAccessCallingConventionRISCV64 calling_convention;
5077 codegen_->GenerateUnresolvedFieldAccess(instruction,
5078 instruction->GetFieldType(),
5079 instruction->GetFieldIndex(),
5080 instruction->GetDexPc(),
5081 calling_convention);
5082 }
5083
VisitUnresolvedStaticFieldGet(HUnresolvedStaticFieldGet * instruction)5084 void LocationsBuilderRISCV64::VisitUnresolvedStaticFieldGet(
5085 HUnresolvedStaticFieldGet* instruction) {
5086 FieldAccessCallingConventionRISCV64 calling_convention;
5087 codegen_->CreateUnresolvedFieldLocationSummary(
5088 instruction, instruction->GetFieldType(), calling_convention);
5089 }
5090
VisitUnresolvedStaticFieldGet(HUnresolvedStaticFieldGet * instruction)5091 void InstructionCodeGeneratorRISCV64::VisitUnresolvedStaticFieldGet(
5092 HUnresolvedStaticFieldGet* instruction) {
5093 FieldAccessCallingConventionRISCV64 calling_convention;
5094 codegen_->GenerateUnresolvedFieldAccess(instruction,
5095 instruction->GetFieldType(),
5096 instruction->GetFieldIndex(),
5097 instruction->GetDexPc(),
5098 calling_convention);
5099 }
5100
VisitUnresolvedStaticFieldSet(HUnresolvedStaticFieldSet * instruction)5101 void LocationsBuilderRISCV64::VisitUnresolvedStaticFieldSet(
5102 HUnresolvedStaticFieldSet* instruction) {
5103 FieldAccessCallingConventionRISCV64 calling_convention;
5104 codegen_->CreateUnresolvedFieldLocationSummary(
5105 instruction, instruction->GetFieldType(), calling_convention);
5106 }
5107
VisitUnresolvedStaticFieldSet(HUnresolvedStaticFieldSet * instruction)5108 void InstructionCodeGeneratorRISCV64::VisitUnresolvedStaticFieldSet(
5109 HUnresolvedStaticFieldSet* instruction) {
5110 FieldAccessCallingConventionRISCV64 calling_convention;
5111 codegen_->GenerateUnresolvedFieldAccess(instruction,
5112 instruction->GetFieldType(),
5113 instruction->GetFieldIndex(),
5114 instruction->GetDexPc(),
5115 calling_convention);
5116 }
5117
VisitSelect(HSelect * instruction)5118 void LocationsBuilderRISCV64::VisitSelect(HSelect* instruction) {
5119 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
5120 if (DataType::IsFloatingPointType(instruction->GetType())) {
5121 locations->SetInAt(0, FpuRegisterOrZeroBitPatternLocation(instruction->GetFalseValue()));
5122 locations->SetInAt(1, FpuRegisterOrZeroBitPatternLocation(instruction->GetTrueValue()));
5123 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
5124 if (!locations->InAt(0).IsConstant() && !locations->InAt(1).IsConstant()) {
5125 locations->AddTemp(Location::RequiresRegister());
5126 }
5127 } else {
5128 locations->SetInAt(0, RegisterOrZeroBitPatternLocation(instruction->GetFalseValue()));
5129 locations->SetInAt(1, RegisterOrZeroBitPatternLocation(instruction->GetTrueValue()));
5130 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
5131 }
5132
5133 if (IsBooleanValueOrMaterializedCondition(instruction->GetCondition())) {
5134 locations->SetInAt(2, Location::RequiresRegister());
5135 }
5136 }
5137
VisitSelect(HSelect * instruction)5138 void InstructionCodeGeneratorRISCV64::VisitSelect(HSelect* instruction) {
5139 LocationSummary* locations = instruction->GetLocations();
5140 HInstruction* cond = instruction->GetCondition();
5141 ScratchRegisterScope srs(GetAssembler());
5142 XRegister tmp = srs.AllocateXRegister();
5143 if (!IsBooleanValueOrMaterializedCondition(cond)) {
5144 DataType::Type cond_type = cond->InputAt(0)->GetType();
5145 IfCondition if_cond = cond->AsCondition()->GetCondition();
5146 if (DataType::IsFloatingPointType(cond_type)) {
5147 GenerateFpCondition(if_cond,
5148 cond->AsCondition()->IsGtBias(),
5149 cond_type,
5150 cond->GetLocations(),
5151 /*label=*/ nullptr,
5152 tmp,
5153 /*to_all_bits=*/ true);
5154 } else {
5155 GenerateIntLongCondition(if_cond, cond->GetLocations(), tmp, /*to_all_bits=*/ true);
5156 }
5157 } else {
5158 // TODO(riscv64): Remove the normalizing SNEZ when we can ensure that booleans
5159 // have only values 0 and 1. b/279302742
5160 __ Snez(tmp, locations->InAt(2).AsRegister<XRegister>());
5161 __ Neg(tmp, tmp);
5162 }
5163
5164 XRegister true_reg, false_reg, xor_reg, out_reg;
5165 DataType::Type type = instruction->GetType();
5166 if (DataType::IsFloatingPointType(type)) {
5167 if (locations->InAt(0).IsConstant()) {
5168 DCHECK(locations->InAt(0).GetConstant()->IsZeroBitPattern());
5169 false_reg = Zero;
5170 } else {
5171 false_reg = srs.AllocateXRegister();
5172 FMvX(false_reg, locations->InAt(0).AsFpuRegister<FRegister>(), type);
5173 }
5174 if (locations->InAt(1).IsConstant()) {
5175 DCHECK(locations->InAt(1).GetConstant()->IsZeroBitPattern());
5176 true_reg = Zero;
5177 } else {
5178 true_reg = (false_reg == Zero) ? srs.AllocateXRegister()
5179 : locations->GetTemp(0).AsRegister<XRegister>();
5180 FMvX(true_reg, locations->InAt(1).AsFpuRegister<FRegister>(), type);
5181 }
5182 // We can clobber the "true value" with the XOR result.
5183 // Note: The XOR is not emitted if `true_reg == Zero`, see below.
5184 xor_reg = true_reg;
5185 out_reg = tmp;
5186 } else {
5187 false_reg = InputXRegisterOrZero(locations->InAt(0));
5188 true_reg = InputXRegisterOrZero(locations->InAt(1));
5189 xor_reg = srs.AllocateXRegister();
5190 out_reg = locations->Out().AsRegister<XRegister>();
5191 }
5192
5193 // We use a branch-free implementation of `HSelect`.
5194 // With `tmp` initialized to 0 for `false` and -1 for `true`:
5195 // xor xor_reg, false_reg, true_reg
5196 // and tmp, tmp, xor_reg
5197 // xor out_reg, tmp, false_reg
5198 if (false_reg == Zero) {
5199 xor_reg = true_reg;
5200 } else if (true_reg == Zero) {
5201 xor_reg = false_reg;
5202 } else {
5203 DCHECK_NE(xor_reg, Zero);
5204 __ Xor(xor_reg, false_reg, true_reg);
5205 }
5206 __ And(tmp, tmp, xor_reg);
5207 __ Xor(out_reg, tmp, false_reg);
5208
5209 if (type == DataType::Type::kFloat64) {
5210 __ FMvDX(locations->Out().AsFpuRegister<FRegister>(), out_reg);
5211 } else if (type == DataType::Type::kFloat32) {
5212 __ FMvWX(locations->Out().AsFpuRegister<FRegister>(), out_reg);
5213 }
5214 }
5215
VisitSub(HSub * instruction)5216 void LocationsBuilderRISCV64::VisitSub(HSub* instruction) {
5217 HandleBinaryOp(instruction);
5218 }
5219
VisitSub(HSub * instruction)5220 void InstructionCodeGeneratorRISCV64::VisitSub(HSub* instruction) {
5221 HandleBinaryOp(instruction);
5222 }
5223
VisitSuspendCheck(HSuspendCheck * instruction)5224 void LocationsBuilderRISCV64::VisitSuspendCheck(HSuspendCheck* instruction) {
5225 LocationSummary* locations = new (GetGraph()->GetAllocator())
5226 LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
5227 // In suspend check slow path, usually there are no caller-save registers at all.
5228 // If SIMD instructions are present, however, we force spilling all live SIMD
5229 // registers in full width (since the runtime only saves/restores lower part).
5230 locations->SetCustomSlowPathCallerSaves(GetGraph()->HasSIMD() ? RegisterSet::AllFpu() :
5231 RegisterSet::Empty());
5232 }
5233
VisitSuspendCheck(HSuspendCheck * instruction)5234 void InstructionCodeGeneratorRISCV64::VisitSuspendCheck(HSuspendCheck* instruction) {
5235 HBasicBlock* block = instruction->GetBlock();
5236 if (block->GetLoopInformation() != nullptr) {
5237 DCHECK(block->GetLoopInformation()->GetSuspendCheck() == instruction);
5238 // The back edge will generate the suspend check.
5239 return;
5240 }
5241 if (block->IsEntryBlock() && instruction->GetNext()->IsGoto()) {
5242 // The goto will generate the suspend check.
5243 return;
5244 }
5245 GenerateSuspendCheck(instruction, nullptr);
5246 }
5247
VisitThrow(HThrow * instruction)5248 void LocationsBuilderRISCV64::VisitThrow(HThrow* instruction) {
5249 LocationSummary* locations = new (GetGraph()->GetAllocator())
5250 LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
5251 InvokeRuntimeCallingConvention calling_convention;
5252 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
5253 }
5254
VisitThrow(HThrow * instruction)5255 void InstructionCodeGeneratorRISCV64::VisitThrow(HThrow* instruction) {
5256 codegen_->InvokeRuntime(kQuickDeliverException, instruction, instruction->GetDexPc());
5257 CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
5258 }
5259
VisitTryBoundary(HTryBoundary * instruction)5260 void LocationsBuilderRISCV64::VisitTryBoundary(HTryBoundary* instruction) {
5261 instruction->SetLocations(nullptr);
5262 }
5263
VisitTryBoundary(HTryBoundary * instruction)5264 void InstructionCodeGeneratorRISCV64::VisitTryBoundary(HTryBoundary* instruction) {
5265 HBasicBlock* successor = instruction->GetNormalFlowSuccessor();
5266 if (!successor->IsExitBlock()) {
5267 HandleGoto(instruction, successor);
5268 }
5269 }
5270
VisitTypeConversion(HTypeConversion * instruction)5271 void LocationsBuilderRISCV64::VisitTypeConversion(HTypeConversion* instruction) {
5272 DataType::Type input_type = instruction->GetInputType();
5273 DataType::Type result_type = instruction->GetResultType();
5274 DCHECK(!DataType::IsTypeConversionImplicit(input_type, result_type))
5275 << input_type << " -> " << result_type;
5276
5277 if ((input_type == DataType::Type::kReference) || (input_type == DataType::Type::kVoid) ||
5278 (result_type == DataType::Type::kReference) || (result_type == DataType::Type::kVoid)) {
5279 LOG(FATAL) << "Unexpected type conversion from " << input_type << " to " << result_type;
5280 }
5281
5282 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
5283
5284 if (DataType::IsFloatingPointType(input_type)) {
5285 locations->SetInAt(0, Location::RequiresFpuRegister());
5286 } else {
5287 locations->SetInAt(0, Location::RequiresRegister());
5288 }
5289
5290 if (DataType::IsFloatingPointType(result_type)) {
5291 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
5292 } else {
5293 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5294 }
5295 }
5296
VisitTypeConversion(HTypeConversion * instruction)5297 void InstructionCodeGeneratorRISCV64::VisitTypeConversion(HTypeConversion* instruction) {
5298 LocationSummary* locations = instruction->GetLocations();
5299 DataType::Type result_type = instruction->GetResultType();
5300 DataType::Type input_type = instruction->GetInputType();
5301
5302 DCHECK(!DataType::IsTypeConversionImplicit(input_type, result_type))
5303 << input_type << " -> " << result_type;
5304
5305 if (DataType::IsIntegralType(result_type) && DataType::IsIntegralType(input_type)) {
5306 XRegister dst = locations->Out().AsRegister<XRegister>();
5307 XRegister src = locations->InAt(0).AsRegister<XRegister>();
5308 switch (result_type) {
5309 case DataType::Type::kUint8:
5310 __ ZextB(dst, src);
5311 break;
5312 case DataType::Type::kInt8:
5313 __ SextB(dst, src);
5314 break;
5315 case DataType::Type::kUint16:
5316 __ ZextH(dst, src);
5317 break;
5318 case DataType::Type::kInt16:
5319 __ SextH(dst, src);
5320 break;
5321 case DataType::Type::kInt32:
5322 case DataType::Type::kInt64:
5323 // Sign-extend 32-bit int into bits 32 through 63 for int-to-long and long-to-int
5324 // conversions, except when the input and output registers are the same and we are not
5325 // converting longs to shorter types. In these cases, do nothing.
5326 if ((input_type == DataType::Type::kInt64) || (dst != src)) {
5327 __ Addiw(dst, src, 0);
5328 }
5329 break;
5330
5331 default:
5332 LOG(FATAL) << "Unexpected type conversion from " << input_type
5333 << " to " << result_type;
5334 UNREACHABLE();
5335 }
5336 } else if (DataType::IsFloatingPointType(result_type) && DataType::IsIntegralType(input_type)) {
5337 FRegister dst = locations->Out().AsFpuRegister<FRegister>();
5338 XRegister src = locations->InAt(0).AsRegister<XRegister>();
5339 if (input_type == DataType::Type::kInt64) {
5340 if (result_type == DataType::Type::kFloat32) {
5341 __ FCvtSL(dst, src, FPRoundingMode::kRNE);
5342 } else {
5343 __ FCvtDL(dst, src, FPRoundingMode::kRNE);
5344 }
5345 } else {
5346 if (result_type == DataType::Type::kFloat32) {
5347 __ FCvtSW(dst, src, FPRoundingMode::kRNE);
5348 } else {
5349 __ FCvtDW(dst, src); // No rounding.
5350 }
5351 }
5352 } else if (DataType::IsIntegralType(result_type) && DataType::IsFloatingPointType(input_type)) {
5353 CHECK(result_type == DataType::Type::kInt32 || result_type == DataType::Type::kInt64);
5354 XRegister dst = locations->Out().AsRegister<XRegister>();
5355 FRegister src = locations->InAt(0).AsFpuRegister<FRegister>();
5356 if (result_type == DataType::Type::kInt64) {
5357 if (input_type == DataType::Type::kFloat32) {
5358 __ FCvtLS(dst, src, FPRoundingMode::kRTZ);
5359 } else {
5360 __ FCvtLD(dst, src, FPRoundingMode::kRTZ);
5361 }
5362 } else {
5363 if (input_type == DataType::Type::kFloat32) {
5364 __ FCvtWS(dst, src, FPRoundingMode::kRTZ);
5365 } else {
5366 __ FCvtWD(dst, src, FPRoundingMode::kRTZ);
5367 }
5368 }
5369 // For NaN inputs we need to return 0.
5370 ScratchRegisterScope srs(GetAssembler());
5371 XRegister tmp = srs.AllocateXRegister();
5372 FClass(tmp, src, input_type);
5373 __ Sltiu(tmp, tmp, kFClassNaNMinValue); // 0 for NaN, 1 otherwise.
5374 __ Neg(tmp, tmp); // 0 for NaN, -1 otherwise.
5375 __ And(dst, dst, tmp); // Cleared for NaN.
5376 } else if (DataType::IsFloatingPointType(result_type) &&
5377 DataType::IsFloatingPointType(input_type)) {
5378 FRegister dst = locations->Out().AsFpuRegister<FRegister>();
5379 FRegister src = locations->InAt(0).AsFpuRegister<FRegister>();
5380 if (result_type == DataType::Type::kFloat32) {
5381 __ FCvtSD(dst, src);
5382 } else {
5383 __ FCvtDS(dst, src);
5384 }
5385 } else {
5386 LOG(FATAL) << "Unexpected or unimplemented type conversion from " << input_type
5387 << " to " << result_type;
5388 UNREACHABLE();
5389 }
5390 }
5391
VisitUShr(HUShr * instruction)5392 void LocationsBuilderRISCV64::VisitUShr(HUShr* instruction) {
5393 HandleShift(instruction);
5394 }
5395
VisitUShr(HUShr * instruction)5396 void InstructionCodeGeneratorRISCV64::VisitUShr(HUShr* instruction) {
5397 HandleShift(instruction);
5398 }
5399
VisitXor(HXor * instruction)5400 void LocationsBuilderRISCV64::VisitXor(HXor* instruction) {
5401 HandleBinaryOp(instruction);
5402 }
5403
VisitXor(HXor * instruction)5404 void InstructionCodeGeneratorRISCV64::VisitXor(HXor* instruction) {
5405 HandleBinaryOp(instruction);
5406 }
5407
VisitRiscv64ShiftAdd(HRiscv64ShiftAdd * instruction)5408 void LocationsBuilderRISCV64::VisitRiscv64ShiftAdd(HRiscv64ShiftAdd* instruction) {
5409 DCHECK(codegen_->GetInstructionSetFeatures().HasZba());
5410 DCHECK_EQ(instruction->GetType(), DataType::Type::kInt64)
5411 << "Unexpected ShiftAdd type: " << instruction->GetType();
5412
5413 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
5414 locations->SetInAt(0, Location::RequiresRegister());
5415 locations->SetInAt(1, Location::RequiresRegister());
5416 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5417 }
5418
VisitRiscv64ShiftAdd(HRiscv64ShiftAdd * instruction)5419 void InstructionCodeGeneratorRISCV64::VisitRiscv64ShiftAdd(HRiscv64ShiftAdd* instruction) {
5420 DCHECK_EQ(instruction->GetType(), DataType::Type::kInt64)
5421 << "Unexpected ShiftAdd type: " << instruction->GetType();
5422 LocationSummary* locations = instruction->GetLocations();
5423 XRegister first = locations->InAt(0).AsRegister<XRegister>();
5424 XRegister second = locations->InAt(1).AsRegister<XRegister>();
5425 XRegister dest = locations->Out().AsRegister<XRegister>();
5426
5427 switch (instruction->GetDistance()) {
5428 case 1:
5429 __ Sh1Add(dest, first, second);
5430 break;
5431 case 2:
5432 __ Sh2Add(dest, first, second);
5433 break;
5434 case 3:
5435 __ Sh3Add(dest, first, second);
5436 break;
5437 default:
5438 LOG(FATAL) << "Unexpected distance of ShiftAdd: " << instruction->GetDistance();
5439 UNREACHABLE();
5440 }
5441 }
5442
VisitBitwiseNegatedRight(HBitwiseNegatedRight * instruction)5443 void LocationsBuilderRISCV64::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) {
5444 DCHECK(codegen_->GetInstructionSetFeatures().HasZbb());
5445 DCHECK(DataType::IsIntegralType(instruction->GetType())) << instruction->GetType();
5446
5447 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
5448 locations->SetInAt(0, Location::RequiresRegister());
5449 locations->SetInAt(1, Location::RequiresRegister());
5450 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5451 }
5452
VisitBitwiseNegatedRight(HBitwiseNegatedRight * instruction)5453 void InstructionCodeGeneratorRISCV64::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) {
5454 LocationSummary* locations = instruction->GetLocations();
5455 XRegister lhs = locations->InAt(0).AsRegister<XRegister>();
5456 XRegister rhs = locations->InAt(1).AsRegister<XRegister>();
5457 XRegister dst = locations->Out().AsRegister<XRegister>();
5458
5459 switch (instruction->GetOpKind()) {
5460 case HInstruction::kAnd:
5461 __ Andn(dst, lhs, rhs);
5462 break;
5463 case HInstruction::kOr:
5464 __ Orn(dst, lhs, rhs);
5465 break;
5466 case HInstruction::kXor:
5467 __ Xnor(dst, lhs, rhs);
5468 break;
5469 default:
5470 LOG(FATAL) << "Unreachable";
5471 }
5472 }
5473
VisitVecReplicateScalar(HVecReplicateScalar * instruction)5474 void LocationsBuilderRISCV64::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
5475 UNUSED(instruction);
5476 LOG(FATAL) << "Unimplemented";
5477 }
5478
VisitVecReplicateScalar(HVecReplicateScalar * instruction)5479 void InstructionCodeGeneratorRISCV64::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
5480 UNUSED(instruction);
5481 LOG(FATAL) << "Unimplemented";
5482 }
5483
VisitVecExtractScalar(HVecExtractScalar * instruction)5484 void LocationsBuilderRISCV64::VisitVecExtractScalar(HVecExtractScalar* instruction) {
5485 UNUSED(instruction);
5486 LOG(FATAL) << "Unimplemented";
5487 }
5488
VisitVecExtractScalar(HVecExtractScalar * instruction)5489 void InstructionCodeGeneratorRISCV64::VisitVecExtractScalar(HVecExtractScalar* instruction) {
5490 UNUSED(instruction);
5491 LOG(FATAL) << "Unimplemented";
5492 }
5493
VisitVecReduce(HVecReduce * instruction)5494 void LocationsBuilderRISCV64::VisitVecReduce(HVecReduce* instruction) {
5495 UNUSED(instruction);
5496 LOG(FATAL) << "Unimplemented";
5497 }
5498
VisitVecReduce(HVecReduce * instruction)5499 void InstructionCodeGeneratorRISCV64::VisitVecReduce(HVecReduce* instruction) {
5500 UNUSED(instruction);
5501 LOG(FATAL) << "Unimplemented";
5502 }
5503
VisitVecCnv(HVecCnv * instruction)5504 void LocationsBuilderRISCV64::VisitVecCnv(HVecCnv* instruction) {
5505 UNUSED(instruction);
5506 LOG(FATAL) << "Unimplemented";
5507 }
5508
VisitVecCnv(HVecCnv * instruction)5509 void InstructionCodeGeneratorRISCV64::VisitVecCnv(HVecCnv* instruction) {
5510 UNUSED(instruction);
5511 LOG(FATAL) << "Unimplemented";
5512 }
5513
VisitVecNeg(HVecNeg * instruction)5514 void LocationsBuilderRISCV64::VisitVecNeg(HVecNeg* instruction) {
5515 UNUSED(instruction);
5516 LOG(FATAL) << "Unimplemented";
5517 }
5518
VisitVecNeg(HVecNeg * instruction)5519 void InstructionCodeGeneratorRISCV64::VisitVecNeg(HVecNeg* instruction) {
5520 UNUSED(instruction);
5521 LOG(FATAL) << "Unimplemented";
5522 }
5523
VisitVecAbs(HVecAbs * instruction)5524 void LocationsBuilderRISCV64::VisitVecAbs(HVecAbs* instruction) {
5525 UNUSED(instruction);
5526 LOG(FATAL) << "Unimplemented";
5527 }
5528
VisitVecAbs(HVecAbs * instruction)5529 void InstructionCodeGeneratorRISCV64::VisitVecAbs(HVecAbs* instruction) {
5530 UNUSED(instruction);
5531 LOG(FATAL) << "Unimplemented";
5532 }
5533
VisitVecNot(HVecNot * instruction)5534 void LocationsBuilderRISCV64::VisitVecNot(HVecNot* instruction) {
5535 UNUSED(instruction);
5536 LOG(FATAL) << "Unimplemented";
5537 }
5538
VisitVecNot(HVecNot * instruction)5539 void InstructionCodeGeneratorRISCV64::VisitVecNot(HVecNot* instruction) {
5540 UNUSED(instruction);
5541 LOG(FATAL) << "Unimplemented";
5542 }
5543
VisitVecAdd(HVecAdd * instruction)5544 void LocationsBuilderRISCV64::VisitVecAdd(HVecAdd* instruction) {
5545 UNUSED(instruction);
5546 LOG(FATAL) << "Unimplemented";
5547 }
5548
VisitVecAdd(HVecAdd * instruction)5549 void InstructionCodeGeneratorRISCV64::VisitVecAdd(HVecAdd* instruction) {
5550 UNUSED(instruction);
5551 LOG(FATAL) << "Unimplemented";
5552 }
5553
VisitVecHalvingAdd(HVecHalvingAdd * instruction)5554 void LocationsBuilderRISCV64::VisitVecHalvingAdd(HVecHalvingAdd* instruction) {
5555 UNUSED(instruction);
5556 LOG(FATAL) << "Unimplemented";
5557 }
5558
VisitVecHalvingAdd(HVecHalvingAdd * instruction)5559 void InstructionCodeGeneratorRISCV64::VisitVecHalvingAdd(HVecHalvingAdd* instruction) {
5560 UNUSED(instruction);
5561 LOG(FATAL) << "Unimplemented";
5562 }
5563
VisitVecSub(HVecSub * instruction)5564 void LocationsBuilderRISCV64::VisitVecSub(HVecSub* instruction) {
5565 UNUSED(instruction);
5566 LOG(FATAL) << "Unimplemented";
5567 }
5568
VisitVecSub(HVecSub * instruction)5569 void InstructionCodeGeneratorRISCV64::VisitVecSub(HVecSub* instruction) {
5570 UNUSED(instruction);
5571 LOG(FATAL) << "Unimplemented";
5572 }
5573
VisitVecMul(HVecMul * instruction)5574 void LocationsBuilderRISCV64::VisitVecMul(HVecMul* instruction) {
5575 UNUSED(instruction);
5576 LOG(FATAL) << "Unimplemented";
5577 }
5578
VisitVecMul(HVecMul * instruction)5579 void InstructionCodeGeneratorRISCV64::VisitVecMul(HVecMul* instruction) {
5580 UNUSED(instruction);
5581 LOG(FATAL) << "Unimplemented";
5582 }
5583
VisitVecDiv(HVecDiv * instruction)5584 void LocationsBuilderRISCV64::VisitVecDiv(HVecDiv* instruction) {
5585 UNUSED(instruction);
5586 LOG(FATAL) << "Unimplemented";
5587 }
5588
VisitVecDiv(HVecDiv * instruction)5589 void InstructionCodeGeneratorRISCV64::VisitVecDiv(HVecDiv* instruction) {
5590 UNUSED(instruction);
5591 LOG(FATAL) << "Unimplemented";
5592 }
5593
VisitVecMin(HVecMin * instruction)5594 void LocationsBuilderRISCV64::VisitVecMin(HVecMin* instruction) {
5595 UNUSED(instruction);
5596 LOG(FATAL) << "Unimplemented";
5597 }
5598
VisitVecMin(HVecMin * instruction)5599 void InstructionCodeGeneratorRISCV64::VisitVecMin(HVecMin* instruction) {
5600 UNUSED(instruction);
5601 LOG(FATAL) << "Unimplemented";
5602 }
5603
VisitVecMax(HVecMax * instruction)5604 void LocationsBuilderRISCV64::VisitVecMax(HVecMax* instruction) {
5605 UNUSED(instruction);
5606 LOG(FATAL) << "Unimplemented";
5607 }
5608
VisitVecMax(HVecMax * instruction)5609 void InstructionCodeGeneratorRISCV64::VisitVecMax(HVecMax* instruction) {
5610 UNUSED(instruction);
5611 LOG(FATAL) << "Unimplemented";
5612 }
5613
VisitVecAnd(HVecAnd * instruction)5614 void LocationsBuilderRISCV64::VisitVecAnd(HVecAnd* instruction) {
5615 UNUSED(instruction);
5616 LOG(FATAL) << "Unimplemented";
5617 }
5618
VisitVecAnd(HVecAnd * instruction)5619 void InstructionCodeGeneratorRISCV64::VisitVecAnd(HVecAnd* instruction) {
5620 UNUSED(instruction);
5621 LOG(FATAL) << "Unimplemented";
5622 }
5623
VisitVecAndNot(HVecAndNot * instruction)5624 void LocationsBuilderRISCV64::VisitVecAndNot(HVecAndNot* instruction) {
5625 UNUSED(instruction);
5626 LOG(FATAL) << "Unimplemented";
5627 }
5628
VisitVecAndNot(HVecAndNot * instruction)5629 void InstructionCodeGeneratorRISCV64::VisitVecAndNot(HVecAndNot* instruction) {
5630 UNUSED(instruction);
5631 LOG(FATAL) << "Unimplemented";
5632 }
5633
VisitVecOr(HVecOr * instruction)5634 void LocationsBuilderRISCV64::VisitVecOr(HVecOr* instruction) {
5635 UNUSED(instruction);
5636 LOG(FATAL) << "Unimplemented";
5637 }
5638
VisitVecOr(HVecOr * instruction)5639 void InstructionCodeGeneratorRISCV64::VisitVecOr(HVecOr* instruction) {
5640 UNUSED(instruction);
5641 LOG(FATAL) << "Unimplemented";
5642 }
5643
VisitVecXor(HVecXor * instruction)5644 void LocationsBuilderRISCV64::VisitVecXor(HVecXor* instruction) {
5645 UNUSED(instruction);
5646 LOG(FATAL) << "Unimplemented";
5647 }
5648
VisitVecXor(HVecXor * instruction)5649 void InstructionCodeGeneratorRISCV64::VisitVecXor(HVecXor* instruction) {
5650 UNUSED(instruction);
5651 LOG(FATAL) << "Unimplemented";
5652 }
5653
VisitVecSaturationAdd(HVecSaturationAdd * instruction)5654 void LocationsBuilderRISCV64::VisitVecSaturationAdd(HVecSaturationAdd* instruction) {
5655 UNUSED(instruction);
5656 LOG(FATAL) << "Unimplemented";
5657 }
5658
VisitVecSaturationAdd(HVecSaturationAdd * instruction)5659 void InstructionCodeGeneratorRISCV64::VisitVecSaturationAdd(HVecSaturationAdd* instruction) {
5660 UNUSED(instruction);
5661 LOG(FATAL) << "Unimplemented";
5662 }
5663
VisitVecSaturationSub(HVecSaturationSub * instruction)5664 void LocationsBuilderRISCV64::VisitVecSaturationSub(HVecSaturationSub* instruction) {
5665 UNUSED(instruction);
5666 LOG(FATAL) << "Unimplemented";
5667 }
5668
VisitVecSaturationSub(HVecSaturationSub * instruction)5669 void InstructionCodeGeneratorRISCV64::VisitVecSaturationSub(HVecSaturationSub* instruction) {
5670 UNUSED(instruction);
5671 LOG(FATAL) << "Unimplemented";
5672 }
5673
VisitVecShl(HVecShl * instruction)5674 void LocationsBuilderRISCV64::VisitVecShl(HVecShl* instruction) {
5675 UNUSED(instruction);
5676 LOG(FATAL) << "Unimplemented";
5677 }
5678
VisitVecShl(HVecShl * instruction)5679 void InstructionCodeGeneratorRISCV64::VisitVecShl(HVecShl* instruction) {
5680 UNUSED(instruction);
5681 LOG(FATAL) << "Unimplemented";
5682 }
5683
VisitVecShr(HVecShr * instruction)5684 void LocationsBuilderRISCV64::VisitVecShr(HVecShr* instruction) {
5685 UNUSED(instruction);
5686 LOG(FATAL) << "Unimplemented";
5687 }
5688
VisitVecShr(HVecShr * instruction)5689 void InstructionCodeGeneratorRISCV64::VisitVecShr(HVecShr* instruction) {
5690 UNUSED(instruction);
5691 LOG(FATAL) << "Unimplemented";
5692 }
5693
VisitVecUShr(HVecUShr * instruction)5694 void LocationsBuilderRISCV64::VisitVecUShr(HVecUShr* instruction) {
5695 UNUSED(instruction);
5696 LOG(FATAL) << "Unimplemented";
5697 }
5698
VisitVecUShr(HVecUShr * instruction)5699 void InstructionCodeGeneratorRISCV64::VisitVecUShr(HVecUShr* instruction) {
5700 UNUSED(instruction);
5701 LOG(FATAL) << "Unimplemented";
5702 }
5703
VisitVecSetScalars(HVecSetScalars * instruction)5704 void LocationsBuilderRISCV64::VisitVecSetScalars(HVecSetScalars* instruction) {
5705 UNUSED(instruction);
5706 LOG(FATAL) << "Unimplemented";
5707 }
5708
VisitVecSetScalars(HVecSetScalars * instruction)5709 void InstructionCodeGeneratorRISCV64::VisitVecSetScalars(HVecSetScalars* instruction) {
5710 UNUSED(instruction);
5711 LOG(FATAL) << "Unimplemented";
5712 }
5713
VisitVecMultiplyAccumulate(HVecMultiplyAccumulate * instruction)5714 void LocationsBuilderRISCV64::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instruction) {
5715 UNUSED(instruction);
5716 LOG(FATAL) << "Unimplemented";
5717 }
5718
VisitVecMultiplyAccumulate(HVecMultiplyAccumulate * instruction)5719 void InstructionCodeGeneratorRISCV64::VisitVecMultiplyAccumulate(
5720 HVecMultiplyAccumulate* instruction) {
5721 UNUSED(instruction);
5722 LOG(FATAL) << "Unimplemented";
5723 }
5724
VisitVecSADAccumulate(HVecSADAccumulate * instruction)5725 void LocationsBuilderRISCV64::VisitVecSADAccumulate(HVecSADAccumulate* instruction) {
5726 UNUSED(instruction);
5727 LOG(FATAL) << "Unimplemented";
5728 }
5729
VisitVecSADAccumulate(HVecSADAccumulate * instruction)5730 void InstructionCodeGeneratorRISCV64::VisitVecSADAccumulate(HVecSADAccumulate* instruction) {
5731 UNUSED(instruction);
5732 LOG(FATAL) << "Unimplemented";
5733 }
5734
VisitVecDotProd(HVecDotProd * instruction)5735 void LocationsBuilderRISCV64::VisitVecDotProd(HVecDotProd* instruction) {
5736 UNUSED(instruction);
5737 LOG(FATAL) << "Unimplemented";
5738 }
5739
VisitVecDotProd(HVecDotProd * instruction)5740 void InstructionCodeGeneratorRISCV64::VisitVecDotProd(HVecDotProd* instruction) {
5741 UNUSED(instruction);
5742 LOG(FATAL) << "Unimplemented";
5743 }
5744
VisitVecLoad(HVecLoad * instruction)5745 void LocationsBuilderRISCV64::VisitVecLoad(HVecLoad* instruction) {
5746 UNUSED(instruction);
5747 LOG(FATAL) << "Unimplemented";
5748 }
5749
VisitVecLoad(HVecLoad * instruction)5750 void InstructionCodeGeneratorRISCV64::VisitVecLoad(HVecLoad* instruction) {
5751 UNUSED(instruction);
5752 LOG(FATAL) << "Unimplemented";
5753 }
5754
VisitVecStore(HVecStore * instruction)5755 void LocationsBuilderRISCV64::VisitVecStore(HVecStore* instruction) {
5756 UNUSED(instruction);
5757 LOG(FATAL) << "Unimplemented";
5758 }
5759
VisitVecStore(HVecStore * instruction)5760 void InstructionCodeGeneratorRISCV64::VisitVecStore(HVecStore* instruction) {
5761 UNUSED(instruction);
5762 LOG(FATAL) << "Unimplemented";
5763 }
5764
VisitVecPredSetAll(HVecPredSetAll * instruction)5765 void LocationsBuilderRISCV64::VisitVecPredSetAll(HVecPredSetAll* instruction) {
5766 UNUSED(instruction);
5767 LOG(FATAL) << "Unimplemented";
5768 }
5769
VisitVecPredSetAll(HVecPredSetAll * instruction)5770 void InstructionCodeGeneratorRISCV64::VisitVecPredSetAll(HVecPredSetAll* instruction) {
5771 UNUSED(instruction);
5772 LOG(FATAL) << "Unimplemented";
5773 }
5774
VisitVecPredWhile(HVecPredWhile * instruction)5775 void LocationsBuilderRISCV64::VisitVecPredWhile(HVecPredWhile* instruction) {
5776 UNUSED(instruction);
5777 LOG(FATAL) << "Unimplemented";
5778 }
5779
VisitVecPredWhile(HVecPredWhile * instruction)5780 void InstructionCodeGeneratorRISCV64::VisitVecPredWhile(HVecPredWhile* instruction) {
5781 UNUSED(instruction);
5782 LOG(FATAL) << "Unimplemented";
5783 }
5784
VisitVecPredToBoolean(HVecPredToBoolean * instruction)5785 void LocationsBuilderRISCV64::VisitVecPredToBoolean(HVecPredToBoolean* instruction) {
5786 UNUSED(instruction);
5787 LOG(FATAL) << "Unimplemented";
5788 }
5789
VisitVecPredToBoolean(HVecPredToBoolean * instruction)5790 void InstructionCodeGeneratorRISCV64::VisitVecPredToBoolean(HVecPredToBoolean* instruction) {
5791 UNUSED(instruction);
5792 LOG(FATAL) << "Unimplemented";
5793 }
5794
VisitVecCondition(HVecCondition * instruction)5795 void LocationsBuilderRISCV64::VisitVecCondition(HVecCondition* instruction) {
5796 UNUSED(instruction);
5797 LOG(FATAL) << "Unimplemented";
5798 }
5799
VisitVecCondition(HVecCondition * instruction)5800 void InstructionCodeGeneratorRISCV64::VisitVecCondition(HVecCondition* instruction) {
5801 UNUSED(instruction);
5802 LOG(FATAL) << "Unimplemented";
5803 }
5804
VisitVecPredNot(HVecPredNot * instruction)5805 void LocationsBuilderRISCV64::VisitVecPredNot(HVecPredNot* instruction) {
5806 UNUSED(instruction);
5807 LOG(FATAL) << "Unimplemented";
5808 }
5809
VisitVecPredNot(HVecPredNot * instruction)5810 void InstructionCodeGeneratorRISCV64::VisitVecPredNot(HVecPredNot* instruction) {
5811 UNUSED(instruction);
5812 LOG(FATAL) << "Unimplemented";
5813 }
5814
5815 namespace detail {
5816
5817 // Mark which intrinsics we don't have handcrafted code for.
5818 template <Intrinsics T>
5819 struct IsUnimplemented {
5820 bool is_unimplemented = false;
5821 };
5822
5823 #define TRUE_OVERRIDE(Name) \
5824 template <> \
5825 struct IsUnimplemented<Intrinsics::k##Name> { \
5826 bool is_unimplemented = true; \
5827 };
5828 UNIMPLEMENTED_INTRINSIC_LIST_RISCV64(TRUE_OVERRIDE)
5829 #undef TRUE_OVERRIDE
5830
5831 static constexpr bool kIsIntrinsicUnimplemented[] = {
5832 false, // kNone
5833 #define IS_UNIMPLEMENTED(Intrinsic, ...) \
5834 IsUnimplemented<Intrinsics::k##Intrinsic>().is_unimplemented,
5835 ART_INTRINSICS_LIST(IS_UNIMPLEMENTED)
5836 #undef IS_UNIMPLEMENTED
5837 };
5838
5839 } // namespace detail
5840
CodeGeneratorRISCV64(HGraph * graph,const CompilerOptions & compiler_options,OptimizingCompilerStats * stats)5841 CodeGeneratorRISCV64::CodeGeneratorRISCV64(HGraph* graph,
5842 const CompilerOptions& compiler_options,
5843 OptimizingCompilerStats* stats)
5844 : CodeGenerator(graph,
5845 kNumberOfXRegisters,
5846 kNumberOfFRegisters,
5847 /*number_of_register_pairs=*/ 0u,
5848 ComputeRegisterMask(kCoreCalleeSaves, arraysize(kCoreCalleeSaves)),
5849 ComputeRegisterMask(kFpuCalleeSaves, arraysize(kFpuCalleeSaves)),
5850 compiler_options,
5851 stats,
5852 ArrayRef<const bool>(detail::kIsIntrinsicUnimplemented)),
5853 assembler_(graph->GetAllocator(),
5854 compiler_options.GetInstructionSetFeatures()->AsRiscv64InstructionSetFeatures()),
5855 location_builder_(graph, this),
5856 instruction_visitor_(graph, this),
5857 block_labels_(nullptr),
5858 move_resolver_(graph->GetAllocator(), this),
5859 uint32_literals_(std::less<uint32_t>(),
5860 graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
5861 uint64_literals_(std::less<uint64_t>(),
5862 graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
5863 boot_image_method_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
5864 method_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
5865 boot_image_type_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
5866 app_image_type_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
5867 type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
5868 public_type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
5869 package_type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
5870 boot_image_string_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
5871 string_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
5872 boot_image_jni_entrypoint_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
5873 boot_image_other_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
5874 jit_string_patches_(StringReferenceValueComparator(),
5875 graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
5876 jit_class_patches_(TypeReferenceValueComparator(),
5877 graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)) {
5878 // Always mark the RA register to be saved.
5879 AddAllocatedRegister(Location::RegisterLocation(RA));
5880 }
5881
MaybeIncrementHotness(HSuspendCheck * suspend_check,bool is_frame_entry)5882 void CodeGeneratorRISCV64::MaybeIncrementHotness(HSuspendCheck* suspend_check,
5883 bool is_frame_entry) {
5884 if (GetCompilerOptions().CountHotnessInCompiledCode()) {
5885 ScratchRegisterScope srs(GetAssembler());
5886 XRegister method = is_frame_entry ? kArtMethodRegister : srs.AllocateXRegister();
5887 if (!is_frame_entry) {
5888 __ Loadd(method, SP, 0);
5889 }
5890 XRegister counter = srs.AllocateXRegister();
5891 __ Loadhu(counter, method, ArtMethod::HotnessCountOffset().Int32Value());
5892 Riscv64Label done;
5893 DCHECK_EQ(0u, interpreter::kNterpHotnessValue);
5894 __ Beqz(counter, &done); // Can clobber `TMP` if taken.
5895 __ Addi(counter, counter, -1);
5896 // We may not have another scratch register available for `Storeh`()`,
5897 // so we must use the `Sh()` function directly.
5898 static_assert(IsInt<12>(ArtMethod::HotnessCountOffset().Int32Value()));
5899 __ Sh(counter, method, ArtMethod::HotnessCountOffset().Int32Value());
5900 __ Bind(&done);
5901 }
5902
5903 if (GetGraph()->IsCompilingBaseline() &&
5904 GetGraph()->IsUsefulOptimizing() &&
5905 !Runtime::Current()->IsAotCompiler()) {
5906 ProfilingInfo* info = GetGraph()->GetProfilingInfo();
5907 DCHECK(info != nullptr);
5908 DCHECK(!HasEmptyFrame());
5909 uint64_t address = reinterpret_cast64<uint64_t>(info) +
5910 ProfilingInfo::BaselineHotnessCountOffset().SizeValue();
5911 auto [base_address, imm12] = SplitJitAddress(address);
5912 ScratchRegisterScope srs(GetAssembler());
5913 XRegister counter = srs.AllocateXRegister();
5914 XRegister tmp = RA;
5915 __ LoadConst64(tmp, base_address);
5916 SlowPathCodeRISCV64* slow_path =
5917 new (GetScopedAllocator()) CompileOptimizedSlowPathRISCV64(suspend_check, tmp, imm12);
5918 AddSlowPath(slow_path);
5919 __ Lhu(counter, tmp, imm12);
5920 __ Beqz(counter, slow_path->GetEntryLabel()); // Can clobber `TMP` if taken.
5921 __ Addi(counter, counter, -1);
5922 __ Sh(counter, tmp, imm12);
5923 __ Bind(slow_path->GetExitLabel());
5924 }
5925 }
5926
CanUseImplicitSuspendCheck() const5927 bool CodeGeneratorRISCV64::CanUseImplicitSuspendCheck() const {
5928 // TODO(riscv64): Implement implicit suspend checks to reduce code size.
5929 return false;
5930 }
5931
GenerateMemoryBarrier(MemBarrierKind kind)5932 void CodeGeneratorRISCV64::GenerateMemoryBarrier(MemBarrierKind kind) {
5933 switch (kind) {
5934 case MemBarrierKind::kAnyAny:
5935 __ Fence(/*pred=*/ kFenceRead | kFenceWrite, /*succ=*/ kFenceRead | kFenceWrite);
5936 break;
5937 case MemBarrierKind::kAnyStore:
5938 __ Fence(/*pred=*/ kFenceRead | kFenceWrite, /*succ=*/ kFenceWrite);
5939 break;
5940 case MemBarrierKind::kLoadAny:
5941 __ Fence(/*pred=*/ kFenceRead, /*succ=*/ kFenceRead | kFenceWrite);
5942 break;
5943 case MemBarrierKind::kStoreStore:
5944 __ Fence(/*pred=*/ kFenceWrite, /*succ=*/ kFenceWrite);
5945 break;
5946
5947 default:
5948 LOG(FATAL) << "Unexpected memory barrier " << kind;
5949 UNREACHABLE();
5950 }
5951 }
5952
GenerateFrameEntry()5953 void CodeGeneratorRISCV64::GenerateFrameEntry() {
5954 // Check if we need to generate the clinit check. We will jump to the
5955 // resolution stub if the class is not initialized and the executing thread is
5956 // not the thread initializing it.
5957 // We do this before constructing the frame to get the correct stack trace if
5958 // an exception is thrown.
5959 if (GetCompilerOptions().ShouldCompileWithClinitCheck(GetGraph()->GetArtMethod())) {
5960 Riscv64Label resolution;
5961 Riscv64Label memory_barrier;
5962
5963 ScratchRegisterScope srs(GetAssembler());
5964 XRegister tmp = srs.AllocateXRegister();
5965 XRegister tmp2 = srs.AllocateXRegister();
5966
5967 // We don't emit a read barrier here to save on code size. We rely on the
5968 // resolution trampoline to do a clinit check before re-entering this code.
5969 __ Loadwu(tmp2, kArtMethodRegister, ArtMethod::DeclaringClassOffset().Int32Value());
5970
5971 // We shall load the full 32-bit status word with sign-extension and compare as unsigned
5972 // to sign-extended shifted status values. This yields the same comparison as loading and
5973 // materializing unsigned but the constant is materialized with a single LUI instruction.
5974 __ Loadw(tmp, tmp2, mirror::Class::StatusOffset().SizeValue()); // Sign-extended.
5975
5976 // Check if we're visibly initialized.
5977 __ Li(tmp2, ShiftedSignExtendedClassStatusValue<ClassStatus::kVisiblyInitialized>());
5978 __ Bgeu(tmp, tmp2, &frame_entry_label_); // Can clobber `TMP` if taken.
5979
5980 // Check if we're initialized and jump to code that does a memory barrier if so.
5981 __ Li(tmp2, ShiftedSignExtendedClassStatusValue<ClassStatus::kInitialized>());
5982 __ Bgeu(tmp, tmp2, &memory_barrier); // Can clobber `TMP` if taken.
5983
5984 // Check if we're initializing and the thread initializing is the one
5985 // executing the code.
5986 __ Li(tmp2, ShiftedSignExtendedClassStatusValue<ClassStatus::kInitializing>());
5987 __ Bltu(tmp, tmp2, &resolution); // Can clobber `TMP` if taken.
5988
5989 __ Loadwu(tmp2, kArtMethodRegister, ArtMethod::DeclaringClassOffset().Int32Value());
5990 __ Loadw(tmp, tmp2, mirror::Class::ClinitThreadIdOffset().Int32Value());
5991 __ Loadw(tmp2, TR, Thread::TidOffset<kRiscv64PointerSize>().Int32Value());
5992 __ Beq(tmp, tmp2, &frame_entry_label_);
5993 __ Bind(&resolution);
5994
5995 // Jump to the resolution stub.
5996 ThreadOffset64 entrypoint_offset =
5997 GetThreadOffset<kRiscv64PointerSize>(kQuickQuickResolutionTrampoline);
5998 __ Loadd(tmp, TR, entrypoint_offset.Int32Value());
5999 __ Jr(tmp);
6000
6001 __ Bind(&memory_barrier);
6002 GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
6003 }
6004 __ Bind(&frame_entry_label_);
6005
6006 bool do_overflow_check =
6007 FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kRiscv64) || !IsLeafMethod();
6008
6009 if (do_overflow_check) {
6010 DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks());
6011 __ Loadw(
6012 Zero, SP, -static_cast<int32_t>(GetStackOverflowReservedBytes(InstructionSet::kRiscv64)));
6013 RecordPcInfo(nullptr, 0);
6014 }
6015
6016 if (!HasEmptyFrame()) {
6017 // Make sure the frame size isn't unreasonably large.
6018 DCHECK_LE(GetFrameSize(), GetMaximumFrameSize());
6019
6020 // Spill callee-saved registers.
6021
6022 uint32_t frame_size = GetFrameSize();
6023
6024 IncreaseFrame(frame_size);
6025
6026 uint32_t offset = frame_size;
6027 for (size_t i = arraysize(kCoreCalleeSaves); i != 0; ) {
6028 --i;
6029 XRegister reg = kCoreCalleeSaves[i];
6030 if (allocated_registers_.ContainsCoreRegister(reg)) {
6031 offset -= kRiscv64DoublewordSize;
6032 __ Stored(reg, SP, offset);
6033 __ cfi().RelOffset(dwarf::Reg::Riscv64Core(reg), offset);
6034 }
6035 }
6036
6037 for (size_t i = arraysize(kFpuCalleeSaves); i != 0; ) {
6038 --i;
6039 FRegister reg = kFpuCalleeSaves[i];
6040 if (allocated_registers_.ContainsFloatingPointRegister(reg)) {
6041 offset -= kRiscv64DoublewordSize;
6042 __ FStored(reg, SP, offset);
6043 __ cfi().RelOffset(dwarf::Reg::Riscv64Fp(reg), offset);
6044 }
6045 }
6046
6047 // Save the current method if we need it. Note that we do not
6048 // do this in HCurrentMethod, as the instruction might have been removed
6049 // in the SSA graph.
6050 if (RequiresCurrentMethod()) {
6051 __ Stored(kArtMethodRegister, SP, 0);
6052 }
6053
6054 if (GetGraph()->HasShouldDeoptimizeFlag()) {
6055 // Initialize should_deoptimize flag to 0.
6056 __ Storew(Zero, SP, GetStackOffsetOfShouldDeoptimizeFlag());
6057 }
6058 }
6059 MaybeIncrementHotness(/* suspend_check= */ nullptr, /*is_frame_entry=*/ true);
6060 }
6061
GenerateFrameExit()6062 void CodeGeneratorRISCV64::GenerateFrameExit() {
6063 __ cfi().RememberState();
6064
6065 if (!HasEmptyFrame()) {
6066 // Restore callee-saved registers.
6067
6068 // For better instruction scheduling restore RA before other registers.
6069 uint32_t offset = GetFrameSize();
6070 for (size_t i = arraysize(kCoreCalleeSaves); i != 0; ) {
6071 --i;
6072 XRegister reg = kCoreCalleeSaves[i];
6073 if (allocated_registers_.ContainsCoreRegister(reg)) {
6074 offset -= kRiscv64DoublewordSize;
6075 __ Loadd(reg, SP, offset);
6076 __ cfi().Restore(dwarf::Reg::Riscv64Core(reg));
6077 }
6078 }
6079
6080 for (size_t i = arraysize(kFpuCalleeSaves); i != 0; ) {
6081 --i;
6082 FRegister reg = kFpuCalleeSaves[i];
6083 if (allocated_registers_.ContainsFloatingPointRegister(reg)) {
6084 offset -= kRiscv64DoublewordSize;
6085 __ FLoadd(reg, SP, offset);
6086 __ cfi().Restore(dwarf::Reg::Riscv64Fp(reg));
6087 }
6088 }
6089
6090 DecreaseFrame(GetFrameSize());
6091 }
6092
6093 __ Jr(RA);
6094
6095 __ cfi().RestoreState();
6096 __ cfi().DefCFAOffset(GetFrameSize());
6097 }
6098
Bind(HBasicBlock * block)6099 void CodeGeneratorRISCV64::Bind(HBasicBlock* block) { __ Bind(GetLabelOf(block)); }
6100
MoveConstant(Location destination,int32_t value)6101 void CodeGeneratorRISCV64::MoveConstant(Location destination, int32_t value) {
6102 DCHECK(destination.IsRegister());
6103 __ LoadConst32(destination.AsRegister<XRegister>(), value);
6104 }
6105
MoveLocation(Location destination,Location source,DataType::Type dst_type)6106 void CodeGeneratorRISCV64::MoveLocation(Location destination,
6107 Location source,
6108 DataType::Type dst_type) {
6109 if (source.Equals(destination)) {
6110 return;
6111 }
6112
6113 // A valid move type can always be inferred from the destination and source locations.
6114 // When moving from and to a register, the `dst_type` can be used to generate 32-bit instead
6115 // of 64-bit moves but it's generally OK to use 64-bit moves for 32-bit values in registers.
6116 bool unspecified_type = (dst_type == DataType::Type::kVoid);
6117 // TODO(riscv64): Is the destination type known in all cases?
6118 // TODO(riscv64): Can unspecified `dst_type` move 32-bit GPR to FPR without NaN-boxing?
6119 CHECK(!unspecified_type);
6120
6121 if (destination.IsRegister() || destination.IsFpuRegister()) {
6122 if (unspecified_type) {
6123 HConstant* src_cst = source.IsConstant() ? source.GetConstant() : nullptr;
6124 if (source.IsStackSlot() ||
6125 (src_cst != nullptr &&
6126 (src_cst->IsIntConstant() || src_cst->IsFloatConstant() || src_cst->IsNullConstant()))) {
6127 // For stack slots and 32-bit constants, a 32-bit type is appropriate.
6128 dst_type = destination.IsRegister() ? DataType::Type::kInt32 : DataType::Type::kFloat32;
6129 } else {
6130 // If the source is a double stack slot or a 64-bit constant, a 64-bit type
6131 // is appropriate. Else the source is a register, and since the type has not
6132 // been specified, we chose a 64-bit type to force a 64-bit move.
6133 dst_type = destination.IsRegister() ? DataType::Type::kInt64 : DataType::Type::kFloat64;
6134 }
6135 }
6136 DCHECK((destination.IsFpuRegister() && DataType::IsFloatingPointType(dst_type)) ||
6137 (destination.IsRegister() && !DataType::IsFloatingPointType(dst_type)));
6138
6139 if (source.IsStackSlot() || source.IsDoubleStackSlot()) {
6140 // Move to GPR/FPR from stack
6141 if (DataType::IsFloatingPointType(dst_type)) {
6142 if (DataType::Is64BitType(dst_type)) {
6143 __ FLoadd(destination.AsFpuRegister<FRegister>(), SP, source.GetStackIndex());
6144 } else {
6145 __ FLoadw(destination.AsFpuRegister<FRegister>(), SP, source.GetStackIndex());
6146 }
6147 } else {
6148 if (DataType::Is64BitType(dst_type)) {
6149 __ Loadd(destination.AsRegister<XRegister>(), SP, source.GetStackIndex());
6150 } else if (dst_type == DataType::Type::kReference) {
6151 __ Loadwu(destination.AsRegister<XRegister>(), SP, source.GetStackIndex());
6152 } else {
6153 __ Loadw(destination.AsRegister<XRegister>(), SP, source.GetStackIndex());
6154 }
6155 }
6156 } else if (source.IsConstant()) {
6157 // Move to GPR/FPR from constant
6158 // TODO(riscv64): Consider using literals for difficult-to-materialize 64-bit constants.
6159 int64_t value = GetInt64ValueOf(source.GetConstant()->AsConstant());
6160 ScratchRegisterScope srs(GetAssembler());
6161 XRegister gpr = DataType::IsFloatingPointType(dst_type)
6162 ? srs.AllocateXRegister()
6163 : destination.AsRegister<XRegister>();
6164 if (DataType::IsFloatingPointType(dst_type) && value == 0) {
6165 gpr = Zero; // Note: The scratch register allocated above shall not be used.
6166 } else {
6167 // Note: For `float` we load the sign-extended value here as it can sometimes yield
6168 // a shorter instruction sequence. The higher 32 bits shall be ignored during the
6169 // transfer to FP reg and the result shall be correctly NaN-boxed.
6170 __ LoadConst64(gpr, value);
6171 }
6172 if (dst_type == DataType::Type::kFloat32) {
6173 __ FMvWX(destination.AsFpuRegister<FRegister>(), gpr);
6174 } else if (dst_type == DataType::Type::kFloat64) {
6175 __ FMvDX(destination.AsFpuRegister<FRegister>(), gpr);
6176 }
6177 } else if (source.IsRegister()) {
6178 if (destination.IsRegister()) {
6179 // Move to GPR from GPR
6180 __ Mv(destination.AsRegister<XRegister>(), source.AsRegister<XRegister>());
6181 } else {
6182 DCHECK(destination.IsFpuRegister());
6183 if (DataType::Is64BitType(dst_type)) {
6184 __ FMvDX(destination.AsFpuRegister<FRegister>(), source.AsRegister<XRegister>());
6185 } else {
6186 __ FMvWX(destination.AsFpuRegister<FRegister>(), source.AsRegister<XRegister>());
6187 }
6188 }
6189 } else if (source.IsFpuRegister()) {
6190 if (destination.IsFpuRegister()) {
6191 if (GetGraph()->HasSIMD()) {
6192 LOG(FATAL) << "Vector extension is unsupported";
6193 UNREACHABLE();
6194 } else {
6195 // Move to FPR from FPR
6196 if (dst_type == DataType::Type::kFloat32) {
6197 __ FMvS(destination.AsFpuRegister<FRegister>(), source.AsFpuRegister<FRegister>());
6198 } else {
6199 DCHECK_EQ(dst_type, DataType::Type::kFloat64);
6200 __ FMvD(destination.AsFpuRegister<FRegister>(), source.AsFpuRegister<FRegister>());
6201 }
6202 }
6203 } else {
6204 DCHECK(destination.IsRegister());
6205 if (DataType::Is64BitType(dst_type)) {
6206 __ FMvXD(destination.AsRegister<XRegister>(), source.AsFpuRegister<FRegister>());
6207 } else {
6208 __ FMvXW(destination.AsRegister<XRegister>(), source.AsFpuRegister<FRegister>());
6209 }
6210 }
6211 }
6212 } else if (destination.IsSIMDStackSlot()) {
6213 LOG(FATAL) << "SIMD is unsupported";
6214 UNREACHABLE();
6215 } else { // The destination is not a register. It must be a stack slot.
6216 DCHECK(destination.IsStackSlot() || destination.IsDoubleStackSlot());
6217 if (source.IsRegister() || source.IsFpuRegister()) {
6218 if (unspecified_type) {
6219 if (source.IsRegister()) {
6220 dst_type = destination.IsStackSlot() ? DataType::Type::kInt32 : DataType::Type::kInt64;
6221 } else {
6222 dst_type =
6223 destination.IsStackSlot() ? DataType::Type::kFloat32 : DataType::Type::kFloat64;
6224 }
6225 }
6226 DCHECK_EQ(source.IsFpuRegister(), DataType::IsFloatingPointType(dst_type));
6227 // For direct @CriticalNative calls, we need to sign-extend narrow integral args
6228 // to 64 bits, so widening integral values is allowed. Narrowing is forbidden.
6229 DCHECK_IMPLIES(DataType::IsFloatingPointType(dst_type) || destination.IsStackSlot(),
6230 destination.IsDoubleStackSlot() == DataType::Is64BitType(dst_type));
6231 // Move to stack from GPR/FPR
6232 if (destination.IsDoubleStackSlot()) {
6233 if (source.IsRegister()) {
6234 __ Stored(source.AsRegister<XRegister>(), SP, destination.GetStackIndex());
6235 } else {
6236 __ FStored(source.AsFpuRegister<FRegister>(), SP, destination.GetStackIndex());
6237 }
6238 } else {
6239 if (source.IsRegister()) {
6240 __ Storew(source.AsRegister<XRegister>(), SP, destination.GetStackIndex());
6241 } else {
6242 __ FStorew(source.AsFpuRegister<FRegister>(), SP, destination.GetStackIndex());
6243 }
6244 }
6245 } else if (source.IsConstant()) {
6246 // Move to stack from constant
6247 int64_t value = GetInt64ValueOf(source.GetConstant());
6248 ScratchRegisterScope srs(GetAssembler());
6249 XRegister gpr = (value != 0) ? srs.AllocateXRegister() : Zero;
6250 if (value != 0) {
6251 __ LoadConst64(gpr, value);
6252 }
6253 if (destination.IsStackSlot()) {
6254 __ Storew(gpr, SP, destination.GetStackIndex());
6255 } else {
6256 DCHECK(destination.IsDoubleStackSlot());
6257 __ Stored(gpr, SP, destination.GetStackIndex());
6258 }
6259 } else {
6260 DCHECK(source.IsStackSlot() || source.IsDoubleStackSlot());
6261 // For direct @CriticalNative calls, we need to sign-extend narrow integral args
6262 // to 64 bits, so widening move is allowed. Narrowing move is forbidden.
6263 DCHECK_IMPLIES(destination.IsStackSlot(), source.IsStackSlot());
6264 // Move to stack from stack
6265 ScratchRegisterScope srs(GetAssembler());
6266 XRegister tmp = srs.AllocateXRegister();
6267 if (source.IsStackSlot()) {
6268 __ Loadw(tmp, SP, source.GetStackIndex());
6269 } else {
6270 __ Loadd(tmp, SP, source.GetStackIndex());
6271 }
6272 if (destination.IsStackSlot()) {
6273 __ Storew(tmp, SP, destination.GetStackIndex());
6274 } else {
6275 __ Stored(tmp, SP, destination.GetStackIndex());
6276 }
6277 }
6278 }
6279 }
6280
AddLocationAsTemp(Location location,LocationSummary * locations)6281 void CodeGeneratorRISCV64::AddLocationAsTemp(Location location, LocationSummary* locations) {
6282 if (location.IsRegister()) {
6283 locations->AddTemp(location);
6284 } else {
6285 UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location;
6286 }
6287 }
6288
SetupBlockedRegisters() const6289 void CodeGeneratorRISCV64::SetupBlockedRegisters() const {
6290 // ZERO, GP, SP, RA, TP and TR(S1) are reserved and can't be allocated.
6291 blocked_core_registers_[Zero] = true;
6292 blocked_core_registers_[GP] = true;
6293 blocked_core_registers_[SP] = true;
6294 blocked_core_registers_[RA] = true;
6295 blocked_core_registers_[TP] = true;
6296 blocked_core_registers_[TR] = true; // ART Thread register.
6297
6298 // TMP(T6), TMP2(T5) and FTMP(FT11) are used as temporary/scratch registers.
6299 blocked_core_registers_[TMP] = true;
6300 blocked_core_registers_[TMP2] = true;
6301 blocked_fpu_registers_[FTMP] = true;
6302
6303 if (GetGraph()->IsDebuggable()) {
6304 // Stubs do not save callee-save floating point registers. If the graph
6305 // is debuggable, we need to deal with these registers differently. For
6306 // now, just block them.
6307 for (size_t i = 0; i < arraysize(kFpuCalleeSaves); ++i) {
6308 blocked_fpu_registers_[kFpuCalleeSaves[i]] = true;
6309 }
6310 }
6311 }
6312
SaveCoreRegister(size_t stack_index,uint32_t reg_id)6313 size_t CodeGeneratorRISCV64::SaveCoreRegister(size_t stack_index, uint32_t reg_id) {
6314 __ Stored(XRegister(reg_id), SP, stack_index);
6315 return kRiscv64DoublewordSize;
6316 }
6317
RestoreCoreRegister(size_t stack_index,uint32_t reg_id)6318 size_t CodeGeneratorRISCV64::RestoreCoreRegister(size_t stack_index, uint32_t reg_id) {
6319 __ Loadd(XRegister(reg_id), SP, stack_index);
6320 return kRiscv64DoublewordSize;
6321 }
6322
SaveFloatingPointRegister(size_t stack_index,uint32_t reg_id)6323 size_t CodeGeneratorRISCV64::SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
6324 if (GetGraph()->HasSIMD()) {
6325 // TODO(riscv64): RISC-V vector extension.
6326 UNIMPLEMENTED(FATAL) << "Vector extension is unsupported";
6327 UNREACHABLE();
6328 }
6329 __ FStored(FRegister(reg_id), SP, stack_index);
6330 return kRiscv64FloatRegSizeInBytes;
6331 }
6332
RestoreFloatingPointRegister(size_t stack_index,uint32_t reg_id)6333 size_t CodeGeneratorRISCV64::RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
6334 if (GetGraph()->HasSIMD()) {
6335 // TODO(riscv64): RISC-V vector extension.
6336 UNIMPLEMENTED(FATAL) << "Vector extension is unsupported";
6337 UNREACHABLE();
6338 }
6339 __ FLoadd(FRegister(reg_id), SP, stack_index);
6340 return kRiscv64FloatRegSizeInBytes;
6341 }
6342
DumpCoreRegister(std::ostream & stream,int reg) const6343 void CodeGeneratorRISCV64::DumpCoreRegister(std::ostream& stream, int reg) const {
6344 stream << XRegister(reg);
6345 }
6346
DumpFloatingPointRegister(std::ostream & stream,int reg) const6347 void CodeGeneratorRISCV64::DumpFloatingPointRegister(std::ostream& stream, int reg) const {
6348 stream << FRegister(reg);
6349 }
6350
GetInstructionSetFeatures() const6351 const Riscv64InstructionSetFeatures& CodeGeneratorRISCV64::GetInstructionSetFeatures() const {
6352 return *GetCompilerOptions().GetInstructionSetFeatures()->AsRiscv64InstructionSetFeatures();
6353 }
6354
Finalize()6355 void CodeGeneratorRISCV64::Finalize() {
6356 // Ensure that we fix up branches and literal loads and emit the literal pool.
6357 __ FinalizeCode();
6358
6359 // Adjust native pc offsets in stack maps.
6360 StackMapStream* stack_map_stream = GetStackMapStream();
6361 for (size_t i = 0, num = stack_map_stream->GetNumberOfStackMaps(); i != num; ++i) {
6362 uint32_t old_position = stack_map_stream->GetStackMapNativePcOffset(i);
6363 uint32_t new_position = __ GetAdjustedPosition(old_position);
6364 DCHECK_GE(new_position, old_position);
6365 stack_map_stream->SetStackMapNativePcOffset(i, new_position);
6366 }
6367
6368 // Adjust pc offsets for the disassembly information.
6369 if (disasm_info_ != nullptr) {
6370 GeneratedCodeInterval* frame_entry_interval = disasm_info_->GetFrameEntryInterval();
6371 frame_entry_interval->start = __ GetAdjustedPosition(frame_entry_interval->start);
6372 frame_entry_interval->end = __ GetAdjustedPosition(frame_entry_interval->end);
6373 for (auto& entry : *disasm_info_->GetInstructionIntervals()) {
6374 entry.second.start = __ GetAdjustedPosition(entry.second.start);
6375 entry.second.end = __ GetAdjustedPosition(entry.second.end);
6376 }
6377 for (auto& entry : *disasm_info_->GetSlowPathIntervals()) {
6378 entry.code_interval.start = __ GetAdjustedPosition(entry.code_interval.start);
6379 entry.code_interval.end = __ GetAdjustedPosition(entry.code_interval.end);
6380 }
6381 }
6382 }
6383
6384 // Generate code to invoke a runtime entry point.
InvokeRuntime(QuickEntrypointEnum entrypoint,HInstruction * instruction,uint32_t dex_pc,SlowPathCode * slow_path)6385 void CodeGeneratorRISCV64::InvokeRuntime(QuickEntrypointEnum entrypoint,
6386 HInstruction* instruction,
6387 uint32_t dex_pc,
6388 SlowPathCode* slow_path) {
6389 ValidateInvokeRuntime(entrypoint, instruction, slow_path);
6390
6391 ThreadOffset64 entrypoint_offset = GetThreadOffset<kRiscv64PointerSize>(entrypoint);
6392
6393 // TODO(riscv64): Reduce code size for AOT by using shared trampolines for slow path
6394 // runtime calls across the entire oat file.
6395 __ Loadd(RA, TR, entrypoint_offset.Int32Value());
6396 __ Jalr(RA);
6397 if (EntrypointRequiresStackMap(entrypoint)) {
6398 RecordPcInfo(instruction, dex_pc, slow_path);
6399 }
6400 }
6401
6402 // Generate code to invoke a runtime entry point, but do not record
6403 // PC-related information in a stack map.
InvokeRuntimeWithoutRecordingPcInfo(int32_t entry_point_offset,HInstruction * instruction,SlowPathCode * slow_path)6404 void CodeGeneratorRISCV64::InvokeRuntimeWithoutRecordingPcInfo(int32_t entry_point_offset,
6405 HInstruction* instruction,
6406 SlowPathCode* slow_path) {
6407 ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction, slow_path);
6408 __ Loadd(RA, TR, entry_point_offset);
6409 __ Jalr(RA);
6410 }
6411
IncreaseFrame(size_t adjustment)6412 void CodeGeneratorRISCV64::IncreaseFrame(size_t adjustment) {
6413 int32_t adjustment32 = dchecked_integral_cast<int32_t>(adjustment);
6414 __ AddConst64(SP, SP, -adjustment32);
6415 GetAssembler()->cfi().AdjustCFAOffset(adjustment32);
6416 }
6417
DecreaseFrame(size_t adjustment)6418 void CodeGeneratorRISCV64::DecreaseFrame(size_t adjustment) {
6419 int32_t adjustment32 = dchecked_integral_cast<int32_t>(adjustment);
6420 __ AddConst64(SP, SP, adjustment32);
6421 GetAssembler()->cfi().AdjustCFAOffset(-adjustment32);
6422 }
6423
GenerateNop()6424 void CodeGeneratorRISCV64::GenerateNop() {
6425 __ Nop();
6426 }
6427
GenerateImplicitNullCheck(HNullCheck * instruction)6428 void CodeGeneratorRISCV64::GenerateImplicitNullCheck(HNullCheck* instruction) {
6429 if (CanMoveNullCheckToUser(instruction)) {
6430 return;
6431 }
6432 Location obj = instruction->GetLocations()->InAt(0);
6433
6434 __ Lw(Zero, obj.AsRegister<XRegister>(), 0);
6435 RecordPcInfo(instruction, instruction->GetDexPc());
6436 }
6437
GenerateExplicitNullCheck(HNullCheck * instruction)6438 void CodeGeneratorRISCV64::GenerateExplicitNullCheck(HNullCheck* instruction) {
6439 SlowPathCodeRISCV64* slow_path = new (GetScopedAllocator()) NullCheckSlowPathRISCV64(instruction);
6440 AddSlowPath(slow_path);
6441
6442 Location obj = instruction->GetLocations()->InAt(0);
6443
6444 __ Beqz(obj.AsRegister<XRegister>(), slow_path->GetEntryLabel());
6445 }
6446
GetSupportedLoadStringKind(HLoadString::LoadKind desired_string_load_kind)6447 HLoadString::LoadKind CodeGeneratorRISCV64::GetSupportedLoadStringKind(
6448 HLoadString::LoadKind desired_string_load_kind) {
6449 switch (desired_string_load_kind) {
6450 case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
6451 case HLoadString::LoadKind::kBootImageRelRo:
6452 case HLoadString::LoadKind::kBssEntry:
6453 DCHECK(!Runtime::Current()->UseJitCompilation());
6454 break;
6455 case HLoadString::LoadKind::kJitBootImageAddress:
6456 case HLoadString::LoadKind::kJitTableAddress:
6457 DCHECK(Runtime::Current()->UseJitCompilation());
6458 break;
6459 case HLoadString::LoadKind::kRuntimeCall:
6460 break;
6461 }
6462 return desired_string_load_kind;
6463 }
6464
GetSupportedLoadClassKind(HLoadClass::LoadKind desired_class_load_kind)6465 HLoadClass::LoadKind CodeGeneratorRISCV64::GetSupportedLoadClassKind(
6466 HLoadClass::LoadKind desired_class_load_kind) {
6467 switch (desired_class_load_kind) {
6468 case HLoadClass::LoadKind::kInvalid:
6469 LOG(FATAL) << "UNREACHABLE";
6470 UNREACHABLE();
6471 case HLoadClass::LoadKind::kReferrersClass:
6472 break;
6473 case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
6474 case HLoadClass::LoadKind::kBootImageRelRo:
6475 case HLoadClass::LoadKind::kAppImageRelRo:
6476 case HLoadClass::LoadKind::kBssEntry:
6477 case HLoadClass::LoadKind::kBssEntryPublic:
6478 case HLoadClass::LoadKind::kBssEntryPackage:
6479 DCHECK(!Runtime::Current()->UseJitCompilation());
6480 break;
6481 case HLoadClass::LoadKind::kJitBootImageAddress:
6482 case HLoadClass::LoadKind::kJitTableAddress:
6483 DCHECK(Runtime::Current()->UseJitCompilation());
6484 break;
6485 case HLoadClass::LoadKind::kRuntimeCall:
6486 break;
6487 }
6488 return desired_class_load_kind;
6489 }
6490
GetSupportedInvokeStaticOrDirectDispatch(const HInvokeStaticOrDirect::DispatchInfo & desired_dispatch_info,ArtMethod * method)6491 HInvokeStaticOrDirect::DispatchInfo CodeGeneratorRISCV64::GetSupportedInvokeStaticOrDirectDispatch(
6492 const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info, ArtMethod* method) {
6493 UNUSED(method);
6494 // On RISCV64 we support all dispatch types.
6495 return desired_dispatch_info;
6496 }
6497
NewBootImageIntrinsicPatch(uint32_t intrinsic_data,const PcRelativePatchInfo * info_high)6498 CodeGeneratorRISCV64::PcRelativePatchInfo* CodeGeneratorRISCV64::NewBootImageIntrinsicPatch(
6499 uint32_t intrinsic_data, const PcRelativePatchInfo* info_high) {
6500 return NewPcRelativePatch(
6501 /* dex_file= */ nullptr, intrinsic_data, info_high, &boot_image_other_patches_);
6502 }
6503
NewBootImageRelRoPatch(uint32_t boot_image_offset,const PcRelativePatchInfo * info_high)6504 CodeGeneratorRISCV64::PcRelativePatchInfo* CodeGeneratorRISCV64::NewBootImageRelRoPatch(
6505 uint32_t boot_image_offset, const PcRelativePatchInfo* info_high) {
6506 return NewPcRelativePatch(
6507 /* dex_file= */ nullptr, boot_image_offset, info_high, &boot_image_other_patches_);
6508 }
6509
NewBootImageMethodPatch(MethodReference target_method,const PcRelativePatchInfo * info_high)6510 CodeGeneratorRISCV64::PcRelativePatchInfo* CodeGeneratorRISCV64::NewBootImageMethodPatch(
6511 MethodReference target_method, const PcRelativePatchInfo* info_high) {
6512 return NewPcRelativePatch(
6513 target_method.dex_file, target_method.index, info_high, &boot_image_method_patches_);
6514 }
6515
NewMethodBssEntryPatch(MethodReference target_method,const PcRelativePatchInfo * info_high)6516 CodeGeneratorRISCV64::PcRelativePatchInfo* CodeGeneratorRISCV64::NewMethodBssEntryPatch(
6517 MethodReference target_method, const PcRelativePatchInfo* info_high) {
6518 return NewPcRelativePatch(
6519 target_method.dex_file, target_method.index, info_high, &method_bss_entry_patches_);
6520 }
6521
NewBootImageTypePatch(const DexFile & dex_file,dex::TypeIndex type_index,const PcRelativePatchInfo * info_high)6522 CodeGeneratorRISCV64::PcRelativePatchInfo* CodeGeneratorRISCV64::NewBootImageTypePatch(
6523 const DexFile& dex_file, dex::TypeIndex type_index, const PcRelativePatchInfo* info_high) {
6524 return NewPcRelativePatch(&dex_file, type_index.index_, info_high, &boot_image_type_patches_);
6525 }
6526
NewAppImageTypePatch(const DexFile & dex_file,dex::TypeIndex type_index,const PcRelativePatchInfo * info_high)6527 CodeGeneratorRISCV64::PcRelativePatchInfo* CodeGeneratorRISCV64::NewAppImageTypePatch(
6528 const DexFile& dex_file, dex::TypeIndex type_index, const PcRelativePatchInfo* info_high) {
6529 return NewPcRelativePatch(&dex_file, type_index.index_, info_high, &app_image_type_patches_);
6530 }
6531
NewBootImageJniEntrypointPatch(MethodReference target_method,const PcRelativePatchInfo * info_high)6532 CodeGeneratorRISCV64::PcRelativePatchInfo* CodeGeneratorRISCV64::NewBootImageJniEntrypointPatch(
6533 MethodReference target_method, const PcRelativePatchInfo* info_high) {
6534 return NewPcRelativePatch(
6535 target_method.dex_file, target_method.index, info_high, &boot_image_jni_entrypoint_patches_);
6536 }
6537
NewTypeBssEntryPatch(HLoadClass * load_class,const PcRelativePatchInfo * info_high)6538 CodeGeneratorRISCV64::PcRelativePatchInfo* CodeGeneratorRISCV64::NewTypeBssEntryPatch(
6539 HLoadClass* load_class,
6540 const PcRelativePatchInfo* info_high) {
6541 const DexFile& dex_file = load_class->GetDexFile();
6542 dex::TypeIndex type_index = load_class->GetTypeIndex();
6543 ArenaDeque<PcRelativePatchInfo>* patches = nullptr;
6544 switch (load_class->GetLoadKind()) {
6545 case HLoadClass::LoadKind::kBssEntry:
6546 patches = &type_bss_entry_patches_;
6547 break;
6548 case HLoadClass::LoadKind::kBssEntryPublic:
6549 patches = &public_type_bss_entry_patches_;
6550 break;
6551 case HLoadClass::LoadKind::kBssEntryPackage:
6552 patches = &package_type_bss_entry_patches_;
6553 break;
6554 default:
6555 LOG(FATAL) << "Unexpected load kind: " << load_class->GetLoadKind();
6556 UNREACHABLE();
6557 }
6558 return NewPcRelativePatch(&dex_file, type_index.index_, info_high, patches);
6559 }
6560
NewBootImageStringPatch(const DexFile & dex_file,dex::StringIndex string_index,const PcRelativePatchInfo * info_high)6561 CodeGeneratorRISCV64::PcRelativePatchInfo* CodeGeneratorRISCV64::NewBootImageStringPatch(
6562 const DexFile& dex_file, dex::StringIndex string_index, const PcRelativePatchInfo* info_high) {
6563 return NewPcRelativePatch(&dex_file, string_index.index_, info_high, &boot_image_string_patches_);
6564 }
6565
NewStringBssEntryPatch(const DexFile & dex_file,dex::StringIndex string_index,const PcRelativePatchInfo * info_high)6566 CodeGeneratorRISCV64::PcRelativePatchInfo* CodeGeneratorRISCV64::NewStringBssEntryPatch(
6567 const DexFile& dex_file, dex::StringIndex string_index, const PcRelativePatchInfo* info_high) {
6568 return NewPcRelativePatch(&dex_file, string_index.index_, info_high, &string_bss_entry_patches_);
6569 }
6570
NewPcRelativePatch(const DexFile * dex_file,uint32_t offset_or_index,const PcRelativePatchInfo * info_high,ArenaDeque<PcRelativePatchInfo> * patches)6571 CodeGeneratorRISCV64::PcRelativePatchInfo* CodeGeneratorRISCV64::NewPcRelativePatch(
6572 const DexFile* dex_file,
6573 uint32_t offset_or_index,
6574 const PcRelativePatchInfo* info_high,
6575 ArenaDeque<PcRelativePatchInfo>* patches) {
6576 patches->emplace_back(dex_file, offset_or_index, info_high);
6577 return &patches->back();
6578 }
6579
DeduplicateUint32Literal(uint32_t value)6580 Literal* CodeGeneratorRISCV64::DeduplicateUint32Literal(uint32_t value) {
6581 return uint32_literals_.GetOrCreate(value,
6582 [this, value]() { return __ NewLiteral<uint32_t>(value); });
6583 }
6584
DeduplicateUint64Literal(uint64_t value)6585 Literal* CodeGeneratorRISCV64::DeduplicateUint64Literal(uint64_t value) {
6586 return uint64_literals_.GetOrCreate(value,
6587 [this, value]() { return __ NewLiteral<uint64_t>(value); });
6588 }
6589
DeduplicateBootImageAddressLiteral(uint64_t address)6590 Literal* CodeGeneratorRISCV64::DeduplicateBootImageAddressLiteral(uint64_t address) {
6591 return DeduplicateUint32Literal(dchecked_integral_cast<uint32_t>(address));
6592 }
6593
DeduplicateJitStringLiteral(const DexFile & dex_file,dex::StringIndex string_index,Handle<mirror::String> handle)6594 Literal* CodeGeneratorRISCV64::DeduplicateJitStringLiteral(const DexFile& dex_file,
6595 dex::StringIndex string_index,
6596 Handle<mirror::String> handle) {
6597 ReserveJitStringRoot(StringReference(&dex_file, string_index), handle);
6598 return jit_string_patches_.GetOrCreate(
6599 StringReference(&dex_file, string_index),
6600 [this]() { return __ NewLiteral<uint32_t>(/* value= */ 0u); });
6601 }
6602
DeduplicateJitClassLiteral(const DexFile & dex_file,dex::TypeIndex type_index,Handle<mirror::Class> handle)6603 Literal* CodeGeneratorRISCV64::DeduplicateJitClassLiteral(const DexFile& dex_file,
6604 dex::TypeIndex type_index,
6605 Handle<mirror::Class> handle) {
6606 ReserveJitClassRoot(TypeReference(&dex_file, type_index), handle);
6607 return jit_class_patches_.GetOrCreate(
6608 TypeReference(&dex_file, type_index),
6609 [this]() { return __ NewLiteral<uint32_t>(/* value= */ 0u); });
6610 }
6611
PatchJitRootUse(uint8_t * code,const uint8_t * roots_data,const Literal * literal,uint64_t index_in_table) const6612 void CodeGeneratorRISCV64::PatchJitRootUse(uint8_t* code,
6613 const uint8_t* roots_data,
6614 const Literal* literal,
6615 uint64_t index_in_table) const {
6616 uint32_t literal_offset = GetAssembler().GetLabelLocation(literal->GetLabel());
6617 uintptr_t address =
6618 reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>);
6619 reinterpret_cast<uint32_t*>(code + literal_offset)[0] = dchecked_integral_cast<uint32_t>(address);
6620 }
6621
EmitJitRootPatches(uint8_t * code,const uint8_t * roots_data)6622 void CodeGeneratorRISCV64::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) {
6623 for (const auto& entry : jit_string_patches_) {
6624 const StringReference& string_reference = entry.first;
6625 Literal* table_entry_literal = entry.second;
6626 uint64_t index_in_table = GetJitStringRootIndex(string_reference);
6627 PatchJitRootUse(code, roots_data, table_entry_literal, index_in_table);
6628 }
6629 for (const auto& entry : jit_class_patches_) {
6630 const TypeReference& type_reference = entry.first;
6631 Literal* table_entry_literal = entry.second;
6632 uint64_t index_in_table = GetJitClassRootIndex(type_reference);
6633 PatchJitRootUse(code, roots_data, table_entry_literal, index_in_table);
6634 }
6635 }
6636
EmitPcRelativeAuipcPlaceholder(PcRelativePatchInfo * info_high,XRegister out)6637 void CodeGeneratorRISCV64::EmitPcRelativeAuipcPlaceholder(PcRelativePatchInfo* info_high,
6638 XRegister out) {
6639 DCHECK(info_high->pc_insn_label == &info_high->label);
6640 __ Bind(&info_high->label);
6641 __ Auipc(out, /*imm20=*/ kLinkTimeOffsetPlaceholderHigh);
6642 }
6643
EmitPcRelativeAddiPlaceholder(PcRelativePatchInfo * info_low,XRegister rd,XRegister rs1)6644 void CodeGeneratorRISCV64::EmitPcRelativeAddiPlaceholder(PcRelativePatchInfo* info_low,
6645 XRegister rd,
6646 XRegister rs1) {
6647 DCHECK(info_low->pc_insn_label != &info_low->label);
6648 __ Bind(&info_low->label);
6649 __ Addi(rd, rs1, /*imm12=*/ kLinkTimeOffsetPlaceholderLow);
6650 }
6651
EmitPcRelativeLwuPlaceholder(PcRelativePatchInfo * info_low,XRegister rd,XRegister rs1)6652 void CodeGeneratorRISCV64::EmitPcRelativeLwuPlaceholder(PcRelativePatchInfo* info_low,
6653 XRegister rd,
6654 XRegister rs1) {
6655 DCHECK(info_low->pc_insn_label != &info_low->label);
6656 __ Bind(&info_low->label);
6657 __ Lwu(rd, rs1, /*offset=*/ kLinkTimeOffsetPlaceholderLow);
6658 }
6659
EmitPcRelativeLdPlaceholder(PcRelativePatchInfo * info_low,XRegister rd,XRegister rs1)6660 void CodeGeneratorRISCV64::EmitPcRelativeLdPlaceholder(PcRelativePatchInfo* info_low,
6661 XRegister rd,
6662 XRegister rs1) {
6663 DCHECK(info_low->pc_insn_label != &info_low->label);
6664 __ Bind(&info_low->label);
6665 __ Ld(rd, rs1, /*offset=*/ kLinkTimeOffsetPlaceholderLow);
6666 }
6667
6668 template <linker::LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)>
EmitPcRelativeLinkerPatches(const ArenaDeque<PcRelativePatchInfo> & infos,ArenaVector<linker::LinkerPatch> * linker_patches)6669 inline void CodeGeneratorRISCV64::EmitPcRelativeLinkerPatches(
6670 const ArenaDeque<PcRelativePatchInfo>& infos,
6671 ArenaVector<linker::LinkerPatch>* linker_patches) {
6672 for (const PcRelativePatchInfo& info : infos) {
6673 linker_patches->push_back(Factory(__ GetLabelLocation(&info.label),
6674 info.target_dex_file,
6675 __ GetLabelLocation(info.pc_insn_label),
6676 info.offset_or_index));
6677 }
6678 }
6679
6680 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)6681 linker::LinkerPatch NoDexFileAdapter(size_t literal_offset,
6682 const DexFile* target_dex_file,
6683 uint32_t pc_insn_offset,
6684 uint32_t boot_image_offset) {
6685 DCHECK(target_dex_file == nullptr); // Unused for these patches, should be null.
6686 return Factory(literal_offset, pc_insn_offset, boot_image_offset);
6687 }
6688
EmitLinkerPatches(ArenaVector<linker::LinkerPatch> * linker_patches)6689 void CodeGeneratorRISCV64::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* linker_patches) {
6690 DCHECK(linker_patches->empty());
6691 size_t size =
6692 boot_image_method_patches_.size() +
6693 method_bss_entry_patches_.size() +
6694 boot_image_type_patches_.size() +
6695 app_image_type_patches_.size() +
6696 type_bss_entry_patches_.size() +
6697 public_type_bss_entry_patches_.size() +
6698 package_type_bss_entry_patches_.size() +
6699 boot_image_string_patches_.size() +
6700 string_bss_entry_patches_.size() +
6701 boot_image_jni_entrypoint_patches_.size() +
6702 boot_image_other_patches_.size();
6703 linker_patches->reserve(size);
6704 if (GetCompilerOptions().IsBootImage() || GetCompilerOptions().IsBootImageExtension()) {
6705 EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeMethodPatch>(
6706 boot_image_method_patches_, linker_patches);
6707 EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeTypePatch>(
6708 boot_image_type_patches_, linker_patches);
6709 EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeStringPatch>(
6710 boot_image_string_patches_, linker_patches);
6711 } else {
6712 DCHECK(boot_image_method_patches_.empty());
6713 DCHECK(boot_image_type_patches_.empty());
6714 DCHECK(boot_image_string_patches_.empty());
6715 }
6716 DCHECK_IMPLIES(!GetCompilerOptions().IsAppImage(), app_image_type_patches_.empty());
6717 if (GetCompilerOptions().IsBootImage()) {
6718 EmitPcRelativeLinkerPatches<NoDexFileAdapter<linker::LinkerPatch::IntrinsicReferencePatch>>(
6719 boot_image_other_patches_, linker_patches);
6720 } else {
6721 EmitPcRelativeLinkerPatches<NoDexFileAdapter<linker::LinkerPatch::BootImageRelRoPatch>>(
6722 boot_image_other_patches_, linker_patches);
6723 EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeAppImageRelRoPatch>(
6724 app_image_type_patches_, linker_patches);
6725 }
6726 EmitPcRelativeLinkerPatches<linker::LinkerPatch::MethodBssEntryPatch>(
6727 method_bss_entry_patches_, linker_patches);
6728 EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeBssEntryPatch>(
6729 type_bss_entry_patches_, linker_patches);
6730 EmitPcRelativeLinkerPatches<linker::LinkerPatch::PublicTypeBssEntryPatch>(
6731 public_type_bss_entry_patches_, linker_patches);
6732 EmitPcRelativeLinkerPatches<linker::LinkerPatch::PackageTypeBssEntryPatch>(
6733 package_type_bss_entry_patches_, linker_patches);
6734 EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringBssEntryPatch>(
6735 string_bss_entry_patches_, linker_patches);
6736 EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeJniEntrypointPatch>(
6737 boot_image_jni_entrypoint_patches_, linker_patches);
6738 DCHECK_EQ(size, linker_patches->size());
6739 }
6740
LoadTypeForBootImageIntrinsic(XRegister dest,TypeReference target_type)6741 void CodeGeneratorRISCV64::LoadTypeForBootImageIntrinsic(XRegister dest,
6742 TypeReference target_type) {
6743 // Load the type the same way as for HLoadClass::LoadKind::kBootImageLinkTimePcRelative.
6744 DCHECK(GetCompilerOptions().IsBootImage() || GetCompilerOptions().IsBootImageExtension());
6745 PcRelativePatchInfo* info_high =
6746 NewBootImageTypePatch(*target_type.dex_file, target_type.TypeIndex());
6747 EmitPcRelativeAuipcPlaceholder(info_high, dest);
6748 PcRelativePatchInfo* info_low =
6749 NewBootImageTypePatch(*target_type.dex_file, target_type.TypeIndex(), info_high);
6750 EmitPcRelativeAddiPlaceholder(info_low, dest, dest);
6751 }
6752
LoadBootImageRelRoEntry(XRegister dest,uint32_t boot_image_offset)6753 void CodeGeneratorRISCV64::LoadBootImageRelRoEntry(XRegister dest, uint32_t boot_image_offset) {
6754 PcRelativePatchInfo* info_high = NewBootImageRelRoPatch(boot_image_offset);
6755 EmitPcRelativeAuipcPlaceholder(info_high, dest);
6756 PcRelativePatchInfo* info_low = NewBootImageRelRoPatch(boot_image_offset, info_high);
6757 // Note: Boot image is in the low 4GiB and the entry is always 32-bit, so emit a 32-bit load.
6758 EmitPcRelativeLwuPlaceholder(info_low, dest, dest);
6759 }
6760
LoadBootImageAddress(XRegister dest,uint32_t boot_image_reference)6761 void CodeGeneratorRISCV64::LoadBootImageAddress(XRegister dest, uint32_t boot_image_reference) {
6762 if (GetCompilerOptions().IsBootImage()) {
6763 PcRelativePatchInfo* info_high = NewBootImageIntrinsicPatch(boot_image_reference);
6764 EmitPcRelativeAuipcPlaceholder(info_high, dest);
6765 PcRelativePatchInfo* info_low = NewBootImageIntrinsicPatch(boot_image_reference, info_high);
6766 EmitPcRelativeAddiPlaceholder(info_low, dest, dest);
6767 } else if (GetCompilerOptions().GetCompilePic()) {
6768 LoadBootImageRelRoEntry(dest, boot_image_reference);
6769 } else {
6770 DCHECK(GetCompilerOptions().IsJitCompiler());
6771 gc::Heap* heap = Runtime::Current()->GetHeap();
6772 DCHECK(!heap->GetBootImageSpaces().empty());
6773 const uint8_t* address = heap->GetBootImageSpaces()[0]->Begin() + boot_image_reference;
6774 // Note: Boot image is in the low 4GiB (usually the low 2GiB, requiring just LUI+ADDI).
6775 // We may not have an available scratch register for `LoadConst64()` but it never
6776 // emits better code than `Li()` for 32-bit unsigned constants anyway.
6777 __ Li(dest, reinterpret_cast32<uint32_t>(address));
6778 }
6779 }
6780
LoadIntrinsicDeclaringClass(XRegister dest,HInvoke * invoke)6781 void CodeGeneratorRISCV64::LoadIntrinsicDeclaringClass(XRegister dest, HInvoke* invoke) {
6782 DCHECK_NE(invoke->GetIntrinsic(), Intrinsics::kNone);
6783 if (GetCompilerOptions().IsBootImage()) {
6784 MethodReference target_method = invoke->GetResolvedMethodReference();
6785 dex::TypeIndex type_idx = target_method.dex_file->GetMethodId(target_method.index).class_idx_;
6786 LoadTypeForBootImageIntrinsic(dest, TypeReference(target_method.dex_file, type_idx));
6787 } else {
6788 uint32_t boot_image_offset = GetBootImageOffsetOfIntrinsicDeclaringClass(invoke);
6789 LoadBootImageAddress(dest, boot_image_offset);
6790 }
6791 }
6792
LoadClassRootForIntrinsic(XRegister dest,ClassRoot class_root)6793 void CodeGeneratorRISCV64::LoadClassRootForIntrinsic(XRegister dest, ClassRoot class_root) {
6794 if (GetCompilerOptions().IsBootImage()) {
6795 ScopedObjectAccess soa(Thread::Current());
6796 ObjPtr<mirror::Class> klass = GetClassRoot(class_root);
6797 TypeReference target_type(&klass->GetDexFile(), klass->GetDexTypeIndex());
6798 LoadTypeForBootImageIntrinsic(dest, target_type);
6799 } else {
6800 uint32_t boot_image_offset = GetBootImageOffset(class_root);
6801 LoadBootImageAddress(dest, boot_image_offset);
6802 }
6803 }
6804
LoadMethod(MethodLoadKind load_kind,Location temp,HInvoke * invoke)6805 void CodeGeneratorRISCV64::LoadMethod(MethodLoadKind load_kind, Location temp, HInvoke* invoke) {
6806 switch (load_kind) {
6807 case MethodLoadKind::kBootImageLinkTimePcRelative: {
6808 DCHECK(GetCompilerOptions().IsBootImage() || GetCompilerOptions().IsBootImageExtension());
6809 CodeGeneratorRISCV64::PcRelativePatchInfo* info_high =
6810 NewBootImageMethodPatch(invoke->GetResolvedMethodReference());
6811 EmitPcRelativeAuipcPlaceholder(info_high, temp.AsRegister<XRegister>());
6812 CodeGeneratorRISCV64::PcRelativePatchInfo* info_low =
6813 NewBootImageMethodPatch(invoke->GetResolvedMethodReference(), info_high);
6814 EmitPcRelativeAddiPlaceholder(
6815 info_low, temp.AsRegister<XRegister>(), temp.AsRegister<XRegister>());
6816 break;
6817 }
6818 case MethodLoadKind::kBootImageRelRo: {
6819 uint32_t boot_image_offset = GetBootImageOffset(invoke);
6820 LoadBootImageRelRoEntry(temp.AsRegister<XRegister>(), boot_image_offset);
6821 break;
6822 }
6823 case MethodLoadKind::kBssEntry: {
6824 PcRelativePatchInfo* info_high = NewMethodBssEntryPatch(invoke->GetMethodReference());
6825 EmitPcRelativeAuipcPlaceholder(info_high, temp.AsRegister<XRegister>());
6826 PcRelativePatchInfo* info_low =
6827 NewMethodBssEntryPatch(invoke->GetMethodReference(), info_high);
6828 EmitPcRelativeLdPlaceholder(
6829 info_low, temp.AsRegister<XRegister>(), temp.AsRegister<XRegister>());
6830 break;
6831 }
6832 case MethodLoadKind::kJitDirectAddress: {
6833 __ LoadConst64(temp.AsRegister<XRegister>(),
6834 reinterpret_cast<uint64_t>(invoke->GetResolvedMethod()));
6835 break;
6836 }
6837 case MethodLoadKind::kRuntimeCall: {
6838 // Test situation, don't do anything.
6839 break;
6840 }
6841 default: {
6842 LOG(FATAL) << "Load kind should have already been handled " << load_kind;
6843 UNREACHABLE();
6844 }
6845 }
6846 }
6847
GenerateStaticOrDirectCall(HInvokeStaticOrDirect * invoke,Location temp,SlowPathCode * slow_path)6848 void CodeGeneratorRISCV64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke,
6849 Location temp,
6850 SlowPathCode* slow_path) {
6851 // All registers are assumed to be correctly set up per the calling convention.
6852 Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp.
6853
6854 switch (invoke->GetMethodLoadKind()) {
6855 case MethodLoadKind::kStringInit: {
6856 // temp = thread->string_init_entrypoint
6857 uint32_t offset =
6858 GetThreadOffset<kRiscv64PointerSize>(invoke->GetStringInitEntryPoint()).Int32Value();
6859 __ Loadd(temp.AsRegister<XRegister>(), TR, offset);
6860 break;
6861 }
6862 case MethodLoadKind::kRecursive:
6863 callee_method = invoke->GetLocations()->InAt(invoke->GetCurrentMethodIndex());
6864 break;
6865 case MethodLoadKind::kRuntimeCall:
6866 GenerateInvokeStaticOrDirectRuntimeCall(invoke, temp, slow_path);
6867 return; // No code pointer retrieval; the runtime performs the call directly.
6868 case MethodLoadKind::kBootImageLinkTimePcRelative:
6869 DCHECK(GetCompilerOptions().IsBootImage() || GetCompilerOptions().IsBootImageExtension());
6870 if (invoke->GetCodePtrLocation() == CodePtrLocation::kCallCriticalNative) {
6871 // Do not materialize the method pointer, load directly the entrypoint.
6872 CodeGeneratorRISCV64::PcRelativePatchInfo* info_high =
6873 NewBootImageJniEntrypointPatch(invoke->GetResolvedMethodReference());
6874 EmitPcRelativeAuipcPlaceholder(info_high, RA);
6875 CodeGeneratorRISCV64::PcRelativePatchInfo* info_low =
6876 NewBootImageJniEntrypointPatch(invoke->GetResolvedMethodReference(), info_high);
6877 EmitPcRelativeLdPlaceholder(info_low, RA, RA);
6878 break;
6879 }
6880 FALLTHROUGH_INTENDED;
6881 default:
6882 LoadMethod(invoke->GetMethodLoadKind(), temp, invoke);
6883 break;
6884 }
6885
6886 switch (invoke->GetCodePtrLocation()) {
6887 case CodePtrLocation::kCallSelf:
6888 DCHECK(!GetGraph()->HasShouldDeoptimizeFlag());
6889 __ Jal(&frame_entry_label_);
6890 RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
6891 break;
6892 case CodePtrLocation::kCallArtMethod:
6893 // RA = callee_method->entry_point_from_quick_compiled_code_;
6894 __ Loadd(RA,
6895 callee_method.AsRegister<XRegister>(),
6896 ArtMethod::EntryPointFromQuickCompiledCodeOffset(kRiscv64PointerSize).Int32Value());
6897 // RA()
6898 __ Jalr(RA);
6899 RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
6900 break;
6901 case CodePtrLocation::kCallCriticalNative: {
6902 size_t out_frame_size =
6903 PrepareCriticalNativeCall<CriticalNativeCallingConventionVisitorRiscv64,
6904 kNativeStackAlignment,
6905 GetCriticalNativeDirectCallFrameSize>(invoke);
6906 if (invoke->GetMethodLoadKind() == MethodLoadKind::kBootImageLinkTimePcRelative) {
6907 // Entrypoint is already loaded in RA.
6908 } else {
6909 // RA = callee_method->ptr_sized_fields_.data_; // EntryPointFromJni
6910 MemberOffset offset = ArtMethod::EntryPointFromJniOffset(kRiscv64PointerSize);
6911 __ Loadd(RA, callee_method.AsRegister<XRegister>(), offset.Int32Value());
6912 }
6913 __ Jalr(RA);
6914 RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
6915 // The result is returned the same way in native ABI and managed ABI. No result conversion is
6916 // needed, see comments in `Riscv64JniCallingConvention::RequiresSmallResultTypeExtension()`.
6917 if (out_frame_size != 0u) {
6918 DecreaseFrame(out_frame_size);
6919 }
6920 break;
6921 }
6922 }
6923
6924 DCHECK(!IsLeafMethod());
6925 }
6926
MaybeGenerateInlineCacheCheck(HInstruction * instruction,XRegister klass)6927 void CodeGeneratorRISCV64::MaybeGenerateInlineCacheCheck(HInstruction* instruction,
6928 XRegister klass) {
6929 if (ProfilingInfoBuilder::IsInlineCacheUseful(instruction->AsInvoke(), this)) {
6930 ProfilingInfo* info = GetGraph()->GetProfilingInfo();
6931 DCHECK(info != nullptr);
6932 InlineCache* cache = ProfilingInfoBuilder::GetInlineCache(
6933 info, GetCompilerOptions(), instruction->AsInvoke());
6934 if (cache != nullptr) {
6935 uint64_t address = reinterpret_cast64<uint64_t>(cache);
6936 Riscv64Label done;
6937 // The `art_quick_update_inline_cache` expects the inline cache in T5.
6938 XRegister ic_reg = T5;
6939 ScratchRegisterScope srs(GetAssembler());
6940 DCHECK_EQ(srs.AvailableXRegisters(), 2u);
6941 srs.ExcludeXRegister(ic_reg);
6942 DCHECK_EQ(srs.AvailableXRegisters(), 1u);
6943 __ LoadConst64(ic_reg, address);
6944 {
6945 ScratchRegisterScope srs2(GetAssembler());
6946 XRegister tmp = srs2.AllocateXRegister();
6947 __ Loadd(tmp, ic_reg, InlineCache::ClassesOffset().Int32Value());
6948 // Fast path for a monomorphic cache.
6949 __ Beq(klass, tmp, &done);
6950 }
6951 InvokeRuntime(kQuickUpdateInlineCache, instruction, instruction->GetDexPc());
6952 __ Bind(&done);
6953 } else {
6954 // This is unexpected, but we don't guarantee stable compilation across
6955 // JIT runs so just warn about it.
6956 ScopedObjectAccess soa(Thread::Current());
6957 LOG(WARNING) << "Missing inline cache for " << GetGraph()->GetArtMethod()->PrettyMethod();
6958 }
6959 }
6960 }
6961
GenerateVirtualCall(HInvokeVirtual * invoke,Location temp_location,SlowPathCode * slow_path)6962 void CodeGeneratorRISCV64::GenerateVirtualCall(HInvokeVirtual* invoke,
6963 Location temp_location,
6964 SlowPathCode* slow_path) {
6965 // Use the calling convention instead of the location of the receiver, as
6966 // intrinsics may have put the receiver in a different register. In the intrinsics
6967 // slow path, the arguments have been moved to the right place, so here we are
6968 // guaranteed that the receiver is the first register of the calling convention.
6969 InvokeDexCallingConvention calling_convention;
6970 XRegister receiver = calling_convention.GetRegisterAt(0);
6971 XRegister temp = temp_location.AsRegister<XRegister>();
6972 MemberOffset method_offset =
6973 mirror::Class::EmbeddedVTableEntryOffset(invoke->GetVTableIndex(), kRiscv64PointerSize);
6974 MemberOffset class_offset = mirror::Object::ClassOffset();
6975 Offset entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kRiscv64PointerSize);
6976
6977 // temp = object->GetClass();
6978 __ Loadwu(temp, receiver, class_offset.Int32Value());
6979 MaybeRecordImplicitNullCheck(invoke);
6980 // Instead of simply (possibly) unpoisoning `temp` here, we should
6981 // emit a read barrier for the previous class reference load.
6982 // However this is not required in practice, as this is an
6983 // intermediate/temporary reference and because the current
6984 // concurrent copying collector keeps the from-space memory
6985 // intact/accessible until the end of the marking phase (the
6986 // concurrent copying collector may not in the future).
6987 MaybeUnpoisonHeapReference(temp);
6988
6989 // If we're compiling baseline, update the inline cache.
6990 MaybeGenerateInlineCacheCheck(invoke, temp);
6991
6992 // temp = temp->GetMethodAt(method_offset);
6993 __ Loadd(temp, temp, method_offset.Int32Value());
6994 // RA = temp->GetEntryPoint();
6995 __ Loadd(RA, temp, entry_point.Int32Value());
6996 // RA();
6997 __ Jalr(RA);
6998 RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
6999 }
7000
MoveFromReturnRegister(Location trg,DataType::Type type)7001 void CodeGeneratorRISCV64::MoveFromReturnRegister(Location trg, DataType::Type type) {
7002 if (!trg.IsValid()) {
7003 DCHECK_EQ(type, DataType::Type::kVoid);
7004 return;
7005 }
7006
7007 DCHECK_NE(type, DataType::Type::kVoid);
7008
7009 if (DataType::IsIntegralType(type) || type == DataType::Type::kReference) {
7010 XRegister trg_reg = trg.AsRegister<XRegister>();
7011 XRegister res_reg = Riscv64ReturnLocation(type).AsRegister<XRegister>();
7012 if (trg_reg != res_reg) {
7013 __ Mv(trg_reg, res_reg);
7014 }
7015 } else {
7016 FRegister trg_reg = trg.AsFpuRegister<FRegister>();
7017 FRegister res_reg = Riscv64ReturnLocation(type).AsFpuRegister<FRegister>();
7018 if (trg_reg != res_reg) {
7019 __ FMvD(trg_reg, res_reg); // 64-bit move is OK also for `float`.
7020 }
7021 }
7022 }
7023
PoisonHeapReference(XRegister reg)7024 void CodeGeneratorRISCV64::PoisonHeapReference(XRegister reg) {
7025 __ Sub(reg, Zero, reg); // Negate the ref.
7026 __ ZextW(reg, reg); // Zero-extend the 32-bit ref.
7027 }
7028
UnpoisonHeapReference(XRegister reg)7029 void CodeGeneratorRISCV64::UnpoisonHeapReference(XRegister reg) {
7030 __ Sub(reg, Zero, reg); // Negate the ref.
7031 __ ZextW(reg, reg); // Zero-extend the 32-bit ref.
7032 }
7033
MaybePoisonHeapReference(XRegister reg)7034 void CodeGeneratorRISCV64::MaybePoisonHeapReference(XRegister reg) {
7035 if (kPoisonHeapReferences) {
7036 PoisonHeapReference(reg);
7037 }
7038 }
7039
MaybeUnpoisonHeapReference(XRegister reg)7040 void CodeGeneratorRISCV64::MaybeUnpoisonHeapReference(XRegister reg) {
7041 if (kPoisonHeapReferences) {
7042 UnpoisonHeapReference(reg);
7043 }
7044 }
7045
SwapLocations(Location loc1,Location loc2,DataType::Type type)7046 void CodeGeneratorRISCV64::SwapLocations(Location loc1, Location loc2, DataType::Type type) {
7047 DCHECK(!loc1.IsConstant());
7048 DCHECK(!loc2.IsConstant());
7049
7050 if (loc1.Equals(loc2)) {
7051 return;
7052 }
7053
7054 bool is_slot1 = loc1.IsStackSlot() || loc1.IsDoubleStackSlot();
7055 bool is_slot2 = loc2.IsStackSlot() || loc2.IsDoubleStackSlot();
7056 bool is_simd1 = loc1.IsSIMDStackSlot();
7057 bool is_simd2 = loc2.IsSIMDStackSlot();
7058 bool is_fp_reg1 = loc1.IsFpuRegister();
7059 bool is_fp_reg2 = loc2.IsFpuRegister();
7060
7061 if ((is_slot1 != is_slot2) ||
7062 (loc2.IsRegister() && loc1.IsRegister()) ||
7063 (is_fp_reg2 && is_fp_reg1)) {
7064 if ((is_fp_reg2 && is_fp_reg1) && GetGraph()->HasSIMD()) {
7065 LOG(FATAL) << "Unsupported";
7066 UNREACHABLE();
7067 }
7068 ScratchRegisterScope srs(GetAssembler());
7069 Location tmp = (is_fp_reg2 || is_fp_reg1)
7070 ? Location::FpuRegisterLocation(srs.AllocateFRegister())
7071 : Location::RegisterLocation(srs.AllocateXRegister());
7072 MoveLocation(tmp, loc1, type);
7073 MoveLocation(loc1, loc2, type);
7074 MoveLocation(loc2, tmp, type);
7075 } else if (is_slot1 && is_slot2) {
7076 move_resolver_.Exchange(loc1.GetStackIndex(), loc2.GetStackIndex(), loc1.IsDoubleStackSlot());
7077 } else if (is_simd1 && is_simd2) {
7078 // TODO(riscv64): Add VECTOR/SIMD later.
7079 UNIMPLEMENTED(FATAL) << "Vector extension is unsupported";
7080 } else if ((is_fp_reg1 && is_simd2) || (is_fp_reg2 && is_simd1)) {
7081 // TODO(riscv64): Add VECTOR/SIMD later.
7082 UNIMPLEMENTED(FATAL) << "Vector extension is unsupported";
7083 } else {
7084 LOG(FATAL) << "Unimplemented swap between locations " << loc1 << " and " << loc2;
7085 }
7086 }
7087
7088 } // namespace riscv64
7089 } // namespace art
7090