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