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