code_generator_arm.cc revision 888d067a67640e7d9fc349b0451dfe845acad562
1/* 2 * Copyright (C) 2014 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_arm.h" 18 19#include "arch/arm/instruction_set_features_arm.h" 20#include "art_method.h" 21#include "code_generator_utils.h" 22#include "compiled_method.h" 23#include "entrypoints/quick/quick_entrypoints.h" 24#include "gc/accounting/card_table.h" 25#include "intrinsics.h" 26#include "intrinsics_arm.h" 27#include "mirror/array-inl.h" 28#include "mirror/class-inl.h" 29#include "thread.h" 30#include "utils/arm/assembler_arm.h" 31#include "utils/arm/managed_register_arm.h" 32#include "utils/assembler.h" 33#include "utils/stack_checks.h" 34 35namespace art { 36 37template<class MirrorType> 38class GcRoot; 39 40namespace arm { 41 42static bool ExpectedPairLayout(Location location) { 43 // We expected this for both core and fpu register pairs. 44 return ((location.low() & 1) == 0) && (location.low() + 1 == location.high()); 45} 46 47static constexpr int kCurrentMethodStackOffset = 0; 48static constexpr Register kMethodRegisterArgument = R0; 49 50// We unconditionally allocate R5 to ensure we can do long operations 51// with baseline. 52static constexpr Register kCoreSavedRegisterForBaseline = R5; 53static constexpr Register kCoreCalleeSaves[] = 54 { R5, R6, R7, R8, R10, R11, LR }; 55static constexpr SRegister kFpuCalleeSaves[] = 56 { S16, S17, S18, S19, S20, S21, S22, S23, S24, S25, S26, S27, S28, S29, S30, S31 }; 57 58// D31 cannot be split into two S registers, and the register allocator only works on 59// S registers. Therefore there is no need to block it. 60static constexpr DRegister DTMP = D31; 61 62static constexpr uint32_t kPackedSwitchJumpTableThreshold = 6; 63 64#define __ down_cast<ArmAssembler*>(codegen->GetAssembler())-> 65#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArmWordSize, x).Int32Value() 66 67class NullCheckSlowPathARM : public SlowPathCode { 68 public: 69 explicit NullCheckSlowPathARM(HNullCheck* instruction) : instruction_(instruction) {} 70 71 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 72 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 73 __ Bind(GetEntryLabel()); 74 if (instruction_->CanThrowIntoCatchBlock()) { 75 // Live registers will be restored in the catch block if caught. 76 SaveLiveRegisters(codegen, instruction_->GetLocations()); 77 } 78 arm_codegen->InvokeRuntime( 79 QUICK_ENTRY_POINT(pThrowNullPointer), instruction_, instruction_->GetDexPc(), this); 80 CheckEntrypointTypes<kQuickThrowNullPointer, void, void>(); 81 } 82 83 bool IsFatal() const OVERRIDE { return true; } 84 85 const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathARM"; } 86 87 private: 88 HNullCheck* const instruction_; 89 DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM); 90}; 91 92class DivZeroCheckSlowPathARM : public SlowPathCode { 93 public: 94 explicit DivZeroCheckSlowPathARM(HDivZeroCheck* instruction) : instruction_(instruction) {} 95 96 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 97 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 98 __ Bind(GetEntryLabel()); 99 if (instruction_->CanThrowIntoCatchBlock()) { 100 // Live registers will be restored in the catch block if caught. 101 SaveLiveRegisters(codegen, instruction_->GetLocations()); 102 } 103 arm_codegen->InvokeRuntime( 104 QUICK_ENTRY_POINT(pThrowDivZero), instruction_, instruction_->GetDexPc(), this); 105 CheckEntrypointTypes<kQuickThrowDivZero, void, void>(); 106 } 107 108 bool IsFatal() const OVERRIDE { return true; } 109 110 const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathARM"; } 111 112 private: 113 HDivZeroCheck* const instruction_; 114 DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathARM); 115}; 116 117class SuspendCheckSlowPathARM : public SlowPathCode { 118 public: 119 SuspendCheckSlowPathARM(HSuspendCheck* instruction, HBasicBlock* successor) 120 : instruction_(instruction), successor_(successor) {} 121 122 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 123 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 124 __ Bind(GetEntryLabel()); 125 SaveLiveRegisters(codegen, instruction_->GetLocations()); 126 arm_codegen->InvokeRuntime( 127 QUICK_ENTRY_POINT(pTestSuspend), instruction_, instruction_->GetDexPc(), this); 128 CheckEntrypointTypes<kQuickTestSuspend, void, void>(); 129 RestoreLiveRegisters(codegen, instruction_->GetLocations()); 130 if (successor_ == nullptr) { 131 __ b(GetReturnLabel()); 132 } else { 133 __ b(arm_codegen->GetLabelOf(successor_)); 134 } 135 } 136 137 Label* GetReturnLabel() { 138 DCHECK(successor_ == nullptr); 139 return &return_label_; 140 } 141 142 HBasicBlock* GetSuccessor() const { 143 return successor_; 144 } 145 146 const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathARM"; } 147 148 private: 149 HSuspendCheck* const instruction_; 150 // If not null, the block to branch to after the suspend check. 151 HBasicBlock* const successor_; 152 153 // If `successor_` is null, the label to branch to after the suspend check. 154 Label return_label_; 155 156 DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathARM); 157}; 158 159class BoundsCheckSlowPathARM : public SlowPathCode { 160 public: 161 explicit BoundsCheckSlowPathARM(HBoundsCheck* instruction) 162 : instruction_(instruction) {} 163 164 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 165 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 166 LocationSummary* locations = instruction_->GetLocations(); 167 168 __ Bind(GetEntryLabel()); 169 if (instruction_->CanThrowIntoCatchBlock()) { 170 // Live registers will be restored in the catch block if caught. 171 SaveLiveRegisters(codegen, instruction_->GetLocations()); 172 } 173 // We're moving two locations to locations that could overlap, so we need a parallel 174 // move resolver. 175 InvokeRuntimeCallingConvention calling_convention; 176 codegen->EmitParallelMoves( 177 locations->InAt(0), 178 Location::RegisterLocation(calling_convention.GetRegisterAt(0)), 179 Primitive::kPrimInt, 180 locations->InAt(1), 181 Location::RegisterLocation(calling_convention.GetRegisterAt(1)), 182 Primitive::kPrimInt); 183 arm_codegen->InvokeRuntime( 184 QUICK_ENTRY_POINT(pThrowArrayBounds), instruction_, instruction_->GetDexPc(), this); 185 CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>(); 186 } 187 188 bool IsFatal() const OVERRIDE { return true; } 189 190 const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathARM"; } 191 192 private: 193 HBoundsCheck* const instruction_; 194 195 DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARM); 196}; 197 198class LoadClassSlowPathARM : public SlowPathCode { 199 public: 200 LoadClassSlowPathARM(HLoadClass* cls, 201 HInstruction* at, 202 uint32_t dex_pc, 203 bool do_clinit) 204 : cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { 205 DCHECK(at->IsLoadClass() || at->IsClinitCheck()); 206 } 207 208 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 209 LocationSummary* locations = at_->GetLocations(); 210 211 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 212 __ Bind(GetEntryLabel()); 213 SaveLiveRegisters(codegen, locations); 214 215 InvokeRuntimeCallingConvention calling_convention; 216 __ LoadImmediate(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex()); 217 int32_t entry_point_offset = do_clinit_ 218 ? QUICK_ENTRY_POINT(pInitializeStaticStorage) 219 : QUICK_ENTRY_POINT(pInitializeType); 220 arm_codegen->InvokeRuntime(entry_point_offset, at_, dex_pc_, this); 221 if (do_clinit_) { 222 CheckEntrypointTypes<kQuickInitializeStaticStorage, void*, uint32_t>(); 223 } else { 224 CheckEntrypointTypes<kQuickInitializeType, void*, uint32_t>(); 225 } 226 227 // Move the class to the desired location. 228 Location out = locations->Out(); 229 if (out.IsValid()) { 230 DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg())); 231 arm_codegen->Move32(locations->Out(), Location::RegisterLocation(R0)); 232 } 233 RestoreLiveRegisters(codegen, locations); 234 __ b(GetExitLabel()); 235 } 236 237 const char* GetDescription() const OVERRIDE { return "LoadClassSlowPathARM"; } 238 239 private: 240 // The class this slow path will load. 241 HLoadClass* const cls_; 242 243 // The instruction where this slow path is happening. 244 // (Might be the load class or an initialization check). 245 HInstruction* const at_; 246 247 // The dex PC of `at_`. 248 const uint32_t dex_pc_; 249 250 // Whether to initialize the class. 251 const bool do_clinit_; 252 253 DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathARM); 254}; 255 256class LoadStringSlowPathARM : public SlowPathCode { 257 public: 258 explicit LoadStringSlowPathARM(HLoadString* instruction) : instruction_(instruction) {} 259 260 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 261 LocationSummary* locations = instruction_->GetLocations(); 262 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg())); 263 264 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 265 __ Bind(GetEntryLabel()); 266 SaveLiveRegisters(codegen, locations); 267 268 InvokeRuntimeCallingConvention calling_convention; 269 __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction_->GetStringIndex()); 270 arm_codegen->InvokeRuntime( 271 QUICK_ENTRY_POINT(pResolveString), instruction_, instruction_->GetDexPc(), this); 272 CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>(); 273 arm_codegen->Move32(locations->Out(), Location::RegisterLocation(R0)); 274 275 RestoreLiveRegisters(codegen, locations); 276 __ b(GetExitLabel()); 277 } 278 279 const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathARM"; } 280 281 private: 282 HLoadString* const instruction_; 283 284 DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathARM); 285}; 286 287class TypeCheckSlowPathARM : public SlowPathCode { 288 public: 289 TypeCheckSlowPathARM(HInstruction* instruction, bool is_fatal) 290 : instruction_(instruction), is_fatal_(is_fatal) {} 291 292 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 293 LocationSummary* locations = instruction_->GetLocations(); 294 Location object_class = instruction_->IsCheckCast() ? locations->GetTemp(0) 295 : locations->Out(); 296 DCHECK(instruction_->IsCheckCast() 297 || !locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg())); 298 299 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 300 __ Bind(GetEntryLabel()); 301 302 if (!is_fatal_) { 303 SaveLiveRegisters(codegen, locations); 304 } 305 306 // We're moving two locations to locations that could overlap, so we need a parallel 307 // move resolver. 308 InvokeRuntimeCallingConvention calling_convention; 309 codegen->EmitParallelMoves( 310 locations->InAt(1), 311 Location::RegisterLocation(calling_convention.GetRegisterAt(0)), 312 Primitive::kPrimNot, 313 object_class, 314 Location::RegisterLocation(calling_convention.GetRegisterAt(1)), 315 Primitive::kPrimNot); 316 317 if (instruction_->IsInstanceOf()) { 318 arm_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pInstanceofNonTrivial), 319 instruction_, 320 instruction_->GetDexPc(), 321 this); 322 CheckEntrypointTypes< 323 kQuickInstanceofNonTrivial, uint32_t, const mirror::Class*, const mirror::Class*>(); 324 arm_codegen->Move32(locations->Out(), Location::RegisterLocation(R0)); 325 } else { 326 DCHECK(instruction_->IsCheckCast()); 327 arm_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pCheckCast), 328 instruction_, 329 instruction_->GetDexPc(), 330 this); 331 CheckEntrypointTypes<kQuickCheckCast, void, const mirror::Class*, const mirror::Class*>(); 332 } 333 334 if (!is_fatal_) { 335 RestoreLiveRegisters(codegen, locations); 336 __ b(GetExitLabel()); 337 } 338 } 339 340 const char* GetDescription() const OVERRIDE { return "TypeCheckSlowPathARM"; } 341 342 bool IsFatal() const OVERRIDE { return is_fatal_; } 343 344 private: 345 HInstruction* const instruction_; 346 const bool is_fatal_; 347 348 DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathARM); 349}; 350 351class DeoptimizationSlowPathARM : public SlowPathCode { 352 public: 353 explicit DeoptimizationSlowPathARM(HInstruction* instruction) 354 : instruction_(instruction) {} 355 356 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 357 __ Bind(GetEntryLabel()); 358 SaveLiveRegisters(codegen, instruction_->GetLocations()); 359 DCHECK(instruction_->IsDeoptimize()); 360 HDeoptimize* deoptimize = instruction_->AsDeoptimize(); 361 uint32_t dex_pc = deoptimize->GetDexPc(); 362 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 363 arm_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pDeoptimize), instruction_, dex_pc, this); 364 CheckEntrypointTypes<kQuickDeoptimize, void, void>(); 365 } 366 367 const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathARM"; } 368 369 private: 370 HInstruction* const instruction_; 371 DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathARM); 372}; 373 374class ArraySetSlowPathARM : public SlowPathCode { 375 public: 376 explicit ArraySetSlowPathARM(HInstruction* instruction) : instruction_(instruction) {} 377 378 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 379 LocationSummary* locations = instruction_->GetLocations(); 380 __ Bind(GetEntryLabel()); 381 SaveLiveRegisters(codegen, locations); 382 383 InvokeRuntimeCallingConvention calling_convention; 384 HParallelMove parallel_move(codegen->GetGraph()->GetArena()); 385 parallel_move.AddMove( 386 locations->InAt(0), 387 Location::RegisterLocation(calling_convention.GetRegisterAt(0)), 388 Primitive::kPrimNot, 389 nullptr); 390 parallel_move.AddMove( 391 locations->InAt(1), 392 Location::RegisterLocation(calling_convention.GetRegisterAt(1)), 393 Primitive::kPrimInt, 394 nullptr); 395 parallel_move.AddMove( 396 locations->InAt(2), 397 Location::RegisterLocation(calling_convention.GetRegisterAt(2)), 398 Primitive::kPrimNot, 399 nullptr); 400 codegen->GetMoveResolver()->EmitNativeCode(¶llel_move); 401 402 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 403 arm_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pAputObject), 404 instruction_, 405 instruction_->GetDexPc(), 406 this); 407 CheckEntrypointTypes<kQuickAputObject, void, mirror::Array*, int32_t, mirror::Object*>(); 408 RestoreLiveRegisters(codegen, locations); 409 __ b(GetExitLabel()); 410 } 411 412 const char* GetDescription() const OVERRIDE { return "ArraySetSlowPathARM"; } 413 414 private: 415 HInstruction* const instruction_; 416 417 DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathARM); 418}; 419 420// Slow path generating a read barrier for a heap reference. 421class ReadBarrierForHeapReferenceSlowPathARM : public SlowPathCode { 422 public: 423 ReadBarrierForHeapReferenceSlowPathARM(HInstruction* instruction, 424 Location out, 425 Location ref, 426 Location obj, 427 uint32_t offset, 428 Location index) 429 : instruction_(instruction), 430 out_(out), 431 ref_(ref), 432 obj_(obj), 433 offset_(offset), 434 index_(index) { 435 DCHECK(kEmitCompilerReadBarrier); 436 // If `obj` is equal to `out` or `ref`, it means the initial object 437 // has been overwritten by (or after) the heap object reference load 438 // to be instrumented, e.g.: 439 // 440 // __ LoadFromOffset(kLoadWord, out, out, offset); 441 // codegen_->GenerateReadBarrier(instruction, out_loc, out_loc, out_loc, offset); 442 // 443 // In that case, we have lost the information about the original 444 // object, and the emitted read barrier cannot work properly. 445 DCHECK(!obj.Equals(out)) << "obj=" << obj << " out=" << out; 446 DCHECK(!obj.Equals(ref)) << "obj=" << obj << " ref=" << ref; 447 } 448 449 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 450 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 451 LocationSummary* locations = instruction_->GetLocations(); 452 Register reg_out = out_.AsRegister<Register>(); 453 DCHECK(locations->CanCall()); 454 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out)); 455 DCHECK(!instruction_->IsInvoke() || 456 (instruction_->IsInvokeStaticOrDirect() && 457 instruction_->GetLocations()->Intrinsified())); 458 459 __ Bind(GetEntryLabel()); 460 SaveLiveRegisters(codegen, locations); 461 462 // We may have to change the index's value, but as `index_` is a 463 // constant member (like other "inputs" of this slow path), 464 // introduce a copy of it, `index`. 465 Location index = index_; 466 if (index_.IsValid()) { 467 // Handle `index_` for HArrayGet and intrinsic UnsafeGetObject. 468 if (instruction_->IsArrayGet()) { 469 // Compute the actual memory offset and store it in `index`. 470 Register index_reg = index_.AsRegister<Register>(); 471 DCHECK(locations->GetLiveRegisters()->ContainsCoreRegister(index_reg)); 472 if (codegen->IsCoreCalleeSaveRegister(index_reg)) { 473 // We are about to change the value of `index_reg` (see the 474 // calls to art::arm::Thumb2Assembler::Lsl and 475 // art::arm::Thumb2Assembler::AddConstant below), but it has 476 // not been saved by the previous call to 477 // art::SlowPathCode::SaveLiveRegisters, as it is a 478 // callee-save register -- 479 // art::SlowPathCode::SaveLiveRegisters does not consider 480 // callee-save registers, as it has been designed with the 481 // assumption that callee-save registers are supposed to be 482 // handled by the called function. So, as a callee-save 483 // register, `index_reg` _would_ eventually be saved onto 484 // the stack, but it would be too late: we would have 485 // changed its value earlier. Therefore, we manually save 486 // it here into another freely available register, 487 // `free_reg`, chosen of course among the caller-save 488 // registers (as a callee-save `free_reg` register would 489 // exhibit the same problem). 490 // 491 // Note we could have requested a temporary register from 492 // the register allocator instead; but we prefer not to, as 493 // this is a slow path, and we know we can find a 494 // caller-save register that is available. 495 Register free_reg = FindAvailableCallerSaveRegister(codegen); 496 __ Mov(free_reg, index_reg); 497 index_reg = free_reg; 498 index = Location::RegisterLocation(index_reg); 499 } else { 500 // The initial register stored in `index_` has already been 501 // saved in the call to art::SlowPathCode::SaveLiveRegisters 502 // (as it is not a callee-save register), so we can freely 503 // use it. 504 } 505 // Shifting the index value contained in `index_reg` by the scale 506 // factor (2) cannot overflow in practice, as the runtime is 507 // unable to allocate object arrays with a size larger than 508 // 2^26 - 1 (that is, 2^28 - 4 bytes). 509 __ Lsl(index_reg, index_reg, TIMES_4); 510 static_assert( 511 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), 512 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); 513 __ AddConstant(index_reg, index_reg, offset_); 514 } else { 515 DCHECK(instruction_->IsInvoke()); 516 DCHECK(instruction_->GetLocations()->Intrinsified()); 517 DCHECK((instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObject) || 518 (instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile)) 519 << instruction_->AsInvoke()->GetIntrinsic(); 520 DCHECK_EQ(offset_, 0U); 521 DCHECK(index_.IsRegisterPair()); 522 // UnsafeGet's offset location is a register pair, the low 523 // part contains the correct offset. 524 index = index_.ToLow(); 525 } 526 } 527 528 // We're moving two or three locations to locations that could 529 // overlap, so we need a parallel move resolver. 530 InvokeRuntimeCallingConvention calling_convention; 531 HParallelMove parallel_move(codegen->GetGraph()->GetArena()); 532 parallel_move.AddMove(ref_, 533 Location::RegisterLocation(calling_convention.GetRegisterAt(0)), 534 Primitive::kPrimNot, 535 nullptr); 536 parallel_move.AddMove(obj_, 537 Location::RegisterLocation(calling_convention.GetRegisterAt(1)), 538 Primitive::kPrimNot, 539 nullptr); 540 if (index.IsValid()) { 541 parallel_move.AddMove(index, 542 Location::RegisterLocation(calling_convention.GetRegisterAt(2)), 543 Primitive::kPrimInt, 544 nullptr); 545 codegen->GetMoveResolver()->EmitNativeCode(¶llel_move); 546 } else { 547 codegen->GetMoveResolver()->EmitNativeCode(¶llel_move); 548 __ LoadImmediate(calling_convention.GetRegisterAt(2), offset_); 549 } 550 arm_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pReadBarrierSlow), 551 instruction_, 552 instruction_->GetDexPc(), 553 this); 554 CheckEntrypointTypes< 555 kQuickReadBarrierSlow, mirror::Object*, mirror::Object*, mirror::Object*, uint32_t>(); 556 arm_codegen->Move32(out_, Location::RegisterLocation(R0)); 557 558 RestoreLiveRegisters(codegen, locations); 559 __ b(GetExitLabel()); 560 } 561 562 const char* GetDescription() const OVERRIDE { return "ReadBarrierForHeapReferenceSlowPathARM"; } 563 564 private: 565 Register FindAvailableCallerSaveRegister(CodeGenerator* codegen) { 566 size_t ref = static_cast<int>(ref_.AsRegister<Register>()); 567 size_t obj = static_cast<int>(obj_.AsRegister<Register>()); 568 for (size_t i = 0, e = codegen->GetNumberOfCoreRegisters(); i < e; ++i) { 569 if (i != ref && i != obj && !codegen->IsCoreCalleeSaveRegister(i)) { 570 return static_cast<Register>(i); 571 } 572 } 573 // We shall never fail to find a free caller-save register, as 574 // there are more than two core caller-save registers on ARM 575 // (meaning it is possible to find one which is different from 576 // `ref` and `obj`). 577 DCHECK_GT(codegen->GetNumberOfCoreCallerSaveRegisters(), 2u); 578 LOG(FATAL) << "Could not find a free caller-save register"; 579 UNREACHABLE(); 580 } 581 582 HInstruction* const instruction_; 583 const Location out_; 584 const Location ref_; 585 const Location obj_; 586 const uint32_t offset_; 587 // An additional location containing an index to an array. 588 // Only used for HArrayGet and the UnsafeGetObject & 589 // UnsafeGetObjectVolatile intrinsics. 590 const Location index_; 591 592 DISALLOW_COPY_AND_ASSIGN(ReadBarrierForHeapReferenceSlowPathARM); 593}; 594 595// Slow path generating a read barrier for a GC root. 596class ReadBarrierForRootSlowPathARM : public SlowPathCode { 597 public: 598 ReadBarrierForRootSlowPathARM(HInstruction* instruction, Location out, Location root) 599 : instruction_(instruction), out_(out), root_(root) {} 600 601 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { 602 LocationSummary* locations = instruction_->GetLocations(); 603 Register reg_out = out_.AsRegister<Register>(); 604 DCHECK(locations->CanCall()); 605 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out)); 606 DCHECK(instruction_->IsLoadClass() || instruction_->IsLoadString()); 607 608 __ Bind(GetEntryLabel()); 609 SaveLiveRegisters(codegen, locations); 610 611 InvokeRuntimeCallingConvention calling_convention; 612 CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); 613 arm_codegen->Move32(Location::RegisterLocation(calling_convention.GetRegisterAt(0)), root_); 614 arm_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pReadBarrierForRootSlow), 615 instruction_, 616 instruction_->GetDexPc(), 617 this); 618 CheckEntrypointTypes<kQuickReadBarrierForRootSlow, mirror::Object*, GcRoot<mirror::Object>*>(); 619 arm_codegen->Move32(out_, Location::RegisterLocation(R0)); 620 621 RestoreLiveRegisters(codegen, locations); 622 __ b(GetExitLabel()); 623 } 624 625 const char* GetDescription() const OVERRIDE { return "ReadBarrierForRootSlowPathARM"; } 626 627 private: 628 HInstruction* const instruction_; 629 const Location out_; 630 const Location root_; 631 632 DISALLOW_COPY_AND_ASSIGN(ReadBarrierForRootSlowPathARM); 633}; 634 635#undef __ 636#define __ down_cast<ArmAssembler*>(GetAssembler())-> 637 638inline Condition ARMCondition(IfCondition cond) { 639 switch (cond) { 640 case kCondEQ: return EQ; 641 case kCondNE: return NE; 642 case kCondLT: return LT; 643 case kCondLE: return LE; 644 case kCondGT: return GT; 645 case kCondGE: return GE; 646 case kCondB: return LO; 647 case kCondBE: return LS; 648 case kCondA: return HI; 649 case kCondAE: return HS; 650 } 651 LOG(FATAL) << "Unreachable"; 652 UNREACHABLE(); 653} 654 655// Maps signed condition to unsigned condition. 656inline Condition ARMUnsignedCondition(IfCondition cond) { 657 switch (cond) { 658 case kCondEQ: return EQ; 659 case kCondNE: return NE; 660 // Signed to unsigned. 661 case kCondLT: return LO; 662 case kCondLE: return LS; 663 case kCondGT: return HI; 664 case kCondGE: return HS; 665 // Unsigned remain unchanged. 666 case kCondB: return LO; 667 case kCondBE: return LS; 668 case kCondA: return HI; 669 case kCondAE: return HS; 670 } 671 LOG(FATAL) << "Unreachable"; 672 UNREACHABLE(); 673} 674 675void CodeGeneratorARM::DumpCoreRegister(std::ostream& stream, int reg) const { 676 stream << Register(reg); 677} 678 679void CodeGeneratorARM::DumpFloatingPointRegister(std::ostream& stream, int reg) const { 680 stream << SRegister(reg); 681} 682 683size_t CodeGeneratorARM::SaveCoreRegister(size_t stack_index, uint32_t reg_id) { 684 __ StoreToOffset(kStoreWord, static_cast<Register>(reg_id), SP, stack_index); 685 return kArmWordSize; 686} 687 688size_t CodeGeneratorARM::RestoreCoreRegister(size_t stack_index, uint32_t reg_id) { 689 __ LoadFromOffset(kLoadWord, static_cast<Register>(reg_id), SP, stack_index); 690 return kArmWordSize; 691} 692 693size_t CodeGeneratorARM::SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) { 694 __ StoreSToOffset(static_cast<SRegister>(reg_id), SP, stack_index); 695 return kArmWordSize; 696} 697 698size_t CodeGeneratorARM::RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) { 699 __ LoadSFromOffset(static_cast<SRegister>(reg_id), SP, stack_index); 700 return kArmWordSize; 701} 702 703CodeGeneratorARM::CodeGeneratorARM(HGraph* graph, 704 const ArmInstructionSetFeatures& isa_features, 705 const CompilerOptions& compiler_options, 706 OptimizingCompilerStats* stats) 707 : CodeGenerator(graph, 708 kNumberOfCoreRegisters, 709 kNumberOfSRegisters, 710 kNumberOfRegisterPairs, 711 ComputeRegisterMask(reinterpret_cast<const int*>(kCoreCalleeSaves), 712 arraysize(kCoreCalleeSaves)), 713 ComputeRegisterMask(reinterpret_cast<const int*>(kFpuCalleeSaves), 714 arraysize(kFpuCalleeSaves)), 715 compiler_options, 716 stats), 717 block_labels_(nullptr), 718 location_builder_(graph, this), 719 instruction_visitor_(graph, this), 720 move_resolver_(graph->GetArena(), this), 721 assembler_(), 722 isa_features_(isa_features), 723 method_patches_(MethodReferenceComparator(), 724 graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), 725 call_patches_(MethodReferenceComparator(), 726 graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), 727 relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { 728 // Always save the LR register to mimic Quick. 729 AddAllocatedRegister(Location::RegisterLocation(LR)); 730} 731 732void CodeGeneratorARM::Finalize(CodeAllocator* allocator) { 733 // Ensure that we fix up branches and literal loads and emit the literal pool. 734 __ FinalizeCode(); 735 736 // Adjust native pc offsets in stack maps. 737 for (size_t i = 0, num = stack_map_stream_.GetNumberOfStackMaps(); i != num; ++i) { 738 uint32_t old_position = stack_map_stream_.GetStackMap(i).native_pc_offset; 739 uint32_t new_position = __ GetAdjustedPosition(old_position); 740 stack_map_stream_.SetStackMapNativePcOffset(i, new_position); 741 } 742 // Adjust pc offsets for the disassembly information. 743 if (disasm_info_ != nullptr) { 744 GeneratedCodeInterval* frame_entry_interval = disasm_info_->GetFrameEntryInterval(); 745 frame_entry_interval->start = __ GetAdjustedPosition(frame_entry_interval->start); 746 frame_entry_interval->end = __ GetAdjustedPosition(frame_entry_interval->end); 747 for (auto& it : *disasm_info_->GetInstructionIntervals()) { 748 it.second.start = __ GetAdjustedPosition(it.second.start); 749 it.second.end = __ GetAdjustedPosition(it.second.end); 750 } 751 for (auto& it : *disasm_info_->GetSlowPathIntervals()) { 752 it.code_interval.start = __ GetAdjustedPosition(it.code_interval.start); 753 it.code_interval.end = __ GetAdjustedPosition(it.code_interval.end); 754 } 755 } 756 757 CodeGenerator::Finalize(allocator); 758} 759 760Location CodeGeneratorARM::AllocateFreeRegister(Primitive::Type type) const { 761 switch (type) { 762 case Primitive::kPrimLong: { 763 size_t reg = FindFreeEntry(blocked_register_pairs_, kNumberOfRegisterPairs); 764 ArmManagedRegister pair = 765 ArmManagedRegister::FromRegisterPair(static_cast<RegisterPair>(reg)); 766 DCHECK(!blocked_core_registers_[pair.AsRegisterPairLow()]); 767 DCHECK(!blocked_core_registers_[pair.AsRegisterPairHigh()]); 768 769 blocked_core_registers_[pair.AsRegisterPairLow()] = true; 770 blocked_core_registers_[pair.AsRegisterPairHigh()] = true; 771 UpdateBlockedPairRegisters(); 772 return Location::RegisterPairLocation(pair.AsRegisterPairLow(), pair.AsRegisterPairHigh()); 773 } 774 775 case Primitive::kPrimByte: 776 case Primitive::kPrimBoolean: 777 case Primitive::kPrimChar: 778 case Primitive::kPrimShort: 779 case Primitive::kPrimInt: 780 case Primitive::kPrimNot: { 781 int reg = FindFreeEntry(blocked_core_registers_, kNumberOfCoreRegisters); 782 // Block all register pairs that contain `reg`. 783 for (int i = 0; i < kNumberOfRegisterPairs; i++) { 784 ArmManagedRegister current = 785 ArmManagedRegister::FromRegisterPair(static_cast<RegisterPair>(i)); 786 if (current.AsRegisterPairLow() == reg || current.AsRegisterPairHigh() == reg) { 787 blocked_register_pairs_[i] = true; 788 } 789 } 790 return Location::RegisterLocation(reg); 791 } 792 793 case Primitive::kPrimFloat: { 794 int reg = FindFreeEntry(blocked_fpu_registers_, kNumberOfSRegisters); 795 return Location::FpuRegisterLocation(reg); 796 } 797 798 case Primitive::kPrimDouble: { 799 int reg = FindTwoFreeConsecutiveAlignedEntries(blocked_fpu_registers_, kNumberOfSRegisters); 800 DCHECK_EQ(reg % 2, 0); 801 return Location::FpuRegisterPairLocation(reg, reg + 1); 802 } 803 804 case Primitive::kPrimVoid: 805 LOG(FATAL) << "Unreachable type " << type; 806 } 807 808 return Location::NoLocation(); 809} 810 811void CodeGeneratorARM::SetupBlockedRegisters(bool is_baseline) const { 812 // Don't allocate the dalvik style register pair passing. 813 blocked_register_pairs_[R1_R2] = true; 814 815 // Stack register, LR and PC are always reserved. 816 blocked_core_registers_[SP] = true; 817 blocked_core_registers_[LR] = true; 818 blocked_core_registers_[PC] = true; 819 820 // Reserve thread register. 821 blocked_core_registers_[TR] = true; 822 823 // Reserve temp register. 824 blocked_core_registers_[IP] = true; 825 826 if (is_baseline) { 827 for (size_t i = 0; i < arraysize(kCoreCalleeSaves); ++i) { 828 blocked_core_registers_[kCoreCalleeSaves[i]] = true; 829 } 830 831 blocked_core_registers_[kCoreSavedRegisterForBaseline] = false; 832 } 833 834 if (is_baseline || GetGraph()->IsDebuggable()) { 835 // Stubs do not save callee-save floating point registers. If the graph 836 // is debuggable, we need to deal with these registers differently. For 837 // now, just block them. 838 for (size_t i = 0; i < arraysize(kFpuCalleeSaves); ++i) { 839 blocked_fpu_registers_[kFpuCalleeSaves[i]] = true; 840 } 841 } 842 843 UpdateBlockedPairRegisters(); 844} 845 846void CodeGeneratorARM::UpdateBlockedPairRegisters() const { 847 for (int i = 0; i < kNumberOfRegisterPairs; i++) { 848 ArmManagedRegister current = 849 ArmManagedRegister::FromRegisterPair(static_cast<RegisterPair>(i)); 850 if (blocked_core_registers_[current.AsRegisterPairLow()] 851 || blocked_core_registers_[current.AsRegisterPairHigh()]) { 852 blocked_register_pairs_[i] = true; 853 } 854 } 855} 856 857InstructionCodeGeneratorARM::InstructionCodeGeneratorARM(HGraph* graph, CodeGeneratorARM* codegen) 858 : HGraphVisitor(graph), 859 assembler_(codegen->GetAssembler()), 860 codegen_(codegen) {} 861 862void CodeGeneratorARM::ComputeSpillMask() { 863 core_spill_mask_ = allocated_registers_.GetCoreRegisters() & core_callee_save_mask_; 864 // Save one extra register for baseline. Note that on thumb2, there is no easy 865 // instruction to restore just the PC, so this actually helps both baseline 866 // and non-baseline to save and restore at least two registers at entry and exit. 867 core_spill_mask_ |= (1 << kCoreSavedRegisterForBaseline); 868 DCHECK_NE(core_spill_mask_, 0u) << "At least the return address register must be saved"; 869 fpu_spill_mask_ = allocated_registers_.GetFloatingPointRegisters() & fpu_callee_save_mask_; 870 // We use vpush and vpop for saving and restoring floating point registers, which take 871 // a SRegister and the number of registers to save/restore after that SRegister. We 872 // therefore update the `fpu_spill_mask_` to also contain those registers not allocated, 873 // but in the range. 874 if (fpu_spill_mask_ != 0) { 875 uint32_t least_significant_bit = LeastSignificantBit(fpu_spill_mask_); 876 uint32_t most_significant_bit = MostSignificantBit(fpu_spill_mask_); 877 for (uint32_t i = least_significant_bit + 1 ; i < most_significant_bit; ++i) { 878 fpu_spill_mask_ |= (1 << i); 879 } 880 } 881} 882 883static dwarf::Reg DWARFReg(Register reg) { 884 return dwarf::Reg::ArmCore(static_cast<int>(reg)); 885} 886 887static dwarf::Reg DWARFReg(SRegister reg) { 888 return dwarf::Reg::ArmFp(static_cast<int>(reg)); 889} 890 891void CodeGeneratorARM::GenerateFrameEntry() { 892 bool skip_overflow_check = 893 IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kArm); 894 DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks()); 895 __ Bind(&frame_entry_label_); 896 897 if (HasEmptyFrame()) { 898 return; 899 } 900 901 if (!skip_overflow_check) { 902 __ AddConstant(IP, SP, -static_cast<int32_t>(GetStackOverflowReservedBytes(kArm))); 903 __ LoadFromOffset(kLoadWord, IP, IP, 0); 904 RecordPcInfo(nullptr, 0); 905 } 906 907 __ PushList(core_spill_mask_); 908 __ cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(core_spill_mask_)); 909 __ cfi().RelOffsetForMany(DWARFReg(kMethodRegisterArgument), 0, core_spill_mask_, kArmWordSize); 910 if (fpu_spill_mask_ != 0) { 911 SRegister start_register = SRegister(LeastSignificantBit(fpu_spill_mask_)); 912 __ vpushs(start_register, POPCOUNT(fpu_spill_mask_)); 913 __ cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(fpu_spill_mask_)); 914 __ cfi().RelOffsetForMany(DWARFReg(S0), 0, fpu_spill_mask_, kArmWordSize); 915 } 916 int adjust = GetFrameSize() - FrameEntrySpillSize(); 917 __ AddConstant(SP, -adjust); 918 __ cfi().AdjustCFAOffset(adjust); 919 __ StoreToOffset(kStoreWord, kMethodRegisterArgument, SP, 0); 920} 921 922void CodeGeneratorARM::GenerateFrameExit() { 923 if (HasEmptyFrame()) { 924 __ bx(LR); 925 return; 926 } 927 __ cfi().RememberState(); 928 int adjust = GetFrameSize() - FrameEntrySpillSize(); 929 __ AddConstant(SP, adjust); 930 __ cfi().AdjustCFAOffset(-adjust); 931 if (fpu_spill_mask_ != 0) { 932 SRegister start_register = SRegister(LeastSignificantBit(fpu_spill_mask_)); 933 __ vpops(start_register, POPCOUNT(fpu_spill_mask_)); 934 __ cfi().AdjustCFAOffset(-kArmPointerSize * POPCOUNT(fpu_spill_mask_)); 935 __ cfi().RestoreMany(DWARFReg(SRegister(0)), fpu_spill_mask_); 936 } 937 // Pop LR into PC to return. 938 DCHECK_NE(core_spill_mask_ & (1 << LR), 0U); 939 uint32_t pop_mask = (core_spill_mask_ & (~(1 << LR))) | 1 << PC; 940 __ PopList(pop_mask); 941 __ cfi().RestoreState(); 942 __ cfi().DefCFAOffset(GetFrameSize()); 943} 944 945void CodeGeneratorARM::Bind(HBasicBlock* block) { 946 Label* label = GetLabelOf(block); 947 __ BindTrackedLabel(label); 948} 949 950Location CodeGeneratorARM::GetStackLocation(HLoadLocal* load) const { 951 switch (load->GetType()) { 952 case Primitive::kPrimLong: 953 case Primitive::kPrimDouble: 954 return Location::DoubleStackSlot(GetStackSlot(load->GetLocal())); 955 956 case Primitive::kPrimInt: 957 case Primitive::kPrimNot: 958 case Primitive::kPrimFloat: 959 return Location::StackSlot(GetStackSlot(load->GetLocal())); 960 961 case Primitive::kPrimBoolean: 962 case Primitive::kPrimByte: 963 case Primitive::kPrimChar: 964 case Primitive::kPrimShort: 965 case Primitive::kPrimVoid: 966 LOG(FATAL) << "Unexpected type " << load->GetType(); 967 UNREACHABLE(); 968 } 969 970 LOG(FATAL) << "Unreachable"; 971 UNREACHABLE(); 972} 973 974Location InvokeDexCallingConventionVisitorARM::GetNextLocation(Primitive::Type type) { 975 switch (type) { 976 case Primitive::kPrimBoolean: 977 case Primitive::kPrimByte: 978 case Primitive::kPrimChar: 979 case Primitive::kPrimShort: 980 case Primitive::kPrimInt: 981 case Primitive::kPrimNot: { 982 uint32_t index = gp_index_++; 983 uint32_t stack_index = stack_index_++; 984 if (index < calling_convention.GetNumberOfRegisters()) { 985 return Location::RegisterLocation(calling_convention.GetRegisterAt(index)); 986 } else { 987 return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index)); 988 } 989 } 990 991 case Primitive::kPrimLong: { 992 uint32_t index = gp_index_; 993 uint32_t stack_index = stack_index_; 994 gp_index_ += 2; 995 stack_index_ += 2; 996 if (index + 1 < calling_convention.GetNumberOfRegisters()) { 997 if (calling_convention.GetRegisterAt(index) == R1) { 998 // Skip R1, and use R2_R3 instead. 999 gp_index_++; 1000 index++; 1001 } 1002 } 1003 if (index + 1 < calling_convention.GetNumberOfRegisters()) { 1004 DCHECK_EQ(calling_convention.GetRegisterAt(index) + 1, 1005 calling_convention.GetRegisterAt(index + 1)); 1006 1007 return Location::RegisterPairLocation(calling_convention.GetRegisterAt(index), 1008 calling_convention.GetRegisterAt(index + 1)); 1009 } else { 1010 return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index)); 1011 } 1012 } 1013 1014 case Primitive::kPrimFloat: { 1015 uint32_t stack_index = stack_index_++; 1016 if (float_index_ % 2 == 0) { 1017 float_index_ = std::max(double_index_, float_index_); 1018 } 1019 if (float_index_ < calling_convention.GetNumberOfFpuRegisters()) { 1020 return Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(float_index_++)); 1021 } else { 1022 return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index)); 1023 } 1024 } 1025 1026 case Primitive::kPrimDouble: { 1027 double_index_ = std::max(double_index_, RoundUp(float_index_, 2)); 1028 uint32_t stack_index = stack_index_; 1029 stack_index_ += 2; 1030 if (double_index_ + 1 < calling_convention.GetNumberOfFpuRegisters()) { 1031 uint32_t index = double_index_; 1032 double_index_ += 2; 1033 Location result = Location::FpuRegisterPairLocation( 1034 calling_convention.GetFpuRegisterAt(index), 1035 calling_convention.GetFpuRegisterAt(index + 1)); 1036 DCHECK(ExpectedPairLayout(result)); 1037 return result; 1038 } else { 1039 return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index)); 1040 } 1041 } 1042 1043 case Primitive::kPrimVoid: 1044 LOG(FATAL) << "Unexpected parameter type " << type; 1045 break; 1046 } 1047 return Location::NoLocation(); 1048} 1049 1050Location InvokeDexCallingConventionVisitorARM::GetReturnLocation(Primitive::Type type) const { 1051 switch (type) { 1052 case Primitive::kPrimBoolean: 1053 case Primitive::kPrimByte: 1054 case Primitive::kPrimChar: 1055 case Primitive::kPrimShort: 1056 case Primitive::kPrimInt: 1057 case Primitive::kPrimNot: { 1058 return Location::RegisterLocation(R0); 1059 } 1060 1061 case Primitive::kPrimFloat: { 1062 return Location::FpuRegisterLocation(S0); 1063 } 1064 1065 case Primitive::kPrimLong: { 1066 return Location::RegisterPairLocation(R0, R1); 1067 } 1068 1069 case Primitive::kPrimDouble: { 1070 return Location::FpuRegisterPairLocation(S0, S1); 1071 } 1072 1073 case Primitive::kPrimVoid: 1074 return Location::NoLocation(); 1075 } 1076 1077 UNREACHABLE(); 1078} 1079 1080Location InvokeDexCallingConventionVisitorARM::GetMethodLocation() const { 1081 return Location::RegisterLocation(kMethodRegisterArgument); 1082} 1083 1084void CodeGeneratorARM::Move32(Location destination, Location source) { 1085 if (source.Equals(destination)) { 1086 return; 1087 } 1088 if (destination.IsRegister()) { 1089 if (source.IsRegister()) { 1090 __ Mov(destination.AsRegister<Register>(), source.AsRegister<Register>()); 1091 } else if (source.IsFpuRegister()) { 1092 __ vmovrs(destination.AsRegister<Register>(), source.AsFpuRegister<SRegister>()); 1093 } else { 1094 __ LoadFromOffset(kLoadWord, destination.AsRegister<Register>(), SP, source.GetStackIndex()); 1095 } 1096 } else if (destination.IsFpuRegister()) { 1097 if (source.IsRegister()) { 1098 __ vmovsr(destination.AsFpuRegister<SRegister>(), source.AsRegister<Register>()); 1099 } else if (source.IsFpuRegister()) { 1100 __ vmovs(destination.AsFpuRegister<SRegister>(), source.AsFpuRegister<SRegister>()); 1101 } else { 1102 __ LoadSFromOffset(destination.AsFpuRegister<SRegister>(), SP, source.GetStackIndex()); 1103 } 1104 } else { 1105 DCHECK(destination.IsStackSlot()) << destination; 1106 if (source.IsRegister()) { 1107 __ StoreToOffset(kStoreWord, source.AsRegister<Register>(), SP, destination.GetStackIndex()); 1108 } else if (source.IsFpuRegister()) { 1109 __ StoreSToOffset(source.AsFpuRegister<SRegister>(), SP, destination.GetStackIndex()); 1110 } else { 1111 DCHECK(source.IsStackSlot()) << source; 1112 __ LoadFromOffset(kLoadWord, IP, SP, source.GetStackIndex()); 1113 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 1114 } 1115 } 1116} 1117 1118void CodeGeneratorARM::Move64(Location destination, Location source) { 1119 if (source.Equals(destination)) { 1120 return; 1121 } 1122 if (destination.IsRegisterPair()) { 1123 if (source.IsRegisterPair()) { 1124 EmitParallelMoves( 1125 Location::RegisterLocation(source.AsRegisterPairHigh<Register>()), 1126 Location::RegisterLocation(destination.AsRegisterPairHigh<Register>()), 1127 Primitive::kPrimInt, 1128 Location::RegisterLocation(source.AsRegisterPairLow<Register>()), 1129 Location::RegisterLocation(destination.AsRegisterPairLow<Register>()), 1130 Primitive::kPrimInt); 1131 } else if (source.IsFpuRegister()) { 1132 UNIMPLEMENTED(FATAL); 1133 } else if (source.IsFpuRegisterPair()) { 1134 __ vmovrrd(destination.AsRegisterPairLow<Register>(), 1135 destination.AsRegisterPairHigh<Register>(), 1136 FromLowSToD(source.AsFpuRegisterPairLow<SRegister>())); 1137 } else { 1138 DCHECK(source.IsDoubleStackSlot()); 1139 DCHECK(ExpectedPairLayout(destination)); 1140 __ LoadFromOffset(kLoadWordPair, destination.AsRegisterPairLow<Register>(), 1141 SP, source.GetStackIndex()); 1142 } 1143 } else if (destination.IsFpuRegisterPair()) { 1144 if (source.IsDoubleStackSlot()) { 1145 __ LoadDFromOffset(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), 1146 SP, 1147 source.GetStackIndex()); 1148 } else if (source.IsRegisterPair()) { 1149 __ vmovdrr(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), 1150 source.AsRegisterPairLow<Register>(), 1151 source.AsRegisterPairHigh<Register>()); 1152 } else { 1153 UNIMPLEMENTED(FATAL); 1154 } 1155 } else { 1156 DCHECK(destination.IsDoubleStackSlot()); 1157 if (source.IsRegisterPair()) { 1158 // No conflict possible, so just do the moves. 1159 if (source.AsRegisterPairLow<Register>() == R1) { 1160 DCHECK_EQ(source.AsRegisterPairHigh<Register>(), R2); 1161 __ StoreToOffset(kStoreWord, R1, SP, destination.GetStackIndex()); 1162 __ StoreToOffset(kStoreWord, R2, SP, destination.GetHighStackIndex(kArmWordSize)); 1163 } else { 1164 __ StoreToOffset(kStoreWordPair, source.AsRegisterPairLow<Register>(), 1165 SP, destination.GetStackIndex()); 1166 } 1167 } else if (source.IsFpuRegisterPair()) { 1168 __ StoreDToOffset(FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()), 1169 SP, 1170 destination.GetStackIndex()); 1171 } else { 1172 DCHECK(source.IsDoubleStackSlot()); 1173 EmitParallelMoves( 1174 Location::StackSlot(source.GetStackIndex()), 1175 Location::StackSlot(destination.GetStackIndex()), 1176 Primitive::kPrimInt, 1177 Location::StackSlot(source.GetHighStackIndex(kArmWordSize)), 1178 Location::StackSlot(destination.GetHighStackIndex(kArmWordSize)), 1179 Primitive::kPrimInt); 1180 } 1181 } 1182} 1183 1184void CodeGeneratorARM::Move(HInstruction* instruction, Location location, HInstruction* move_for) { 1185 LocationSummary* locations = instruction->GetLocations(); 1186 if (instruction->IsCurrentMethod()) { 1187 Move32(location, Location::StackSlot(kCurrentMethodStackOffset)); 1188 } else if (locations != nullptr && locations->Out().Equals(location)) { 1189 return; 1190 } else if (locations != nullptr && locations->Out().IsConstant()) { 1191 HConstant* const_to_move = locations->Out().GetConstant(); 1192 if (const_to_move->IsIntConstant() || const_to_move->IsNullConstant()) { 1193 int32_t value = GetInt32ValueOf(const_to_move); 1194 if (location.IsRegister()) { 1195 __ LoadImmediate(location.AsRegister<Register>(), value); 1196 } else { 1197 DCHECK(location.IsStackSlot()); 1198 __ LoadImmediate(IP, value); 1199 __ StoreToOffset(kStoreWord, IP, SP, location.GetStackIndex()); 1200 } 1201 } else { 1202 DCHECK(const_to_move->IsLongConstant()) << const_to_move->DebugName(); 1203 int64_t value = const_to_move->AsLongConstant()->GetValue(); 1204 if (location.IsRegisterPair()) { 1205 __ LoadImmediate(location.AsRegisterPairLow<Register>(), Low32Bits(value)); 1206 __ LoadImmediate(location.AsRegisterPairHigh<Register>(), High32Bits(value)); 1207 } else { 1208 DCHECK(location.IsDoubleStackSlot()); 1209 __ LoadImmediate(IP, Low32Bits(value)); 1210 __ StoreToOffset(kStoreWord, IP, SP, location.GetStackIndex()); 1211 __ LoadImmediate(IP, High32Bits(value)); 1212 __ StoreToOffset(kStoreWord, IP, SP, location.GetHighStackIndex(kArmWordSize)); 1213 } 1214 } 1215 } else if (instruction->IsLoadLocal()) { 1216 uint32_t stack_slot = GetStackSlot(instruction->AsLoadLocal()->GetLocal()); 1217 switch (instruction->GetType()) { 1218 case Primitive::kPrimBoolean: 1219 case Primitive::kPrimByte: 1220 case Primitive::kPrimChar: 1221 case Primitive::kPrimShort: 1222 case Primitive::kPrimInt: 1223 case Primitive::kPrimNot: 1224 case Primitive::kPrimFloat: 1225 Move32(location, Location::StackSlot(stack_slot)); 1226 break; 1227 1228 case Primitive::kPrimLong: 1229 case Primitive::kPrimDouble: 1230 Move64(location, Location::DoubleStackSlot(stack_slot)); 1231 break; 1232 1233 default: 1234 LOG(FATAL) << "Unexpected type " << instruction->GetType(); 1235 } 1236 } else if (instruction->IsTemporary()) { 1237 Location temp_location = GetTemporaryLocation(instruction->AsTemporary()); 1238 if (temp_location.IsStackSlot()) { 1239 Move32(location, temp_location); 1240 } else { 1241 DCHECK(temp_location.IsDoubleStackSlot()); 1242 Move64(location, temp_location); 1243 } 1244 } else { 1245 DCHECK((instruction->GetNext() == move_for) || instruction->GetNext()->IsTemporary()); 1246 switch (instruction->GetType()) { 1247 case Primitive::kPrimBoolean: 1248 case Primitive::kPrimByte: 1249 case Primitive::kPrimChar: 1250 case Primitive::kPrimShort: 1251 case Primitive::kPrimNot: 1252 case Primitive::kPrimInt: 1253 case Primitive::kPrimFloat: 1254 Move32(location, locations->Out()); 1255 break; 1256 1257 case Primitive::kPrimLong: 1258 case Primitive::kPrimDouble: 1259 Move64(location, locations->Out()); 1260 break; 1261 1262 default: 1263 LOG(FATAL) << "Unexpected type " << instruction->GetType(); 1264 } 1265 } 1266} 1267 1268void CodeGeneratorARM::MoveConstant(Location location, int32_t value) { 1269 DCHECK(location.IsRegister()); 1270 __ LoadImmediate(location.AsRegister<Register>(), value); 1271} 1272 1273void CodeGeneratorARM::MoveLocation(Location dst, Location src, Primitive::Type dst_type) { 1274 if (Primitive::Is64BitType(dst_type)) { 1275 Move64(dst, src); 1276 } else { 1277 Move32(dst, src); 1278 } 1279} 1280 1281void CodeGeneratorARM::AddLocationAsTemp(Location location, LocationSummary* locations) { 1282 if (location.IsRegister()) { 1283 locations->AddTemp(location); 1284 } else if (location.IsRegisterPair()) { 1285 locations->AddTemp(Location::RegisterLocation(location.AsRegisterPairLow<Register>())); 1286 locations->AddTemp(Location::RegisterLocation(location.AsRegisterPairHigh<Register>())); 1287 } else { 1288 UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location; 1289 } 1290} 1291 1292void CodeGeneratorARM::InvokeRuntime(QuickEntrypointEnum entrypoint, 1293 HInstruction* instruction, 1294 uint32_t dex_pc, 1295 SlowPathCode* slow_path) { 1296 InvokeRuntime(GetThreadOffset<kArmWordSize>(entrypoint).Int32Value(), 1297 instruction, 1298 dex_pc, 1299 slow_path); 1300} 1301 1302void CodeGeneratorARM::InvokeRuntime(int32_t entry_point_offset, 1303 HInstruction* instruction, 1304 uint32_t dex_pc, 1305 SlowPathCode* slow_path) { 1306 ValidateInvokeRuntime(instruction, slow_path); 1307 __ LoadFromOffset(kLoadWord, LR, TR, entry_point_offset); 1308 __ blx(LR); 1309 RecordPcInfo(instruction, dex_pc, slow_path); 1310} 1311 1312void InstructionCodeGeneratorARM::HandleGoto(HInstruction* got, HBasicBlock* successor) { 1313 DCHECK(!successor->IsExitBlock()); 1314 1315 HBasicBlock* block = got->GetBlock(); 1316 HInstruction* previous = got->GetPrevious(); 1317 1318 HLoopInformation* info = block->GetLoopInformation(); 1319 if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) { 1320 codegen_->ClearSpillSlotsFromLoopPhisInStackMap(info->GetSuspendCheck()); 1321 GenerateSuspendCheck(info->GetSuspendCheck(), successor); 1322 return; 1323 } 1324 1325 if (block->IsEntryBlock() && (previous != nullptr) && previous->IsSuspendCheck()) { 1326 GenerateSuspendCheck(previous->AsSuspendCheck(), nullptr); 1327 } 1328 if (!codegen_->GoesToNextBlock(got->GetBlock(), successor)) { 1329 __ b(codegen_->GetLabelOf(successor)); 1330 } 1331} 1332 1333void LocationsBuilderARM::VisitGoto(HGoto* got) { 1334 got->SetLocations(nullptr); 1335} 1336 1337void InstructionCodeGeneratorARM::VisitGoto(HGoto* got) { 1338 HandleGoto(got, got->GetSuccessor()); 1339} 1340 1341void LocationsBuilderARM::VisitTryBoundary(HTryBoundary* try_boundary) { 1342 try_boundary->SetLocations(nullptr); 1343} 1344 1345void InstructionCodeGeneratorARM::VisitTryBoundary(HTryBoundary* try_boundary) { 1346 HBasicBlock* successor = try_boundary->GetNormalFlowSuccessor(); 1347 if (!successor->IsExitBlock()) { 1348 HandleGoto(try_boundary, successor); 1349 } 1350} 1351 1352void LocationsBuilderARM::VisitExit(HExit* exit) { 1353 exit->SetLocations(nullptr); 1354} 1355 1356void InstructionCodeGeneratorARM::VisitExit(HExit* exit ATTRIBUTE_UNUSED) { 1357} 1358 1359void InstructionCodeGeneratorARM::GenerateCompareWithImmediate(Register left, int32_t right) { 1360 ShifterOperand operand; 1361 if (GetAssembler()->ShifterOperandCanHold(R0, left, CMP, right, &operand)) { 1362 __ cmp(left, operand); 1363 } else { 1364 Register temp = IP; 1365 __ LoadImmediate(temp, right); 1366 __ cmp(left, ShifterOperand(temp)); 1367 } 1368} 1369 1370void InstructionCodeGeneratorARM::GenerateFPJumps(HCondition* cond, 1371 Label* true_label, 1372 Label* false_label) { 1373 __ vmstat(); // transfer FP status register to ARM APSR. 1374 // TODO: merge into a single branch (except "equal or unordered" and "not equal") 1375 if (cond->IsFPConditionTrueIfNaN()) { 1376 __ b(true_label, VS); // VS for unordered. 1377 } else if (cond->IsFPConditionFalseIfNaN()) { 1378 __ b(false_label, VS); // VS for unordered. 1379 } 1380 __ b(true_label, ARMCondition(cond->GetCondition())); 1381} 1382 1383void InstructionCodeGeneratorARM::GenerateLongComparesAndJumps(HCondition* cond, 1384 Label* true_label, 1385 Label* false_label) { 1386 LocationSummary* locations = cond->GetLocations(); 1387 Location left = locations->InAt(0); 1388 Location right = locations->InAt(1); 1389 IfCondition if_cond = cond->GetCondition(); 1390 1391 Register left_high = left.AsRegisterPairHigh<Register>(); 1392 Register left_low = left.AsRegisterPairLow<Register>(); 1393 IfCondition true_high_cond = if_cond; 1394 IfCondition false_high_cond = cond->GetOppositeCondition(); 1395 Condition final_condition = ARMUnsignedCondition(if_cond); // unsigned on lower part 1396 1397 // Set the conditions for the test, remembering that == needs to be 1398 // decided using the low words. 1399 // TODO: consider avoiding jumps with temporary and CMP low+SBC high 1400 switch (if_cond) { 1401 case kCondEQ: 1402 case kCondNE: 1403 // Nothing to do. 1404 break; 1405 case kCondLT: 1406 false_high_cond = kCondGT; 1407 break; 1408 case kCondLE: 1409 true_high_cond = kCondLT; 1410 break; 1411 case kCondGT: 1412 false_high_cond = kCondLT; 1413 break; 1414 case kCondGE: 1415 true_high_cond = kCondGT; 1416 break; 1417 case kCondB: 1418 false_high_cond = kCondA; 1419 break; 1420 case kCondBE: 1421 true_high_cond = kCondB; 1422 break; 1423 case kCondA: 1424 false_high_cond = kCondB; 1425 break; 1426 case kCondAE: 1427 true_high_cond = kCondA; 1428 break; 1429 } 1430 if (right.IsConstant()) { 1431 int64_t value = right.GetConstant()->AsLongConstant()->GetValue(); 1432 int32_t val_low = Low32Bits(value); 1433 int32_t val_high = High32Bits(value); 1434 1435 GenerateCompareWithImmediate(left_high, val_high); 1436 if (if_cond == kCondNE) { 1437 __ b(true_label, ARMCondition(true_high_cond)); 1438 } else if (if_cond == kCondEQ) { 1439 __ b(false_label, ARMCondition(false_high_cond)); 1440 } else { 1441 __ b(true_label, ARMCondition(true_high_cond)); 1442 __ b(false_label, ARMCondition(false_high_cond)); 1443 } 1444 // Must be equal high, so compare the lows. 1445 GenerateCompareWithImmediate(left_low, val_low); 1446 } else { 1447 Register right_high = right.AsRegisterPairHigh<Register>(); 1448 Register right_low = right.AsRegisterPairLow<Register>(); 1449 1450 __ cmp(left_high, ShifterOperand(right_high)); 1451 if (if_cond == kCondNE) { 1452 __ b(true_label, ARMCondition(true_high_cond)); 1453 } else if (if_cond == kCondEQ) { 1454 __ b(false_label, ARMCondition(false_high_cond)); 1455 } else { 1456 __ b(true_label, ARMCondition(true_high_cond)); 1457 __ b(false_label, ARMCondition(false_high_cond)); 1458 } 1459 // Must be equal high, so compare the lows. 1460 __ cmp(left_low, ShifterOperand(right_low)); 1461 } 1462 // The last comparison might be unsigned. 1463 // TODO: optimize cases where this is always true/false 1464 __ b(true_label, final_condition); 1465} 1466 1467void InstructionCodeGeneratorARM::GenerateCompareTestAndBranch(HCondition* condition, 1468 Label* true_target_in, 1469 Label* false_target_in) { 1470 // Generated branching requires both targets to be explicit. If either of the 1471 // targets is nullptr (fallthrough) use and bind `fallthrough_target` instead. 1472 Label fallthrough_target; 1473 Label* true_target = true_target_in == nullptr ? &fallthrough_target : true_target_in; 1474 Label* false_target = false_target_in == nullptr ? &fallthrough_target : false_target_in; 1475 1476 LocationSummary* locations = condition->GetLocations(); 1477 Location left = locations->InAt(0); 1478 Location right = locations->InAt(1); 1479 1480 Primitive::Type type = condition->InputAt(0)->GetType(); 1481 switch (type) { 1482 case Primitive::kPrimLong: 1483 GenerateLongComparesAndJumps(condition, true_target, false_target); 1484 break; 1485 case Primitive::kPrimFloat: 1486 __ vcmps(left.AsFpuRegister<SRegister>(), right.AsFpuRegister<SRegister>()); 1487 GenerateFPJumps(condition, true_target, false_target); 1488 break; 1489 case Primitive::kPrimDouble: 1490 __ vcmpd(FromLowSToD(left.AsFpuRegisterPairLow<SRegister>()), 1491 FromLowSToD(right.AsFpuRegisterPairLow<SRegister>())); 1492 GenerateFPJumps(condition, true_target, false_target); 1493 break; 1494 default: 1495 LOG(FATAL) << "Unexpected compare type " << type; 1496 } 1497 1498 if (false_target != &fallthrough_target) { 1499 __ b(false_target); 1500 } 1501 1502 if (fallthrough_target.IsLinked()) { 1503 __ Bind(&fallthrough_target); 1504 } 1505} 1506 1507void InstructionCodeGeneratorARM::GenerateTestAndBranch(HInstruction* instruction, 1508 size_t condition_input_index, 1509 Label* true_target, 1510 Label* false_target) { 1511 HInstruction* cond = instruction->InputAt(condition_input_index); 1512 1513 if (true_target == nullptr && false_target == nullptr) { 1514 // Nothing to do. The code always falls through. 1515 return; 1516 } else if (cond->IsIntConstant()) { 1517 // Constant condition, statically compared against 1. 1518 if (cond->AsIntConstant()->IsOne()) { 1519 if (true_target != nullptr) { 1520 __ b(true_target); 1521 } 1522 } else { 1523 DCHECK(cond->AsIntConstant()->IsZero()); 1524 if (false_target != nullptr) { 1525 __ b(false_target); 1526 } 1527 } 1528 return; 1529 } 1530 1531 // The following code generates these patterns: 1532 // (1) true_target == nullptr && false_target != nullptr 1533 // - opposite condition true => branch to false_target 1534 // (2) true_target != nullptr && false_target == nullptr 1535 // - condition true => branch to true_target 1536 // (3) true_target != nullptr && false_target != nullptr 1537 // - condition true => branch to true_target 1538 // - branch to false_target 1539 if (IsBooleanValueOrMaterializedCondition(cond)) { 1540 // Condition has been materialized, compare the output to 0. 1541 Location cond_val = instruction->GetLocations()->InAt(condition_input_index); 1542 DCHECK(cond_val.IsRegister()); 1543 if (true_target == nullptr) { 1544 __ CompareAndBranchIfZero(cond_val.AsRegister<Register>(), false_target); 1545 } else { 1546 __ CompareAndBranchIfNonZero(cond_val.AsRegister<Register>(), true_target); 1547 } 1548 } else { 1549 // Condition has not been materialized. Use its inputs as the comparison and 1550 // its condition as the branch condition. 1551 HCondition* condition = cond->AsCondition(); 1552 1553 // If this is a long or FP comparison that has been folded into 1554 // the HCondition, generate the comparison directly. 1555 Primitive::Type type = condition->InputAt(0)->GetType(); 1556 if (type == Primitive::kPrimLong || Primitive::IsFloatingPointType(type)) { 1557 GenerateCompareTestAndBranch(condition, true_target, false_target); 1558 return; 1559 } 1560 1561 LocationSummary* locations = cond->GetLocations(); 1562 DCHECK(locations->InAt(0).IsRegister()); 1563 Register left = locations->InAt(0).AsRegister<Register>(); 1564 Location right = locations->InAt(1); 1565 if (right.IsRegister()) { 1566 __ cmp(left, ShifterOperand(right.AsRegister<Register>())); 1567 } else { 1568 DCHECK(right.IsConstant()); 1569 GenerateCompareWithImmediate(left, CodeGenerator::GetInt32ValueOf(right.GetConstant())); 1570 } 1571 if (true_target == nullptr) { 1572 __ b(false_target, ARMCondition(condition->GetOppositeCondition())); 1573 } else { 1574 __ b(true_target, ARMCondition(condition->GetCondition())); 1575 } 1576 } 1577 1578 // If neither branch falls through (case 3), the conditional branch to `true_target` 1579 // was already emitted (case 2) and we need to emit a jump to `false_target`. 1580 if (true_target != nullptr && false_target != nullptr) { 1581 __ b(false_target); 1582 } 1583} 1584 1585void LocationsBuilderARM::VisitIf(HIf* if_instr) { 1586 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr); 1587 if (IsBooleanValueOrMaterializedCondition(if_instr->InputAt(0))) { 1588 locations->SetInAt(0, Location::RequiresRegister()); 1589 } 1590} 1591 1592void InstructionCodeGeneratorARM::VisitIf(HIf* if_instr) { 1593 HBasicBlock* true_successor = if_instr->IfTrueSuccessor(); 1594 HBasicBlock* false_successor = if_instr->IfFalseSuccessor(); 1595 Label* true_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), true_successor) ? 1596 nullptr : codegen_->GetLabelOf(true_successor); 1597 Label* false_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), false_successor) ? 1598 nullptr : codegen_->GetLabelOf(false_successor); 1599 GenerateTestAndBranch(if_instr, /* condition_input_index */ 0, true_target, false_target); 1600} 1601 1602void LocationsBuilderARM::VisitDeoptimize(HDeoptimize* deoptimize) { 1603 LocationSummary* locations = new (GetGraph()->GetArena()) 1604 LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath); 1605 if (IsBooleanValueOrMaterializedCondition(deoptimize->InputAt(0))) { 1606 locations->SetInAt(0, Location::RequiresRegister()); 1607 } 1608} 1609 1610void InstructionCodeGeneratorARM::VisitDeoptimize(HDeoptimize* deoptimize) { 1611 SlowPathCode* slow_path = new (GetGraph()->GetArena()) DeoptimizationSlowPathARM(deoptimize); 1612 codegen_->AddSlowPath(slow_path); 1613 GenerateTestAndBranch(deoptimize, 1614 /* condition_input_index */ 0, 1615 slow_path->GetEntryLabel(), 1616 /* false_target */ nullptr); 1617} 1618 1619void LocationsBuilderARM::VisitCondition(HCondition* cond) { 1620 LocationSummary* locations = 1621 new (GetGraph()->GetArena()) LocationSummary(cond, LocationSummary::kNoCall); 1622 // Handle the long/FP comparisons made in instruction simplification. 1623 switch (cond->InputAt(0)->GetType()) { 1624 case Primitive::kPrimLong: 1625 locations->SetInAt(0, Location::RequiresRegister()); 1626 locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1))); 1627 if (cond->NeedsMaterialization()) { 1628 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 1629 } 1630 break; 1631 1632 case Primitive::kPrimFloat: 1633 case Primitive::kPrimDouble: 1634 locations->SetInAt(0, Location::RequiresFpuRegister()); 1635 locations->SetInAt(1, Location::RequiresFpuRegister()); 1636 if (cond->NeedsMaterialization()) { 1637 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 1638 } 1639 break; 1640 1641 default: 1642 locations->SetInAt(0, Location::RequiresRegister()); 1643 locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1))); 1644 if (cond->NeedsMaterialization()) { 1645 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 1646 } 1647 } 1648} 1649 1650void InstructionCodeGeneratorARM::VisitCondition(HCondition* cond) { 1651 if (!cond->NeedsMaterialization()) { 1652 return; 1653 } 1654 1655 LocationSummary* locations = cond->GetLocations(); 1656 Location left = locations->InAt(0); 1657 Location right = locations->InAt(1); 1658 Register out = locations->Out().AsRegister<Register>(); 1659 Label true_label, false_label; 1660 1661 switch (cond->InputAt(0)->GetType()) { 1662 default: { 1663 // Integer case. 1664 if (right.IsRegister()) { 1665 __ cmp(left.AsRegister<Register>(), ShifterOperand(right.AsRegister<Register>())); 1666 } else { 1667 DCHECK(right.IsConstant()); 1668 GenerateCompareWithImmediate(left.AsRegister<Register>(), 1669 CodeGenerator::GetInt32ValueOf(right.GetConstant())); 1670 } 1671 __ it(ARMCondition(cond->GetCondition()), kItElse); 1672 __ mov(locations->Out().AsRegister<Register>(), ShifterOperand(1), 1673 ARMCondition(cond->GetCondition())); 1674 __ mov(locations->Out().AsRegister<Register>(), ShifterOperand(0), 1675 ARMCondition(cond->GetOppositeCondition())); 1676 return; 1677 } 1678 case Primitive::kPrimLong: 1679 GenerateLongComparesAndJumps(cond, &true_label, &false_label); 1680 break; 1681 case Primitive::kPrimFloat: 1682 __ vcmps(left.AsFpuRegister<SRegister>(), right.AsFpuRegister<SRegister>()); 1683 GenerateFPJumps(cond, &true_label, &false_label); 1684 break; 1685 case Primitive::kPrimDouble: 1686 __ vcmpd(FromLowSToD(left.AsFpuRegisterPairLow<SRegister>()), 1687 FromLowSToD(right.AsFpuRegisterPairLow<SRegister>())); 1688 GenerateFPJumps(cond, &true_label, &false_label); 1689 break; 1690 } 1691 1692 // Convert the jumps into the result. 1693 Label done_label; 1694 1695 // False case: result = 0. 1696 __ Bind(&false_label); 1697 __ LoadImmediate(out, 0); 1698 __ b(&done_label); 1699 1700 // True case: result = 1. 1701 __ Bind(&true_label); 1702 __ LoadImmediate(out, 1); 1703 __ Bind(&done_label); 1704} 1705 1706void LocationsBuilderARM::VisitEqual(HEqual* comp) { 1707 VisitCondition(comp); 1708} 1709 1710void InstructionCodeGeneratorARM::VisitEqual(HEqual* comp) { 1711 VisitCondition(comp); 1712} 1713 1714void LocationsBuilderARM::VisitNotEqual(HNotEqual* comp) { 1715 VisitCondition(comp); 1716} 1717 1718void InstructionCodeGeneratorARM::VisitNotEqual(HNotEqual* comp) { 1719 VisitCondition(comp); 1720} 1721 1722void LocationsBuilderARM::VisitLessThan(HLessThan* comp) { 1723 VisitCondition(comp); 1724} 1725 1726void InstructionCodeGeneratorARM::VisitLessThan(HLessThan* comp) { 1727 VisitCondition(comp); 1728} 1729 1730void LocationsBuilderARM::VisitLessThanOrEqual(HLessThanOrEqual* comp) { 1731 VisitCondition(comp); 1732} 1733 1734void InstructionCodeGeneratorARM::VisitLessThanOrEqual(HLessThanOrEqual* comp) { 1735 VisitCondition(comp); 1736} 1737 1738void LocationsBuilderARM::VisitGreaterThan(HGreaterThan* comp) { 1739 VisitCondition(comp); 1740} 1741 1742void InstructionCodeGeneratorARM::VisitGreaterThan(HGreaterThan* comp) { 1743 VisitCondition(comp); 1744} 1745 1746void LocationsBuilderARM::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) { 1747 VisitCondition(comp); 1748} 1749 1750void InstructionCodeGeneratorARM::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) { 1751 VisitCondition(comp); 1752} 1753 1754void LocationsBuilderARM::VisitBelow(HBelow* comp) { 1755 VisitCondition(comp); 1756} 1757 1758void InstructionCodeGeneratorARM::VisitBelow(HBelow* comp) { 1759 VisitCondition(comp); 1760} 1761 1762void LocationsBuilderARM::VisitBelowOrEqual(HBelowOrEqual* comp) { 1763 VisitCondition(comp); 1764} 1765 1766void InstructionCodeGeneratorARM::VisitBelowOrEqual(HBelowOrEqual* comp) { 1767 VisitCondition(comp); 1768} 1769 1770void LocationsBuilderARM::VisitAbove(HAbove* comp) { 1771 VisitCondition(comp); 1772} 1773 1774void InstructionCodeGeneratorARM::VisitAbove(HAbove* comp) { 1775 VisitCondition(comp); 1776} 1777 1778void LocationsBuilderARM::VisitAboveOrEqual(HAboveOrEqual* comp) { 1779 VisitCondition(comp); 1780} 1781 1782void InstructionCodeGeneratorARM::VisitAboveOrEqual(HAboveOrEqual* comp) { 1783 VisitCondition(comp); 1784} 1785 1786void LocationsBuilderARM::VisitLocal(HLocal* local) { 1787 local->SetLocations(nullptr); 1788} 1789 1790void InstructionCodeGeneratorARM::VisitLocal(HLocal* local) { 1791 DCHECK_EQ(local->GetBlock(), GetGraph()->GetEntryBlock()); 1792} 1793 1794void LocationsBuilderARM::VisitLoadLocal(HLoadLocal* load) { 1795 load->SetLocations(nullptr); 1796} 1797 1798void InstructionCodeGeneratorARM::VisitLoadLocal(HLoadLocal* load ATTRIBUTE_UNUSED) { 1799 // Nothing to do, this is driven by the code generator. 1800} 1801 1802void LocationsBuilderARM::VisitStoreLocal(HStoreLocal* store) { 1803 LocationSummary* locations = 1804 new (GetGraph()->GetArena()) LocationSummary(store, LocationSummary::kNoCall); 1805 switch (store->InputAt(1)->GetType()) { 1806 case Primitive::kPrimBoolean: 1807 case Primitive::kPrimByte: 1808 case Primitive::kPrimChar: 1809 case Primitive::kPrimShort: 1810 case Primitive::kPrimInt: 1811 case Primitive::kPrimNot: 1812 case Primitive::kPrimFloat: 1813 locations->SetInAt(1, Location::StackSlot(codegen_->GetStackSlot(store->GetLocal()))); 1814 break; 1815 1816 case Primitive::kPrimLong: 1817 case Primitive::kPrimDouble: 1818 locations->SetInAt(1, Location::DoubleStackSlot(codegen_->GetStackSlot(store->GetLocal()))); 1819 break; 1820 1821 default: 1822 LOG(FATAL) << "Unexpected local type " << store->InputAt(1)->GetType(); 1823 } 1824} 1825 1826void InstructionCodeGeneratorARM::VisitStoreLocal(HStoreLocal* store ATTRIBUTE_UNUSED) { 1827} 1828 1829void LocationsBuilderARM::VisitIntConstant(HIntConstant* constant) { 1830 LocationSummary* locations = 1831 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall); 1832 locations->SetOut(Location::ConstantLocation(constant)); 1833} 1834 1835void InstructionCodeGeneratorARM::VisitIntConstant(HIntConstant* constant ATTRIBUTE_UNUSED) { 1836 // Will be generated at use site. 1837} 1838 1839void LocationsBuilderARM::VisitNullConstant(HNullConstant* constant) { 1840 LocationSummary* locations = 1841 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall); 1842 locations->SetOut(Location::ConstantLocation(constant)); 1843} 1844 1845void InstructionCodeGeneratorARM::VisitNullConstant(HNullConstant* constant ATTRIBUTE_UNUSED) { 1846 // Will be generated at use site. 1847} 1848 1849void LocationsBuilderARM::VisitLongConstant(HLongConstant* constant) { 1850 LocationSummary* locations = 1851 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall); 1852 locations->SetOut(Location::ConstantLocation(constant)); 1853} 1854 1855void InstructionCodeGeneratorARM::VisitLongConstant(HLongConstant* constant ATTRIBUTE_UNUSED) { 1856 // Will be generated at use site. 1857} 1858 1859void LocationsBuilderARM::VisitFloatConstant(HFloatConstant* constant) { 1860 LocationSummary* locations = 1861 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall); 1862 locations->SetOut(Location::ConstantLocation(constant)); 1863} 1864 1865void InstructionCodeGeneratorARM::VisitFloatConstant(HFloatConstant* constant ATTRIBUTE_UNUSED) { 1866 // Will be generated at use site. 1867} 1868 1869void LocationsBuilderARM::VisitDoubleConstant(HDoubleConstant* constant) { 1870 LocationSummary* locations = 1871 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall); 1872 locations->SetOut(Location::ConstantLocation(constant)); 1873} 1874 1875void InstructionCodeGeneratorARM::VisitDoubleConstant(HDoubleConstant* constant ATTRIBUTE_UNUSED) { 1876 // Will be generated at use site. 1877} 1878 1879void LocationsBuilderARM::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) { 1880 memory_barrier->SetLocations(nullptr); 1881} 1882 1883void InstructionCodeGeneratorARM::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) { 1884 GenerateMemoryBarrier(memory_barrier->GetBarrierKind()); 1885} 1886 1887void LocationsBuilderARM::VisitReturnVoid(HReturnVoid* ret) { 1888 ret->SetLocations(nullptr); 1889} 1890 1891void InstructionCodeGeneratorARM::VisitReturnVoid(HReturnVoid* ret ATTRIBUTE_UNUSED) { 1892 codegen_->GenerateFrameExit(); 1893} 1894 1895void LocationsBuilderARM::VisitReturn(HReturn* ret) { 1896 LocationSummary* locations = 1897 new (GetGraph()->GetArena()) LocationSummary(ret, LocationSummary::kNoCall); 1898 locations->SetInAt(0, parameter_visitor_.GetReturnLocation(ret->InputAt(0)->GetType())); 1899} 1900 1901void InstructionCodeGeneratorARM::VisitReturn(HReturn* ret ATTRIBUTE_UNUSED) { 1902 codegen_->GenerateFrameExit(); 1903} 1904 1905void LocationsBuilderARM::VisitInvokeUnresolved(HInvokeUnresolved* invoke) { 1906 // The trampoline uses the same calling convention as dex calling conventions, 1907 // except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain 1908 // the method_idx. 1909 HandleInvoke(invoke); 1910} 1911 1912void InstructionCodeGeneratorARM::VisitInvokeUnresolved(HInvokeUnresolved* invoke) { 1913 codegen_->GenerateInvokeUnresolvedRuntimeCall(invoke); 1914} 1915 1916void LocationsBuilderARM::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { 1917 // When we do not run baseline, explicit clinit checks triggered by static 1918 // invokes must have been pruned by art::PrepareForRegisterAllocation. 1919 DCHECK(codegen_->IsBaseline() || !invoke->IsStaticWithExplicitClinitCheck()); 1920 1921 IntrinsicLocationsBuilderARM intrinsic(GetGraph()->GetArena(), 1922 codegen_->GetAssembler(), 1923 codegen_->GetInstructionSetFeatures()); 1924 if (intrinsic.TryDispatch(invoke)) { 1925 return; 1926 } 1927 1928 HandleInvoke(invoke); 1929} 1930 1931static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorARM* codegen) { 1932 if (invoke->GetLocations()->Intrinsified()) { 1933 IntrinsicCodeGeneratorARM intrinsic(codegen); 1934 intrinsic.Dispatch(invoke); 1935 return true; 1936 } 1937 return false; 1938} 1939 1940void InstructionCodeGeneratorARM::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { 1941 // When we do not run baseline, explicit clinit checks triggered by static 1942 // invokes must have been pruned by art::PrepareForRegisterAllocation. 1943 DCHECK(codegen_->IsBaseline() || !invoke->IsStaticWithExplicitClinitCheck()); 1944 1945 if (TryGenerateIntrinsicCode(invoke, codegen_)) { 1946 return; 1947 } 1948 1949 LocationSummary* locations = invoke->GetLocations(); 1950 codegen_->GenerateStaticOrDirectCall( 1951 invoke, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation()); 1952 codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); 1953} 1954 1955void LocationsBuilderARM::HandleInvoke(HInvoke* invoke) { 1956 InvokeDexCallingConventionVisitorARM calling_convention_visitor; 1957 CodeGenerator::CreateCommonInvokeLocationSummary(invoke, &calling_convention_visitor); 1958} 1959 1960void LocationsBuilderARM::VisitInvokeVirtual(HInvokeVirtual* invoke) { 1961 IntrinsicLocationsBuilderARM intrinsic(GetGraph()->GetArena(), 1962 codegen_->GetAssembler(), 1963 codegen_->GetInstructionSetFeatures()); 1964 if (intrinsic.TryDispatch(invoke)) { 1965 return; 1966 } 1967 1968 HandleInvoke(invoke); 1969} 1970 1971void InstructionCodeGeneratorARM::VisitInvokeVirtual(HInvokeVirtual* invoke) { 1972 if (TryGenerateIntrinsicCode(invoke, codegen_)) { 1973 return; 1974 } 1975 1976 codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0)); 1977 DCHECK(!codegen_->IsLeafMethod()); 1978 codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); 1979} 1980 1981void LocationsBuilderARM::VisitInvokeInterface(HInvokeInterface* invoke) { 1982 HandleInvoke(invoke); 1983 // Add the hidden argument. 1984 invoke->GetLocations()->AddTemp(Location::RegisterLocation(R12)); 1985} 1986 1987void InstructionCodeGeneratorARM::VisitInvokeInterface(HInvokeInterface* invoke) { 1988 // TODO: b/18116999, our IMTs can miss an IncompatibleClassChangeError. 1989 LocationSummary* locations = invoke->GetLocations(); 1990 Register temp = locations->GetTemp(0).AsRegister<Register>(); 1991 Register hidden_reg = locations->GetTemp(1).AsRegister<Register>(); 1992 uint32_t method_offset = mirror::Class::EmbeddedImTableEntryOffset( 1993 invoke->GetImtIndex() % mirror::Class::kImtSize, kArmPointerSize).Uint32Value(); 1994 Location receiver = locations->InAt(0); 1995 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 1996 1997 // Set the hidden argument. This is safe to do this here, as R12 1998 // won't be modified thereafter, before the `blx` (call) instruction. 1999 DCHECK_EQ(R12, hidden_reg); 2000 __ LoadImmediate(hidden_reg, invoke->GetDexMethodIndex()); 2001 2002 if (receiver.IsStackSlot()) { 2003 __ LoadFromOffset(kLoadWord, temp, SP, receiver.GetStackIndex()); 2004 // /* HeapReference<Class> */ temp = temp->klass_ 2005 __ LoadFromOffset(kLoadWord, temp, temp, class_offset); 2006 } else { 2007 // /* HeapReference<Class> */ temp = receiver->klass_ 2008 __ LoadFromOffset(kLoadWord, temp, receiver.AsRegister<Register>(), class_offset); 2009 } 2010 codegen_->MaybeRecordImplicitNullCheck(invoke); 2011 // Instead of simply (possibly) unpoisoning `temp` here, we should 2012 // emit a read barrier for the previous class reference load. 2013 // However this is not required in practice, as this is an 2014 // intermediate/temporary reference and because the current 2015 // concurrent copying collector keeps the from-space memory 2016 // intact/accessible until the end of the marking phase (the 2017 // concurrent copying collector may not in the future). 2018 __ MaybeUnpoisonHeapReference(temp); 2019 // temp = temp->GetImtEntryAt(method_offset); 2020 uint32_t entry_point = 2021 ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmWordSize).Int32Value(); 2022 __ LoadFromOffset(kLoadWord, temp, temp, method_offset); 2023 // LR = temp->GetEntryPoint(); 2024 __ LoadFromOffset(kLoadWord, LR, temp, entry_point); 2025 // LR(); 2026 __ blx(LR); 2027 DCHECK(!codegen_->IsLeafMethod()); 2028 codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); 2029} 2030 2031void LocationsBuilderARM::VisitNeg(HNeg* neg) { 2032 LocationSummary* locations = 2033 new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall); 2034 switch (neg->GetResultType()) { 2035 case Primitive::kPrimInt: { 2036 locations->SetInAt(0, Location::RequiresRegister()); 2037 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2038 break; 2039 } 2040 case Primitive::kPrimLong: { 2041 locations->SetInAt(0, Location::RequiresRegister()); 2042 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 2043 break; 2044 } 2045 2046 case Primitive::kPrimFloat: 2047 case Primitive::kPrimDouble: 2048 locations->SetInAt(0, Location::RequiresFpuRegister()); 2049 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 2050 break; 2051 2052 default: 2053 LOG(FATAL) << "Unexpected neg type " << neg->GetResultType(); 2054 } 2055} 2056 2057void InstructionCodeGeneratorARM::VisitNeg(HNeg* neg) { 2058 LocationSummary* locations = neg->GetLocations(); 2059 Location out = locations->Out(); 2060 Location in = locations->InAt(0); 2061 switch (neg->GetResultType()) { 2062 case Primitive::kPrimInt: 2063 DCHECK(in.IsRegister()); 2064 __ rsb(out.AsRegister<Register>(), in.AsRegister<Register>(), ShifterOperand(0)); 2065 break; 2066 2067 case Primitive::kPrimLong: 2068 DCHECK(in.IsRegisterPair()); 2069 // out.lo = 0 - in.lo (and update the carry/borrow (C) flag) 2070 __ rsbs(out.AsRegisterPairLow<Register>(), 2071 in.AsRegisterPairLow<Register>(), 2072 ShifterOperand(0)); 2073 // We cannot emit an RSC (Reverse Subtract with Carry) 2074 // instruction here, as it does not exist in the Thumb-2 2075 // instruction set. We use the following approach 2076 // using SBC and SUB instead. 2077 // 2078 // out.hi = -C 2079 __ sbc(out.AsRegisterPairHigh<Register>(), 2080 out.AsRegisterPairHigh<Register>(), 2081 ShifterOperand(out.AsRegisterPairHigh<Register>())); 2082 // out.hi = out.hi - in.hi 2083 __ sub(out.AsRegisterPairHigh<Register>(), 2084 out.AsRegisterPairHigh<Register>(), 2085 ShifterOperand(in.AsRegisterPairHigh<Register>())); 2086 break; 2087 2088 case Primitive::kPrimFloat: 2089 DCHECK(in.IsFpuRegister()); 2090 __ vnegs(out.AsFpuRegister<SRegister>(), in.AsFpuRegister<SRegister>()); 2091 break; 2092 2093 case Primitive::kPrimDouble: 2094 DCHECK(in.IsFpuRegisterPair()); 2095 __ vnegd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), 2096 FromLowSToD(in.AsFpuRegisterPairLow<SRegister>())); 2097 break; 2098 2099 default: 2100 LOG(FATAL) << "Unexpected neg type " << neg->GetResultType(); 2101 } 2102} 2103 2104void LocationsBuilderARM::VisitTypeConversion(HTypeConversion* conversion) { 2105 Primitive::Type result_type = conversion->GetResultType(); 2106 Primitive::Type input_type = conversion->GetInputType(); 2107 DCHECK_NE(result_type, input_type); 2108 2109 // The float-to-long, double-to-long and long-to-float type conversions 2110 // rely on a call to the runtime. 2111 LocationSummary::CallKind call_kind = 2112 (((input_type == Primitive::kPrimFloat || input_type == Primitive::kPrimDouble) 2113 && result_type == Primitive::kPrimLong) 2114 || (input_type == Primitive::kPrimLong && result_type == Primitive::kPrimFloat)) 2115 ? LocationSummary::kCall 2116 : LocationSummary::kNoCall; 2117 LocationSummary* locations = 2118 new (GetGraph()->GetArena()) LocationSummary(conversion, call_kind); 2119 2120 // The Java language does not allow treating boolean as an integral type but 2121 // our bit representation makes it safe. 2122 2123 switch (result_type) { 2124 case Primitive::kPrimByte: 2125 switch (input_type) { 2126 case Primitive::kPrimBoolean: 2127 // Boolean input is a result of code transformations. 2128 case Primitive::kPrimShort: 2129 case Primitive::kPrimInt: 2130 case Primitive::kPrimChar: 2131 // Processing a Dex `int-to-byte' instruction. 2132 locations->SetInAt(0, Location::RequiresRegister()); 2133 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2134 break; 2135 2136 default: 2137 LOG(FATAL) << "Unexpected type conversion from " << input_type 2138 << " to " << result_type; 2139 } 2140 break; 2141 2142 case Primitive::kPrimShort: 2143 switch (input_type) { 2144 case Primitive::kPrimBoolean: 2145 // Boolean input is a result of code transformations. 2146 case Primitive::kPrimByte: 2147 case Primitive::kPrimInt: 2148 case Primitive::kPrimChar: 2149 // Processing a Dex `int-to-short' instruction. 2150 locations->SetInAt(0, Location::RequiresRegister()); 2151 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2152 break; 2153 2154 default: 2155 LOG(FATAL) << "Unexpected type conversion from " << input_type 2156 << " to " << result_type; 2157 } 2158 break; 2159 2160 case Primitive::kPrimInt: 2161 switch (input_type) { 2162 case Primitive::kPrimLong: 2163 // Processing a Dex `long-to-int' instruction. 2164 locations->SetInAt(0, Location::Any()); 2165 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2166 break; 2167 2168 case Primitive::kPrimFloat: 2169 // Processing a Dex `float-to-int' instruction. 2170 locations->SetInAt(0, Location::RequiresFpuRegister()); 2171 locations->SetOut(Location::RequiresRegister()); 2172 locations->AddTemp(Location::RequiresFpuRegister()); 2173 break; 2174 2175 case Primitive::kPrimDouble: 2176 // Processing a Dex `double-to-int' instruction. 2177 locations->SetInAt(0, Location::RequiresFpuRegister()); 2178 locations->SetOut(Location::RequiresRegister()); 2179 locations->AddTemp(Location::RequiresFpuRegister()); 2180 break; 2181 2182 default: 2183 LOG(FATAL) << "Unexpected type conversion from " << input_type 2184 << " to " << result_type; 2185 } 2186 break; 2187 2188 case Primitive::kPrimLong: 2189 switch (input_type) { 2190 case Primitive::kPrimBoolean: 2191 // Boolean input is a result of code transformations. 2192 case Primitive::kPrimByte: 2193 case Primitive::kPrimShort: 2194 case Primitive::kPrimInt: 2195 case Primitive::kPrimChar: 2196 // Processing a Dex `int-to-long' instruction. 2197 locations->SetInAt(0, Location::RequiresRegister()); 2198 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2199 break; 2200 2201 case Primitive::kPrimFloat: { 2202 // Processing a Dex `float-to-long' instruction. 2203 InvokeRuntimeCallingConvention calling_convention; 2204 locations->SetInAt(0, Location::FpuRegisterLocation( 2205 calling_convention.GetFpuRegisterAt(0))); 2206 locations->SetOut(Location::RegisterPairLocation(R0, R1)); 2207 break; 2208 } 2209 2210 case Primitive::kPrimDouble: { 2211 // Processing a Dex `double-to-long' instruction. 2212 InvokeRuntimeCallingConvention calling_convention; 2213 locations->SetInAt(0, Location::FpuRegisterPairLocation( 2214 calling_convention.GetFpuRegisterAt(0), 2215 calling_convention.GetFpuRegisterAt(1))); 2216 locations->SetOut(Location::RegisterPairLocation(R0, R1)); 2217 break; 2218 } 2219 2220 default: 2221 LOG(FATAL) << "Unexpected type conversion from " << input_type 2222 << " to " << result_type; 2223 } 2224 break; 2225 2226 case Primitive::kPrimChar: 2227 switch (input_type) { 2228 case Primitive::kPrimBoolean: 2229 // Boolean input is a result of code transformations. 2230 case Primitive::kPrimByte: 2231 case Primitive::kPrimShort: 2232 case Primitive::kPrimInt: 2233 // Processing a Dex `int-to-char' instruction. 2234 locations->SetInAt(0, Location::RequiresRegister()); 2235 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2236 break; 2237 2238 default: 2239 LOG(FATAL) << "Unexpected type conversion from " << input_type 2240 << " to " << result_type; 2241 } 2242 break; 2243 2244 case Primitive::kPrimFloat: 2245 switch (input_type) { 2246 case Primitive::kPrimBoolean: 2247 // Boolean input is a result of code transformations. 2248 case Primitive::kPrimByte: 2249 case Primitive::kPrimShort: 2250 case Primitive::kPrimInt: 2251 case Primitive::kPrimChar: 2252 // Processing a Dex `int-to-float' instruction. 2253 locations->SetInAt(0, Location::RequiresRegister()); 2254 locations->SetOut(Location::RequiresFpuRegister()); 2255 break; 2256 2257 case Primitive::kPrimLong: { 2258 // Processing a Dex `long-to-float' instruction. 2259 InvokeRuntimeCallingConvention calling_convention; 2260 locations->SetInAt(0, Location::RegisterPairLocation( 2261 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1))); 2262 locations->SetOut(Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0))); 2263 break; 2264 } 2265 2266 case Primitive::kPrimDouble: 2267 // Processing a Dex `double-to-float' instruction. 2268 locations->SetInAt(0, Location::RequiresFpuRegister()); 2269 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 2270 break; 2271 2272 default: 2273 LOG(FATAL) << "Unexpected type conversion from " << input_type 2274 << " to " << result_type; 2275 }; 2276 break; 2277 2278 case Primitive::kPrimDouble: 2279 switch (input_type) { 2280 case Primitive::kPrimBoolean: 2281 // Boolean input is a result of code transformations. 2282 case Primitive::kPrimByte: 2283 case Primitive::kPrimShort: 2284 case Primitive::kPrimInt: 2285 case Primitive::kPrimChar: 2286 // Processing a Dex `int-to-double' instruction. 2287 locations->SetInAt(0, Location::RequiresRegister()); 2288 locations->SetOut(Location::RequiresFpuRegister()); 2289 break; 2290 2291 case Primitive::kPrimLong: 2292 // Processing a Dex `long-to-double' instruction. 2293 locations->SetInAt(0, Location::RequiresRegister()); 2294 locations->SetOut(Location::RequiresFpuRegister()); 2295 locations->AddTemp(Location::RequiresFpuRegister()); 2296 locations->AddTemp(Location::RequiresFpuRegister()); 2297 break; 2298 2299 case Primitive::kPrimFloat: 2300 // Processing a Dex `float-to-double' instruction. 2301 locations->SetInAt(0, Location::RequiresFpuRegister()); 2302 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 2303 break; 2304 2305 default: 2306 LOG(FATAL) << "Unexpected type conversion from " << input_type 2307 << " to " << result_type; 2308 }; 2309 break; 2310 2311 default: 2312 LOG(FATAL) << "Unexpected type conversion from " << input_type 2313 << " to " << result_type; 2314 } 2315} 2316 2317void InstructionCodeGeneratorARM::VisitTypeConversion(HTypeConversion* conversion) { 2318 LocationSummary* locations = conversion->GetLocations(); 2319 Location out = locations->Out(); 2320 Location in = locations->InAt(0); 2321 Primitive::Type result_type = conversion->GetResultType(); 2322 Primitive::Type input_type = conversion->GetInputType(); 2323 DCHECK_NE(result_type, input_type); 2324 switch (result_type) { 2325 case Primitive::kPrimByte: 2326 switch (input_type) { 2327 case Primitive::kPrimBoolean: 2328 // Boolean input is a result of code transformations. 2329 case Primitive::kPrimShort: 2330 case Primitive::kPrimInt: 2331 case Primitive::kPrimChar: 2332 // Processing a Dex `int-to-byte' instruction. 2333 __ sbfx(out.AsRegister<Register>(), in.AsRegister<Register>(), 0, 8); 2334 break; 2335 2336 default: 2337 LOG(FATAL) << "Unexpected type conversion from " << input_type 2338 << " to " << result_type; 2339 } 2340 break; 2341 2342 case Primitive::kPrimShort: 2343 switch (input_type) { 2344 case Primitive::kPrimBoolean: 2345 // Boolean input is a result of code transformations. 2346 case Primitive::kPrimByte: 2347 case Primitive::kPrimInt: 2348 case Primitive::kPrimChar: 2349 // Processing a Dex `int-to-short' instruction. 2350 __ sbfx(out.AsRegister<Register>(), in.AsRegister<Register>(), 0, 16); 2351 break; 2352 2353 default: 2354 LOG(FATAL) << "Unexpected type conversion from " << input_type 2355 << " to " << result_type; 2356 } 2357 break; 2358 2359 case Primitive::kPrimInt: 2360 switch (input_type) { 2361 case Primitive::kPrimLong: 2362 // Processing a Dex `long-to-int' instruction. 2363 DCHECK(out.IsRegister()); 2364 if (in.IsRegisterPair()) { 2365 __ Mov(out.AsRegister<Register>(), in.AsRegisterPairLow<Register>()); 2366 } else if (in.IsDoubleStackSlot()) { 2367 __ LoadFromOffset(kLoadWord, out.AsRegister<Register>(), SP, in.GetStackIndex()); 2368 } else { 2369 DCHECK(in.IsConstant()); 2370 DCHECK(in.GetConstant()->IsLongConstant()); 2371 int64_t value = in.GetConstant()->AsLongConstant()->GetValue(); 2372 __ LoadImmediate(out.AsRegister<Register>(), static_cast<int32_t>(value)); 2373 } 2374 break; 2375 2376 case Primitive::kPrimFloat: { 2377 // Processing a Dex `float-to-int' instruction. 2378 SRegister temp = locations->GetTemp(0).AsFpuRegisterPairLow<SRegister>(); 2379 __ vmovs(temp, in.AsFpuRegister<SRegister>()); 2380 __ vcvtis(temp, temp); 2381 __ vmovrs(out.AsRegister<Register>(), temp); 2382 break; 2383 } 2384 2385 case Primitive::kPrimDouble: { 2386 // Processing a Dex `double-to-int' instruction. 2387 SRegister temp_s = locations->GetTemp(0).AsFpuRegisterPairLow<SRegister>(); 2388 DRegister temp_d = FromLowSToD(temp_s); 2389 __ vmovd(temp_d, FromLowSToD(in.AsFpuRegisterPairLow<SRegister>())); 2390 __ vcvtid(temp_s, temp_d); 2391 __ vmovrs(out.AsRegister<Register>(), temp_s); 2392 break; 2393 } 2394 2395 default: 2396 LOG(FATAL) << "Unexpected type conversion from " << input_type 2397 << " to " << result_type; 2398 } 2399 break; 2400 2401 case Primitive::kPrimLong: 2402 switch (input_type) { 2403 case Primitive::kPrimBoolean: 2404 // Boolean input is a result of code transformations. 2405 case Primitive::kPrimByte: 2406 case Primitive::kPrimShort: 2407 case Primitive::kPrimInt: 2408 case Primitive::kPrimChar: 2409 // Processing a Dex `int-to-long' instruction. 2410 DCHECK(out.IsRegisterPair()); 2411 DCHECK(in.IsRegister()); 2412 __ Mov(out.AsRegisterPairLow<Register>(), in.AsRegister<Register>()); 2413 // Sign extension. 2414 __ Asr(out.AsRegisterPairHigh<Register>(), 2415 out.AsRegisterPairLow<Register>(), 2416 31); 2417 break; 2418 2419 case Primitive::kPrimFloat: 2420 // Processing a Dex `float-to-long' instruction. 2421 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pF2l), 2422 conversion, 2423 conversion->GetDexPc(), 2424 nullptr); 2425 CheckEntrypointTypes<kQuickF2l, int64_t, float>(); 2426 break; 2427 2428 case Primitive::kPrimDouble: 2429 // Processing a Dex `double-to-long' instruction. 2430 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pD2l), 2431 conversion, 2432 conversion->GetDexPc(), 2433 nullptr); 2434 CheckEntrypointTypes<kQuickD2l, int64_t, double>(); 2435 break; 2436 2437 default: 2438 LOG(FATAL) << "Unexpected type conversion from " << input_type 2439 << " to " << result_type; 2440 } 2441 break; 2442 2443 case Primitive::kPrimChar: 2444 switch (input_type) { 2445 case Primitive::kPrimBoolean: 2446 // Boolean input is a result of code transformations. 2447 case Primitive::kPrimByte: 2448 case Primitive::kPrimShort: 2449 case Primitive::kPrimInt: 2450 // Processing a Dex `int-to-char' instruction. 2451 __ ubfx(out.AsRegister<Register>(), in.AsRegister<Register>(), 0, 16); 2452 break; 2453 2454 default: 2455 LOG(FATAL) << "Unexpected type conversion from " << input_type 2456 << " to " << result_type; 2457 } 2458 break; 2459 2460 case Primitive::kPrimFloat: 2461 switch (input_type) { 2462 case Primitive::kPrimBoolean: 2463 // Boolean input is a result of code transformations. 2464 case Primitive::kPrimByte: 2465 case Primitive::kPrimShort: 2466 case Primitive::kPrimInt: 2467 case Primitive::kPrimChar: { 2468 // Processing a Dex `int-to-float' instruction. 2469 __ vmovsr(out.AsFpuRegister<SRegister>(), in.AsRegister<Register>()); 2470 __ vcvtsi(out.AsFpuRegister<SRegister>(), out.AsFpuRegister<SRegister>()); 2471 break; 2472 } 2473 2474 case Primitive::kPrimLong: 2475 // Processing a Dex `long-to-float' instruction. 2476 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pL2f), 2477 conversion, 2478 conversion->GetDexPc(), 2479 nullptr); 2480 CheckEntrypointTypes<kQuickL2f, float, int64_t>(); 2481 break; 2482 2483 case Primitive::kPrimDouble: 2484 // Processing a Dex `double-to-float' instruction. 2485 __ vcvtsd(out.AsFpuRegister<SRegister>(), 2486 FromLowSToD(in.AsFpuRegisterPairLow<SRegister>())); 2487 break; 2488 2489 default: 2490 LOG(FATAL) << "Unexpected type conversion from " << input_type 2491 << " to " << result_type; 2492 }; 2493 break; 2494 2495 case Primitive::kPrimDouble: 2496 switch (input_type) { 2497 case Primitive::kPrimBoolean: 2498 // Boolean input is a result of code transformations. 2499 case Primitive::kPrimByte: 2500 case Primitive::kPrimShort: 2501 case Primitive::kPrimInt: 2502 case Primitive::kPrimChar: { 2503 // Processing a Dex `int-to-double' instruction. 2504 __ vmovsr(out.AsFpuRegisterPairLow<SRegister>(), in.AsRegister<Register>()); 2505 __ vcvtdi(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), 2506 out.AsFpuRegisterPairLow<SRegister>()); 2507 break; 2508 } 2509 2510 case Primitive::kPrimLong: { 2511 // Processing a Dex `long-to-double' instruction. 2512 Register low = in.AsRegisterPairLow<Register>(); 2513 Register high = in.AsRegisterPairHigh<Register>(); 2514 SRegister out_s = out.AsFpuRegisterPairLow<SRegister>(); 2515 DRegister out_d = FromLowSToD(out_s); 2516 SRegister temp_s = locations->GetTemp(0).AsFpuRegisterPairLow<SRegister>(); 2517 DRegister temp_d = FromLowSToD(temp_s); 2518 SRegister constant_s = locations->GetTemp(1).AsFpuRegisterPairLow<SRegister>(); 2519 DRegister constant_d = FromLowSToD(constant_s); 2520 2521 // temp_d = int-to-double(high) 2522 __ vmovsr(temp_s, high); 2523 __ vcvtdi(temp_d, temp_s); 2524 // constant_d = k2Pow32EncodingForDouble 2525 __ LoadDImmediate(constant_d, bit_cast<double, int64_t>(k2Pow32EncodingForDouble)); 2526 // out_d = unsigned-to-double(low) 2527 __ vmovsr(out_s, low); 2528 __ vcvtdu(out_d, out_s); 2529 // out_d += temp_d * constant_d 2530 __ vmlad(out_d, temp_d, constant_d); 2531 break; 2532 } 2533 2534 case Primitive::kPrimFloat: 2535 // Processing a Dex `float-to-double' instruction. 2536 __ vcvtds(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), 2537 in.AsFpuRegister<SRegister>()); 2538 break; 2539 2540 default: 2541 LOG(FATAL) << "Unexpected type conversion from " << input_type 2542 << " to " << result_type; 2543 }; 2544 break; 2545 2546 default: 2547 LOG(FATAL) << "Unexpected type conversion from " << input_type 2548 << " to " << result_type; 2549 } 2550} 2551 2552void LocationsBuilderARM::VisitAdd(HAdd* add) { 2553 LocationSummary* locations = 2554 new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall); 2555 switch (add->GetResultType()) { 2556 case Primitive::kPrimInt: { 2557 locations->SetInAt(0, Location::RequiresRegister()); 2558 locations->SetInAt(1, Location::RegisterOrConstant(add->InputAt(1))); 2559 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2560 break; 2561 } 2562 2563 case Primitive::kPrimLong: { 2564 locations->SetInAt(0, Location::RequiresRegister()); 2565 locations->SetInAt(1, Location::RequiresRegister()); 2566 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2567 break; 2568 } 2569 2570 case Primitive::kPrimFloat: 2571 case Primitive::kPrimDouble: { 2572 locations->SetInAt(0, Location::RequiresFpuRegister()); 2573 locations->SetInAt(1, Location::RequiresFpuRegister()); 2574 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 2575 break; 2576 } 2577 2578 default: 2579 LOG(FATAL) << "Unexpected add type " << add->GetResultType(); 2580 } 2581} 2582 2583void InstructionCodeGeneratorARM::VisitAdd(HAdd* add) { 2584 LocationSummary* locations = add->GetLocations(); 2585 Location out = locations->Out(); 2586 Location first = locations->InAt(0); 2587 Location second = locations->InAt(1); 2588 switch (add->GetResultType()) { 2589 case Primitive::kPrimInt: 2590 if (second.IsRegister()) { 2591 __ add(out.AsRegister<Register>(), 2592 first.AsRegister<Register>(), 2593 ShifterOperand(second.AsRegister<Register>())); 2594 } else { 2595 __ AddConstant(out.AsRegister<Register>(), 2596 first.AsRegister<Register>(), 2597 second.GetConstant()->AsIntConstant()->GetValue()); 2598 } 2599 break; 2600 2601 case Primitive::kPrimLong: { 2602 DCHECK(second.IsRegisterPair()); 2603 __ adds(out.AsRegisterPairLow<Register>(), 2604 first.AsRegisterPairLow<Register>(), 2605 ShifterOperand(second.AsRegisterPairLow<Register>())); 2606 __ adc(out.AsRegisterPairHigh<Register>(), 2607 first.AsRegisterPairHigh<Register>(), 2608 ShifterOperand(second.AsRegisterPairHigh<Register>())); 2609 break; 2610 } 2611 2612 case Primitive::kPrimFloat: 2613 __ vadds(out.AsFpuRegister<SRegister>(), 2614 first.AsFpuRegister<SRegister>(), 2615 second.AsFpuRegister<SRegister>()); 2616 break; 2617 2618 case Primitive::kPrimDouble: 2619 __ vaddd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), 2620 FromLowSToD(first.AsFpuRegisterPairLow<SRegister>()), 2621 FromLowSToD(second.AsFpuRegisterPairLow<SRegister>())); 2622 break; 2623 2624 default: 2625 LOG(FATAL) << "Unexpected add type " << add->GetResultType(); 2626 } 2627} 2628 2629void LocationsBuilderARM::VisitSub(HSub* sub) { 2630 LocationSummary* locations = 2631 new (GetGraph()->GetArena()) LocationSummary(sub, LocationSummary::kNoCall); 2632 switch (sub->GetResultType()) { 2633 case Primitive::kPrimInt: { 2634 locations->SetInAt(0, Location::RequiresRegister()); 2635 locations->SetInAt(1, Location::RegisterOrConstant(sub->InputAt(1))); 2636 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2637 break; 2638 } 2639 2640 case Primitive::kPrimLong: { 2641 locations->SetInAt(0, Location::RequiresRegister()); 2642 locations->SetInAt(1, Location::RequiresRegister()); 2643 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2644 break; 2645 } 2646 case Primitive::kPrimFloat: 2647 case Primitive::kPrimDouble: { 2648 locations->SetInAt(0, Location::RequiresFpuRegister()); 2649 locations->SetInAt(1, Location::RequiresFpuRegister()); 2650 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 2651 break; 2652 } 2653 default: 2654 LOG(FATAL) << "Unexpected sub type " << sub->GetResultType(); 2655 } 2656} 2657 2658void InstructionCodeGeneratorARM::VisitSub(HSub* sub) { 2659 LocationSummary* locations = sub->GetLocations(); 2660 Location out = locations->Out(); 2661 Location first = locations->InAt(0); 2662 Location second = locations->InAt(1); 2663 switch (sub->GetResultType()) { 2664 case Primitive::kPrimInt: { 2665 if (second.IsRegister()) { 2666 __ sub(out.AsRegister<Register>(), 2667 first.AsRegister<Register>(), 2668 ShifterOperand(second.AsRegister<Register>())); 2669 } else { 2670 __ AddConstant(out.AsRegister<Register>(), 2671 first.AsRegister<Register>(), 2672 -second.GetConstant()->AsIntConstant()->GetValue()); 2673 } 2674 break; 2675 } 2676 2677 case Primitive::kPrimLong: { 2678 DCHECK(second.IsRegisterPair()); 2679 __ subs(out.AsRegisterPairLow<Register>(), 2680 first.AsRegisterPairLow<Register>(), 2681 ShifterOperand(second.AsRegisterPairLow<Register>())); 2682 __ sbc(out.AsRegisterPairHigh<Register>(), 2683 first.AsRegisterPairHigh<Register>(), 2684 ShifterOperand(second.AsRegisterPairHigh<Register>())); 2685 break; 2686 } 2687 2688 case Primitive::kPrimFloat: { 2689 __ vsubs(out.AsFpuRegister<SRegister>(), 2690 first.AsFpuRegister<SRegister>(), 2691 second.AsFpuRegister<SRegister>()); 2692 break; 2693 } 2694 2695 case Primitive::kPrimDouble: { 2696 __ vsubd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), 2697 FromLowSToD(first.AsFpuRegisterPairLow<SRegister>()), 2698 FromLowSToD(second.AsFpuRegisterPairLow<SRegister>())); 2699 break; 2700 } 2701 2702 2703 default: 2704 LOG(FATAL) << "Unexpected sub type " << sub->GetResultType(); 2705 } 2706} 2707 2708void LocationsBuilderARM::VisitMul(HMul* mul) { 2709 LocationSummary* locations = 2710 new (GetGraph()->GetArena()) LocationSummary(mul, LocationSummary::kNoCall); 2711 switch (mul->GetResultType()) { 2712 case Primitive::kPrimInt: 2713 case Primitive::kPrimLong: { 2714 locations->SetInAt(0, Location::RequiresRegister()); 2715 locations->SetInAt(1, Location::RequiresRegister()); 2716 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2717 break; 2718 } 2719 2720 case Primitive::kPrimFloat: 2721 case Primitive::kPrimDouble: { 2722 locations->SetInAt(0, Location::RequiresFpuRegister()); 2723 locations->SetInAt(1, Location::RequiresFpuRegister()); 2724 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 2725 break; 2726 } 2727 2728 default: 2729 LOG(FATAL) << "Unexpected mul type " << mul->GetResultType(); 2730 } 2731} 2732 2733void InstructionCodeGeneratorARM::VisitMul(HMul* mul) { 2734 LocationSummary* locations = mul->GetLocations(); 2735 Location out = locations->Out(); 2736 Location first = locations->InAt(0); 2737 Location second = locations->InAt(1); 2738 switch (mul->GetResultType()) { 2739 case Primitive::kPrimInt: { 2740 __ mul(out.AsRegister<Register>(), 2741 first.AsRegister<Register>(), 2742 second.AsRegister<Register>()); 2743 break; 2744 } 2745 case Primitive::kPrimLong: { 2746 Register out_hi = out.AsRegisterPairHigh<Register>(); 2747 Register out_lo = out.AsRegisterPairLow<Register>(); 2748 Register in1_hi = first.AsRegisterPairHigh<Register>(); 2749 Register in1_lo = first.AsRegisterPairLow<Register>(); 2750 Register in2_hi = second.AsRegisterPairHigh<Register>(); 2751 Register in2_lo = second.AsRegisterPairLow<Register>(); 2752 2753 // Extra checks to protect caused by the existence of R1_R2. 2754 // The algorithm is wrong if out.hi is either in1.lo or in2.lo: 2755 // (e.g. in1=r0_r1, in2=r2_r3 and out=r1_r2); 2756 DCHECK_NE(out_hi, in1_lo); 2757 DCHECK_NE(out_hi, in2_lo); 2758 2759 // input: in1 - 64 bits, in2 - 64 bits 2760 // output: out 2761 // formula: out.hi : out.lo = (in1.lo * in2.hi + in1.hi * in2.lo)* 2^32 + in1.lo * in2.lo 2762 // parts: out.hi = in1.lo * in2.hi + in1.hi * in2.lo + (in1.lo * in2.lo)[63:32] 2763 // parts: out.lo = (in1.lo * in2.lo)[31:0] 2764 2765 // IP <- in1.lo * in2.hi 2766 __ mul(IP, in1_lo, in2_hi); 2767 // out.hi <- in1.lo * in2.hi + in1.hi * in2.lo 2768 __ mla(out_hi, in1_hi, in2_lo, IP); 2769 // out.lo <- (in1.lo * in2.lo)[31:0]; 2770 __ umull(out_lo, IP, in1_lo, in2_lo); 2771 // out.hi <- in2.hi * in1.lo + in2.lo * in1.hi + (in1.lo * in2.lo)[63:32] 2772 __ add(out_hi, out_hi, ShifterOperand(IP)); 2773 break; 2774 } 2775 2776 case Primitive::kPrimFloat: { 2777 __ vmuls(out.AsFpuRegister<SRegister>(), 2778 first.AsFpuRegister<SRegister>(), 2779 second.AsFpuRegister<SRegister>()); 2780 break; 2781 } 2782 2783 case Primitive::kPrimDouble: { 2784 __ vmuld(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), 2785 FromLowSToD(first.AsFpuRegisterPairLow<SRegister>()), 2786 FromLowSToD(second.AsFpuRegisterPairLow<SRegister>())); 2787 break; 2788 } 2789 2790 default: 2791 LOG(FATAL) << "Unexpected mul type " << mul->GetResultType(); 2792 } 2793} 2794 2795void InstructionCodeGeneratorARM::DivRemOneOrMinusOne(HBinaryOperation* instruction) { 2796 DCHECK(instruction->IsDiv() || instruction->IsRem()); 2797 DCHECK(instruction->GetResultType() == Primitive::kPrimInt); 2798 2799 LocationSummary* locations = instruction->GetLocations(); 2800 Location second = locations->InAt(1); 2801 DCHECK(second.IsConstant()); 2802 2803 Register out = locations->Out().AsRegister<Register>(); 2804 Register dividend = locations->InAt(0).AsRegister<Register>(); 2805 int32_t imm = second.GetConstant()->AsIntConstant()->GetValue(); 2806 DCHECK(imm == 1 || imm == -1); 2807 2808 if (instruction->IsRem()) { 2809 __ LoadImmediate(out, 0); 2810 } else { 2811 if (imm == 1) { 2812 __ Mov(out, dividend); 2813 } else { 2814 __ rsb(out, dividend, ShifterOperand(0)); 2815 } 2816 } 2817} 2818 2819void InstructionCodeGeneratorARM::DivRemByPowerOfTwo(HBinaryOperation* instruction) { 2820 DCHECK(instruction->IsDiv() || instruction->IsRem()); 2821 DCHECK(instruction->GetResultType() == Primitive::kPrimInt); 2822 2823 LocationSummary* locations = instruction->GetLocations(); 2824 Location second = locations->InAt(1); 2825 DCHECK(second.IsConstant()); 2826 2827 Register out = locations->Out().AsRegister<Register>(); 2828 Register dividend = locations->InAt(0).AsRegister<Register>(); 2829 Register temp = locations->GetTemp(0).AsRegister<Register>(); 2830 int32_t imm = second.GetConstant()->AsIntConstant()->GetValue(); 2831 uint32_t abs_imm = static_cast<uint32_t>(std::abs(imm)); 2832 DCHECK(IsPowerOfTwo(abs_imm)); 2833 int ctz_imm = CTZ(abs_imm); 2834 2835 if (ctz_imm == 1) { 2836 __ Lsr(temp, dividend, 32 - ctz_imm); 2837 } else { 2838 __ Asr(temp, dividend, 31); 2839 __ Lsr(temp, temp, 32 - ctz_imm); 2840 } 2841 __ add(out, temp, ShifterOperand(dividend)); 2842 2843 if (instruction->IsDiv()) { 2844 __ Asr(out, out, ctz_imm); 2845 if (imm < 0) { 2846 __ rsb(out, out, ShifterOperand(0)); 2847 } 2848 } else { 2849 __ ubfx(out, out, 0, ctz_imm); 2850 __ sub(out, out, ShifterOperand(temp)); 2851 } 2852} 2853 2854void InstructionCodeGeneratorARM::GenerateDivRemWithAnyConstant(HBinaryOperation* instruction) { 2855 DCHECK(instruction->IsDiv() || instruction->IsRem()); 2856 DCHECK(instruction->GetResultType() == Primitive::kPrimInt); 2857 2858 LocationSummary* locations = instruction->GetLocations(); 2859 Location second = locations->InAt(1); 2860 DCHECK(second.IsConstant()); 2861 2862 Register out = locations->Out().AsRegister<Register>(); 2863 Register dividend = locations->InAt(0).AsRegister<Register>(); 2864 Register temp1 = locations->GetTemp(0).AsRegister<Register>(); 2865 Register temp2 = locations->GetTemp(1).AsRegister<Register>(); 2866 int64_t imm = second.GetConstant()->AsIntConstant()->GetValue(); 2867 2868 int64_t magic; 2869 int shift; 2870 CalculateMagicAndShiftForDivRem(imm, false /* is_long */, &magic, &shift); 2871 2872 __ LoadImmediate(temp1, magic); 2873 __ smull(temp2, temp1, dividend, temp1); 2874 2875 if (imm > 0 && magic < 0) { 2876 __ add(temp1, temp1, ShifterOperand(dividend)); 2877 } else if (imm < 0 && magic > 0) { 2878 __ sub(temp1, temp1, ShifterOperand(dividend)); 2879 } 2880 2881 if (shift != 0) { 2882 __ Asr(temp1, temp1, shift); 2883 } 2884 2885 if (instruction->IsDiv()) { 2886 __ sub(out, temp1, ShifterOperand(temp1, ASR, 31)); 2887 } else { 2888 __ sub(temp1, temp1, ShifterOperand(temp1, ASR, 31)); 2889 // TODO: Strength reduction for mls. 2890 __ LoadImmediate(temp2, imm); 2891 __ mls(out, temp1, temp2, dividend); 2892 } 2893} 2894 2895void InstructionCodeGeneratorARM::GenerateDivRemConstantIntegral(HBinaryOperation* instruction) { 2896 DCHECK(instruction->IsDiv() || instruction->IsRem()); 2897 DCHECK(instruction->GetResultType() == Primitive::kPrimInt); 2898 2899 LocationSummary* locations = instruction->GetLocations(); 2900 Location second = locations->InAt(1); 2901 DCHECK(second.IsConstant()); 2902 2903 int32_t imm = second.GetConstant()->AsIntConstant()->GetValue(); 2904 if (imm == 0) { 2905 // Do not generate anything. DivZeroCheck would prevent any code to be executed. 2906 } else if (imm == 1 || imm == -1) { 2907 DivRemOneOrMinusOne(instruction); 2908 } else if (IsPowerOfTwo(std::abs(imm))) { 2909 DivRemByPowerOfTwo(instruction); 2910 } else { 2911 DCHECK(imm <= -2 || imm >= 2); 2912 GenerateDivRemWithAnyConstant(instruction); 2913 } 2914} 2915 2916void LocationsBuilderARM::VisitDiv(HDiv* div) { 2917 LocationSummary::CallKind call_kind = LocationSummary::kNoCall; 2918 if (div->GetResultType() == Primitive::kPrimLong) { 2919 // pLdiv runtime call. 2920 call_kind = LocationSummary::kCall; 2921 } else if (div->GetResultType() == Primitive::kPrimInt && div->InputAt(1)->IsConstant()) { 2922 // sdiv will be replaced by other instruction sequence. 2923 } else if (div->GetResultType() == Primitive::kPrimInt && 2924 !codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { 2925 // pIdivmod runtime call. 2926 call_kind = LocationSummary::kCall; 2927 } 2928 2929 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(div, call_kind); 2930 2931 switch (div->GetResultType()) { 2932 case Primitive::kPrimInt: { 2933 if (div->InputAt(1)->IsConstant()) { 2934 locations->SetInAt(0, Location::RequiresRegister()); 2935 locations->SetInAt(1, Location::ConstantLocation(div->InputAt(1)->AsConstant())); 2936 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2937 int32_t abs_imm = std::abs(div->InputAt(1)->AsIntConstant()->GetValue()); 2938 if (abs_imm <= 1) { 2939 // No temp register required. 2940 } else { 2941 locations->AddTemp(Location::RequiresRegister()); 2942 if (!IsPowerOfTwo(abs_imm)) { 2943 locations->AddTemp(Location::RequiresRegister()); 2944 } 2945 } 2946 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { 2947 locations->SetInAt(0, Location::RequiresRegister()); 2948 locations->SetInAt(1, Location::RequiresRegister()); 2949 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2950 } else { 2951 InvokeRuntimeCallingConvention calling_convention; 2952 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 2953 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); 2954 // Note: divrem will compute both the quotient and the remainder as the pair R0 and R1, but 2955 // we only need the former. 2956 locations->SetOut(Location::RegisterLocation(R0)); 2957 } 2958 break; 2959 } 2960 case Primitive::kPrimLong: { 2961 InvokeRuntimeCallingConvention calling_convention; 2962 locations->SetInAt(0, Location::RegisterPairLocation( 2963 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1))); 2964 locations->SetInAt(1, Location::RegisterPairLocation( 2965 calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3))); 2966 locations->SetOut(Location::RegisterPairLocation(R0, R1)); 2967 break; 2968 } 2969 case Primitive::kPrimFloat: 2970 case Primitive::kPrimDouble: { 2971 locations->SetInAt(0, Location::RequiresFpuRegister()); 2972 locations->SetInAt(1, Location::RequiresFpuRegister()); 2973 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 2974 break; 2975 } 2976 2977 default: 2978 LOG(FATAL) << "Unexpected div type " << div->GetResultType(); 2979 } 2980} 2981 2982void InstructionCodeGeneratorARM::VisitDiv(HDiv* div) { 2983 LocationSummary* locations = div->GetLocations(); 2984 Location out = locations->Out(); 2985 Location first = locations->InAt(0); 2986 Location second = locations->InAt(1); 2987 2988 switch (div->GetResultType()) { 2989 case Primitive::kPrimInt: { 2990 if (second.IsConstant()) { 2991 GenerateDivRemConstantIntegral(div); 2992 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { 2993 __ sdiv(out.AsRegister<Register>(), 2994 first.AsRegister<Register>(), 2995 second.AsRegister<Register>()); 2996 } else { 2997 InvokeRuntimeCallingConvention calling_convention; 2998 DCHECK_EQ(calling_convention.GetRegisterAt(0), first.AsRegister<Register>()); 2999 DCHECK_EQ(calling_convention.GetRegisterAt(1), second.AsRegister<Register>()); 3000 DCHECK_EQ(R0, out.AsRegister<Register>()); 3001 3002 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pIdivmod), div, div->GetDexPc(), nullptr); 3003 CheckEntrypointTypes<kQuickIdivmod, int32_t, int32_t, int32_t>(); 3004 } 3005 break; 3006 } 3007 3008 case Primitive::kPrimLong: { 3009 InvokeRuntimeCallingConvention calling_convention; 3010 DCHECK_EQ(calling_convention.GetRegisterAt(0), first.AsRegisterPairLow<Register>()); 3011 DCHECK_EQ(calling_convention.GetRegisterAt(1), first.AsRegisterPairHigh<Register>()); 3012 DCHECK_EQ(calling_convention.GetRegisterAt(2), second.AsRegisterPairLow<Register>()); 3013 DCHECK_EQ(calling_convention.GetRegisterAt(3), second.AsRegisterPairHigh<Register>()); 3014 DCHECK_EQ(R0, out.AsRegisterPairLow<Register>()); 3015 DCHECK_EQ(R1, out.AsRegisterPairHigh<Register>()); 3016 3017 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pLdiv), div, div->GetDexPc(), nullptr); 3018 CheckEntrypointTypes<kQuickLdiv, int64_t, int64_t, int64_t>(); 3019 break; 3020 } 3021 3022 case Primitive::kPrimFloat: { 3023 __ vdivs(out.AsFpuRegister<SRegister>(), 3024 first.AsFpuRegister<SRegister>(), 3025 second.AsFpuRegister<SRegister>()); 3026 break; 3027 } 3028 3029 case Primitive::kPrimDouble: { 3030 __ vdivd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), 3031 FromLowSToD(first.AsFpuRegisterPairLow<SRegister>()), 3032 FromLowSToD(second.AsFpuRegisterPairLow<SRegister>())); 3033 break; 3034 } 3035 3036 default: 3037 LOG(FATAL) << "Unexpected div type " << div->GetResultType(); 3038 } 3039} 3040 3041void LocationsBuilderARM::VisitRem(HRem* rem) { 3042 Primitive::Type type = rem->GetResultType(); 3043 3044 // Most remainders are implemented in the runtime. 3045 LocationSummary::CallKind call_kind = LocationSummary::kCall; 3046 if (rem->GetResultType() == Primitive::kPrimInt && rem->InputAt(1)->IsConstant()) { 3047 // sdiv will be replaced by other instruction sequence. 3048 call_kind = LocationSummary::kNoCall; 3049 } else if ((rem->GetResultType() == Primitive::kPrimInt) 3050 && codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { 3051 // Have hardware divide instruction for int, do it with three instructions. 3052 call_kind = LocationSummary::kNoCall; 3053 } 3054 3055 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(rem, call_kind); 3056 3057 switch (type) { 3058 case Primitive::kPrimInt: { 3059 if (rem->InputAt(1)->IsConstant()) { 3060 locations->SetInAt(0, Location::RequiresRegister()); 3061 locations->SetInAt(1, Location::ConstantLocation(rem->InputAt(1)->AsConstant())); 3062 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 3063 int32_t abs_imm = std::abs(rem->InputAt(1)->AsIntConstant()->GetValue()); 3064 if (abs_imm <= 1) { 3065 // No temp register required. 3066 } else { 3067 locations->AddTemp(Location::RequiresRegister()); 3068 if (!IsPowerOfTwo(abs_imm)) { 3069 locations->AddTemp(Location::RequiresRegister()); 3070 } 3071 } 3072 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { 3073 locations->SetInAt(0, Location::RequiresRegister()); 3074 locations->SetInAt(1, Location::RequiresRegister()); 3075 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 3076 locations->AddTemp(Location::RequiresRegister()); 3077 } else { 3078 InvokeRuntimeCallingConvention calling_convention; 3079 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 3080 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); 3081 // Note: divrem will compute both the quotient and the remainder as the pair R0 and R1, but 3082 // we only need the latter. 3083 locations->SetOut(Location::RegisterLocation(R1)); 3084 } 3085 break; 3086 } 3087 case Primitive::kPrimLong: { 3088 InvokeRuntimeCallingConvention calling_convention; 3089 locations->SetInAt(0, Location::RegisterPairLocation( 3090 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1))); 3091 locations->SetInAt(1, Location::RegisterPairLocation( 3092 calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3))); 3093 // The runtime helper puts the output in R2,R3. 3094 locations->SetOut(Location::RegisterPairLocation(R2, R3)); 3095 break; 3096 } 3097 case Primitive::kPrimFloat: { 3098 InvokeRuntimeCallingConvention calling_convention; 3099 locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0))); 3100 locations->SetInAt(1, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(1))); 3101 locations->SetOut(Location::FpuRegisterLocation(S0)); 3102 break; 3103 } 3104 3105 case Primitive::kPrimDouble: { 3106 InvokeRuntimeCallingConvention calling_convention; 3107 locations->SetInAt(0, Location::FpuRegisterPairLocation( 3108 calling_convention.GetFpuRegisterAt(0), calling_convention.GetFpuRegisterAt(1))); 3109 locations->SetInAt(1, Location::FpuRegisterPairLocation( 3110 calling_convention.GetFpuRegisterAt(2), calling_convention.GetFpuRegisterAt(3))); 3111 locations->SetOut(Location::Location::FpuRegisterPairLocation(S0, S1)); 3112 break; 3113 } 3114 3115 default: 3116 LOG(FATAL) << "Unexpected rem type " << type; 3117 } 3118} 3119 3120void InstructionCodeGeneratorARM::VisitRem(HRem* rem) { 3121 LocationSummary* locations = rem->GetLocations(); 3122 Location out = locations->Out(); 3123 Location first = locations->InAt(0); 3124 Location second = locations->InAt(1); 3125 3126 Primitive::Type type = rem->GetResultType(); 3127 switch (type) { 3128 case Primitive::kPrimInt: { 3129 if (second.IsConstant()) { 3130 GenerateDivRemConstantIntegral(rem); 3131 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { 3132 Register reg1 = first.AsRegister<Register>(); 3133 Register reg2 = second.AsRegister<Register>(); 3134 Register temp = locations->GetTemp(0).AsRegister<Register>(); 3135 3136 // temp = reg1 / reg2 (integer division) 3137 // dest = reg1 - temp * reg2 3138 __ sdiv(temp, reg1, reg2); 3139 __ mls(out.AsRegister<Register>(), temp, reg2, reg1); 3140 } else { 3141 InvokeRuntimeCallingConvention calling_convention; 3142 DCHECK_EQ(calling_convention.GetRegisterAt(0), first.AsRegister<Register>()); 3143 DCHECK_EQ(calling_convention.GetRegisterAt(1), second.AsRegister<Register>()); 3144 DCHECK_EQ(R1, out.AsRegister<Register>()); 3145 3146 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pIdivmod), rem, rem->GetDexPc(), nullptr); 3147 CheckEntrypointTypes<kQuickIdivmod, int32_t, int32_t, int32_t>(); 3148 } 3149 break; 3150 } 3151 3152 case Primitive::kPrimLong: { 3153 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pLmod), rem, rem->GetDexPc(), nullptr); 3154 CheckEntrypointTypes<kQuickLmod, int64_t, int64_t, int64_t>(); 3155 break; 3156 } 3157 3158 case Primitive::kPrimFloat: { 3159 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pFmodf), rem, rem->GetDexPc(), nullptr); 3160 CheckEntrypointTypes<kQuickFmodf, float, float, float>(); 3161 break; 3162 } 3163 3164 case Primitive::kPrimDouble: { 3165 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pFmod), rem, rem->GetDexPc(), nullptr); 3166 CheckEntrypointTypes<kQuickFmod, double, double, double>(); 3167 break; 3168 } 3169 3170 default: 3171 LOG(FATAL) << "Unexpected rem type " << type; 3172 } 3173} 3174 3175void LocationsBuilderARM::VisitDivZeroCheck(HDivZeroCheck* instruction) { 3176 LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() 3177 ? LocationSummary::kCallOnSlowPath 3178 : LocationSummary::kNoCall; 3179 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); 3180 locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0))); 3181 if (instruction->HasUses()) { 3182 locations->SetOut(Location::SameAsFirstInput()); 3183 } 3184} 3185 3186void InstructionCodeGeneratorARM::VisitDivZeroCheck(HDivZeroCheck* instruction) { 3187 SlowPathCode* slow_path = new (GetGraph()->GetArena()) DivZeroCheckSlowPathARM(instruction); 3188 codegen_->AddSlowPath(slow_path); 3189 3190 LocationSummary* locations = instruction->GetLocations(); 3191 Location value = locations->InAt(0); 3192 3193 switch (instruction->GetType()) { 3194 case Primitive::kPrimByte: 3195 case Primitive::kPrimChar: 3196 case Primitive::kPrimShort: 3197 case Primitive::kPrimInt: { 3198 if (value.IsRegister()) { 3199 __ CompareAndBranchIfZero(value.AsRegister<Register>(), slow_path->GetEntryLabel()); 3200 } else { 3201 DCHECK(value.IsConstant()) << value; 3202 if (value.GetConstant()->AsIntConstant()->GetValue() == 0) { 3203 __ b(slow_path->GetEntryLabel()); 3204 } 3205 } 3206 break; 3207 } 3208 case Primitive::kPrimLong: { 3209 if (value.IsRegisterPair()) { 3210 __ orrs(IP, 3211 value.AsRegisterPairLow<Register>(), 3212 ShifterOperand(value.AsRegisterPairHigh<Register>())); 3213 __ b(slow_path->GetEntryLabel(), EQ); 3214 } else { 3215 DCHECK(value.IsConstant()) << value; 3216 if (value.GetConstant()->AsLongConstant()->GetValue() == 0) { 3217 __ b(slow_path->GetEntryLabel()); 3218 } 3219 } 3220 break; 3221 default: 3222 LOG(FATAL) << "Unexpected type for HDivZeroCheck " << instruction->GetType(); 3223 } 3224 } 3225} 3226 3227void LocationsBuilderARM::HandleShift(HBinaryOperation* op) { 3228 DCHECK(op->IsShl() || op->IsShr() || op->IsUShr()); 3229 3230 LocationSummary* locations = 3231 new (GetGraph()->GetArena()) LocationSummary(op, LocationSummary::kNoCall); 3232 3233 switch (op->GetResultType()) { 3234 case Primitive::kPrimInt: { 3235 locations->SetInAt(0, Location::RequiresRegister()); 3236 if (op->InputAt(1)->IsConstant()) { 3237 locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant())); 3238 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 3239 } else { 3240 locations->SetInAt(1, Location::RequiresRegister()); 3241 // Make the output overlap, as it will be used to hold the masked 3242 // second input. 3243 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 3244 } 3245 break; 3246 } 3247 case Primitive::kPrimLong: { 3248 locations->SetInAt(0, Location::RequiresRegister()); 3249 if (op->InputAt(1)->IsConstant()) { 3250 locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant())); 3251 // For simplicity, use kOutputOverlap even though we only require that low registers 3252 // don't clash with high registers which the register allocator currently guarantees. 3253 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 3254 } else { 3255 locations->SetInAt(1, Location::RequiresRegister()); 3256 locations->AddTemp(Location::RequiresRegister()); 3257 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 3258 } 3259 break; 3260 } 3261 default: 3262 LOG(FATAL) << "Unexpected operation type " << op->GetResultType(); 3263 } 3264} 3265 3266void InstructionCodeGeneratorARM::HandleShift(HBinaryOperation* op) { 3267 DCHECK(op->IsShl() || op->IsShr() || op->IsUShr()); 3268 3269 LocationSummary* locations = op->GetLocations(); 3270 Location out = locations->Out(); 3271 Location first = locations->InAt(0); 3272 Location second = locations->InAt(1); 3273 3274 Primitive::Type type = op->GetResultType(); 3275 switch (type) { 3276 case Primitive::kPrimInt: { 3277 Register out_reg = out.AsRegister<Register>(); 3278 Register first_reg = first.AsRegister<Register>(); 3279 if (second.IsRegister()) { 3280 Register second_reg = second.AsRegister<Register>(); 3281 // Arm doesn't mask the shift count so we need to do it ourselves. 3282 __ and_(out_reg, second_reg, ShifterOperand(kMaxIntShiftValue)); 3283 if (op->IsShl()) { 3284 __ Lsl(out_reg, first_reg, out_reg); 3285 } else if (op->IsShr()) { 3286 __ Asr(out_reg, first_reg, out_reg); 3287 } else { 3288 __ Lsr(out_reg, first_reg, out_reg); 3289 } 3290 } else { 3291 int32_t cst = second.GetConstant()->AsIntConstant()->GetValue(); 3292 uint32_t shift_value = static_cast<uint32_t>(cst & kMaxIntShiftValue); 3293 if (shift_value == 0) { // arm does not support shifting with 0 immediate. 3294 __ Mov(out_reg, first_reg); 3295 } else if (op->IsShl()) { 3296 __ Lsl(out_reg, first_reg, shift_value); 3297 } else if (op->IsShr()) { 3298 __ Asr(out_reg, first_reg, shift_value); 3299 } else { 3300 __ Lsr(out_reg, first_reg, shift_value); 3301 } 3302 } 3303 break; 3304 } 3305 case Primitive::kPrimLong: { 3306 Register o_h = out.AsRegisterPairHigh<Register>(); 3307 Register o_l = out.AsRegisterPairLow<Register>(); 3308 3309 Register high = first.AsRegisterPairHigh<Register>(); 3310 Register low = first.AsRegisterPairLow<Register>(); 3311 3312 if (second.IsRegister()) { 3313 Register temp = locations->GetTemp(0).AsRegister<Register>(); 3314 3315 Register second_reg = second.AsRegister<Register>(); 3316 3317 if (op->IsShl()) { 3318 __ and_(o_l, second_reg, ShifterOperand(kMaxLongShiftValue)); 3319 // Shift the high part 3320 __ Lsl(o_h, high, o_l); 3321 // Shift the low part and `or` what overflew on the high part 3322 __ rsb(temp, o_l, ShifterOperand(kArmBitsPerWord)); 3323 __ Lsr(temp, low, temp); 3324 __ orr(o_h, o_h, ShifterOperand(temp)); 3325 // If the shift is > 32 bits, override the high part 3326 __ subs(temp, o_l, ShifterOperand(kArmBitsPerWord)); 3327 __ it(PL); 3328 __ Lsl(o_h, low, temp, PL); 3329 // Shift the low part 3330 __ Lsl(o_l, low, o_l); 3331 } else if (op->IsShr()) { 3332 __ and_(o_h, second_reg, ShifterOperand(kMaxLongShiftValue)); 3333 // Shift the low part 3334 __ Lsr(o_l, low, o_h); 3335 // Shift the high part and `or` what underflew on the low part 3336 __ rsb(temp, o_h, ShifterOperand(kArmBitsPerWord)); 3337 __ Lsl(temp, high, temp); 3338 __ orr(o_l, o_l, ShifterOperand(temp)); 3339 // If the shift is > 32 bits, override the low part 3340 __ subs(temp, o_h, ShifterOperand(kArmBitsPerWord)); 3341 __ it(PL); 3342 __ Asr(o_l, high, temp, PL); 3343 // Shift the high part 3344 __ Asr(o_h, high, o_h); 3345 } else { 3346 __ and_(o_h, second_reg, ShifterOperand(kMaxLongShiftValue)); 3347 // same as Shr except we use `Lsr`s and not `Asr`s 3348 __ Lsr(o_l, low, o_h); 3349 __ rsb(temp, o_h, ShifterOperand(kArmBitsPerWord)); 3350 __ Lsl(temp, high, temp); 3351 __ orr(o_l, o_l, ShifterOperand(temp)); 3352 __ subs(temp, o_h, ShifterOperand(kArmBitsPerWord)); 3353 __ it(PL); 3354 __ Lsr(o_l, high, temp, PL); 3355 __ Lsr(o_h, high, o_h); 3356 } 3357 } else { 3358 // Register allocator doesn't create partial overlap. 3359 DCHECK_NE(o_l, high); 3360 DCHECK_NE(o_h, low); 3361 int32_t cst = second.GetConstant()->AsIntConstant()->GetValue(); 3362 uint32_t shift_value = static_cast<uint32_t>(cst & kMaxLongShiftValue); 3363 if (shift_value > 32) { 3364 if (op->IsShl()) { 3365 __ Lsl(o_h, low, shift_value - 32); 3366 __ LoadImmediate(o_l, 0); 3367 } else if (op->IsShr()) { 3368 __ Asr(o_l, high, shift_value - 32); 3369 __ Asr(o_h, high, 31); 3370 } else { 3371 __ Lsr(o_l, high, shift_value - 32); 3372 __ LoadImmediate(o_h, 0); 3373 } 3374 } else if (shift_value == 32) { 3375 if (op->IsShl()) { 3376 __ mov(o_h, ShifterOperand(low)); 3377 __ LoadImmediate(o_l, 0); 3378 } else if (op->IsShr()) { 3379 __ mov(o_l, ShifterOperand(high)); 3380 __ Asr(o_h, high, 31); 3381 } else { 3382 __ mov(o_l, ShifterOperand(high)); 3383 __ LoadImmediate(o_h, 0); 3384 } 3385 } else if (shift_value == 1) { 3386 if (op->IsShl()) { 3387 __ Lsls(o_l, low, 1); 3388 __ adc(o_h, high, ShifterOperand(high)); 3389 } else if (op->IsShr()) { 3390 __ Asrs(o_h, high, 1); 3391 __ Rrx(o_l, low); 3392 } else { 3393 __ Lsrs(o_h, high, 1); 3394 __ Rrx(o_l, low); 3395 } 3396 } else { 3397 DCHECK(2 <= shift_value && shift_value < 32) << shift_value; 3398 if (op->IsShl()) { 3399 __ Lsl(o_h, high, shift_value); 3400 __ orr(o_h, o_h, ShifterOperand(low, LSR, 32 - shift_value)); 3401 __ Lsl(o_l, low, shift_value); 3402 } else if (op->IsShr()) { 3403 __ Lsr(o_l, low, shift_value); 3404 __ orr(o_l, o_l, ShifterOperand(high, LSL, 32 - shift_value)); 3405 __ Asr(o_h, high, shift_value); 3406 } else { 3407 __ Lsr(o_l, low, shift_value); 3408 __ orr(o_l, o_l, ShifterOperand(high, LSL, 32 - shift_value)); 3409 __ Lsr(o_h, high, shift_value); 3410 } 3411 } 3412 } 3413 break; 3414 } 3415 default: 3416 LOG(FATAL) << "Unexpected operation type " << type; 3417 UNREACHABLE(); 3418 } 3419} 3420 3421void LocationsBuilderARM::VisitShl(HShl* shl) { 3422 HandleShift(shl); 3423} 3424 3425void InstructionCodeGeneratorARM::VisitShl(HShl* shl) { 3426 HandleShift(shl); 3427} 3428 3429void LocationsBuilderARM::VisitShr(HShr* shr) { 3430 HandleShift(shr); 3431} 3432 3433void InstructionCodeGeneratorARM::VisitShr(HShr* shr) { 3434 HandleShift(shr); 3435} 3436 3437void LocationsBuilderARM::VisitUShr(HUShr* ushr) { 3438 HandleShift(ushr); 3439} 3440 3441void InstructionCodeGeneratorARM::VisitUShr(HUShr* ushr) { 3442 HandleShift(ushr); 3443} 3444 3445void LocationsBuilderARM::VisitNewInstance(HNewInstance* instruction) { 3446 LocationSummary* locations = 3447 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); 3448 InvokeRuntimeCallingConvention calling_convention; 3449 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 3450 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); 3451 locations->SetOut(Location::RegisterLocation(R0)); 3452} 3453 3454void InstructionCodeGeneratorARM::VisitNewInstance(HNewInstance* instruction) { 3455 // Note: if heap poisoning is enabled, the entry point takes cares 3456 // of poisoning the reference. 3457 codegen_->InvokeRuntime(instruction->GetEntrypoint(), 3458 instruction, 3459 instruction->GetDexPc(), 3460 nullptr); 3461 CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, ArtMethod*>(); 3462} 3463 3464void LocationsBuilderARM::VisitNewArray(HNewArray* instruction) { 3465 LocationSummary* locations = 3466 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); 3467 InvokeRuntimeCallingConvention calling_convention; 3468 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 3469 locations->SetOut(Location::RegisterLocation(R0)); 3470 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); 3471 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); 3472} 3473 3474void InstructionCodeGeneratorARM::VisitNewArray(HNewArray* instruction) { 3475 InvokeRuntimeCallingConvention calling_convention; 3476 __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex()); 3477 // Note: if heap poisoning is enabled, the entry point takes cares 3478 // of poisoning the reference. 3479 codegen_->InvokeRuntime(instruction->GetEntrypoint(), 3480 instruction, 3481 instruction->GetDexPc(), 3482 nullptr); 3483 CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>(); 3484} 3485 3486void LocationsBuilderARM::VisitParameterValue(HParameterValue* instruction) { 3487 LocationSummary* locations = 3488 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 3489 Location location = parameter_visitor_.GetNextLocation(instruction->GetType()); 3490 if (location.IsStackSlot()) { 3491 location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize()); 3492 } else if (location.IsDoubleStackSlot()) { 3493 location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize()); 3494 } 3495 locations->SetOut(location); 3496} 3497 3498void InstructionCodeGeneratorARM::VisitParameterValue( 3499 HParameterValue* instruction ATTRIBUTE_UNUSED) { 3500 // Nothing to do, the parameter is already at its location. 3501} 3502 3503void LocationsBuilderARM::VisitCurrentMethod(HCurrentMethod* instruction) { 3504 LocationSummary* locations = 3505 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 3506 locations->SetOut(Location::RegisterLocation(kMethodRegisterArgument)); 3507} 3508 3509void InstructionCodeGeneratorARM::VisitCurrentMethod(HCurrentMethod* instruction ATTRIBUTE_UNUSED) { 3510 // Nothing to do, the method is already at its location. 3511} 3512 3513void LocationsBuilderARM::VisitNot(HNot* not_) { 3514 LocationSummary* locations = 3515 new (GetGraph()->GetArena()) LocationSummary(not_, LocationSummary::kNoCall); 3516 locations->SetInAt(0, Location::RequiresRegister()); 3517 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 3518} 3519 3520void InstructionCodeGeneratorARM::VisitNot(HNot* not_) { 3521 LocationSummary* locations = not_->GetLocations(); 3522 Location out = locations->Out(); 3523 Location in = locations->InAt(0); 3524 switch (not_->GetResultType()) { 3525 case Primitive::kPrimInt: 3526 __ mvn(out.AsRegister<Register>(), ShifterOperand(in.AsRegister<Register>())); 3527 break; 3528 3529 case Primitive::kPrimLong: 3530 __ mvn(out.AsRegisterPairLow<Register>(), 3531 ShifterOperand(in.AsRegisterPairLow<Register>())); 3532 __ mvn(out.AsRegisterPairHigh<Register>(), 3533 ShifterOperand(in.AsRegisterPairHigh<Register>())); 3534 break; 3535 3536 default: 3537 LOG(FATAL) << "Unimplemented type for not operation " << not_->GetResultType(); 3538 } 3539} 3540 3541void LocationsBuilderARM::VisitBooleanNot(HBooleanNot* bool_not) { 3542 LocationSummary* locations = 3543 new (GetGraph()->GetArena()) LocationSummary(bool_not, LocationSummary::kNoCall); 3544 locations->SetInAt(0, Location::RequiresRegister()); 3545 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 3546} 3547 3548void InstructionCodeGeneratorARM::VisitBooleanNot(HBooleanNot* bool_not) { 3549 LocationSummary* locations = bool_not->GetLocations(); 3550 Location out = locations->Out(); 3551 Location in = locations->InAt(0); 3552 __ eor(out.AsRegister<Register>(), in.AsRegister<Register>(), ShifterOperand(1)); 3553} 3554 3555void LocationsBuilderARM::VisitCompare(HCompare* compare) { 3556 LocationSummary* locations = 3557 new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall); 3558 switch (compare->InputAt(0)->GetType()) { 3559 case Primitive::kPrimLong: { 3560 locations->SetInAt(0, Location::RequiresRegister()); 3561 locations->SetInAt(1, Location::RequiresRegister()); 3562 // Output overlaps because it is written before doing the low comparison. 3563 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 3564 break; 3565 } 3566 case Primitive::kPrimFloat: 3567 case Primitive::kPrimDouble: { 3568 locations->SetInAt(0, Location::RequiresFpuRegister()); 3569 locations->SetInAt(1, Location::RequiresFpuRegister()); 3570 locations->SetOut(Location::RequiresRegister()); 3571 break; 3572 } 3573 default: 3574 LOG(FATAL) << "Unexpected type for compare operation " << compare->InputAt(0)->GetType(); 3575 } 3576} 3577 3578void InstructionCodeGeneratorARM::VisitCompare(HCompare* compare) { 3579 LocationSummary* locations = compare->GetLocations(); 3580 Register out = locations->Out().AsRegister<Register>(); 3581 Location left = locations->InAt(0); 3582 Location right = locations->InAt(1); 3583 3584 Label less, greater, done; 3585 Primitive::Type type = compare->InputAt(0)->GetType(); 3586 switch (type) { 3587 case Primitive::kPrimLong: { 3588 __ cmp(left.AsRegisterPairHigh<Register>(), 3589 ShifterOperand(right.AsRegisterPairHigh<Register>())); // Signed compare. 3590 __ b(&less, LT); 3591 __ b(&greater, GT); 3592 // Do LoadImmediate before the last `cmp`, as LoadImmediate might affect the status flags. 3593 __ LoadImmediate(out, 0); 3594 __ cmp(left.AsRegisterPairLow<Register>(), 3595 ShifterOperand(right.AsRegisterPairLow<Register>())); // Unsigned compare. 3596 break; 3597 } 3598 case Primitive::kPrimFloat: 3599 case Primitive::kPrimDouble: { 3600 __ LoadImmediate(out, 0); 3601 if (type == Primitive::kPrimFloat) { 3602 __ vcmps(left.AsFpuRegister<SRegister>(), right.AsFpuRegister<SRegister>()); 3603 } else { 3604 __ vcmpd(FromLowSToD(left.AsFpuRegisterPairLow<SRegister>()), 3605 FromLowSToD(right.AsFpuRegisterPairLow<SRegister>())); 3606 } 3607 __ vmstat(); // transfer FP status register to ARM APSR. 3608 __ b(compare->IsGtBias() ? &greater : &less, VS); // VS for unordered. 3609 break; 3610 } 3611 default: 3612 LOG(FATAL) << "Unexpected compare type " << type; 3613 } 3614 __ b(&done, EQ); 3615 __ b(&less, LO); // LO is for both: unsigned compare for longs and 'less than' for floats. 3616 3617 __ Bind(&greater); 3618 __ LoadImmediate(out, 1); 3619 __ b(&done); 3620 3621 __ Bind(&less); 3622 __ LoadImmediate(out, -1); 3623 3624 __ Bind(&done); 3625} 3626 3627void LocationsBuilderARM::VisitPhi(HPhi* instruction) { 3628 LocationSummary* locations = 3629 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 3630 for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) { 3631 locations->SetInAt(i, Location::Any()); 3632 } 3633 locations->SetOut(Location::Any()); 3634} 3635 3636void InstructionCodeGeneratorARM::VisitPhi(HPhi* instruction ATTRIBUTE_UNUSED) { 3637 LOG(FATAL) << "Unreachable"; 3638} 3639 3640void InstructionCodeGeneratorARM::GenerateMemoryBarrier(MemBarrierKind kind) { 3641 // TODO (ported from quick): revisit Arm barrier kinds 3642 DmbOptions flavor = DmbOptions::ISH; // quiet c++ warnings 3643 switch (kind) { 3644 case MemBarrierKind::kAnyStore: 3645 case MemBarrierKind::kLoadAny: 3646 case MemBarrierKind::kAnyAny: { 3647 flavor = DmbOptions::ISH; 3648 break; 3649 } 3650 case MemBarrierKind::kStoreStore: { 3651 flavor = DmbOptions::ISHST; 3652 break; 3653 } 3654 default: 3655 LOG(FATAL) << "Unexpected memory barrier " << kind; 3656 } 3657 __ dmb(flavor); 3658} 3659 3660void InstructionCodeGeneratorARM::GenerateWideAtomicLoad(Register addr, 3661 uint32_t offset, 3662 Register out_lo, 3663 Register out_hi) { 3664 if (offset != 0) { 3665 // Ensure `out_lo` is different from `addr`, so that loading 3666 // `offset` into `out_lo` does not clutter `addr`. 3667 DCHECK_NE(out_lo, addr); 3668 __ LoadImmediate(out_lo, offset); 3669 __ add(IP, addr, ShifterOperand(out_lo)); 3670 addr = IP; 3671 } 3672 __ ldrexd(out_lo, out_hi, addr); 3673} 3674 3675void InstructionCodeGeneratorARM::GenerateWideAtomicStore(Register addr, 3676 uint32_t offset, 3677 Register value_lo, 3678 Register value_hi, 3679 Register temp1, 3680 Register temp2, 3681 HInstruction* instruction) { 3682 Label fail; 3683 if (offset != 0) { 3684 __ LoadImmediate(temp1, offset); 3685 __ add(IP, addr, ShifterOperand(temp1)); 3686 addr = IP; 3687 } 3688 __ Bind(&fail); 3689 // We need a load followed by store. (The address used in a STREX instruction must 3690 // be the same as the address in the most recently executed LDREX instruction.) 3691 __ ldrexd(temp1, temp2, addr); 3692 codegen_->MaybeRecordImplicitNullCheck(instruction); 3693 __ strexd(temp1, value_lo, value_hi, addr); 3694 __ CompareAndBranchIfNonZero(temp1, &fail); 3695} 3696 3697void LocationsBuilderARM::HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info) { 3698 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet()); 3699 3700 LocationSummary* locations = 3701 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 3702 locations->SetInAt(0, Location::RequiresRegister()); 3703 3704 Primitive::Type field_type = field_info.GetFieldType(); 3705 if (Primitive::IsFloatingPointType(field_type)) { 3706 locations->SetInAt(1, Location::RequiresFpuRegister()); 3707 } else { 3708 locations->SetInAt(1, Location::RequiresRegister()); 3709 } 3710 3711 bool is_wide = field_type == Primitive::kPrimLong || field_type == Primitive::kPrimDouble; 3712 bool generate_volatile = field_info.IsVolatile() 3713 && is_wide 3714 && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd(); 3715 bool needs_write_barrier = 3716 CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1)); 3717 // Temporary registers for the write barrier. 3718 // TODO: consider renaming StoreNeedsWriteBarrier to StoreNeedsGCMark. 3719 if (needs_write_barrier) { 3720 locations->AddTemp(Location::RequiresRegister()); // Possibly used for reference poisoning too. 3721 locations->AddTemp(Location::RequiresRegister()); 3722 } else if (generate_volatile) { 3723 // Arm encoding have some additional constraints for ldrexd/strexd: 3724 // - registers need to be consecutive 3725 // - the first register should be even but not R14. 3726 // We don't test for Arm yet, and the assertion makes sure that we revisit this if we ever 3727 // enable Arm encoding. 3728 DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet()); 3729 3730 locations->AddTemp(Location::RequiresRegister()); 3731 locations->AddTemp(Location::RequiresRegister()); 3732 if (field_type == Primitive::kPrimDouble) { 3733 // For doubles we need two more registers to copy the value. 3734 locations->AddTemp(Location::RegisterLocation(R2)); 3735 locations->AddTemp(Location::RegisterLocation(R3)); 3736 } 3737 } 3738} 3739 3740void InstructionCodeGeneratorARM::HandleFieldSet(HInstruction* instruction, 3741 const FieldInfo& field_info, 3742 bool value_can_be_null) { 3743 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet()); 3744 3745 LocationSummary* locations = instruction->GetLocations(); 3746 Register base = locations->InAt(0).AsRegister<Register>(); 3747 Location value = locations->InAt(1); 3748 3749 bool is_volatile = field_info.IsVolatile(); 3750 bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd(); 3751 Primitive::Type field_type = field_info.GetFieldType(); 3752 uint32_t offset = field_info.GetFieldOffset().Uint32Value(); 3753 bool needs_write_barrier = 3754 CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1)); 3755 3756 if (is_volatile) { 3757 GenerateMemoryBarrier(MemBarrierKind::kAnyStore); 3758 } 3759 3760 switch (field_type) { 3761 case Primitive::kPrimBoolean: 3762 case Primitive::kPrimByte: { 3763 __ StoreToOffset(kStoreByte, value.AsRegister<Register>(), base, offset); 3764 break; 3765 } 3766 3767 case Primitive::kPrimShort: 3768 case Primitive::kPrimChar: { 3769 __ StoreToOffset(kStoreHalfword, value.AsRegister<Register>(), base, offset); 3770 break; 3771 } 3772 3773 case Primitive::kPrimInt: 3774 case Primitive::kPrimNot: { 3775 if (kPoisonHeapReferences && needs_write_barrier) { 3776 // Note that in the case where `value` is a null reference, 3777 // we do not enter this block, as a null reference does not 3778 // need poisoning. 3779 DCHECK_EQ(field_type, Primitive::kPrimNot); 3780 Register temp = locations->GetTemp(0).AsRegister<Register>(); 3781 __ Mov(temp, value.AsRegister<Register>()); 3782 __ PoisonHeapReference(temp); 3783 __ StoreToOffset(kStoreWord, temp, base, offset); 3784 } else { 3785 __ StoreToOffset(kStoreWord, value.AsRegister<Register>(), base, offset); 3786 } 3787 break; 3788 } 3789 3790 case Primitive::kPrimLong: { 3791 if (is_volatile && !atomic_ldrd_strd) { 3792 GenerateWideAtomicStore(base, offset, 3793 value.AsRegisterPairLow<Register>(), 3794 value.AsRegisterPairHigh<Register>(), 3795 locations->GetTemp(0).AsRegister<Register>(), 3796 locations->GetTemp(1).AsRegister<Register>(), 3797 instruction); 3798 } else { 3799 __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), base, offset); 3800 codegen_->MaybeRecordImplicitNullCheck(instruction); 3801 } 3802 break; 3803 } 3804 3805 case Primitive::kPrimFloat: { 3806 __ StoreSToOffset(value.AsFpuRegister<SRegister>(), base, offset); 3807 break; 3808 } 3809 3810 case Primitive::kPrimDouble: { 3811 DRegister value_reg = FromLowSToD(value.AsFpuRegisterPairLow<SRegister>()); 3812 if (is_volatile && !atomic_ldrd_strd) { 3813 Register value_reg_lo = locations->GetTemp(0).AsRegister<Register>(); 3814 Register value_reg_hi = locations->GetTemp(1).AsRegister<Register>(); 3815 3816 __ vmovrrd(value_reg_lo, value_reg_hi, value_reg); 3817 3818 GenerateWideAtomicStore(base, offset, 3819 value_reg_lo, 3820 value_reg_hi, 3821 locations->GetTemp(2).AsRegister<Register>(), 3822 locations->GetTemp(3).AsRegister<Register>(), 3823 instruction); 3824 } else { 3825 __ StoreDToOffset(value_reg, base, offset); 3826 codegen_->MaybeRecordImplicitNullCheck(instruction); 3827 } 3828 break; 3829 } 3830 3831 case Primitive::kPrimVoid: 3832 LOG(FATAL) << "Unreachable type " << field_type; 3833 UNREACHABLE(); 3834 } 3835 3836 // Longs and doubles are handled in the switch. 3837 if (field_type != Primitive::kPrimLong && field_type != Primitive::kPrimDouble) { 3838 codegen_->MaybeRecordImplicitNullCheck(instruction); 3839 } 3840 3841 if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) { 3842 Register temp = locations->GetTemp(0).AsRegister<Register>(); 3843 Register card = locations->GetTemp(1).AsRegister<Register>(); 3844 codegen_->MarkGCCard( 3845 temp, card, base, value.AsRegister<Register>(), value_can_be_null); 3846 } 3847 3848 if (is_volatile) { 3849 GenerateMemoryBarrier(MemBarrierKind::kAnyAny); 3850 } 3851} 3852 3853void LocationsBuilderARM::HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info) { 3854 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet()); 3855 3856 bool object_field_get_with_read_barrier = 3857 kEmitCompilerReadBarrier && (field_info.GetFieldType() == Primitive::kPrimNot); 3858 LocationSummary* locations = 3859 new (GetGraph()->GetArena()) LocationSummary(instruction, 3860 object_field_get_with_read_barrier ? 3861 LocationSummary::kCallOnSlowPath : 3862 LocationSummary::kNoCall); 3863 locations->SetInAt(0, Location::RequiresRegister()); 3864 3865 bool volatile_for_double = field_info.IsVolatile() 3866 && (field_info.GetFieldType() == Primitive::kPrimDouble) 3867 && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd(); 3868 // The output overlaps in case of volatile long: we don't want the 3869 // code generated by GenerateWideAtomicLoad to overwrite the 3870 // object's location. Likewise, in the case of an object field get 3871 // with read barriers enabled, we do not want the load to overwrite 3872 // the object's location, as we need it to emit the read barrier. 3873 bool overlap = (field_info.IsVolatile() && (field_info.GetFieldType() == Primitive::kPrimLong)) || 3874 object_field_get_with_read_barrier; 3875 3876 if (Primitive::IsFloatingPointType(instruction->GetType())) { 3877 locations->SetOut(Location::RequiresFpuRegister()); 3878 } else { 3879 locations->SetOut(Location::RequiresRegister(), 3880 (overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap)); 3881 } 3882 if (volatile_for_double) { 3883 // Arm encoding have some additional constraints for ldrexd/strexd: 3884 // - registers need to be consecutive 3885 // - the first register should be even but not R14. 3886 // We don't test for Arm yet, and the assertion makes sure that we revisit this if we ever 3887 // enable Arm encoding. 3888 DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet()); 3889 locations->AddTemp(Location::RequiresRegister()); 3890 locations->AddTemp(Location::RequiresRegister()); 3891 } 3892} 3893 3894Location LocationsBuilderARM::ArmEncodableConstantOrRegister(HInstruction* constant, 3895 Opcode opcode) { 3896 DCHECK(!Primitive::IsFloatingPointType(constant->GetType())); 3897 if (constant->IsConstant() && 3898 CanEncodeConstantAsImmediate(constant->AsConstant(), opcode)) { 3899 return Location::ConstantLocation(constant->AsConstant()); 3900 } 3901 return Location::RequiresRegister(); 3902} 3903 3904bool LocationsBuilderARM::CanEncodeConstantAsImmediate(HConstant* input_cst, 3905 Opcode opcode) { 3906 uint64_t value = static_cast<uint64_t>(Int64FromConstant(input_cst)); 3907 if (Primitive::Is64BitType(input_cst->GetType())) { 3908 return CanEncodeConstantAsImmediate(Low32Bits(value), opcode) && 3909 CanEncodeConstantAsImmediate(High32Bits(value), opcode); 3910 } else { 3911 return CanEncodeConstantAsImmediate(Low32Bits(value), opcode); 3912 } 3913} 3914 3915bool LocationsBuilderARM::CanEncodeConstantAsImmediate(uint32_t value, Opcode opcode) { 3916 ShifterOperand so; 3917 ArmAssembler* assembler = codegen_->GetAssembler(); 3918 if (assembler->ShifterOperandCanHold(kNoRegister, kNoRegister, opcode, value, &so)) { 3919 return true; 3920 } 3921 Opcode neg_opcode = kNoOperand; 3922 switch (opcode) { 3923 case AND: 3924 neg_opcode = BIC; 3925 break; 3926 case ORR: 3927 neg_opcode = ORN; 3928 break; 3929 default: 3930 return false; 3931 } 3932 return assembler->ShifterOperandCanHold(kNoRegister, kNoRegister, neg_opcode, ~value, &so); 3933} 3934 3935void InstructionCodeGeneratorARM::HandleFieldGet(HInstruction* instruction, 3936 const FieldInfo& field_info) { 3937 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet()); 3938 3939 LocationSummary* locations = instruction->GetLocations(); 3940 Location base_loc = locations->InAt(0); 3941 Register base = base_loc.AsRegister<Register>(); 3942 Location out = locations->Out(); 3943 bool is_volatile = field_info.IsVolatile(); 3944 bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd(); 3945 Primitive::Type field_type = field_info.GetFieldType(); 3946 uint32_t offset = field_info.GetFieldOffset().Uint32Value(); 3947 3948 switch (field_type) { 3949 case Primitive::kPrimBoolean: { 3950 __ LoadFromOffset(kLoadUnsignedByte, out.AsRegister<Register>(), base, offset); 3951 break; 3952 } 3953 3954 case Primitive::kPrimByte: { 3955 __ LoadFromOffset(kLoadSignedByte, out.AsRegister<Register>(), base, offset); 3956 break; 3957 } 3958 3959 case Primitive::kPrimShort: { 3960 __ LoadFromOffset(kLoadSignedHalfword, out.AsRegister<Register>(), base, offset); 3961 break; 3962 } 3963 3964 case Primitive::kPrimChar: { 3965 __ LoadFromOffset(kLoadUnsignedHalfword, out.AsRegister<Register>(), base, offset); 3966 break; 3967 } 3968 3969 case Primitive::kPrimInt: 3970 case Primitive::kPrimNot: { 3971 __ LoadFromOffset(kLoadWord, out.AsRegister<Register>(), base, offset); 3972 break; 3973 } 3974 3975 case Primitive::kPrimLong: { 3976 if (is_volatile && !atomic_ldrd_strd) { 3977 GenerateWideAtomicLoad(base, offset, 3978 out.AsRegisterPairLow<Register>(), 3979 out.AsRegisterPairHigh<Register>()); 3980 } else { 3981 __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow<Register>(), base, offset); 3982 } 3983 break; 3984 } 3985 3986 case Primitive::kPrimFloat: { 3987 __ LoadSFromOffset(out.AsFpuRegister<SRegister>(), base, offset); 3988 break; 3989 } 3990 3991 case Primitive::kPrimDouble: { 3992 DRegister out_reg = FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()); 3993 if (is_volatile && !atomic_ldrd_strd) { 3994 Register lo = locations->GetTemp(0).AsRegister<Register>(); 3995 Register hi = locations->GetTemp(1).AsRegister<Register>(); 3996 GenerateWideAtomicLoad(base, offset, lo, hi); 3997 codegen_->MaybeRecordImplicitNullCheck(instruction); 3998 __ vmovdrr(out_reg, lo, hi); 3999 } else { 4000 __ LoadDFromOffset(out_reg, base, offset); 4001 codegen_->MaybeRecordImplicitNullCheck(instruction); 4002 } 4003 break; 4004 } 4005 4006 case Primitive::kPrimVoid: 4007 LOG(FATAL) << "Unreachable type " << field_type; 4008 UNREACHABLE(); 4009 } 4010 4011 // Doubles are handled in the switch. 4012 if (field_type != Primitive::kPrimDouble) { 4013 codegen_->MaybeRecordImplicitNullCheck(instruction); 4014 } 4015 4016 if (is_volatile) { 4017 GenerateMemoryBarrier(MemBarrierKind::kLoadAny); 4018 } 4019 4020 if (field_type == Primitive::kPrimNot) { 4021 codegen_->MaybeGenerateReadBarrier(instruction, out, out, base_loc, offset); 4022 } 4023} 4024 4025void LocationsBuilderARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) { 4026 HandleFieldSet(instruction, instruction->GetFieldInfo()); 4027} 4028 4029void InstructionCodeGeneratorARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) { 4030 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull()); 4031} 4032 4033void LocationsBuilderARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { 4034 HandleFieldGet(instruction, instruction->GetFieldInfo()); 4035} 4036 4037void InstructionCodeGeneratorARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { 4038 HandleFieldGet(instruction, instruction->GetFieldInfo()); 4039} 4040 4041void LocationsBuilderARM::VisitStaticFieldGet(HStaticFieldGet* instruction) { 4042 HandleFieldGet(instruction, instruction->GetFieldInfo()); 4043} 4044 4045void InstructionCodeGeneratorARM::VisitStaticFieldGet(HStaticFieldGet* instruction) { 4046 HandleFieldGet(instruction, instruction->GetFieldInfo()); 4047} 4048 4049void LocationsBuilderARM::VisitStaticFieldSet(HStaticFieldSet* instruction) { 4050 HandleFieldSet(instruction, instruction->GetFieldInfo()); 4051} 4052 4053void InstructionCodeGeneratorARM::VisitStaticFieldSet(HStaticFieldSet* instruction) { 4054 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull()); 4055} 4056 4057void LocationsBuilderARM::VisitUnresolvedInstanceFieldGet( 4058 HUnresolvedInstanceFieldGet* instruction) { 4059 FieldAccessCallingConventionARM calling_convention; 4060 codegen_->CreateUnresolvedFieldLocationSummary( 4061 instruction, instruction->GetFieldType(), calling_convention); 4062} 4063 4064void InstructionCodeGeneratorARM::VisitUnresolvedInstanceFieldGet( 4065 HUnresolvedInstanceFieldGet* instruction) { 4066 FieldAccessCallingConventionARM calling_convention; 4067 codegen_->GenerateUnresolvedFieldAccess(instruction, 4068 instruction->GetFieldType(), 4069 instruction->GetFieldIndex(), 4070 instruction->GetDexPc(), 4071 calling_convention); 4072} 4073 4074void LocationsBuilderARM::VisitUnresolvedInstanceFieldSet( 4075 HUnresolvedInstanceFieldSet* instruction) { 4076 FieldAccessCallingConventionARM calling_convention; 4077 codegen_->CreateUnresolvedFieldLocationSummary( 4078 instruction, instruction->GetFieldType(), calling_convention); 4079} 4080 4081void InstructionCodeGeneratorARM::VisitUnresolvedInstanceFieldSet( 4082 HUnresolvedInstanceFieldSet* instruction) { 4083 FieldAccessCallingConventionARM calling_convention; 4084 codegen_->GenerateUnresolvedFieldAccess(instruction, 4085 instruction->GetFieldType(), 4086 instruction->GetFieldIndex(), 4087 instruction->GetDexPc(), 4088 calling_convention); 4089} 4090 4091void LocationsBuilderARM::VisitUnresolvedStaticFieldGet( 4092 HUnresolvedStaticFieldGet* instruction) { 4093 FieldAccessCallingConventionARM calling_convention; 4094 codegen_->CreateUnresolvedFieldLocationSummary( 4095 instruction, instruction->GetFieldType(), calling_convention); 4096} 4097 4098void InstructionCodeGeneratorARM::VisitUnresolvedStaticFieldGet( 4099 HUnresolvedStaticFieldGet* instruction) { 4100 FieldAccessCallingConventionARM calling_convention; 4101 codegen_->GenerateUnresolvedFieldAccess(instruction, 4102 instruction->GetFieldType(), 4103 instruction->GetFieldIndex(), 4104 instruction->GetDexPc(), 4105 calling_convention); 4106} 4107 4108void LocationsBuilderARM::VisitUnresolvedStaticFieldSet( 4109 HUnresolvedStaticFieldSet* instruction) { 4110 FieldAccessCallingConventionARM calling_convention; 4111 codegen_->CreateUnresolvedFieldLocationSummary( 4112 instruction, instruction->GetFieldType(), calling_convention); 4113} 4114 4115void InstructionCodeGeneratorARM::VisitUnresolvedStaticFieldSet( 4116 HUnresolvedStaticFieldSet* instruction) { 4117 FieldAccessCallingConventionARM calling_convention; 4118 codegen_->GenerateUnresolvedFieldAccess(instruction, 4119 instruction->GetFieldType(), 4120 instruction->GetFieldIndex(), 4121 instruction->GetDexPc(), 4122 calling_convention); 4123} 4124 4125void LocationsBuilderARM::VisitNullCheck(HNullCheck* instruction) { 4126 LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() 4127 ? LocationSummary::kCallOnSlowPath 4128 : LocationSummary::kNoCall; 4129 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); 4130 locations->SetInAt(0, Location::RequiresRegister()); 4131 if (instruction->HasUses()) { 4132 locations->SetOut(Location::SameAsFirstInput()); 4133 } 4134} 4135 4136void InstructionCodeGeneratorARM::GenerateImplicitNullCheck(HNullCheck* instruction) { 4137 if (codegen_->CanMoveNullCheckToUser(instruction)) { 4138 return; 4139 } 4140 Location obj = instruction->GetLocations()->InAt(0); 4141 4142 __ LoadFromOffset(kLoadWord, IP, obj.AsRegister<Register>(), 0); 4143 codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); 4144} 4145 4146void InstructionCodeGeneratorARM::GenerateExplicitNullCheck(HNullCheck* instruction) { 4147 SlowPathCode* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathARM(instruction); 4148 codegen_->AddSlowPath(slow_path); 4149 4150 LocationSummary* locations = instruction->GetLocations(); 4151 Location obj = locations->InAt(0); 4152 4153 __ CompareAndBranchIfZero(obj.AsRegister<Register>(), slow_path->GetEntryLabel()); 4154} 4155 4156void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) { 4157 if (codegen_->IsImplicitNullCheckAllowed(instruction)) { 4158 GenerateImplicitNullCheck(instruction); 4159 } else { 4160 GenerateExplicitNullCheck(instruction); 4161 } 4162} 4163 4164void LocationsBuilderARM::VisitArrayGet(HArrayGet* instruction) { 4165 bool object_array_get_with_read_barrier = 4166 kEmitCompilerReadBarrier && (instruction->GetType() == Primitive::kPrimNot); 4167 LocationSummary* locations = 4168 new (GetGraph()->GetArena()) LocationSummary(instruction, 4169 object_array_get_with_read_barrier ? 4170 LocationSummary::kCallOnSlowPath : 4171 LocationSummary::kNoCall); 4172 locations->SetInAt(0, Location::RequiresRegister()); 4173 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); 4174 if (Primitive::IsFloatingPointType(instruction->GetType())) { 4175 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 4176 } else { 4177 // The output overlaps in the case of an object array get with 4178 // read barriers enabled: we do not want the move to overwrite the 4179 // array's location, as we need it to emit the read barrier. 4180 locations->SetOut( 4181 Location::RequiresRegister(), 4182 object_array_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap); 4183 } 4184} 4185 4186void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { 4187 LocationSummary* locations = instruction->GetLocations(); 4188 Location obj_loc = locations->InAt(0); 4189 Register obj = obj_loc.AsRegister<Register>(); 4190 Location index = locations->InAt(1); 4191 Primitive::Type type = instruction->GetType(); 4192 4193 switch (type) { 4194 case Primitive::kPrimBoolean: { 4195 uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value(); 4196 Register out = locations->Out().AsRegister<Register>(); 4197 if (index.IsConstant()) { 4198 size_t offset = 4199 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset; 4200 __ LoadFromOffset(kLoadUnsignedByte, out, obj, offset); 4201 } else { 4202 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>())); 4203 __ LoadFromOffset(kLoadUnsignedByte, out, IP, data_offset); 4204 } 4205 break; 4206 } 4207 4208 case Primitive::kPrimByte: { 4209 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value(); 4210 Register out = locations->Out().AsRegister<Register>(); 4211 if (index.IsConstant()) { 4212 size_t offset = 4213 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset; 4214 __ LoadFromOffset(kLoadSignedByte, out, obj, offset); 4215 } else { 4216 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>())); 4217 __ LoadFromOffset(kLoadSignedByte, out, IP, data_offset); 4218 } 4219 break; 4220 } 4221 4222 case Primitive::kPrimShort: { 4223 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value(); 4224 Register out = locations->Out().AsRegister<Register>(); 4225 if (index.IsConstant()) { 4226 size_t offset = 4227 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; 4228 __ LoadFromOffset(kLoadSignedHalfword, out, obj, offset); 4229 } else { 4230 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_2)); 4231 __ LoadFromOffset(kLoadSignedHalfword, out, IP, data_offset); 4232 } 4233 break; 4234 } 4235 4236 case Primitive::kPrimChar: { 4237 uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value(); 4238 Register out = locations->Out().AsRegister<Register>(); 4239 if (index.IsConstant()) { 4240 size_t offset = 4241 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; 4242 __ LoadFromOffset(kLoadUnsignedHalfword, out, obj, offset); 4243 } else { 4244 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_2)); 4245 __ LoadFromOffset(kLoadUnsignedHalfword, out, IP, data_offset); 4246 } 4247 break; 4248 } 4249 4250 case Primitive::kPrimInt: 4251 case Primitive::kPrimNot: { 4252 static_assert( 4253 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), 4254 "art::mirror::HeapReference<mirror::Object> and int32_t have different sizes."); 4255 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); 4256 Register out = locations->Out().AsRegister<Register>(); 4257 if (index.IsConstant()) { 4258 size_t offset = 4259 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4260 __ LoadFromOffset(kLoadWord, out, obj, offset); 4261 } else { 4262 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4263 __ LoadFromOffset(kLoadWord, out, IP, data_offset); 4264 } 4265 break; 4266 } 4267 4268 case Primitive::kPrimLong: { 4269 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); 4270 Location out = locations->Out(); 4271 if (index.IsConstant()) { 4272 size_t offset = 4273 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; 4274 __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow<Register>(), obj, offset); 4275 } else { 4276 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8)); 4277 __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow<Register>(), IP, data_offset); 4278 } 4279 break; 4280 } 4281 4282 case Primitive::kPrimFloat: { 4283 uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value(); 4284 Location out = locations->Out(); 4285 DCHECK(out.IsFpuRegister()); 4286 if (index.IsConstant()) { 4287 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4288 __ LoadSFromOffset(out.AsFpuRegister<SRegister>(), obj, offset); 4289 } else { 4290 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4291 __ LoadSFromOffset(out.AsFpuRegister<SRegister>(), IP, data_offset); 4292 } 4293 break; 4294 } 4295 4296 case Primitive::kPrimDouble: { 4297 uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value(); 4298 Location out = locations->Out(); 4299 DCHECK(out.IsFpuRegisterPair()); 4300 if (index.IsConstant()) { 4301 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; 4302 __ LoadDFromOffset(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), obj, offset); 4303 } else { 4304 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8)); 4305 __ LoadDFromOffset(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), IP, data_offset); 4306 } 4307 break; 4308 } 4309 4310 case Primitive::kPrimVoid: 4311 LOG(FATAL) << "Unreachable type " << type; 4312 UNREACHABLE(); 4313 } 4314 codegen_->MaybeRecordImplicitNullCheck(instruction); 4315 4316 if (type == Primitive::kPrimNot) { 4317 static_assert( 4318 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), 4319 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); 4320 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); 4321 Location out = locations->Out(); 4322 if (index.IsConstant()) { 4323 uint32_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4324 codegen_->MaybeGenerateReadBarrier(instruction, out, out, obj_loc, offset); 4325 } else { 4326 codegen_->MaybeGenerateReadBarrier(instruction, out, out, obj_loc, data_offset, index); 4327 } 4328 } 4329} 4330 4331void LocationsBuilderARM::VisitArraySet(HArraySet* instruction) { 4332 Primitive::Type value_type = instruction->GetComponentType(); 4333 4334 bool needs_write_barrier = 4335 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue()); 4336 bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck(); 4337 bool object_array_set_with_read_barrier = 4338 kEmitCompilerReadBarrier && (value_type == Primitive::kPrimNot); 4339 4340 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary( 4341 instruction, 4342 (may_need_runtime_call_for_type_check || object_array_set_with_read_barrier) ? 4343 LocationSummary::kCallOnSlowPath : 4344 LocationSummary::kNoCall); 4345 4346 locations->SetInAt(0, Location::RequiresRegister()); 4347 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); 4348 if (Primitive::IsFloatingPointType(value_type)) { 4349 locations->SetInAt(2, Location::RequiresFpuRegister()); 4350 } else { 4351 locations->SetInAt(2, Location::RequiresRegister()); 4352 } 4353 if (needs_write_barrier) { 4354 // Temporary registers for the write barrier. 4355 locations->AddTemp(Location::RequiresRegister()); // Possibly used for ref. poisoning too. 4356 locations->AddTemp(Location::RequiresRegister()); 4357 } 4358} 4359 4360void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) { 4361 LocationSummary* locations = instruction->GetLocations(); 4362 Location array_loc = locations->InAt(0); 4363 Register array = array_loc.AsRegister<Register>(); 4364 Location index = locations->InAt(1); 4365 Primitive::Type value_type = instruction->GetComponentType(); 4366 bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck(); 4367 bool needs_write_barrier = 4368 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue()); 4369 4370 switch (value_type) { 4371 case Primitive::kPrimBoolean: 4372 case Primitive::kPrimByte: { 4373 uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value(); 4374 Register value = locations->InAt(2).AsRegister<Register>(); 4375 if (index.IsConstant()) { 4376 size_t offset = 4377 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset; 4378 __ StoreToOffset(kStoreByte, value, array, offset); 4379 } else { 4380 __ add(IP, array, ShifterOperand(index.AsRegister<Register>())); 4381 __ StoreToOffset(kStoreByte, value, IP, data_offset); 4382 } 4383 break; 4384 } 4385 4386 case Primitive::kPrimShort: 4387 case Primitive::kPrimChar: { 4388 uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value(); 4389 Register value = locations->InAt(2).AsRegister<Register>(); 4390 if (index.IsConstant()) { 4391 size_t offset = 4392 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; 4393 __ StoreToOffset(kStoreHalfword, value, array, offset); 4394 } else { 4395 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_2)); 4396 __ StoreToOffset(kStoreHalfword, value, IP, data_offset); 4397 } 4398 break; 4399 } 4400 4401 case Primitive::kPrimNot: { 4402 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); 4403 Location value_loc = locations->InAt(2); 4404 Register value = value_loc.AsRegister<Register>(); 4405 Register source = value; 4406 4407 if (instruction->InputAt(2)->IsNullConstant()) { 4408 // Just setting null. 4409 if (index.IsConstant()) { 4410 size_t offset = 4411 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4412 __ StoreToOffset(kStoreWord, source, array, offset); 4413 } else { 4414 DCHECK(index.IsRegister()) << index; 4415 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4416 __ StoreToOffset(kStoreWord, source, IP, data_offset); 4417 } 4418 DCHECK(!needs_write_barrier); 4419 DCHECK(!may_need_runtime_call_for_type_check); 4420 break; 4421 } 4422 4423 DCHECK(needs_write_barrier); 4424 Register temp1 = locations->GetTemp(0).AsRegister<Register>(); 4425 Register temp2 = locations->GetTemp(1).AsRegister<Register>(); 4426 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 4427 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); 4428 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); 4429 Label done; 4430 SlowPathCode* slow_path = nullptr; 4431 4432 if (may_need_runtime_call_for_type_check) { 4433 slow_path = new (GetGraph()->GetArena()) ArraySetSlowPathARM(instruction); 4434 codegen_->AddSlowPath(slow_path); 4435 if (instruction->GetValueCanBeNull()) { 4436 Label non_zero; 4437 __ CompareAndBranchIfNonZero(value, &non_zero); 4438 if (index.IsConstant()) { 4439 size_t offset = 4440 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4441 __ StoreToOffset(kStoreWord, value, array, offset); 4442 } else { 4443 DCHECK(index.IsRegister()) << index; 4444 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4445 __ StoreToOffset(kStoreWord, value, IP, data_offset); 4446 } 4447 codegen_->MaybeRecordImplicitNullCheck(instruction); 4448 __ b(&done); 4449 __ Bind(&non_zero); 4450 } 4451 4452 if (kEmitCompilerReadBarrier) { 4453 // When read barriers are enabled, the type checking 4454 // instrumentation requires two read barriers: 4455 // 4456 // __ Mov(temp2, temp1); 4457 // // /* HeapReference<Class> */ temp1 = temp1->component_type_ 4458 // __ LoadFromOffset(kLoadWord, temp1, temp1, component_offset); 4459 // codegen_->GenerateReadBarrier( 4460 // instruction, temp1_loc, temp1_loc, temp2_loc, component_offset); 4461 // 4462 // // /* HeapReference<Class> */ temp2 = value->klass_ 4463 // __ LoadFromOffset(kLoadWord, temp2, value, class_offset); 4464 // codegen_->GenerateReadBarrier( 4465 // instruction, temp2_loc, temp2_loc, value_loc, class_offset, temp1_loc); 4466 // 4467 // __ cmp(temp1, ShifterOperand(temp2)); 4468 // 4469 // However, the second read barrier may trash `temp`, as it 4470 // is a temporary register, and as such would not be saved 4471 // along with live registers before calling the runtime (nor 4472 // restored afterwards). So in this case, we bail out and 4473 // delegate the work to the array set slow path. 4474 // 4475 // TODO: Extend the register allocator to support a new 4476 // "(locally) live temp" location so as to avoid always 4477 // going into the slow path when read barriers are enabled. 4478 __ b(slow_path->GetEntryLabel()); 4479 } else { 4480 // /* HeapReference<Class> */ temp1 = array->klass_ 4481 __ LoadFromOffset(kLoadWord, temp1, array, class_offset); 4482 codegen_->MaybeRecordImplicitNullCheck(instruction); 4483 __ MaybeUnpoisonHeapReference(temp1); 4484 4485 // /* HeapReference<Class> */ temp1 = temp1->component_type_ 4486 __ LoadFromOffset(kLoadWord, temp1, temp1, component_offset); 4487 // /* HeapReference<Class> */ temp2 = value->klass_ 4488 __ LoadFromOffset(kLoadWord, temp2, value, class_offset); 4489 // If heap poisoning is enabled, no need to unpoison `temp1` 4490 // nor `temp2`, as we are comparing two poisoned references. 4491 __ cmp(temp1, ShifterOperand(temp2)); 4492 4493 if (instruction->StaticTypeOfArrayIsObjectArray()) { 4494 Label do_put; 4495 __ b(&do_put, EQ); 4496 // If heap poisoning is enabled, the `temp1` reference has 4497 // not been unpoisoned yet; unpoison it now. 4498 __ MaybeUnpoisonHeapReference(temp1); 4499 4500 // /* HeapReference<Class> */ temp1 = temp1->super_class_ 4501 __ LoadFromOffset(kLoadWord, temp1, temp1, super_offset); 4502 // If heap poisoning is enabled, no need to unpoison 4503 // `temp1`, as we are comparing against null below. 4504 __ CompareAndBranchIfNonZero(temp1, slow_path->GetEntryLabel()); 4505 __ Bind(&do_put); 4506 } else { 4507 __ b(slow_path->GetEntryLabel(), NE); 4508 } 4509 } 4510 } 4511 4512 if (kPoisonHeapReferences) { 4513 // Note that in the case where `value` is a null reference, 4514 // we do not enter this block, as a null reference does not 4515 // need poisoning. 4516 DCHECK_EQ(value_type, Primitive::kPrimNot); 4517 __ Mov(temp1, value); 4518 __ PoisonHeapReference(temp1); 4519 source = temp1; 4520 } 4521 4522 if (index.IsConstant()) { 4523 size_t offset = 4524 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4525 __ StoreToOffset(kStoreWord, source, array, offset); 4526 } else { 4527 DCHECK(index.IsRegister()) << index; 4528 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4529 __ StoreToOffset(kStoreWord, source, IP, data_offset); 4530 } 4531 4532 if (!may_need_runtime_call_for_type_check) { 4533 codegen_->MaybeRecordImplicitNullCheck(instruction); 4534 } 4535 4536 codegen_->MarkGCCard(temp1, temp2, array, value, instruction->GetValueCanBeNull()); 4537 4538 if (done.IsLinked()) { 4539 __ Bind(&done); 4540 } 4541 4542 if (slow_path != nullptr) { 4543 __ Bind(slow_path->GetExitLabel()); 4544 } 4545 4546 break; 4547 } 4548 4549 case Primitive::kPrimInt: { 4550 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); 4551 Register value = locations->InAt(2).AsRegister<Register>(); 4552 if (index.IsConstant()) { 4553 size_t offset = 4554 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4555 __ StoreToOffset(kStoreWord, value, array, offset); 4556 } else { 4557 DCHECK(index.IsRegister()) << index; 4558 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4559 __ StoreToOffset(kStoreWord, value, IP, data_offset); 4560 } 4561 4562 codegen_->MaybeRecordImplicitNullCheck(instruction); 4563 break; 4564 } 4565 4566 case Primitive::kPrimLong: { 4567 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); 4568 Location value = locations->InAt(2); 4569 if (index.IsConstant()) { 4570 size_t offset = 4571 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; 4572 __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), array, offset); 4573 } else { 4574 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8)); 4575 __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), IP, data_offset); 4576 } 4577 break; 4578 } 4579 4580 case Primitive::kPrimFloat: { 4581 uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value(); 4582 Location value = locations->InAt(2); 4583 DCHECK(value.IsFpuRegister()); 4584 if (index.IsConstant()) { 4585 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4586 __ StoreSToOffset(value.AsFpuRegister<SRegister>(), array, offset); 4587 } else { 4588 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4589 __ StoreSToOffset(value.AsFpuRegister<SRegister>(), IP, data_offset); 4590 } 4591 break; 4592 } 4593 4594 case Primitive::kPrimDouble: { 4595 uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value(); 4596 Location value = locations->InAt(2); 4597 DCHECK(value.IsFpuRegisterPair()); 4598 if (index.IsConstant()) { 4599 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; 4600 __ StoreDToOffset(FromLowSToD(value.AsFpuRegisterPairLow<SRegister>()), array, offset); 4601 } else { 4602 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8)); 4603 __ StoreDToOffset(FromLowSToD(value.AsFpuRegisterPairLow<SRegister>()), IP, data_offset); 4604 } 4605 4606 break; 4607 } 4608 4609 case Primitive::kPrimVoid: 4610 LOG(FATAL) << "Unreachable type " << value_type; 4611 UNREACHABLE(); 4612 } 4613 4614 // Ints and objects are handled in the switch. 4615 if (value_type != Primitive::kPrimInt && value_type != Primitive::kPrimNot) { 4616 codegen_->MaybeRecordImplicitNullCheck(instruction); 4617 } 4618} 4619 4620void LocationsBuilderARM::VisitArrayLength(HArrayLength* instruction) { 4621 LocationSummary* locations = 4622 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 4623 locations->SetInAt(0, Location::RequiresRegister()); 4624 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 4625} 4626 4627void InstructionCodeGeneratorARM::VisitArrayLength(HArrayLength* instruction) { 4628 LocationSummary* locations = instruction->GetLocations(); 4629 uint32_t offset = mirror::Array::LengthOffset().Uint32Value(); 4630 Register obj = locations->InAt(0).AsRegister<Register>(); 4631 Register out = locations->Out().AsRegister<Register>(); 4632 __ LoadFromOffset(kLoadWord, out, obj, offset); 4633 codegen_->MaybeRecordImplicitNullCheck(instruction); 4634} 4635 4636void LocationsBuilderARM::VisitBoundsCheck(HBoundsCheck* instruction) { 4637 LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() 4638 ? LocationSummary::kCallOnSlowPath 4639 : LocationSummary::kNoCall; 4640 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); 4641 locations->SetInAt(0, Location::RequiresRegister()); 4642 locations->SetInAt(1, Location::RequiresRegister()); 4643 if (instruction->HasUses()) { 4644 locations->SetOut(Location::SameAsFirstInput()); 4645 } 4646} 4647 4648void InstructionCodeGeneratorARM::VisitBoundsCheck(HBoundsCheck* instruction) { 4649 LocationSummary* locations = instruction->GetLocations(); 4650 SlowPathCode* slow_path = 4651 new (GetGraph()->GetArena()) BoundsCheckSlowPathARM(instruction); 4652 codegen_->AddSlowPath(slow_path); 4653 4654 Register index = locations->InAt(0).AsRegister<Register>(); 4655 Register length = locations->InAt(1).AsRegister<Register>(); 4656 4657 __ cmp(index, ShifterOperand(length)); 4658 __ b(slow_path->GetEntryLabel(), HS); 4659} 4660 4661void CodeGeneratorARM::MarkGCCard(Register temp, 4662 Register card, 4663 Register object, 4664 Register value, 4665 bool can_be_null) { 4666 Label is_null; 4667 if (can_be_null) { 4668 __ CompareAndBranchIfZero(value, &is_null); 4669 } 4670 __ LoadFromOffset(kLoadWord, card, TR, Thread::CardTableOffset<kArmWordSize>().Int32Value()); 4671 __ Lsr(temp, object, gc::accounting::CardTable::kCardShift); 4672 __ strb(card, Address(card, temp)); 4673 if (can_be_null) { 4674 __ Bind(&is_null); 4675 } 4676} 4677 4678void LocationsBuilderARM::VisitTemporary(HTemporary* temp) { 4679 temp->SetLocations(nullptr); 4680} 4681 4682void InstructionCodeGeneratorARM::VisitTemporary(HTemporary* temp ATTRIBUTE_UNUSED) { 4683 // Nothing to do, this is driven by the code generator. 4684} 4685 4686void LocationsBuilderARM::VisitParallelMove(HParallelMove* instruction ATTRIBUTE_UNUSED) { 4687 LOG(FATAL) << "Unreachable"; 4688} 4689 4690void InstructionCodeGeneratorARM::VisitParallelMove(HParallelMove* instruction) { 4691 codegen_->GetMoveResolver()->EmitNativeCode(instruction); 4692} 4693 4694void LocationsBuilderARM::VisitSuspendCheck(HSuspendCheck* instruction) { 4695 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath); 4696} 4697 4698void InstructionCodeGeneratorARM::VisitSuspendCheck(HSuspendCheck* instruction) { 4699 HBasicBlock* block = instruction->GetBlock(); 4700 if (block->GetLoopInformation() != nullptr) { 4701 DCHECK(block->GetLoopInformation()->GetSuspendCheck() == instruction); 4702 // The back edge will generate the suspend check. 4703 return; 4704 } 4705 if (block->IsEntryBlock() && instruction->GetNext()->IsGoto()) { 4706 // The goto will generate the suspend check. 4707 return; 4708 } 4709 GenerateSuspendCheck(instruction, nullptr); 4710} 4711 4712void InstructionCodeGeneratorARM::GenerateSuspendCheck(HSuspendCheck* instruction, 4713 HBasicBlock* successor) { 4714 SuspendCheckSlowPathARM* slow_path = 4715 down_cast<SuspendCheckSlowPathARM*>(instruction->GetSlowPath()); 4716 if (slow_path == nullptr) { 4717 slow_path = new (GetGraph()->GetArena()) SuspendCheckSlowPathARM(instruction, successor); 4718 instruction->SetSlowPath(slow_path); 4719 codegen_->AddSlowPath(slow_path); 4720 if (successor != nullptr) { 4721 DCHECK(successor->IsLoopHeader()); 4722 codegen_->ClearSpillSlotsFromLoopPhisInStackMap(instruction); 4723 } 4724 } else { 4725 DCHECK_EQ(slow_path->GetSuccessor(), successor); 4726 } 4727 4728 __ LoadFromOffset( 4729 kLoadUnsignedHalfword, IP, TR, Thread::ThreadFlagsOffset<kArmWordSize>().Int32Value()); 4730 if (successor == nullptr) { 4731 __ CompareAndBranchIfNonZero(IP, slow_path->GetEntryLabel()); 4732 __ Bind(slow_path->GetReturnLabel()); 4733 } else { 4734 __ CompareAndBranchIfZero(IP, codegen_->GetLabelOf(successor)); 4735 __ b(slow_path->GetEntryLabel()); 4736 } 4737} 4738 4739ArmAssembler* ParallelMoveResolverARM::GetAssembler() const { 4740 return codegen_->GetAssembler(); 4741} 4742 4743void ParallelMoveResolverARM::EmitMove(size_t index) { 4744 MoveOperands* move = moves_[index]; 4745 Location source = move->GetSource(); 4746 Location destination = move->GetDestination(); 4747 4748 if (source.IsRegister()) { 4749 if (destination.IsRegister()) { 4750 __ Mov(destination.AsRegister<Register>(), source.AsRegister<Register>()); 4751 } else { 4752 DCHECK(destination.IsStackSlot()); 4753 __ StoreToOffset(kStoreWord, source.AsRegister<Register>(), 4754 SP, destination.GetStackIndex()); 4755 } 4756 } else if (source.IsStackSlot()) { 4757 if (destination.IsRegister()) { 4758 __ LoadFromOffset(kLoadWord, destination.AsRegister<Register>(), 4759 SP, source.GetStackIndex()); 4760 } else if (destination.IsFpuRegister()) { 4761 __ LoadSFromOffset(destination.AsFpuRegister<SRegister>(), SP, source.GetStackIndex()); 4762 } else { 4763 DCHECK(destination.IsStackSlot()); 4764 __ LoadFromOffset(kLoadWord, IP, SP, source.GetStackIndex()); 4765 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 4766 } 4767 } else if (source.IsFpuRegister()) { 4768 if (destination.IsFpuRegister()) { 4769 __ vmovs(destination.AsFpuRegister<SRegister>(), source.AsFpuRegister<SRegister>()); 4770 } else { 4771 DCHECK(destination.IsStackSlot()); 4772 __ StoreSToOffset(source.AsFpuRegister<SRegister>(), SP, destination.GetStackIndex()); 4773 } 4774 } else if (source.IsDoubleStackSlot()) { 4775 if (destination.IsDoubleStackSlot()) { 4776 __ LoadDFromOffset(DTMP, SP, source.GetStackIndex()); 4777 __ StoreDToOffset(DTMP, SP, destination.GetStackIndex()); 4778 } else if (destination.IsRegisterPair()) { 4779 DCHECK(ExpectedPairLayout(destination)); 4780 __ LoadFromOffset( 4781 kLoadWordPair, destination.AsRegisterPairLow<Register>(), SP, source.GetStackIndex()); 4782 } else { 4783 DCHECK(destination.IsFpuRegisterPair()) << destination; 4784 __ LoadDFromOffset(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), 4785 SP, 4786 source.GetStackIndex()); 4787 } 4788 } else if (source.IsRegisterPair()) { 4789 if (destination.IsRegisterPair()) { 4790 __ Mov(destination.AsRegisterPairLow<Register>(), source.AsRegisterPairLow<Register>()); 4791 __ Mov(destination.AsRegisterPairHigh<Register>(), source.AsRegisterPairHigh<Register>()); 4792 } else { 4793 DCHECK(destination.IsDoubleStackSlot()) << destination; 4794 DCHECK(ExpectedPairLayout(source)); 4795 __ StoreToOffset( 4796 kStoreWordPair, source.AsRegisterPairLow<Register>(), SP, destination.GetStackIndex()); 4797 } 4798 } else if (source.IsFpuRegisterPair()) { 4799 if (destination.IsFpuRegisterPair()) { 4800 __ vmovd(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), 4801 FromLowSToD(source.AsFpuRegisterPairLow<SRegister>())); 4802 } else { 4803 DCHECK(destination.IsDoubleStackSlot()) << destination; 4804 __ StoreDToOffset(FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()), 4805 SP, 4806 destination.GetStackIndex()); 4807 } 4808 } else { 4809 DCHECK(source.IsConstant()) << source; 4810 HConstant* constant = source.GetConstant(); 4811 if (constant->IsIntConstant() || constant->IsNullConstant()) { 4812 int32_t value = CodeGenerator::GetInt32ValueOf(constant); 4813 if (destination.IsRegister()) { 4814 __ LoadImmediate(destination.AsRegister<Register>(), value); 4815 } else { 4816 DCHECK(destination.IsStackSlot()); 4817 __ LoadImmediate(IP, value); 4818 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 4819 } 4820 } else if (constant->IsLongConstant()) { 4821 int64_t value = constant->AsLongConstant()->GetValue(); 4822 if (destination.IsRegisterPair()) { 4823 __ LoadImmediate(destination.AsRegisterPairLow<Register>(), Low32Bits(value)); 4824 __ LoadImmediate(destination.AsRegisterPairHigh<Register>(), High32Bits(value)); 4825 } else { 4826 DCHECK(destination.IsDoubleStackSlot()) << destination; 4827 __ LoadImmediate(IP, Low32Bits(value)); 4828 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 4829 __ LoadImmediate(IP, High32Bits(value)); 4830 __ StoreToOffset(kStoreWord, IP, SP, destination.GetHighStackIndex(kArmWordSize)); 4831 } 4832 } else if (constant->IsDoubleConstant()) { 4833 double value = constant->AsDoubleConstant()->GetValue(); 4834 if (destination.IsFpuRegisterPair()) { 4835 __ LoadDImmediate(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), value); 4836 } else { 4837 DCHECK(destination.IsDoubleStackSlot()) << destination; 4838 uint64_t int_value = bit_cast<uint64_t, double>(value); 4839 __ LoadImmediate(IP, Low32Bits(int_value)); 4840 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 4841 __ LoadImmediate(IP, High32Bits(int_value)); 4842 __ StoreToOffset(kStoreWord, IP, SP, destination.GetHighStackIndex(kArmWordSize)); 4843 } 4844 } else { 4845 DCHECK(constant->IsFloatConstant()) << constant->DebugName(); 4846 float value = constant->AsFloatConstant()->GetValue(); 4847 if (destination.IsFpuRegister()) { 4848 __ LoadSImmediate(destination.AsFpuRegister<SRegister>(), value); 4849 } else { 4850 DCHECK(destination.IsStackSlot()); 4851 __ LoadImmediate(IP, bit_cast<int32_t, float>(value)); 4852 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 4853 } 4854 } 4855 } 4856} 4857 4858void ParallelMoveResolverARM::Exchange(Register reg, int mem) { 4859 __ Mov(IP, reg); 4860 __ LoadFromOffset(kLoadWord, reg, SP, mem); 4861 __ StoreToOffset(kStoreWord, IP, SP, mem); 4862} 4863 4864void ParallelMoveResolverARM::Exchange(int mem1, int mem2) { 4865 ScratchRegisterScope ensure_scratch(this, IP, R0, codegen_->GetNumberOfCoreRegisters()); 4866 int stack_offset = ensure_scratch.IsSpilled() ? kArmWordSize : 0; 4867 __ LoadFromOffset(kLoadWord, static_cast<Register>(ensure_scratch.GetRegister()), 4868 SP, mem1 + stack_offset); 4869 __ LoadFromOffset(kLoadWord, IP, SP, mem2 + stack_offset); 4870 __ StoreToOffset(kStoreWord, static_cast<Register>(ensure_scratch.GetRegister()), 4871 SP, mem2 + stack_offset); 4872 __ StoreToOffset(kStoreWord, IP, SP, mem1 + stack_offset); 4873} 4874 4875void ParallelMoveResolverARM::EmitSwap(size_t index) { 4876 MoveOperands* move = moves_[index]; 4877 Location source = move->GetSource(); 4878 Location destination = move->GetDestination(); 4879 4880 if (source.IsRegister() && destination.IsRegister()) { 4881 DCHECK_NE(source.AsRegister<Register>(), IP); 4882 DCHECK_NE(destination.AsRegister<Register>(), IP); 4883 __ Mov(IP, source.AsRegister<Register>()); 4884 __ Mov(source.AsRegister<Register>(), destination.AsRegister<Register>()); 4885 __ Mov(destination.AsRegister<Register>(), IP); 4886 } else if (source.IsRegister() && destination.IsStackSlot()) { 4887 Exchange(source.AsRegister<Register>(), destination.GetStackIndex()); 4888 } else if (source.IsStackSlot() && destination.IsRegister()) { 4889 Exchange(destination.AsRegister<Register>(), source.GetStackIndex()); 4890 } else if (source.IsStackSlot() && destination.IsStackSlot()) { 4891 Exchange(source.GetStackIndex(), destination.GetStackIndex()); 4892 } else if (source.IsFpuRegister() && destination.IsFpuRegister()) { 4893 __ vmovrs(IP, source.AsFpuRegister<SRegister>()); 4894 __ vmovs(source.AsFpuRegister<SRegister>(), destination.AsFpuRegister<SRegister>()); 4895 __ vmovsr(destination.AsFpuRegister<SRegister>(), IP); 4896 } else if (source.IsRegisterPair() && destination.IsRegisterPair()) { 4897 __ vmovdrr(DTMP, source.AsRegisterPairLow<Register>(), source.AsRegisterPairHigh<Register>()); 4898 __ Mov(source.AsRegisterPairLow<Register>(), destination.AsRegisterPairLow<Register>()); 4899 __ Mov(source.AsRegisterPairHigh<Register>(), destination.AsRegisterPairHigh<Register>()); 4900 __ vmovrrd(destination.AsRegisterPairLow<Register>(), 4901 destination.AsRegisterPairHigh<Register>(), 4902 DTMP); 4903 } else if (source.IsRegisterPair() || destination.IsRegisterPair()) { 4904 Register low_reg = source.IsRegisterPair() 4905 ? source.AsRegisterPairLow<Register>() 4906 : destination.AsRegisterPairLow<Register>(); 4907 int mem = source.IsRegisterPair() 4908 ? destination.GetStackIndex() 4909 : source.GetStackIndex(); 4910 DCHECK(ExpectedPairLayout(source.IsRegisterPair() ? source : destination)); 4911 __ vmovdrr(DTMP, low_reg, static_cast<Register>(low_reg + 1)); 4912 __ LoadFromOffset(kLoadWordPair, low_reg, SP, mem); 4913 __ StoreDToOffset(DTMP, SP, mem); 4914 } else if (source.IsFpuRegisterPair() && destination.IsFpuRegisterPair()) { 4915 DRegister first = FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()); 4916 DRegister second = FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()); 4917 __ vmovd(DTMP, first); 4918 __ vmovd(first, second); 4919 __ vmovd(second, DTMP); 4920 } else if (source.IsFpuRegisterPair() || destination.IsFpuRegisterPair()) { 4921 DRegister reg = source.IsFpuRegisterPair() 4922 ? FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()) 4923 : FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()); 4924 int mem = source.IsFpuRegisterPair() 4925 ? destination.GetStackIndex() 4926 : source.GetStackIndex(); 4927 __ vmovd(DTMP, reg); 4928 __ LoadDFromOffset(reg, SP, mem); 4929 __ StoreDToOffset(DTMP, SP, mem); 4930 } else if (source.IsFpuRegister() || destination.IsFpuRegister()) { 4931 SRegister reg = source.IsFpuRegister() ? source.AsFpuRegister<SRegister>() 4932 : destination.AsFpuRegister<SRegister>(); 4933 int mem = source.IsFpuRegister() 4934 ? destination.GetStackIndex() 4935 : source.GetStackIndex(); 4936 4937 __ vmovrs(IP, reg); 4938 __ LoadSFromOffset(reg, SP, mem); 4939 __ StoreToOffset(kStoreWord, IP, SP, mem); 4940 } else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) { 4941 Exchange(source.GetStackIndex(), destination.GetStackIndex()); 4942 Exchange(source.GetHighStackIndex(kArmWordSize), destination.GetHighStackIndex(kArmWordSize)); 4943 } else { 4944 LOG(FATAL) << "Unimplemented" << source << " <-> " << destination; 4945 } 4946} 4947 4948void ParallelMoveResolverARM::SpillScratch(int reg) { 4949 __ Push(static_cast<Register>(reg)); 4950} 4951 4952void ParallelMoveResolverARM::RestoreScratch(int reg) { 4953 __ Pop(static_cast<Register>(reg)); 4954} 4955 4956void LocationsBuilderARM::VisitLoadClass(HLoadClass* cls) { 4957 InvokeRuntimeCallingConvention calling_convention; 4958 CodeGenerator::CreateLoadClassLocationSummary( 4959 cls, 4960 Location::RegisterLocation(calling_convention.GetRegisterAt(0)), 4961 Location::RegisterLocation(R0), 4962 /* code_generator_supports_read_barrier */ true); 4963} 4964 4965void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) { 4966 LocationSummary* locations = cls->GetLocations(); 4967 if (cls->NeedsAccessCheck()) { 4968 codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex()); 4969 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInitializeTypeAndVerifyAccess), 4970 cls, 4971 cls->GetDexPc(), 4972 nullptr); 4973 CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>(); 4974 return; 4975 } 4976 4977 Location out_loc = locations->Out(); 4978 Register out = out_loc.AsRegister<Register>(); 4979 Register current_method = locations->InAt(0).AsRegister<Register>(); 4980 4981 if (cls->IsReferrersClass()) { 4982 DCHECK(!cls->CanCallRuntime()); 4983 DCHECK(!cls->MustGenerateClinitCheck()); 4984 uint32_t declaring_class_offset = ArtMethod::DeclaringClassOffset().Int32Value(); 4985 if (kEmitCompilerReadBarrier) { 4986 // /* GcRoot<mirror::Class>* */ out = &(current_method->declaring_class_) 4987 __ AddConstant(out, current_method, declaring_class_offset); 4988 // /* mirror::Class* */ out = out->Read() 4989 codegen_->GenerateReadBarrierForRoot(cls, out_loc, out_loc); 4990 } else { 4991 // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_ 4992 __ LoadFromOffset(kLoadWord, out, current_method, declaring_class_offset); 4993 } 4994 } else { 4995 DCHECK(cls->CanCallRuntime()); 4996 // /* GcRoot<mirror::Class>[] */ out = 4997 // current_method.ptr_sized_fields_->dex_cache_resolved_types_ 4998 __ LoadFromOffset(kLoadWord, 4999 out, 5000 current_method, 5001 ArtMethod::DexCacheResolvedTypesOffset(kArmPointerSize).Int32Value()); 5002 5003 size_t cache_offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex()); 5004 if (kEmitCompilerReadBarrier) { 5005 // /* GcRoot<mirror::Class>* */ out = &out[type_index] 5006 __ AddConstant(out, out, cache_offset); 5007 // /* mirror::Class* */ out = out->Read() 5008 codegen_->GenerateReadBarrierForRoot(cls, out_loc, out_loc); 5009 } else { 5010 // /* GcRoot<mirror::Class> */ out = out[type_index] 5011 __ LoadFromOffset(kLoadWord, out, out, cache_offset); 5012 } 5013 5014 SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM( 5015 cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck()); 5016 codegen_->AddSlowPath(slow_path); 5017 __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel()); 5018 if (cls->MustGenerateClinitCheck()) { 5019 GenerateClassInitializationCheck(slow_path, out); 5020 } else { 5021 __ Bind(slow_path->GetExitLabel()); 5022 } 5023 } 5024} 5025 5026void LocationsBuilderARM::VisitClinitCheck(HClinitCheck* check) { 5027 LocationSummary* locations = 5028 new (GetGraph()->GetArena()) LocationSummary(check, LocationSummary::kCallOnSlowPath); 5029 locations->SetInAt(0, Location::RequiresRegister()); 5030 if (check->HasUses()) { 5031 locations->SetOut(Location::SameAsFirstInput()); 5032 } 5033} 5034 5035void InstructionCodeGeneratorARM::VisitClinitCheck(HClinitCheck* check) { 5036 // We assume the class is not null. 5037 SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM( 5038 check->GetLoadClass(), check, check->GetDexPc(), true); 5039 codegen_->AddSlowPath(slow_path); 5040 GenerateClassInitializationCheck(slow_path, 5041 check->GetLocations()->InAt(0).AsRegister<Register>()); 5042} 5043 5044void InstructionCodeGeneratorARM::GenerateClassInitializationCheck( 5045 SlowPathCode* slow_path, Register class_reg) { 5046 __ LoadFromOffset(kLoadWord, IP, class_reg, mirror::Class::StatusOffset().Int32Value()); 5047 __ cmp(IP, ShifterOperand(mirror::Class::kStatusInitialized)); 5048 __ b(slow_path->GetEntryLabel(), LT); 5049 // Even if the initialized flag is set, we may be in a situation where caches are not synced 5050 // properly. Therefore, we do a memory fence. 5051 __ dmb(ISH); 5052 __ Bind(slow_path->GetExitLabel()); 5053} 5054 5055void LocationsBuilderARM::VisitLoadString(HLoadString* load) { 5056 LocationSummary* locations = 5057 new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kCallOnSlowPath); 5058 locations->SetInAt(0, Location::RequiresRegister()); 5059 locations->SetOut(Location::RequiresRegister()); 5060} 5061 5062void InstructionCodeGeneratorARM::VisitLoadString(HLoadString* load) { 5063 SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathARM(load); 5064 codegen_->AddSlowPath(slow_path); 5065 5066 LocationSummary* locations = load->GetLocations(); 5067 Location out_loc = locations->Out(); 5068 Register out = out_loc.AsRegister<Register>(); 5069 Register current_method = locations->InAt(0).AsRegister<Register>(); 5070 5071 uint32_t declaring_class_offset = ArtMethod::DeclaringClassOffset().Int32Value(); 5072 if (kEmitCompilerReadBarrier) { 5073 // /* GcRoot<mirror::Class>* */ out = &(current_method->declaring_class_) 5074 __ AddConstant(out, current_method, declaring_class_offset); 5075 // /* mirror::Class* */ out = out->Read() 5076 codegen_->GenerateReadBarrierForRoot(load, out_loc, out_loc); 5077 } else { 5078 // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_ 5079 __ LoadFromOffset(kLoadWord, out, current_method, declaring_class_offset); 5080 } 5081 5082 // /* GcRoot<mirror::String>[] */ out = out->dex_cache_strings_ 5083 __ LoadFromOffset(kLoadWord, out, out, mirror::Class::DexCacheStringsOffset().Int32Value()); 5084 5085 size_t cache_offset = CodeGenerator::GetCacheOffset(load->GetStringIndex()); 5086 if (kEmitCompilerReadBarrier) { 5087 // /* GcRoot<mirror::String>* */ out = &out[string_index] 5088 __ AddConstant(out, out, cache_offset); 5089 // /* mirror::String* */ out = out->Read() 5090 codegen_->GenerateReadBarrierForRoot(load, out_loc, out_loc); 5091 } else { 5092 // /* GcRoot<mirror::String> */ out = out[string_index] 5093 __ LoadFromOffset(kLoadWord, out, out, cache_offset); 5094 } 5095 5096 __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel()); 5097 __ Bind(slow_path->GetExitLabel()); 5098} 5099 5100static int32_t GetExceptionTlsOffset() { 5101 return Thread::ExceptionOffset<kArmWordSize>().Int32Value(); 5102} 5103 5104void LocationsBuilderARM::VisitLoadException(HLoadException* load) { 5105 LocationSummary* locations = 5106 new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kNoCall); 5107 locations->SetOut(Location::RequiresRegister()); 5108} 5109 5110void InstructionCodeGeneratorARM::VisitLoadException(HLoadException* load) { 5111 Register out = load->GetLocations()->Out().AsRegister<Register>(); 5112 __ LoadFromOffset(kLoadWord, out, TR, GetExceptionTlsOffset()); 5113} 5114 5115void LocationsBuilderARM::VisitClearException(HClearException* clear) { 5116 new (GetGraph()->GetArena()) LocationSummary(clear, LocationSummary::kNoCall); 5117} 5118 5119void InstructionCodeGeneratorARM::VisitClearException(HClearException* clear ATTRIBUTE_UNUSED) { 5120 __ LoadImmediate(IP, 0); 5121 __ StoreToOffset(kStoreWord, IP, TR, GetExceptionTlsOffset()); 5122} 5123 5124void LocationsBuilderARM::VisitThrow(HThrow* instruction) { 5125 LocationSummary* locations = 5126 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); 5127 InvokeRuntimeCallingConvention calling_convention; 5128 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 5129} 5130 5131void InstructionCodeGeneratorARM::VisitThrow(HThrow* instruction) { 5132 codegen_->InvokeRuntime( 5133 QUICK_ENTRY_POINT(pDeliverException), instruction, instruction->GetDexPc(), nullptr); 5134 CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>(); 5135} 5136 5137void LocationsBuilderARM::VisitInstanceOf(HInstanceOf* instruction) { 5138 LocationSummary::CallKind call_kind = LocationSummary::kNoCall; 5139 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind(); 5140 switch (type_check_kind) { 5141 case TypeCheckKind::kExactCheck: 5142 case TypeCheckKind::kAbstractClassCheck: 5143 case TypeCheckKind::kClassHierarchyCheck: 5144 case TypeCheckKind::kArrayObjectCheck: 5145 call_kind = 5146 kEmitCompilerReadBarrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall; 5147 break; 5148 case TypeCheckKind::kArrayCheck: 5149 case TypeCheckKind::kUnresolvedCheck: 5150 case TypeCheckKind::kInterfaceCheck: 5151 call_kind = LocationSummary::kCallOnSlowPath; 5152 break; 5153 } 5154 5155 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); 5156 locations->SetInAt(0, Location::RequiresRegister()); 5157 locations->SetInAt(1, Location::RequiresRegister()); 5158 // The "out" register is used as a temporary, so it overlaps with the inputs. 5159 // Note that TypeCheckSlowPathARM uses this register too. 5160 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 5161 // When read barriers are enabled, we need a temporary register for 5162 // some cases. 5163 if (kEmitCompilerReadBarrier && 5164 (type_check_kind == TypeCheckKind::kAbstractClassCheck || 5165 type_check_kind == TypeCheckKind::kClassHierarchyCheck || 5166 type_check_kind == TypeCheckKind::kArrayObjectCheck)) { 5167 locations->AddTemp(Location::RequiresRegister()); 5168 } 5169} 5170 5171void InstructionCodeGeneratorARM::VisitInstanceOf(HInstanceOf* instruction) { 5172 LocationSummary* locations = instruction->GetLocations(); 5173 Location obj_loc = locations->InAt(0); 5174 Register obj = obj_loc.AsRegister<Register>(); 5175 Register cls = locations->InAt(1).AsRegister<Register>(); 5176 Location out_loc = locations->Out(); 5177 Register out = out_loc.AsRegister<Register>(); 5178 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 5179 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); 5180 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); 5181 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value(); 5182 Label done, zero; 5183 SlowPathCode* slow_path = nullptr; 5184 5185 // Return 0 if `obj` is null. 5186 // avoid null check if we know obj is not null. 5187 if (instruction->MustDoNullCheck()) { 5188 __ CompareAndBranchIfZero(obj, &zero); 5189 } 5190 5191 // /* HeapReference<Class> */ out = obj->klass_ 5192 __ LoadFromOffset(kLoadWord, out, obj, class_offset); 5193 codegen_->MaybeGenerateReadBarrier(instruction, out_loc, out_loc, obj_loc, class_offset); 5194 5195 switch (instruction->GetTypeCheckKind()) { 5196 case TypeCheckKind::kExactCheck: { 5197 __ cmp(out, ShifterOperand(cls)); 5198 // Classes must be equal for the instanceof to succeed. 5199 __ b(&zero, NE); 5200 __ LoadImmediate(out, 1); 5201 __ b(&done); 5202 break; 5203 } 5204 5205 case TypeCheckKind::kAbstractClassCheck: { 5206 // If the class is abstract, we eagerly fetch the super class of the 5207 // object to avoid doing a comparison we know will fail. 5208 Label loop; 5209 __ Bind(&loop); 5210 Location temp_loc = kEmitCompilerReadBarrier ? locations->GetTemp(0) : Location::NoLocation(); 5211 if (kEmitCompilerReadBarrier) { 5212 // Save the value of `out` into `temp` before overwriting it 5213 // in the following move operation, as we will need it for the 5214 // read barrier below. 5215 Register temp = temp_loc.AsRegister<Register>(); 5216 __ Mov(temp, out); 5217 } 5218 // /* HeapReference<Class> */ out = out->super_class_ 5219 __ LoadFromOffset(kLoadWord, out, out, super_offset); 5220 codegen_->MaybeGenerateReadBarrier(instruction, out_loc, out_loc, temp_loc, super_offset); 5221 // If `out` is null, we use it for the result, and jump to `done`. 5222 __ CompareAndBranchIfZero(out, &done); 5223 __ cmp(out, ShifterOperand(cls)); 5224 __ b(&loop, NE); 5225 __ LoadImmediate(out, 1); 5226 if (zero.IsLinked()) { 5227 __ b(&done); 5228 } 5229 break; 5230 } 5231 5232 case TypeCheckKind::kClassHierarchyCheck: { 5233 // Walk over the class hierarchy to find a match. 5234 Label loop, success; 5235 __ Bind(&loop); 5236 __ cmp(out, ShifterOperand(cls)); 5237 __ b(&success, EQ); 5238 Location temp_loc = kEmitCompilerReadBarrier ? locations->GetTemp(0) : Location::NoLocation(); 5239 if (kEmitCompilerReadBarrier) { 5240 // Save the value of `out` into `temp` before overwriting it 5241 // in the following move operation, as we will need it for the 5242 // read barrier below. 5243 Register temp = temp_loc.AsRegister<Register>(); 5244 __ Mov(temp, out); 5245 } 5246 // /* HeapReference<Class> */ out = out->super_class_ 5247 __ LoadFromOffset(kLoadWord, out, out, super_offset); 5248 codegen_->MaybeGenerateReadBarrier(instruction, out_loc, out_loc, temp_loc, super_offset); 5249 __ CompareAndBranchIfNonZero(out, &loop); 5250 // If `out` is null, we use it for the result, and jump to `done`. 5251 __ b(&done); 5252 __ Bind(&success); 5253 __ LoadImmediate(out, 1); 5254 if (zero.IsLinked()) { 5255 __ b(&done); 5256 } 5257 break; 5258 } 5259 5260 case TypeCheckKind::kArrayObjectCheck: { 5261 // Do an exact check. 5262 Label exact_check; 5263 __ cmp(out, ShifterOperand(cls)); 5264 __ b(&exact_check, EQ); 5265 // Otherwise, we need to check that the object's class is a non-primitive array. 5266 Location temp_loc = kEmitCompilerReadBarrier ? locations->GetTemp(0) : Location::NoLocation(); 5267 if (kEmitCompilerReadBarrier) { 5268 // Save the value of `out` into `temp` before overwriting it 5269 // in the following move operation, as we will need it for the 5270 // read barrier below. 5271 Register temp = temp_loc.AsRegister<Register>(); 5272 __ Mov(temp, out); 5273 } 5274 // /* HeapReference<Class> */ out = out->component_type_ 5275 __ LoadFromOffset(kLoadWord, out, out, component_offset); 5276 codegen_->MaybeGenerateReadBarrier(instruction, out_loc, out_loc, temp_loc, component_offset); 5277 // If `out` is null, we use it for the result, and jump to `done`. 5278 __ CompareAndBranchIfZero(out, &done); 5279 __ LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset); 5280 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot"); 5281 __ CompareAndBranchIfNonZero(out, &zero); 5282 __ Bind(&exact_check); 5283 __ LoadImmediate(out, 1); 5284 __ b(&done); 5285 break; 5286 } 5287 5288 case TypeCheckKind::kArrayCheck: { 5289 __ cmp(out, ShifterOperand(cls)); 5290 DCHECK(locations->OnlyCallsOnSlowPath()); 5291 slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARM(instruction, 5292 /* is_fatal */ false); 5293 codegen_->AddSlowPath(slow_path); 5294 __ b(slow_path->GetEntryLabel(), NE); 5295 __ LoadImmediate(out, 1); 5296 if (zero.IsLinked()) { 5297 __ b(&done); 5298 } 5299 break; 5300 } 5301 5302 case TypeCheckKind::kUnresolvedCheck: 5303 case TypeCheckKind::kInterfaceCheck: { 5304 // Note that we indeed only call on slow path, but we always go 5305 // into the slow path for the unresolved & interface check 5306 // cases. 5307 // 5308 // We cannot directly call the InstanceofNonTrivial runtime 5309 // entry point without resorting to a type checking slow path 5310 // here (i.e. by calling InvokeRuntime directly), as it would 5311 // require to assign fixed registers for the inputs of this 5312 // HInstanceOf instruction (following the runtime calling 5313 // convention), which might be cluttered by the potential first 5314 // read barrier emission at the beginning of this method. 5315 DCHECK(locations->OnlyCallsOnSlowPath()); 5316 slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARM(instruction, 5317 /* is_fatal */ false); 5318 codegen_->AddSlowPath(slow_path); 5319 __ b(slow_path->GetEntryLabel()); 5320 if (zero.IsLinked()) { 5321 __ b(&done); 5322 } 5323 break; 5324 } 5325 } 5326 5327 if (zero.IsLinked()) { 5328 __ Bind(&zero); 5329 __ LoadImmediate(out, 0); 5330 } 5331 5332 if (done.IsLinked()) { 5333 __ Bind(&done); 5334 } 5335 5336 if (slow_path != nullptr) { 5337 __ Bind(slow_path->GetExitLabel()); 5338 } 5339} 5340 5341void LocationsBuilderARM::VisitCheckCast(HCheckCast* instruction) { 5342 LocationSummary::CallKind call_kind = LocationSummary::kNoCall; 5343 bool throws_into_catch = instruction->CanThrowIntoCatchBlock(); 5344 5345 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind(); 5346 switch (type_check_kind) { 5347 case TypeCheckKind::kExactCheck: 5348 case TypeCheckKind::kAbstractClassCheck: 5349 case TypeCheckKind::kClassHierarchyCheck: 5350 case TypeCheckKind::kArrayObjectCheck: 5351 call_kind = (throws_into_catch || kEmitCompilerReadBarrier) ? 5352 LocationSummary::kCallOnSlowPath : 5353 LocationSummary::kNoCall; // In fact, call on a fatal (non-returning) slow path. 5354 break; 5355 case TypeCheckKind::kArrayCheck: 5356 case TypeCheckKind::kUnresolvedCheck: 5357 case TypeCheckKind::kInterfaceCheck: 5358 call_kind = LocationSummary::kCallOnSlowPath; 5359 break; 5360 } 5361 5362 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); 5363 locations->SetInAt(0, Location::RequiresRegister()); 5364 locations->SetInAt(1, Location::RequiresRegister()); 5365 // Note that TypeCheckSlowPathARM uses this "temp" register too. 5366 locations->AddTemp(Location::RequiresRegister()); 5367 // When read barriers are enabled, we need an additional temporary 5368 // register for some cases. 5369 if (kEmitCompilerReadBarrier && 5370 (type_check_kind == TypeCheckKind::kAbstractClassCheck || 5371 type_check_kind == TypeCheckKind::kClassHierarchyCheck || 5372 type_check_kind == TypeCheckKind::kArrayObjectCheck)) { 5373 locations->AddTemp(Location::RequiresRegister()); 5374 } 5375} 5376 5377void InstructionCodeGeneratorARM::VisitCheckCast(HCheckCast* instruction) { 5378 LocationSummary* locations = instruction->GetLocations(); 5379 Location obj_loc = locations->InAt(0); 5380 Register obj = obj_loc.AsRegister<Register>(); 5381 Register cls = locations->InAt(1).AsRegister<Register>(); 5382 Location temp_loc = locations->GetTemp(0); 5383 Register temp = temp_loc.AsRegister<Register>(); 5384 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 5385 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); 5386 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); 5387 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value(); 5388 5389 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind(); 5390 bool is_type_check_slow_path_fatal = 5391 (type_check_kind == TypeCheckKind::kExactCheck || 5392 type_check_kind == TypeCheckKind::kAbstractClassCheck || 5393 type_check_kind == TypeCheckKind::kClassHierarchyCheck || 5394 type_check_kind == TypeCheckKind::kArrayObjectCheck) && 5395 !instruction->CanThrowIntoCatchBlock(); 5396 SlowPathCode* type_check_slow_path = 5397 new (GetGraph()->GetArena()) TypeCheckSlowPathARM(instruction, 5398 is_type_check_slow_path_fatal); 5399 codegen_->AddSlowPath(type_check_slow_path); 5400 5401 Label done; 5402 // Avoid null check if we know obj is not null. 5403 if (instruction->MustDoNullCheck()) { 5404 __ CompareAndBranchIfZero(obj, &done); 5405 } 5406 5407 // /* HeapReference<Class> */ temp = obj->klass_ 5408 __ LoadFromOffset(kLoadWord, temp, obj, class_offset); 5409 codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, obj_loc, class_offset); 5410 5411 switch (type_check_kind) { 5412 case TypeCheckKind::kExactCheck: 5413 case TypeCheckKind::kArrayCheck: { 5414 __ cmp(temp, ShifterOperand(cls)); 5415 // Jump to slow path for throwing the exception or doing a 5416 // more involved array check. 5417 __ b(type_check_slow_path->GetEntryLabel(), NE); 5418 break; 5419 } 5420 5421 case TypeCheckKind::kAbstractClassCheck: { 5422 // If the class is abstract, we eagerly fetch the super class of the 5423 // object to avoid doing a comparison we know will fail. 5424 Label loop, compare_classes; 5425 __ Bind(&loop); 5426 Location temp2_loc = 5427 kEmitCompilerReadBarrier ? locations->GetTemp(1) : Location::NoLocation(); 5428 if (kEmitCompilerReadBarrier) { 5429 // Save the value of `temp` into `temp2` before overwriting it 5430 // in the following move operation, as we will need it for the 5431 // read barrier below. 5432 Register temp2 = temp2_loc.AsRegister<Register>(); 5433 __ Mov(temp2, temp); 5434 } 5435 // /* HeapReference<Class> */ temp = temp->super_class_ 5436 __ LoadFromOffset(kLoadWord, temp, temp, super_offset); 5437 codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, temp2_loc, super_offset); 5438 5439 // If the class reference currently in `temp` is not null, jump 5440 // to the `compare_classes` label to compare it with the checked 5441 // class. 5442 __ CompareAndBranchIfNonZero(temp, &compare_classes); 5443 // Otherwise, jump to the slow path to throw the exception. 5444 // 5445 // But before, move back the object's class into `temp` before 5446 // going into the slow path, as it has been overwritten in the 5447 // meantime. 5448 // /* HeapReference<Class> */ temp = obj->klass_ 5449 __ LoadFromOffset(kLoadWord, temp, obj, class_offset); 5450 codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, obj_loc, class_offset); 5451 __ b(type_check_slow_path->GetEntryLabel()); 5452 5453 __ Bind(&compare_classes); 5454 __ cmp(temp, ShifterOperand(cls)); 5455 __ b(&loop, NE); 5456 break; 5457 } 5458 5459 case TypeCheckKind::kClassHierarchyCheck: { 5460 // Walk over the class hierarchy to find a match. 5461 Label loop; 5462 __ Bind(&loop); 5463 __ cmp(temp, ShifterOperand(cls)); 5464 __ b(&done, EQ); 5465 5466 Location temp2_loc = 5467 kEmitCompilerReadBarrier ? locations->GetTemp(1) : Location::NoLocation(); 5468 if (kEmitCompilerReadBarrier) { 5469 // Save the value of `temp` into `temp2` before overwriting it 5470 // in the following move operation, as we will need it for the 5471 // read barrier below. 5472 Register temp2 = temp2_loc.AsRegister<Register>(); 5473 __ Mov(temp2, temp); 5474 } 5475 // /* HeapReference<Class> */ temp = temp->super_class_ 5476 __ LoadFromOffset(kLoadWord, temp, temp, super_offset); 5477 codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, temp2_loc, super_offset); 5478 5479 // If the class reference currently in `temp` is not null, jump 5480 // back at the beginning of the loop. 5481 __ CompareAndBranchIfNonZero(temp, &loop); 5482 // Otherwise, jump to the slow path to throw the exception. 5483 // 5484 // But before, move back the object's class into `temp` before 5485 // going into the slow path, as it has been overwritten in the 5486 // meantime. 5487 // /* HeapReference<Class> */ temp = obj->klass_ 5488 __ LoadFromOffset(kLoadWord, temp, obj, class_offset); 5489 codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, obj_loc, class_offset); 5490 __ b(type_check_slow_path->GetEntryLabel()); 5491 break; 5492 } 5493 5494 case TypeCheckKind::kArrayObjectCheck: { 5495 // Do an exact check. 5496 Label check_non_primitive_component_type; 5497 __ cmp(temp, ShifterOperand(cls)); 5498 __ b(&done, EQ); 5499 5500 // Otherwise, we need to check that the object's class is a non-primitive array. 5501 Location temp2_loc = 5502 kEmitCompilerReadBarrier ? locations->GetTemp(1) : Location::NoLocation(); 5503 if (kEmitCompilerReadBarrier) { 5504 // Save the value of `temp` into `temp2` before overwriting it 5505 // in the following move operation, as we will need it for the 5506 // read barrier below. 5507 Register temp2 = temp2_loc.AsRegister<Register>(); 5508 __ Mov(temp2, temp); 5509 } 5510 // /* HeapReference<Class> */ temp = temp->component_type_ 5511 __ LoadFromOffset(kLoadWord, temp, temp, component_offset); 5512 codegen_->MaybeGenerateReadBarrier( 5513 instruction, temp_loc, temp_loc, temp2_loc, component_offset); 5514 5515 // If the component type is not null (i.e. the object is indeed 5516 // an array), jump to label `check_non_primitive_component_type` 5517 // to further check that this component type is not a primitive 5518 // type. 5519 __ CompareAndBranchIfNonZero(temp, &check_non_primitive_component_type); 5520 // Otherwise, jump to the slow path to throw the exception. 5521 // 5522 // But before, move back the object's class into `temp` before 5523 // going into the slow path, as it has been overwritten in the 5524 // meantime. 5525 // /* HeapReference<Class> */ temp = obj->klass_ 5526 __ LoadFromOffset(kLoadWord, temp, obj, class_offset); 5527 codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, obj_loc, class_offset); 5528 __ b(type_check_slow_path->GetEntryLabel()); 5529 5530 __ Bind(&check_non_primitive_component_type); 5531 __ LoadFromOffset(kLoadUnsignedHalfword, temp, temp, primitive_offset); 5532 static_assert(Primitive::kPrimNot == 0, "Expected 0 for art::Primitive::kPrimNot"); 5533 __ CompareAndBranchIfZero(temp, &done); 5534 // Same comment as above regarding `temp` and the slow path. 5535 // /* HeapReference<Class> */ temp = obj->klass_ 5536 __ LoadFromOffset(kLoadWord, temp, obj, class_offset); 5537 codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, obj_loc, class_offset); 5538 __ b(type_check_slow_path->GetEntryLabel()); 5539 break; 5540 } 5541 5542 case TypeCheckKind::kUnresolvedCheck: 5543 case TypeCheckKind::kInterfaceCheck: 5544 // We always go into the type check slow path for the unresolved & 5545 // interface check cases. 5546 // 5547 // We cannot directly call the CheckCast runtime entry point 5548 // without resorting to a type checking slow path here (i.e. by 5549 // calling InvokeRuntime directly), as it would require to 5550 // assign fixed registers for the inputs of this HInstanceOf 5551 // instruction (following the runtime calling convention), which 5552 // might be cluttered by the potential first read barrier 5553 // emission at the beginning of this method. 5554 __ b(type_check_slow_path->GetEntryLabel()); 5555 break; 5556 } 5557 __ Bind(&done); 5558 5559 __ Bind(type_check_slow_path->GetExitLabel()); 5560} 5561 5562void LocationsBuilderARM::VisitMonitorOperation(HMonitorOperation* instruction) { 5563 LocationSummary* locations = 5564 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); 5565 InvokeRuntimeCallingConvention calling_convention; 5566 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 5567} 5568 5569void InstructionCodeGeneratorARM::VisitMonitorOperation(HMonitorOperation* instruction) { 5570 codegen_->InvokeRuntime(instruction->IsEnter() 5571 ? QUICK_ENTRY_POINT(pLockObject) : QUICK_ENTRY_POINT(pUnlockObject), 5572 instruction, 5573 instruction->GetDexPc(), 5574 nullptr); 5575 if (instruction->IsEnter()) { 5576 CheckEntrypointTypes<kQuickLockObject, void, mirror::Object*>(); 5577 } else { 5578 CheckEntrypointTypes<kQuickUnlockObject, void, mirror::Object*>(); 5579 } 5580} 5581 5582void LocationsBuilderARM::VisitAnd(HAnd* instruction) { HandleBitwiseOperation(instruction, AND); } 5583void LocationsBuilderARM::VisitOr(HOr* instruction) { HandleBitwiseOperation(instruction, ORR); } 5584void LocationsBuilderARM::VisitXor(HXor* instruction) { HandleBitwiseOperation(instruction, EOR); } 5585 5586void LocationsBuilderARM::HandleBitwiseOperation(HBinaryOperation* instruction, Opcode opcode) { 5587 LocationSummary* locations = 5588 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 5589 DCHECK(instruction->GetResultType() == Primitive::kPrimInt 5590 || instruction->GetResultType() == Primitive::kPrimLong); 5591 // Note: GVN reorders commutative operations to have the constant on the right hand side. 5592 locations->SetInAt(0, Location::RequiresRegister()); 5593 locations->SetInAt(1, ArmEncodableConstantOrRegister(instruction->InputAt(1), opcode)); 5594 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 5595} 5596 5597void InstructionCodeGeneratorARM::VisitAnd(HAnd* instruction) { 5598 HandleBitwiseOperation(instruction); 5599} 5600 5601void InstructionCodeGeneratorARM::VisitOr(HOr* instruction) { 5602 HandleBitwiseOperation(instruction); 5603} 5604 5605void InstructionCodeGeneratorARM::VisitXor(HXor* instruction) { 5606 HandleBitwiseOperation(instruction); 5607} 5608 5609void InstructionCodeGeneratorARM::GenerateAndConst(Register out, Register first, uint32_t value) { 5610 // Optimize special cases for individual halfs of `and-long` (`and` is simplified earlier). 5611 if (value == 0xffffffffu) { 5612 if (out != first) { 5613 __ mov(out, ShifterOperand(first)); 5614 } 5615 return; 5616 } 5617 if (value == 0u) { 5618 __ mov(out, ShifterOperand(0)); 5619 return; 5620 } 5621 ShifterOperand so; 5622 if (__ ShifterOperandCanHold(kNoRegister, kNoRegister, AND, value, &so)) { 5623 __ and_(out, first, so); 5624 } else { 5625 DCHECK(__ ShifterOperandCanHold(kNoRegister, kNoRegister, BIC, ~value, &so)); 5626 __ bic(out, first, ShifterOperand(~value)); 5627 } 5628} 5629 5630void InstructionCodeGeneratorARM::GenerateOrrConst(Register out, Register first, uint32_t value) { 5631 // Optimize special cases for individual halfs of `or-long` (`or` is simplified earlier). 5632 if (value == 0u) { 5633 if (out != first) { 5634 __ mov(out, ShifterOperand(first)); 5635 } 5636 return; 5637 } 5638 if (value == 0xffffffffu) { 5639 __ mvn(out, ShifterOperand(0)); 5640 return; 5641 } 5642 ShifterOperand so; 5643 if (__ ShifterOperandCanHold(kNoRegister, kNoRegister, ORR, value, &so)) { 5644 __ orr(out, first, so); 5645 } else { 5646 DCHECK(__ ShifterOperandCanHold(kNoRegister, kNoRegister, ORN, ~value, &so)); 5647 __ orn(out, first, ShifterOperand(~value)); 5648 } 5649} 5650 5651void InstructionCodeGeneratorARM::GenerateEorConst(Register out, Register first, uint32_t value) { 5652 // Optimize special case for individual halfs of `xor-long` (`xor` is simplified earlier). 5653 if (value == 0u) { 5654 if (out != first) { 5655 __ mov(out, ShifterOperand(first)); 5656 } 5657 return; 5658 } 5659 __ eor(out, first, ShifterOperand(value)); 5660} 5661 5662void InstructionCodeGeneratorARM::HandleBitwiseOperation(HBinaryOperation* instruction) { 5663 LocationSummary* locations = instruction->GetLocations(); 5664 Location first = locations->InAt(0); 5665 Location second = locations->InAt(1); 5666 Location out = locations->Out(); 5667 5668 if (second.IsConstant()) { 5669 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant())); 5670 uint32_t value_low = Low32Bits(value); 5671 if (instruction->GetResultType() == Primitive::kPrimInt) { 5672 Register first_reg = first.AsRegister<Register>(); 5673 Register out_reg = out.AsRegister<Register>(); 5674 if (instruction->IsAnd()) { 5675 GenerateAndConst(out_reg, first_reg, value_low); 5676 } else if (instruction->IsOr()) { 5677 GenerateOrrConst(out_reg, first_reg, value_low); 5678 } else { 5679 DCHECK(instruction->IsXor()); 5680 GenerateEorConst(out_reg, first_reg, value_low); 5681 } 5682 } else { 5683 DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong); 5684 uint32_t value_high = High32Bits(value); 5685 Register first_low = first.AsRegisterPairLow<Register>(); 5686 Register first_high = first.AsRegisterPairHigh<Register>(); 5687 Register out_low = out.AsRegisterPairLow<Register>(); 5688 Register out_high = out.AsRegisterPairHigh<Register>(); 5689 if (instruction->IsAnd()) { 5690 GenerateAndConst(out_low, first_low, value_low); 5691 GenerateAndConst(out_high, first_high, value_high); 5692 } else if (instruction->IsOr()) { 5693 GenerateOrrConst(out_low, first_low, value_low); 5694 GenerateOrrConst(out_high, first_high, value_high); 5695 } else { 5696 DCHECK(instruction->IsXor()); 5697 GenerateEorConst(out_low, first_low, value_low); 5698 GenerateEorConst(out_high, first_high, value_high); 5699 } 5700 } 5701 return; 5702 } 5703 5704 if (instruction->GetResultType() == Primitive::kPrimInt) { 5705 Register first_reg = first.AsRegister<Register>(); 5706 ShifterOperand second_reg(second.AsRegister<Register>()); 5707 Register out_reg = out.AsRegister<Register>(); 5708 if (instruction->IsAnd()) { 5709 __ and_(out_reg, first_reg, second_reg); 5710 } else if (instruction->IsOr()) { 5711 __ orr(out_reg, first_reg, second_reg); 5712 } else { 5713 DCHECK(instruction->IsXor()); 5714 __ eor(out_reg, first_reg, second_reg); 5715 } 5716 } else { 5717 DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong); 5718 Register first_low = first.AsRegisterPairLow<Register>(); 5719 Register first_high = first.AsRegisterPairHigh<Register>(); 5720 ShifterOperand second_low(second.AsRegisterPairLow<Register>()); 5721 ShifterOperand second_high(second.AsRegisterPairHigh<Register>()); 5722 Register out_low = out.AsRegisterPairLow<Register>(); 5723 Register out_high = out.AsRegisterPairHigh<Register>(); 5724 if (instruction->IsAnd()) { 5725 __ and_(out_low, first_low, second_low); 5726 __ and_(out_high, first_high, second_high); 5727 } else if (instruction->IsOr()) { 5728 __ orr(out_low, first_low, second_low); 5729 __ orr(out_high, first_high, second_high); 5730 } else { 5731 DCHECK(instruction->IsXor()); 5732 __ eor(out_low, first_low, second_low); 5733 __ eor(out_high, first_high, second_high); 5734 } 5735 } 5736} 5737 5738void CodeGeneratorARM::GenerateReadBarrier(HInstruction* instruction, 5739 Location out, 5740 Location ref, 5741 Location obj, 5742 uint32_t offset, 5743 Location index) { 5744 DCHECK(kEmitCompilerReadBarrier); 5745 5746 // If heap poisoning is enabled, the unpoisoning of the loaded 5747 // reference will be carried out by the runtime within the slow 5748 // path. 5749 // 5750 // Note that `ref` currently does not get unpoisoned (when heap 5751 // poisoning is enabled), which is alright as the `ref` argument is 5752 // not used by the artReadBarrierSlow entry point. 5753 // 5754 // TODO: Unpoison `ref` when it is used by artReadBarrierSlow. 5755 SlowPathCode* slow_path = new (GetGraph()->GetArena()) 5756 ReadBarrierForHeapReferenceSlowPathARM(instruction, out, ref, obj, offset, index); 5757 AddSlowPath(slow_path); 5758 5759 // TODO: When read barrier has a fast path, add it here. 5760 /* Currently the read barrier call is inserted after the original load. 5761 * However, if we have a fast path, we need to perform the load of obj.LockWord *before* the 5762 * original load. This load-load ordering is required by the read barrier. 5763 * The fast path/slow path (for Baker's algorithm) should look like: 5764 * 5765 * bool isGray = obj.LockWord & kReadBarrierMask; 5766 * lfence; // load fence or artificial data dependence to prevent load-load reordering 5767 * ref = obj.field; // this is the original load 5768 * if (isGray) { 5769 * ref = Mark(ref); // ideally the slow path just does Mark(ref) 5770 * } 5771 */ 5772 5773 __ b(slow_path->GetEntryLabel()); 5774 __ Bind(slow_path->GetExitLabel()); 5775} 5776 5777void CodeGeneratorARM::MaybeGenerateReadBarrier(HInstruction* instruction, 5778 Location out, 5779 Location ref, 5780 Location obj, 5781 uint32_t offset, 5782 Location index) { 5783 if (kEmitCompilerReadBarrier) { 5784 // If heap poisoning is enabled, unpoisoning will be taken care of 5785 // by the runtime within the slow path. 5786 GenerateReadBarrier(instruction, out, ref, obj, offset, index); 5787 } else if (kPoisonHeapReferences) { 5788 __ UnpoisonHeapReference(out.AsRegister<Register>()); 5789 } 5790} 5791 5792void CodeGeneratorARM::GenerateReadBarrierForRoot(HInstruction* instruction, 5793 Location out, 5794 Location root) { 5795 DCHECK(kEmitCompilerReadBarrier); 5796 5797 // Note that GC roots are not affected by heap poisoning, so we do 5798 // not need to do anything special for this here. 5799 SlowPathCode* slow_path = 5800 new (GetGraph()->GetArena()) ReadBarrierForRootSlowPathARM(instruction, out, root); 5801 AddSlowPath(slow_path); 5802 5803 // TODO: Implement a fast path for ReadBarrierForRoot, performing 5804 // the following operation (for Baker's algorithm): 5805 // 5806 // if (thread.tls32_.is_gc_marking) { 5807 // root = Mark(root); 5808 // } 5809 5810 __ b(slow_path->GetEntryLabel()); 5811 __ Bind(slow_path->GetExitLabel()); 5812} 5813 5814HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARM::GetSupportedInvokeStaticOrDirectDispatch( 5815 const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info, 5816 MethodReference target_method) { 5817 if (desired_dispatch_info.method_load_kind == 5818 HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative) { 5819 // TODO: Implement this type. For the moment, we fall back to kDexCacheViaMethod. 5820 return HInvokeStaticOrDirect::DispatchInfo { 5821 HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod, 5822 HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod, 5823 0u, 5824 0u 5825 }; 5826 } 5827 if (desired_dispatch_info.code_ptr_location == 5828 HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative) { 5829 const DexFile& outer_dex_file = GetGraph()->GetDexFile(); 5830 if (&outer_dex_file != target_method.dex_file) { 5831 // Calls across dex files are more likely to exceed the available BL range, 5832 // so use absolute patch with fixup if available and kCallArtMethod otherwise. 5833 HInvokeStaticOrDirect::CodePtrLocation code_ptr_location = 5834 (desired_dispatch_info.method_load_kind == 5835 HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup) 5836 ? HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup 5837 : HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod; 5838 return HInvokeStaticOrDirect::DispatchInfo { 5839 desired_dispatch_info.method_load_kind, 5840 code_ptr_location, 5841 desired_dispatch_info.method_load_data, 5842 0u 5843 }; 5844 } 5845 } 5846 return desired_dispatch_info; 5847} 5848 5849void CodeGeneratorARM::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) { 5850 // For better instruction scheduling we load the direct code pointer before the method pointer. 5851 switch (invoke->GetCodePtrLocation()) { 5852 case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup: 5853 // LR = code address from literal pool with link-time patch. 5854 __ LoadLiteral(LR, DeduplicateMethodCodeLiteral(invoke->GetTargetMethod())); 5855 break; 5856 case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect: 5857 // LR = invoke->GetDirectCodePtr(); 5858 __ LoadImmediate(LR, invoke->GetDirectCodePtr()); 5859 break; 5860 default: 5861 break; 5862 } 5863 5864 Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp. 5865 switch (invoke->GetMethodLoadKind()) { 5866 case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: 5867 // temp = thread->string_init_entrypoint 5868 __ LoadFromOffset(kLoadWord, temp.AsRegister<Register>(), TR, invoke->GetStringInitOffset()); 5869 break; 5870 case HInvokeStaticOrDirect::MethodLoadKind::kRecursive: 5871 callee_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex()); 5872 break; 5873 case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress: 5874 __ LoadImmediate(temp.AsRegister<Register>(), invoke->GetMethodAddress()); 5875 break; 5876 case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup: 5877 __ LoadLiteral(temp.AsRegister<Register>(), 5878 DeduplicateMethodAddressLiteral(invoke->GetTargetMethod())); 5879 break; 5880 case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: 5881 // TODO: Implement this type. 5882 // Currently filtered out by GetSupportedInvokeStaticOrDirectDispatch(). 5883 LOG(FATAL) << "Unsupported"; 5884 UNREACHABLE(); 5885 case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: { 5886 Location current_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex()); 5887 Register method_reg; 5888 Register reg = temp.AsRegister<Register>(); 5889 if (current_method.IsRegister()) { 5890 method_reg = current_method.AsRegister<Register>(); 5891 } else { 5892 DCHECK(invoke->GetLocations()->Intrinsified()); 5893 DCHECK(!current_method.IsValid()); 5894 method_reg = reg; 5895 __ LoadFromOffset(kLoadWord, reg, SP, kCurrentMethodStackOffset); 5896 } 5897 // /* ArtMethod*[] */ temp = temp.ptr_sized_fields_->dex_cache_resolved_methods_; 5898 __ LoadFromOffset(kLoadWord, 5899 reg, 5900 method_reg, 5901 ArtMethod::DexCacheResolvedMethodsOffset(kArmPointerSize).Int32Value()); 5902 // temp = temp[index_in_cache] 5903 uint32_t index_in_cache = invoke->GetTargetMethod().dex_method_index; 5904 __ LoadFromOffset(kLoadWord, reg, reg, CodeGenerator::GetCachePointerOffset(index_in_cache)); 5905 break; 5906 } 5907 } 5908 5909 switch (invoke->GetCodePtrLocation()) { 5910 case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf: 5911 __ bl(GetFrameEntryLabel()); 5912 break; 5913 case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative: 5914 relative_call_patches_.emplace_back(invoke->GetTargetMethod()); 5915 __ BindTrackedLabel(&relative_call_patches_.back().label); 5916 // Arbitrarily branch to the BL itself, override at link time. 5917 __ bl(&relative_call_patches_.back().label); 5918 break; 5919 case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup: 5920 case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect: 5921 // LR prepared above for better instruction scheduling. 5922 // LR() 5923 __ blx(LR); 5924 break; 5925 case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod: 5926 // LR = callee_method->entry_point_from_quick_compiled_code_ 5927 __ LoadFromOffset( 5928 kLoadWord, LR, callee_method.AsRegister<Register>(), 5929 ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmWordSize).Int32Value()); 5930 // LR() 5931 __ blx(LR); 5932 break; 5933 } 5934 5935 DCHECK(!IsLeafMethod()); 5936} 5937 5938void CodeGeneratorARM::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp_location) { 5939 Register temp = temp_location.AsRegister<Register>(); 5940 uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset( 5941 invoke->GetVTableIndex(), kArmPointerSize).Uint32Value(); 5942 LocationSummary* locations = invoke->GetLocations(); 5943 Location receiver = locations->InAt(0); 5944 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 5945 DCHECK(receiver.IsRegister()); 5946 // /* HeapReference<Class> */ temp = receiver->klass_ 5947 __ LoadFromOffset(kLoadWord, temp, receiver.AsRegister<Register>(), class_offset); 5948 MaybeRecordImplicitNullCheck(invoke); 5949 // Instead of simply (possibly) unpoisoning `temp` here, we should 5950 // emit a read barrier for the previous class reference load. 5951 // However this is not required in practice, as this is an 5952 // intermediate/temporary reference and because the current 5953 // concurrent copying collector keeps the from-space memory 5954 // intact/accessible until the end of the marking phase (the 5955 // concurrent copying collector may not in the future). 5956 __ MaybeUnpoisonHeapReference(temp); 5957 // temp = temp->GetMethodAt(method_offset); 5958 uint32_t entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset( 5959 kArmWordSize).Int32Value(); 5960 __ LoadFromOffset(kLoadWord, temp, temp, method_offset); 5961 // LR = temp->GetEntryPoint(); 5962 __ LoadFromOffset(kLoadWord, LR, temp, entry_point); 5963 // LR(); 5964 __ blx(LR); 5965} 5966 5967void CodeGeneratorARM::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) { 5968 DCHECK(linker_patches->empty()); 5969 size_t size = method_patches_.size() + call_patches_.size() + relative_call_patches_.size(); 5970 linker_patches->reserve(size); 5971 for (const auto& entry : method_patches_) { 5972 const MethodReference& target_method = entry.first; 5973 Literal* literal = entry.second; 5974 DCHECK(literal->GetLabel()->IsBound()); 5975 uint32_t literal_offset = literal->GetLabel()->Position(); 5976 linker_patches->push_back(LinkerPatch::MethodPatch(literal_offset, 5977 target_method.dex_file, 5978 target_method.dex_method_index)); 5979 } 5980 for (const auto& entry : call_patches_) { 5981 const MethodReference& target_method = entry.first; 5982 Literal* literal = entry.second; 5983 DCHECK(literal->GetLabel()->IsBound()); 5984 uint32_t literal_offset = literal->GetLabel()->Position(); 5985 linker_patches->push_back(LinkerPatch::CodePatch(literal_offset, 5986 target_method.dex_file, 5987 target_method.dex_method_index)); 5988 } 5989 for (const MethodPatchInfo<Label>& info : relative_call_patches_) { 5990 uint32_t literal_offset = info.label.Position(); 5991 linker_patches->push_back(LinkerPatch::RelativeCodePatch(literal_offset, 5992 info.target_method.dex_file, 5993 info.target_method.dex_method_index)); 5994 } 5995} 5996 5997Literal* CodeGeneratorARM::DeduplicateMethodLiteral(MethodReference target_method, 5998 MethodToLiteralMap* map) { 5999 // Look up the literal for target_method. 6000 auto lb = map->lower_bound(target_method); 6001 if (lb != map->end() && !map->key_comp()(target_method, lb->first)) { 6002 return lb->second; 6003 } 6004 // We don't have a literal for this method yet, insert a new one. 6005 Literal* literal = __ NewLiteral<uint32_t>(0u); 6006 map->PutBefore(lb, target_method, literal); 6007 return literal; 6008} 6009 6010Literal* CodeGeneratorARM::DeduplicateMethodAddressLiteral(MethodReference target_method) { 6011 return DeduplicateMethodLiteral(target_method, &method_patches_); 6012} 6013 6014Literal* CodeGeneratorARM::DeduplicateMethodCodeLiteral(MethodReference target_method) { 6015 return DeduplicateMethodLiteral(target_method, &call_patches_); 6016} 6017 6018void LocationsBuilderARM::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) { 6019 // Nothing to do, this should be removed during prepare for register allocator. 6020 LOG(FATAL) << "Unreachable"; 6021} 6022 6023void InstructionCodeGeneratorARM::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) { 6024 // Nothing to do, this should be removed during prepare for register allocator. 6025 LOG(FATAL) << "Unreachable"; 6026} 6027 6028void LocationsBuilderARM::VisitFakeString(HFakeString* instruction) { 6029 DCHECK(codegen_->IsBaseline()); 6030 LocationSummary* locations = 6031 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 6032 locations->SetOut(Location::ConstantLocation(GetGraph()->GetNullConstant())); 6033} 6034 6035void InstructionCodeGeneratorARM::VisitFakeString(HFakeString* instruction ATTRIBUTE_UNUSED) { 6036 DCHECK(codegen_->IsBaseline()); 6037 // Will be generated at use site. 6038} 6039 6040// Simple implementation of packed switch - generate cascaded compare/jumps. 6041void LocationsBuilderARM::VisitPackedSwitch(HPackedSwitch* switch_instr) { 6042 LocationSummary* locations = 6043 new (GetGraph()->GetArena()) LocationSummary(switch_instr, LocationSummary::kNoCall); 6044 locations->SetInAt(0, Location::RequiresRegister()); 6045 if (switch_instr->GetNumEntries() >= kPackedSwitchJumpTableThreshold && 6046 codegen_->GetAssembler()->IsThumb()) { 6047 locations->AddTemp(Location::RequiresRegister()); // We need a temp for the table base. 6048 if (switch_instr->GetStartValue() != 0) { 6049 locations->AddTemp(Location::RequiresRegister()); // We need a temp for the bias. 6050 } 6051 } 6052} 6053 6054void InstructionCodeGeneratorARM::VisitPackedSwitch(HPackedSwitch* switch_instr) { 6055 int32_t lower_bound = switch_instr->GetStartValue(); 6056 uint32_t num_entries = switch_instr->GetNumEntries(); 6057 LocationSummary* locations = switch_instr->GetLocations(); 6058 Register value_reg = locations->InAt(0).AsRegister<Register>(); 6059 HBasicBlock* default_block = switch_instr->GetDefaultBlock(); 6060 6061 if (num_entries < kPackedSwitchJumpTableThreshold || !codegen_->GetAssembler()->IsThumb()) { 6062 // Create a series of compare/jumps. 6063 const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors(); 6064 for (uint32_t i = 0; i < num_entries; i++) { 6065 GenerateCompareWithImmediate(value_reg, lower_bound + i); 6066 __ b(codegen_->GetLabelOf(successors[i]), EQ); 6067 } 6068 6069 // And the default for any other value. 6070 if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) { 6071 __ b(codegen_->GetLabelOf(default_block)); 6072 } 6073 } else { 6074 // Create a table lookup. 6075 Register temp_reg = locations->GetTemp(0).AsRegister<Register>(); 6076 6077 // Materialize a pointer to the switch table 6078 std::vector<Label*> labels(num_entries); 6079 const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors(); 6080 for (uint32_t i = 0; i < num_entries; i++) { 6081 labels[i] = codegen_->GetLabelOf(successors[i]); 6082 } 6083 JumpTable* table = __ CreateJumpTable(std::move(labels), temp_reg); 6084 6085 // Remove the bias. 6086 Register key_reg; 6087 if (lower_bound != 0) { 6088 key_reg = locations->GetTemp(1).AsRegister<Register>(); 6089 __ AddConstant(key_reg, value_reg, -lower_bound); 6090 } else { 6091 key_reg = value_reg; 6092 } 6093 6094 // Check whether the value is in the table, jump to default block if not. 6095 __ CmpConstant(key_reg, num_entries - 1); 6096 __ b(codegen_->GetLabelOf(default_block), Condition::HI); 6097 6098 // Load the displacement from the table. 6099 __ ldr(temp_reg, Address(temp_reg, key_reg, Shift::LSL, 2)); 6100 6101 // Dispatch is a direct add to the PC (for Thumb2). 6102 __ EmitJumpTableDispatch(table, temp_reg); 6103 } 6104} 6105 6106void CodeGeneratorARM::MoveFromReturnRegister(Location trg, Primitive::Type type) { 6107 if (!trg.IsValid()) { 6108 DCHECK(type == Primitive::kPrimVoid); 6109 return; 6110 } 6111 6112 DCHECK_NE(type, Primitive::kPrimVoid); 6113 6114 Location return_loc = InvokeDexCallingConventionVisitorARM().GetReturnLocation(type); 6115 if (return_loc.Equals(trg)) { 6116 return; 6117 } 6118 6119 // TODO: Consider pairs in the parallel move resolver, then this could be nicely merged 6120 // with the last branch. 6121 if (type == Primitive::kPrimLong) { 6122 HParallelMove parallel_move(GetGraph()->GetArena()); 6123 parallel_move.AddMove(return_loc.ToLow(), trg.ToLow(), Primitive::kPrimInt, nullptr); 6124 parallel_move.AddMove(return_loc.ToHigh(), trg.ToHigh(), Primitive::kPrimInt, nullptr); 6125 GetMoveResolver()->EmitNativeCode(¶llel_move); 6126 } else if (type == Primitive::kPrimDouble) { 6127 HParallelMove parallel_move(GetGraph()->GetArena()); 6128 parallel_move.AddMove(return_loc.ToLow(), trg.ToLow(), Primitive::kPrimFloat, nullptr); 6129 parallel_move.AddMove(return_loc.ToHigh(), trg.ToHigh(), Primitive::kPrimFloat, nullptr); 6130 GetMoveResolver()->EmitNativeCode(¶llel_move); 6131 } else { 6132 // Let the parallel move resolver take care of all of this. 6133 HParallelMove parallel_move(GetGraph()->GetArena()); 6134 parallel_move.AddMove(return_loc, trg, type, nullptr); 6135 GetMoveResolver()->EmitNativeCode(¶llel_move); 6136 } 6137} 6138 6139#undef __ 6140#undef QUICK_ENTRY_POINT 6141 6142} // namespace arm 6143} // namespace art 6144