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