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