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