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