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