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