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