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