code_generator_arm.cc revision 88f288e3564d79d87c0cd8bb831ec5a791ba4861
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 uint32_t method_offset = mirror::Class::EmbeddedImTableEntryOffset( 1893 invoke->GetImtIndex() % mirror::Class::kImtSize, kArmPointerSize).Uint32Value(); 1894 Location receiver = locations->InAt(0); 1895 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 1896 1897 // Set the hidden argument. This is safe to do this here, as R12 1898 // won't be modified thereafter, before the `blx` (call) instruction. 1899 DCHECK_EQ(R12, hidden_reg); 1900 __ LoadImmediate(hidden_reg, invoke->GetDexMethodIndex()); 1901 1902 if (receiver.IsStackSlot()) { 1903 __ LoadFromOffset(kLoadWord, temp, SP, receiver.GetStackIndex()); 1904 // /* HeapReference<Class> */ temp = temp->klass_ 1905 __ LoadFromOffset(kLoadWord, temp, temp, class_offset); 1906 } else { 1907 // /* HeapReference<Class> */ temp = receiver->klass_ 1908 __ LoadFromOffset(kLoadWord, temp, receiver.AsRegister<Register>(), class_offset); 1909 } 1910 codegen_->MaybeRecordImplicitNullCheck(invoke); 1911 // Instead of simply (possibly) unpoisoning `temp` here, we should 1912 // emit a read barrier for the previous class reference load. 1913 // However this is not required in practice, as this is an 1914 // intermediate/temporary reference and because the current 1915 // concurrent copying collector keeps the from-space memory 1916 // intact/accessible until the end of the marking phase (the 1917 // concurrent copying collector may not in the future). 1918 __ MaybeUnpoisonHeapReference(temp); 1919 // temp = temp->GetImtEntryAt(method_offset); 1920 uint32_t entry_point = 1921 ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmWordSize).Int32Value(); 1922 __ LoadFromOffset(kLoadWord, temp, temp, method_offset); 1923 // LR = temp->GetEntryPoint(); 1924 __ LoadFromOffset(kLoadWord, LR, temp, entry_point); 1925 // LR(); 1926 __ blx(LR); 1927 DCHECK(!codegen_->IsLeafMethod()); 1928 codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); 1929} 1930 1931void LocationsBuilderARM::VisitNeg(HNeg* neg) { 1932 LocationSummary* locations = 1933 new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall); 1934 switch (neg->GetResultType()) { 1935 case Primitive::kPrimInt: { 1936 locations->SetInAt(0, Location::RequiresRegister()); 1937 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 1938 break; 1939 } 1940 case Primitive::kPrimLong: { 1941 locations->SetInAt(0, Location::RequiresRegister()); 1942 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 1943 break; 1944 } 1945 1946 case Primitive::kPrimFloat: 1947 case Primitive::kPrimDouble: 1948 locations->SetInAt(0, Location::RequiresFpuRegister()); 1949 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 1950 break; 1951 1952 default: 1953 LOG(FATAL) << "Unexpected neg type " << neg->GetResultType(); 1954 } 1955} 1956 1957void InstructionCodeGeneratorARM::VisitNeg(HNeg* neg) { 1958 LocationSummary* locations = neg->GetLocations(); 1959 Location out = locations->Out(); 1960 Location in = locations->InAt(0); 1961 switch (neg->GetResultType()) { 1962 case Primitive::kPrimInt: 1963 DCHECK(in.IsRegister()); 1964 __ rsb(out.AsRegister<Register>(), in.AsRegister<Register>(), ShifterOperand(0)); 1965 break; 1966 1967 case Primitive::kPrimLong: 1968 DCHECK(in.IsRegisterPair()); 1969 // out.lo = 0 - in.lo (and update the carry/borrow (C) flag) 1970 __ rsbs(out.AsRegisterPairLow<Register>(), 1971 in.AsRegisterPairLow<Register>(), 1972 ShifterOperand(0)); 1973 // We cannot emit an RSC (Reverse Subtract with Carry) 1974 // instruction here, as it does not exist in the Thumb-2 1975 // instruction set. We use the following approach 1976 // using SBC and SUB instead. 1977 // 1978 // out.hi = -C 1979 __ sbc(out.AsRegisterPairHigh<Register>(), 1980 out.AsRegisterPairHigh<Register>(), 1981 ShifterOperand(out.AsRegisterPairHigh<Register>())); 1982 // out.hi = out.hi - in.hi 1983 __ sub(out.AsRegisterPairHigh<Register>(), 1984 out.AsRegisterPairHigh<Register>(), 1985 ShifterOperand(in.AsRegisterPairHigh<Register>())); 1986 break; 1987 1988 case Primitive::kPrimFloat: 1989 DCHECK(in.IsFpuRegister()); 1990 __ vnegs(out.AsFpuRegister<SRegister>(), in.AsFpuRegister<SRegister>()); 1991 break; 1992 1993 case Primitive::kPrimDouble: 1994 DCHECK(in.IsFpuRegisterPair()); 1995 __ vnegd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), 1996 FromLowSToD(in.AsFpuRegisterPairLow<SRegister>())); 1997 break; 1998 1999 default: 2000 LOG(FATAL) << "Unexpected neg type " << neg->GetResultType(); 2001 } 2002} 2003 2004void LocationsBuilderARM::VisitTypeConversion(HTypeConversion* conversion) { 2005 Primitive::Type result_type = conversion->GetResultType(); 2006 Primitive::Type input_type = conversion->GetInputType(); 2007 DCHECK_NE(result_type, input_type); 2008 2009 // The float-to-long, double-to-long and long-to-float type conversions 2010 // rely on a call to the runtime. 2011 LocationSummary::CallKind call_kind = 2012 (((input_type == Primitive::kPrimFloat || input_type == Primitive::kPrimDouble) 2013 && result_type == Primitive::kPrimLong) 2014 || (input_type == Primitive::kPrimLong && result_type == Primitive::kPrimFloat)) 2015 ? LocationSummary::kCall 2016 : LocationSummary::kNoCall; 2017 LocationSummary* locations = 2018 new (GetGraph()->GetArena()) LocationSummary(conversion, call_kind); 2019 2020 // The Java language does not allow treating boolean as an integral type but 2021 // our bit representation makes it safe. 2022 2023 switch (result_type) { 2024 case Primitive::kPrimByte: 2025 switch (input_type) { 2026 case Primitive::kPrimLong: 2027 // Type conversion from long to byte is a result of code transformations. 2028 case Primitive::kPrimBoolean: 2029 // Boolean input is a result of code transformations. 2030 case Primitive::kPrimShort: 2031 case Primitive::kPrimInt: 2032 case Primitive::kPrimChar: 2033 // Processing a Dex `int-to-byte' instruction. 2034 locations->SetInAt(0, Location::RequiresRegister()); 2035 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2036 break; 2037 2038 default: 2039 LOG(FATAL) << "Unexpected type conversion from " << input_type 2040 << " to " << result_type; 2041 } 2042 break; 2043 2044 case Primitive::kPrimShort: 2045 switch (input_type) { 2046 case Primitive::kPrimLong: 2047 // Type conversion from long to short is a result of code transformations. 2048 case Primitive::kPrimBoolean: 2049 // Boolean input is a result of code transformations. 2050 case Primitive::kPrimByte: 2051 case Primitive::kPrimInt: 2052 case Primitive::kPrimChar: 2053 // Processing a Dex `int-to-short' instruction. 2054 locations->SetInAt(0, Location::RequiresRegister()); 2055 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2056 break; 2057 2058 default: 2059 LOG(FATAL) << "Unexpected type conversion from " << input_type 2060 << " to " << result_type; 2061 } 2062 break; 2063 2064 case Primitive::kPrimInt: 2065 switch (input_type) { 2066 case Primitive::kPrimLong: 2067 // Processing a Dex `long-to-int' instruction. 2068 locations->SetInAt(0, Location::Any()); 2069 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2070 break; 2071 2072 case Primitive::kPrimFloat: 2073 // Processing a Dex `float-to-int' instruction. 2074 locations->SetInAt(0, Location::RequiresFpuRegister()); 2075 locations->SetOut(Location::RequiresRegister()); 2076 locations->AddTemp(Location::RequiresFpuRegister()); 2077 break; 2078 2079 case Primitive::kPrimDouble: 2080 // Processing a Dex `double-to-int' instruction. 2081 locations->SetInAt(0, Location::RequiresFpuRegister()); 2082 locations->SetOut(Location::RequiresRegister()); 2083 locations->AddTemp(Location::RequiresFpuRegister()); 2084 break; 2085 2086 default: 2087 LOG(FATAL) << "Unexpected type conversion from " << input_type 2088 << " to " << result_type; 2089 } 2090 break; 2091 2092 case Primitive::kPrimLong: 2093 switch (input_type) { 2094 case Primitive::kPrimBoolean: 2095 // Boolean input is a result of code transformations. 2096 case Primitive::kPrimByte: 2097 case Primitive::kPrimShort: 2098 case Primitive::kPrimInt: 2099 case Primitive::kPrimChar: 2100 // Processing a Dex `int-to-long' instruction. 2101 locations->SetInAt(0, Location::RequiresRegister()); 2102 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2103 break; 2104 2105 case Primitive::kPrimFloat: { 2106 // Processing a Dex `float-to-long' instruction. 2107 InvokeRuntimeCallingConvention calling_convention; 2108 locations->SetInAt(0, Location::FpuRegisterLocation( 2109 calling_convention.GetFpuRegisterAt(0))); 2110 locations->SetOut(Location::RegisterPairLocation(R0, R1)); 2111 break; 2112 } 2113 2114 case Primitive::kPrimDouble: { 2115 // Processing a Dex `double-to-long' instruction. 2116 InvokeRuntimeCallingConvention calling_convention; 2117 locations->SetInAt(0, Location::FpuRegisterPairLocation( 2118 calling_convention.GetFpuRegisterAt(0), 2119 calling_convention.GetFpuRegisterAt(1))); 2120 locations->SetOut(Location::RegisterPairLocation(R0, R1)); 2121 break; 2122 } 2123 2124 default: 2125 LOG(FATAL) << "Unexpected type conversion from " << input_type 2126 << " to " << result_type; 2127 } 2128 break; 2129 2130 case Primitive::kPrimChar: 2131 switch (input_type) { 2132 case Primitive::kPrimLong: 2133 // Type conversion from long to char is a result of code transformations. 2134 case Primitive::kPrimBoolean: 2135 // Boolean input is a result of code transformations. 2136 case Primitive::kPrimByte: 2137 case Primitive::kPrimShort: 2138 case Primitive::kPrimInt: 2139 // Processing a Dex `int-to-char' instruction. 2140 locations->SetInAt(0, Location::RequiresRegister()); 2141 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2142 break; 2143 2144 default: 2145 LOG(FATAL) << "Unexpected type conversion from " << input_type 2146 << " to " << result_type; 2147 } 2148 break; 2149 2150 case Primitive::kPrimFloat: 2151 switch (input_type) { 2152 case Primitive::kPrimBoolean: 2153 // Boolean input is a result of code transformations. 2154 case Primitive::kPrimByte: 2155 case Primitive::kPrimShort: 2156 case Primitive::kPrimInt: 2157 case Primitive::kPrimChar: 2158 // Processing a Dex `int-to-float' instruction. 2159 locations->SetInAt(0, Location::RequiresRegister()); 2160 locations->SetOut(Location::RequiresFpuRegister()); 2161 break; 2162 2163 case Primitive::kPrimLong: { 2164 // Processing a Dex `long-to-float' instruction. 2165 InvokeRuntimeCallingConvention calling_convention; 2166 locations->SetInAt(0, Location::RegisterPairLocation( 2167 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1))); 2168 locations->SetOut(Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0))); 2169 break; 2170 } 2171 2172 case Primitive::kPrimDouble: 2173 // Processing a Dex `double-to-float' instruction. 2174 locations->SetInAt(0, Location::RequiresFpuRegister()); 2175 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 2176 break; 2177 2178 default: 2179 LOG(FATAL) << "Unexpected type conversion from " << input_type 2180 << " to " << result_type; 2181 }; 2182 break; 2183 2184 case Primitive::kPrimDouble: 2185 switch (input_type) { 2186 case Primitive::kPrimBoolean: 2187 // Boolean input is a result of code transformations. 2188 case Primitive::kPrimByte: 2189 case Primitive::kPrimShort: 2190 case Primitive::kPrimInt: 2191 case Primitive::kPrimChar: 2192 // Processing a Dex `int-to-double' instruction. 2193 locations->SetInAt(0, Location::RequiresRegister()); 2194 locations->SetOut(Location::RequiresFpuRegister()); 2195 break; 2196 2197 case Primitive::kPrimLong: 2198 // Processing a Dex `long-to-double' instruction. 2199 locations->SetInAt(0, Location::RequiresRegister()); 2200 locations->SetOut(Location::RequiresFpuRegister()); 2201 locations->AddTemp(Location::RequiresFpuRegister()); 2202 locations->AddTemp(Location::RequiresFpuRegister()); 2203 break; 2204 2205 case Primitive::kPrimFloat: 2206 // Processing a Dex `float-to-double' instruction. 2207 locations->SetInAt(0, Location::RequiresFpuRegister()); 2208 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 2209 break; 2210 2211 default: 2212 LOG(FATAL) << "Unexpected type conversion from " << input_type 2213 << " to " << result_type; 2214 }; 2215 break; 2216 2217 default: 2218 LOG(FATAL) << "Unexpected type conversion from " << input_type 2219 << " to " << result_type; 2220 } 2221} 2222 2223void InstructionCodeGeneratorARM::VisitTypeConversion(HTypeConversion* conversion) { 2224 LocationSummary* locations = conversion->GetLocations(); 2225 Location out = locations->Out(); 2226 Location in = locations->InAt(0); 2227 Primitive::Type result_type = conversion->GetResultType(); 2228 Primitive::Type input_type = conversion->GetInputType(); 2229 DCHECK_NE(result_type, input_type); 2230 switch (result_type) { 2231 case Primitive::kPrimByte: 2232 switch (input_type) { 2233 case Primitive::kPrimLong: 2234 // Type conversion from long to byte is a result of code transformations. 2235 __ sbfx(out.AsRegister<Register>(), in.AsRegisterPairLow<Register>(), 0, 8); 2236 break; 2237 case Primitive::kPrimBoolean: 2238 // Boolean input is a result of code transformations. 2239 case Primitive::kPrimShort: 2240 case Primitive::kPrimInt: 2241 case Primitive::kPrimChar: 2242 // Processing a Dex `int-to-byte' instruction. 2243 __ sbfx(out.AsRegister<Register>(), in.AsRegister<Register>(), 0, 8); 2244 break; 2245 2246 default: 2247 LOG(FATAL) << "Unexpected type conversion from " << input_type 2248 << " to " << result_type; 2249 } 2250 break; 2251 2252 case Primitive::kPrimShort: 2253 switch (input_type) { 2254 case Primitive::kPrimLong: 2255 // Type conversion from long to short is a result of code transformations. 2256 __ sbfx(out.AsRegister<Register>(), in.AsRegisterPairLow<Register>(), 0, 16); 2257 break; 2258 case Primitive::kPrimBoolean: 2259 // Boolean input is a result of code transformations. 2260 case Primitive::kPrimByte: 2261 case Primitive::kPrimInt: 2262 case Primitive::kPrimChar: 2263 // Processing a Dex `int-to-short' instruction. 2264 __ sbfx(out.AsRegister<Register>(), in.AsRegister<Register>(), 0, 16); 2265 break; 2266 2267 default: 2268 LOG(FATAL) << "Unexpected type conversion from " << input_type 2269 << " to " << result_type; 2270 } 2271 break; 2272 2273 case Primitive::kPrimInt: 2274 switch (input_type) { 2275 case Primitive::kPrimLong: 2276 // Processing a Dex `long-to-int' instruction. 2277 DCHECK(out.IsRegister()); 2278 if (in.IsRegisterPair()) { 2279 __ Mov(out.AsRegister<Register>(), in.AsRegisterPairLow<Register>()); 2280 } else if (in.IsDoubleStackSlot()) { 2281 __ LoadFromOffset(kLoadWord, out.AsRegister<Register>(), SP, in.GetStackIndex()); 2282 } else { 2283 DCHECK(in.IsConstant()); 2284 DCHECK(in.GetConstant()->IsLongConstant()); 2285 int64_t value = in.GetConstant()->AsLongConstant()->GetValue(); 2286 __ LoadImmediate(out.AsRegister<Register>(), static_cast<int32_t>(value)); 2287 } 2288 break; 2289 2290 case Primitive::kPrimFloat: { 2291 // Processing a Dex `float-to-int' instruction. 2292 SRegister temp = locations->GetTemp(0).AsFpuRegisterPairLow<SRegister>(); 2293 __ vmovs(temp, in.AsFpuRegister<SRegister>()); 2294 __ vcvtis(temp, temp); 2295 __ vmovrs(out.AsRegister<Register>(), temp); 2296 break; 2297 } 2298 2299 case Primitive::kPrimDouble: { 2300 // Processing a Dex `double-to-int' instruction. 2301 SRegister temp_s = locations->GetTemp(0).AsFpuRegisterPairLow<SRegister>(); 2302 DRegister temp_d = FromLowSToD(temp_s); 2303 __ vmovd(temp_d, FromLowSToD(in.AsFpuRegisterPairLow<SRegister>())); 2304 __ vcvtid(temp_s, temp_d); 2305 __ vmovrs(out.AsRegister<Register>(), temp_s); 2306 break; 2307 } 2308 2309 default: 2310 LOG(FATAL) << "Unexpected type conversion from " << input_type 2311 << " to " << result_type; 2312 } 2313 break; 2314 2315 case Primitive::kPrimLong: 2316 switch (input_type) { 2317 case Primitive::kPrimBoolean: 2318 // Boolean input is a result of code transformations. 2319 case Primitive::kPrimByte: 2320 case Primitive::kPrimShort: 2321 case Primitive::kPrimInt: 2322 case Primitive::kPrimChar: 2323 // Processing a Dex `int-to-long' instruction. 2324 DCHECK(out.IsRegisterPair()); 2325 DCHECK(in.IsRegister()); 2326 __ Mov(out.AsRegisterPairLow<Register>(), in.AsRegister<Register>()); 2327 // Sign extension. 2328 __ Asr(out.AsRegisterPairHigh<Register>(), 2329 out.AsRegisterPairLow<Register>(), 2330 31); 2331 break; 2332 2333 case Primitive::kPrimFloat: 2334 // Processing a Dex `float-to-long' instruction. 2335 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pF2l), 2336 conversion, 2337 conversion->GetDexPc(), 2338 nullptr); 2339 CheckEntrypointTypes<kQuickF2l, int64_t, float>(); 2340 break; 2341 2342 case Primitive::kPrimDouble: 2343 // Processing a Dex `double-to-long' instruction. 2344 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pD2l), 2345 conversion, 2346 conversion->GetDexPc(), 2347 nullptr); 2348 CheckEntrypointTypes<kQuickD2l, int64_t, double>(); 2349 break; 2350 2351 default: 2352 LOG(FATAL) << "Unexpected type conversion from " << input_type 2353 << " to " << result_type; 2354 } 2355 break; 2356 2357 case Primitive::kPrimChar: 2358 switch (input_type) { 2359 case Primitive::kPrimLong: 2360 // Type conversion from long to char is a result of code transformations. 2361 __ ubfx(out.AsRegister<Register>(), in.AsRegisterPairLow<Register>(), 0, 16); 2362 break; 2363 case Primitive::kPrimBoolean: 2364 // Boolean input is a result of code transformations. 2365 case Primitive::kPrimByte: 2366 case Primitive::kPrimShort: 2367 case Primitive::kPrimInt: 2368 // Processing a Dex `int-to-char' instruction. 2369 __ ubfx(out.AsRegister<Register>(), in.AsRegister<Register>(), 0, 16); 2370 break; 2371 2372 default: 2373 LOG(FATAL) << "Unexpected type conversion from " << input_type 2374 << " to " << result_type; 2375 } 2376 break; 2377 2378 case Primitive::kPrimFloat: 2379 switch (input_type) { 2380 case Primitive::kPrimBoolean: 2381 // Boolean input is a result of code transformations. 2382 case Primitive::kPrimByte: 2383 case Primitive::kPrimShort: 2384 case Primitive::kPrimInt: 2385 case Primitive::kPrimChar: { 2386 // Processing a Dex `int-to-float' instruction. 2387 __ vmovsr(out.AsFpuRegister<SRegister>(), in.AsRegister<Register>()); 2388 __ vcvtsi(out.AsFpuRegister<SRegister>(), out.AsFpuRegister<SRegister>()); 2389 break; 2390 } 2391 2392 case Primitive::kPrimLong: 2393 // Processing a Dex `long-to-float' instruction. 2394 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pL2f), 2395 conversion, 2396 conversion->GetDexPc(), 2397 nullptr); 2398 CheckEntrypointTypes<kQuickL2f, float, int64_t>(); 2399 break; 2400 2401 case Primitive::kPrimDouble: 2402 // Processing a Dex `double-to-float' instruction. 2403 __ vcvtsd(out.AsFpuRegister<SRegister>(), 2404 FromLowSToD(in.AsFpuRegisterPairLow<SRegister>())); 2405 break; 2406 2407 default: 2408 LOG(FATAL) << "Unexpected type conversion from " << input_type 2409 << " to " << result_type; 2410 }; 2411 break; 2412 2413 case Primitive::kPrimDouble: 2414 switch (input_type) { 2415 case Primitive::kPrimBoolean: 2416 // Boolean input is a result of code transformations. 2417 case Primitive::kPrimByte: 2418 case Primitive::kPrimShort: 2419 case Primitive::kPrimInt: 2420 case Primitive::kPrimChar: { 2421 // Processing a Dex `int-to-double' instruction. 2422 __ vmovsr(out.AsFpuRegisterPairLow<SRegister>(), in.AsRegister<Register>()); 2423 __ vcvtdi(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), 2424 out.AsFpuRegisterPairLow<SRegister>()); 2425 break; 2426 } 2427 2428 case Primitive::kPrimLong: { 2429 // Processing a Dex `long-to-double' instruction. 2430 Register low = in.AsRegisterPairLow<Register>(); 2431 Register high = in.AsRegisterPairHigh<Register>(); 2432 SRegister out_s = out.AsFpuRegisterPairLow<SRegister>(); 2433 DRegister out_d = FromLowSToD(out_s); 2434 SRegister temp_s = locations->GetTemp(0).AsFpuRegisterPairLow<SRegister>(); 2435 DRegister temp_d = FromLowSToD(temp_s); 2436 SRegister constant_s = locations->GetTemp(1).AsFpuRegisterPairLow<SRegister>(); 2437 DRegister constant_d = FromLowSToD(constant_s); 2438 2439 // temp_d = int-to-double(high) 2440 __ vmovsr(temp_s, high); 2441 __ vcvtdi(temp_d, temp_s); 2442 // constant_d = k2Pow32EncodingForDouble 2443 __ LoadDImmediate(constant_d, bit_cast<double, int64_t>(k2Pow32EncodingForDouble)); 2444 // out_d = unsigned-to-double(low) 2445 __ vmovsr(out_s, low); 2446 __ vcvtdu(out_d, out_s); 2447 // out_d += temp_d * constant_d 2448 __ vmlad(out_d, temp_d, constant_d); 2449 break; 2450 } 2451 2452 case Primitive::kPrimFloat: 2453 // Processing a Dex `float-to-double' instruction. 2454 __ vcvtds(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), 2455 in.AsFpuRegister<SRegister>()); 2456 break; 2457 2458 default: 2459 LOG(FATAL) << "Unexpected type conversion from " << input_type 2460 << " to " << result_type; 2461 }; 2462 break; 2463 2464 default: 2465 LOG(FATAL) << "Unexpected type conversion from " << input_type 2466 << " to " << result_type; 2467 } 2468} 2469 2470void LocationsBuilderARM::VisitAdd(HAdd* add) { 2471 LocationSummary* locations = 2472 new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall); 2473 switch (add->GetResultType()) { 2474 case Primitive::kPrimInt: { 2475 locations->SetInAt(0, Location::RequiresRegister()); 2476 locations->SetInAt(1, Location::RegisterOrConstant(add->InputAt(1))); 2477 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2478 break; 2479 } 2480 2481 case Primitive::kPrimLong: { 2482 locations->SetInAt(0, Location::RequiresRegister()); 2483 locations->SetInAt(1, Location::RequiresRegister()); 2484 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2485 break; 2486 } 2487 2488 case Primitive::kPrimFloat: 2489 case Primitive::kPrimDouble: { 2490 locations->SetInAt(0, Location::RequiresFpuRegister()); 2491 locations->SetInAt(1, Location::RequiresFpuRegister()); 2492 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 2493 break; 2494 } 2495 2496 default: 2497 LOG(FATAL) << "Unexpected add type " << add->GetResultType(); 2498 } 2499} 2500 2501void InstructionCodeGeneratorARM::VisitAdd(HAdd* add) { 2502 LocationSummary* locations = add->GetLocations(); 2503 Location out = locations->Out(); 2504 Location first = locations->InAt(0); 2505 Location second = locations->InAt(1); 2506 switch (add->GetResultType()) { 2507 case Primitive::kPrimInt: 2508 if (second.IsRegister()) { 2509 __ add(out.AsRegister<Register>(), 2510 first.AsRegister<Register>(), 2511 ShifterOperand(second.AsRegister<Register>())); 2512 } else { 2513 __ AddConstant(out.AsRegister<Register>(), 2514 first.AsRegister<Register>(), 2515 second.GetConstant()->AsIntConstant()->GetValue()); 2516 } 2517 break; 2518 2519 case Primitive::kPrimLong: { 2520 DCHECK(second.IsRegisterPair()); 2521 __ adds(out.AsRegisterPairLow<Register>(), 2522 first.AsRegisterPairLow<Register>(), 2523 ShifterOperand(second.AsRegisterPairLow<Register>())); 2524 __ adc(out.AsRegisterPairHigh<Register>(), 2525 first.AsRegisterPairHigh<Register>(), 2526 ShifterOperand(second.AsRegisterPairHigh<Register>())); 2527 break; 2528 } 2529 2530 case Primitive::kPrimFloat: 2531 __ vadds(out.AsFpuRegister<SRegister>(), 2532 first.AsFpuRegister<SRegister>(), 2533 second.AsFpuRegister<SRegister>()); 2534 break; 2535 2536 case Primitive::kPrimDouble: 2537 __ vaddd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), 2538 FromLowSToD(first.AsFpuRegisterPairLow<SRegister>()), 2539 FromLowSToD(second.AsFpuRegisterPairLow<SRegister>())); 2540 break; 2541 2542 default: 2543 LOG(FATAL) << "Unexpected add type " << add->GetResultType(); 2544 } 2545} 2546 2547void LocationsBuilderARM::VisitSub(HSub* sub) { 2548 LocationSummary* locations = 2549 new (GetGraph()->GetArena()) LocationSummary(sub, LocationSummary::kNoCall); 2550 switch (sub->GetResultType()) { 2551 case Primitive::kPrimInt: { 2552 locations->SetInAt(0, Location::RequiresRegister()); 2553 locations->SetInAt(1, Location::RegisterOrConstant(sub->InputAt(1))); 2554 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2555 break; 2556 } 2557 2558 case Primitive::kPrimLong: { 2559 locations->SetInAt(0, Location::RequiresRegister()); 2560 locations->SetInAt(1, Location::RequiresRegister()); 2561 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2562 break; 2563 } 2564 case Primitive::kPrimFloat: 2565 case Primitive::kPrimDouble: { 2566 locations->SetInAt(0, Location::RequiresFpuRegister()); 2567 locations->SetInAt(1, Location::RequiresFpuRegister()); 2568 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 2569 break; 2570 } 2571 default: 2572 LOG(FATAL) << "Unexpected sub type " << sub->GetResultType(); 2573 } 2574} 2575 2576void InstructionCodeGeneratorARM::VisitSub(HSub* sub) { 2577 LocationSummary* locations = sub->GetLocations(); 2578 Location out = locations->Out(); 2579 Location first = locations->InAt(0); 2580 Location second = locations->InAt(1); 2581 switch (sub->GetResultType()) { 2582 case Primitive::kPrimInt: { 2583 if (second.IsRegister()) { 2584 __ sub(out.AsRegister<Register>(), 2585 first.AsRegister<Register>(), 2586 ShifterOperand(second.AsRegister<Register>())); 2587 } else { 2588 __ AddConstant(out.AsRegister<Register>(), 2589 first.AsRegister<Register>(), 2590 -second.GetConstant()->AsIntConstant()->GetValue()); 2591 } 2592 break; 2593 } 2594 2595 case Primitive::kPrimLong: { 2596 DCHECK(second.IsRegisterPair()); 2597 __ subs(out.AsRegisterPairLow<Register>(), 2598 first.AsRegisterPairLow<Register>(), 2599 ShifterOperand(second.AsRegisterPairLow<Register>())); 2600 __ sbc(out.AsRegisterPairHigh<Register>(), 2601 first.AsRegisterPairHigh<Register>(), 2602 ShifterOperand(second.AsRegisterPairHigh<Register>())); 2603 break; 2604 } 2605 2606 case Primitive::kPrimFloat: { 2607 __ vsubs(out.AsFpuRegister<SRegister>(), 2608 first.AsFpuRegister<SRegister>(), 2609 second.AsFpuRegister<SRegister>()); 2610 break; 2611 } 2612 2613 case Primitive::kPrimDouble: { 2614 __ vsubd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), 2615 FromLowSToD(first.AsFpuRegisterPairLow<SRegister>()), 2616 FromLowSToD(second.AsFpuRegisterPairLow<SRegister>())); 2617 break; 2618 } 2619 2620 2621 default: 2622 LOG(FATAL) << "Unexpected sub type " << sub->GetResultType(); 2623 } 2624} 2625 2626void LocationsBuilderARM::VisitMul(HMul* mul) { 2627 LocationSummary* locations = 2628 new (GetGraph()->GetArena()) LocationSummary(mul, LocationSummary::kNoCall); 2629 switch (mul->GetResultType()) { 2630 case Primitive::kPrimInt: 2631 case Primitive::kPrimLong: { 2632 locations->SetInAt(0, Location::RequiresRegister()); 2633 locations->SetInAt(1, Location::RequiresRegister()); 2634 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2635 break; 2636 } 2637 2638 case Primitive::kPrimFloat: 2639 case Primitive::kPrimDouble: { 2640 locations->SetInAt(0, Location::RequiresFpuRegister()); 2641 locations->SetInAt(1, Location::RequiresFpuRegister()); 2642 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 2643 break; 2644 } 2645 2646 default: 2647 LOG(FATAL) << "Unexpected mul type " << mul->GetResultType(); 2648 } 2649} 2650 2651void InstructionCodeGeneratorARM::VisitMul(HMul* mul) { 2652 LocationSummary* locations = mul->GetLocations(); 2653 Location out = locations->Out(); 2654 Location first = locations->InAt(0); 2655 Location second = locations->InAt(1); 2656 switch (mul->GetResultType()) { 2657 case Primitive::kPrimInt: { 2658 __ mul(out.AsRegister<Register>(), 2659 first.AsRegister<Register>(), 2660 second.AsRegister<Register>()); 2661 break; 2662 } 2663 case Primitive::kPrimLong: { 2664 Register out_hi = out.AsRegisterPairHigh<Register>(); 2665 Register out_lo = out.AsRegisterPairLow<Register>(); 2666 Register in1_hi = first.AsRegisterPairHigh<Register>(); 2667 Register in1_lo = first.AsRegisterPairLow<Register>(); 2668 Register in2_hi = second.AsRegisterPairHigh<Register>(); 2669 Register in2_lo = second.AsRegisterPairLow<Register>(); 2670 2671 // Extra checks to protect caused by the existence of R1_R2. 2672 // The algorithm is wrong if out.hi is either in1.lo or in2.lo: 2673 // (e.g. in1=r0_r1, in2=r2_r3 and out=r1_r2); 2674 DCHECK_NE(out_hi, in1_lo); 2675 DCHECK_NE(out_hi, in2_lo); 2676 2677 // input: in1 - 64 bits, in2 - 64 bits 2678 // output: out 2679 // formula: out.hi : out.lo = (in1.lo * in2.hi + in1.hi * in2.lo)* 2^32 + in1.lo * in2.lo 2680 // parts: out.hi = in1.lo * in2.hi + in1.hi * in2.lo + (in1.lo * in2.lo)[63:32] 2681 // parts: out.lo = (in1.lo * in2.lo)[31:0] 2682 2683 // IP <- in1.lo * in2.hi 2684 __ mul(IP, in1_lo, in2_hi); 2685 // out.hi <- in1.lo * in2.hi + in1.hi * in2.lo 2686 __ mla(out_hi, in1_hi, in2_lo, IP); 2687 // out.lo <- (in1.lo * in2.lo)[31:0]; 2688 __ umull(out_lo, IP, in1_lo, in2_lo); 2689 // out.hi <- in2.hi * in1.lo + in2.lo * in1.hi + (in1.lo * in2.lo)[63:32] 2690 __ add(out_hi, out_hi, ShifterOperand(IP)); 2691 break; 2692 } 2693 2694 case Primitive::kPrimFloat: { 2695 __ vmuls(out.AsFpuRegister<SRegister>(), 2696 first.AsFpuRegister<SRegister>(), 2697 second.AsFpuRegister<SRegister>()); 2698 break; 2699 } 2700 2701 case Primitive::kPrimDouble: { 2702 __ vmuld(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), 2703 FromLowSToD(first.AsFpuRegisterPairLow<SRegister>()), 2704 FromLowSToD(second.AsFpuRegisterPairLow<SRegister>())); 2705 break; 2706 } 2707 2708 default: 2709 LOG(FATAL) << "Unexpected mul type " << mul->GetResultType(); 2710 } 2711} 2712 2713void InstructionCodeGeneratorARM::DivRemOneOrMinusOne(HBinaryOperation* instruction) { 2714 DCHECK(instruction->IsDiv() || instruction->IsRem()); 2715 DCHECK(instruction->GetResultType() == Primitive::kPrimInt); 2716 2717 LocationSummary* locations = instruction->GetLocations(); 2718 Location second = locations->InAt(1); 2719 DCHECK(second.IsConstant()); 2720 2721 Register out = locations->Out().AsRegister<Register>(); 2722 Register dividend = locations->InAt(0).AsRegister<Register>(); 2723 int32_t imm = second.GetConstant()->AsIntConstant()->GetValue(); 2724 DCHECK(imm == 1 || imm == -1); 2725 2726 if (instruction->IsRem()) { 2727 __ LoadImmediate(out, 0); 2728 } else { 2729 if (imm == 1) { 2730 __ Mov(out, dividend); 2731 } else { 2732 __ rsb(out, dividend, ShifterOperand(0)); 2733 } 2734 } 2735} 2736 2737void InstructionCodeGeneratorARM::DivRemByPowerOfTwo(HBinaryOperation* instruction) { 2738 DCHECK(instruction->IsDiv() || instruction->IsRem()); 2739 DCHECK(instruction->GetResultType() == Primitive::kPrimInt); 2740 2741 LocationSummary* locations = instruction->GetLocations(); 2742 Location second = locations->InAt(1); 2743 DCHECK(second.IsConstant()); 2744 2745 Register out = locations->Out().AsRegister<Register>(); 2746 Register dividend = locations->InAt(0).AsRegister<Register>(); 2747 Register temp = locations->GetTemp(0).AsRegister<Register>(); 2748 int32_t imm = second.GetConstant()->AsIntConstant()->GetValue(); 2749 uint32_t abs_imm = static_cast<uint32_t>(AbsOrMin(imm)); 2750 int ctz_imm = CTZ(abs_imm); 2751 2752 if (ctz_imm == 1) { 2753 __ Lsr(temp, dividend, 32 - ctz_imm); 2754 } else { 2755 __ Asr(temp, dividend, 31); 2756 __ Lsr(temp, temp, 32 - ctz_imm); 2757 } 2758 __ add(out, temp, ShifterOperand(dividend)); 2759 2760 if (instruction->IsDiv()) { 2761 __ Asr(out, out, ctz_imm); 2762 if (imm < 0) { 2763 __ rsb(out, out, ShifterOperand(0)); 2764 } 2765 } else { 2766 __ ubfx(out, out, 0, ctz_imm); 2767 __ sub(out, out, ShifterOperand(temp)); 2768 } 2769} 2770 2771void InstructionCodeGeneratorARM::GenerateDivRemWithAnyConstant(HBinaryOperation* instruction) { 2772 DCHECK(instruction->IsDiv() || instruction->IsRem()); 2773 DCHECK(instruction->GetResultType() == Primitive::kPrimInt); 2774 2775 LocationSummary* locations = instruction->GetLocations(); 2776 Location second = locations->InAt(1); 2777 DCHECK(second.IsConstant()); 2778 2779 Register out = locations->Out().AsRegister<Register>(); 2780 Register dividend = locations->InAt(0).AsRegister<Register>(); 2781 Register temp1 = locations->GetTemp(0).AsRegister<Register>(); 2782 Register temp2 = locations->GetTemp(1).AsRegister<Register>(); 2783 int64_t imm = second.GetConstant()->AsIntConstant()->GetValue(); 2784 2785 int64_t magic; 2786 int shift; 2787 CalculateMagicAndShiftForDivRem(imm, false /* is_long */, &magic, &shift); 2788 2789 __ LoadImmediate(temp1, magic); 2790 __ smull(temp2, temp1, dividend, temp1); 2791 2792 if (imm > 0 && magic < 0) { 2793 __ add(temp1, temp1, ShifterOperand(dividend)); 2794 } else if (imm < 0 && magic > 0) { 2795 __ sub(temp1, temp1, ShifterOperand(dividend)); 2796 } 2797 2798 if (shift != 0) { 2799 __ Asr(temp1, temp1, shift); 2800 } 2801 2802 if (instruction->IsDiv()) { 2803 __ sub(out, temp1, ShifterOperand(temp1, ASR, 31)); 2804 } else { 2805 __ sub(temp1, temp1, ShifterOperand(temp1, ASR, 31)); 2806 // TODO: Strength reduction for mls. 2807 __ LoadImmediate(temp2, imm); 2808 __ mls(out, temp1, temp2, dividend); 2809 } 2810} 2811 2812void InstructionCodeGeneratorARM::GenerateDivRemConstantIntegral(HBinaryOperation* instruction) { 2813 DCHECK(instruction->IsDiv() || instruction->IsRem()); 2814 DCHECK(instruction->GetResultType() == Primitive::kPrimInt); 2815 2816 LocationSummary* locations = instruction->GetLocations(); 2817 Location second = locations->InAt(1); 2818 DCHECK(second.IsConstant()); 2819 2820 int32_t imm = second.GetConstant()->AsIntConstant()->GetValue(); 2821 if (imm == 0) { 2822 // Do not generate anything. DivZeroCheck would prevent any code to be executed. 2823 } else if (imm == 1 || imm == -1) { 2824 DivRemOneOrMinusOne(instruction); 2825 } else if (IsPowerOfTwo(AbsOrMin(imm))) { 2826 DivRemByPowerOfTwo(instruction); 2827 } else { 2828 DCHECK(imm <= -2 || imm >= 2); 2829 GenerateDivRemWithAnyConstant(instruction); 2830 } 2831} 2832 2833void LocationsBuilderARM::VisitDiv(HDiv* div) { 2834 LocationSummary::CallKind call_kind = LocationSummary::kNoCall; 2835 if (div->GetResultType() == Primitive::kPrimLong) { 2836 // pLdiv runtime call. 2837 call_kind = LocationSummary::kCall; 2838 } else if (div->GetResultType() == Primitive::kPrimInt && div->InputAt(1)->IsConstant()) { 2839 // sdiv will be replaced by other instruction sequence. 2840 } else if (div->GetResultType() == Primitive::kPrimInt && 2841 !codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { 2842 // pIdivmod runtime call. 2843 call_kind = LocationSummary::kCall; 2844 } 2845 2846 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(div, call_kind); 2847 2848 switch (div->GetResultType()) { 2849 case Primitive::kPrimInt: { 2850 if (div->InputAt(1)->IsConstant()) { 2851 locations->SetInAt(0, Location::RequiresRegister()); 2852 locations->SetInAt(1, Location::ConstantLocation(div->InputAt(1)->AsConstant())); 2853 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2854 int32_t value = div->InputAt(1)->AsIntConstant()->GetValue(); 2855 if (value == 1 || value == 0 || value == -1) { 2856 // No temp register required. 2857 } else { 2858 locations->AddTemp(Location::RequiresRegister()); 2859 if (!IsPowerOfTwo(AbsOrMin(value))) { 2860 locations->AddTemp(Location::RequiresRegister()); 2861 } 2862 } 2863 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { 2864 locations->SetInAt(0, Location::RequiresRegister()); 2865 locations->SetInAt(1, Location::RequiresRegister()); 2866 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2867 } else { 2868 InvokeRuntimeCallingConvention calling_convention; 2869 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 2870 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); 2871 // Note: divrem will compute both the quotient and the remainder as the pair R0 and R1, but 2872 // we only need the former. 2873 locations->SetOut(Location::RegisterLocation(R0)); 2874 } 2875 break; 2876 } 2877 case Primitive::kPrimLong: { 2878 InvokeRuntimeCallingConvention calling_convention; 2879 locations->SetInAt(0, Location::RegisterPairLocation( 2880 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1))); 2881 locations->SetInAt(1, Location::RegisterPairLocation( 2882 calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3))); 2883 locations->SetOut(Location::RegisterPairLocation(R0, R1)); 2884 break; 2885 } 2886 case Primitive::kPrimFloat: 2887 case Primitive::kPrimDouble: { 2888 locations->SetInAt(0, Location::RequiresFpuRegister()); 2889 locations->SetInAt(1, Location::RequiresFpuRegister()); 2890 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 2891 break; 2892 } 2893 2894 default: 2895 LOG(FATAL) << "Unexpected div type " << div->GetResultType(); 2896 } 2897} 2898 2899void InstructionCodeGeneratorARM::VisitDiv(HDiv* div) { 2900 LocationSummary* locations = div->GetLocations(); 2901 Location out = locations->Out(); 2902 Location first = locations->InAt(0); 2903 Location second = locations->InAt(1); 2904 2905 switch (div->GetResultType()) { 2906 case Primitive::kPrimInt: { 2907 if (second.IsConstant()) { 2908 GenerateDivRemConstantIntegral(div); 2909 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { 2910 __ sdiv(out.AsRegister<Register>(), 2911 first.AsRegister<Register>(), 2912 second.AsRegister<Register>()); 2913 } else { 2914 InvokeRuntimeCallingConvention calling_convention; 2915 DCHECK_EQ(calling_convention.GetRegisterAt(0), first.AsRegister<Register>()); 2916 DCHECK_EQ(calling_convention.GetRegisterAt(1), second.AsRegister<Register>()); 2917 DCHECK_EQ(R0, out.AsRegister<Register>()); 2918 2919 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pIdivmod), div, div->GetDexPc(), nullptr); 2920 CheckEntrypointTypes<kQuickIdivmod, int32_t, int32_t, int32_t>(); 2921 } 2922 break; 2923 } 2924 2925 case Primitive::kPrimLong: { 2926 InvokeRuntimeCallingConvention calling_convention; 2927 DCHECK_EQ(calling_convention.GetRegisterAt(0), first.AsRegisterPairLow<Register>()); 2928 DCHECK_EQ(calling_convention.GetRegisterAt(1), first.AsRegisterPairHigh<Register>()); 2929 DCHECK_EQ(calling_convention.GetRegisterAt(2), second.AsRegisterPairLow<Register>()); 2930 DCHECK_EQ(calling_convention.GetRegisterAt(3), second.AsRegisterPairHigh<Register>()); 2931 DCHECK_EQ(R0, out.AsRegisterPairLow<Register>()); 2932 DCHECK_EQ(R1, out.AsRegisterPairHigh<Register>()); 2933 2934 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pLdiv), div, div->GetDexPc(), nullptr); 2935 CheckEntrypointTypes<kQuickLdiv, int64_t, int64_t, int64_t>(); 2936 break; 2937 } 2938 2939 case Primitive::kPrimFloat: { 2940 __ vdivs(out.AsFpuRegister<SRegister>(), 2941 first.AsFpuRegister<SRegister>(), 2942 second.AsFpuRegister<SRegister>()); 2943 break; 2944 } 2945 2946 case Primitive::kPrimDouble: { 2947 __ vdivd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), 2948 FromLowSToD(first.AsFpuRegisterPairLow<SRegister>()), 2949 FromLowSToD(second.AsFpuRegisterPairLow<SRegister>())); 2950 break; 2951 } 2952 2953 default: 2954 LOG(FATAL) << "Unexpected div type " << div->GetResultType(); 2955 } 2956} 2957 2958void LocationsBuilderARM::VisitRem(HRem* rem) { 2959 Primitive::Type type = rem->GetResultType(); 2960 2961 // Most remainders are implemented in the runtime. 2962 LocationSummary::CallKind call_kind = LocationSummary::kCall; 2963 if (rem->GetResultType() == Primitive::kPrimInt && rem->InputAt(1)->IsConstant()) { 2964 // sdiv will be replaced by other instruction sequence. 2965 call_kind = LocationSummary::kNoCall; 2966 } else if ((rem->GetResultType() == Primitive::kPrimInt) 2967 && codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { 2968 // Have hardware divide instruction for int, do it with three instructions. 2969 call_kind = LocationSummary::kNoCall; 2970 } 2971 2972 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(rem, call_kind); 2973 2974 switch (type) { 2975 case Primitive::kPrimInt: { 2976 if (rem->InputAt(1)->IsConstant()) { 2977 locations->SetInAt(0, Location::RequiresRegister()); 2978 locations->SetInAt(1, Location::ConstantLocation(rem->InputAt(1)->AsConstant())); 2979 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2980 int32_t value = rem->InputAt(1)->AsIntConstant()->GetValue(); 2981 if (value == 1 || value == 0 || value == -1) { 2982 // No temp register required. 2983 } else { 2984 locations->AddTemp(Location::RequiresRegister()); 2985 if (!IsPowerOfTwo(AbsOrMin(value))) { 2986 locations->AddTemp(Location::RequiresRegister()); 2987 } 2988 } 2989 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { 2990 locations->SetInAt(0, Location::RequiresRegister()); 2991 locations->SetInAt(1, Location::RequiresRegister()); 2992 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 2993 locations->AddTemp(Location::RequiresRegister()); 2994 } else { 2995 InvokeRuntimeCallingConvention calling_convention; 2996 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 2997 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); 2998 // Note: divrem will compute both the quotient and the remainder as the pair R0 and R1, but 2999 // we only need the latter. 3000 locations->SetOut(Location::RegisterLocation(R1)); 3001 } 3002 break; 3003 } 3004 case Primitive::kPrimLong: { 3005 InvokeRuntimeCallingConvention calling_convention; 3006 locations->SetInAt(0, Location::RegisterPairLocation( 3007 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1))); 3008 locations->SetInAt(1, Location::RegisterPairLocation( 3009 calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3))); 3010 // The runtime helper puts the output in R2,R3. 3011 locations->SetOut(Location::RegisterPairLocation(R2, R3)); 3012 break; 3013 } 3014 case Primitive::kPrimFloat: { 3015 InvokeRuntimeCallingConvention calling_convention; 3016 locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0))); 3017 locations->SetInAt(1, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(1))); 3018 locations->SetOut(Location::FpuRegisterLocation(S0)); 3019 break; 3020 } 3021 3022 case Primitive::kPrimDouble: { 3023 InvokeRuntimeCallingConvention calling_convention; 3024 locations->SetInAt(0, Location::FpuRegisterPairLocation( 3025 calling_convention.GetFpuRegisterAt(0), calling_convention.GetFpuRegisterAt(1))); 3026 locations->SetInAt(1, Location::FpuRegisterPairLocation( 3027 calling_convention.GetFpuRegisterAt(2), calling_convention.GetFpuRegisterAt(3))); 3028 locations->SetOut(Location::Location::FpuRegisterPairLocation(S0, S1)); 3029 break; 3030 } 3031 3032 default: 3033 LOG(FATAL) << "Unexpected rem type " << type; 3034 } 3035} 3036 3037void InstructionCodeGeneratorARM::VisitRem(HRem* rem) { 3038 LocationSummary* locations = rem->GetLocations(); 3039 Location out = locations->Out(); 3040 Location first = locations->InAt(0); 3041 Location second = locations->InAt(1); 3042 3043 Primitive::Type type = rem->GetResultType(); 3044 switch (type) { 3045 case Primitive::kPrimInt: { 3046 if (second.IsConstant()) { 3047 GenerateDivRemConstantIntegral(rem); 3048 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { 3049 Register reg1 = first.AsRegister<Register>(); 3050 Register reg2 = second.AsRegister<Register>(); 3051 Register temp = locations->GetTemp(0).AsRegister<Register>(); 3052 3053 // temp = reg1 / reg2 (integer division) 3054 // dest = reg1 - temp * reg2 3055 __ sdiv(temp, reg1, reg2); 3056 __ mls(out.AsRegister<Register>(), temp, reg2, reg1); 3057 } else { 3058 InvokeRuntimeCallingConvention calling_convention; 3059 DCHECK_EQ(calling_convention.GetRegisterAt(0), first.AsRegister<Register>()); 3060 DCHECK_EQ(calling_convention.GetRegisterAt(1), second.AsRegister<Register>()); 3061 DCHECK_EQ(R1, out.AsRegister<Register>()); 3062 3063 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pIdivmod), rem, rem->GetDexPc(), nullptr); 3064 CheckEntrypointTypes<kQuickIdivmod, int32_t, int32_t, int32_t>(); 3065 } 3066 break; 3067 } 3068 3069 case Primitive::kPrimLong: { 3070 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pLmod), rem, rem->GetDexPc(), nullptr); 3071 CheckEntrypointTypes<kQuickLmod, int64_t, int64_t, int64_t>(); 3072 break; 3073 } 3074 3075 case Primitive::kPrimFloat: { 3076 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pFmodf), rem, rem->GetDexPc(), nullptr); 3077 CheckEntrypointTypes<kQuickFmodf, float, float, float>(); 3078 break; 3079 } 3080 3081 case Primitive::kPrimDouble: { 3082 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pFmod), rem, rem->GetDexPc(), nullptr); 3083 CheckEntrypointTypes<kQuickFmod, double, double, double>(); 3084 break; 3085 } 3086 3087 default: 3088 LOG(FATAL) << "Unexpected rem type " << type; 3089 } 3090} 3091 3092void LocationsBuilderARM::VisitDivZeroCheck(HDivZeroCheck* instruction) { 3093 LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() 3094 ? LocationSummary::kCallOnSlowPath 3095 : LocationSummary::kNoCall; 3096 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); 3097 locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0))); 3098 if (instruction->HasUses()) { 3099 locations->SetOut(Location::SameAsFirstInput()); 3100 } 3101} 3102 3103void InstructionCodeGeneratorARM::VisitDivZeroCheck(HDivZeroCheck* instruction) { 3104 SlowPathCode* slow_path = new (GetGraph()->GetArena()) DivZeroCheckSlowPathARM(instruction); 3105 codegen_->AddSlowPath(slow_path); 3106 3107 LocationSummary* locations = instruction->GetLocations(); 3108 Location value = locations->InAt(0); 3109 3110 switch (instruction->GetType()) { 3111 case Primitive::kPrimBoolean: 3112 case Primitive::kPrimByte: 3113 case Primitive::kPrimChar: 3114 case Primitive::kPrimShort: 3115 case Primitive::kPrimInt: { 3116 if (value.IsRegister()) { 3117 __ CompareAndBranchIfZero(value.AsRegister<Register>(), slow_path->GetEntryLabel()); 3118 } else { 3119 DCHECK(value.IsConstant()) << value; 3120 if (value.GetConstant()->AsIntConstant()->GetValue() == 0) { 3121 __ b(slow_path->GetEntryLabel()); 3122 } 3123 } 3124 break; 3125 } 3126 case Primitive::kPrimLong: { 3127 if (value.IsRegisterPair()) { 3128 __ orrs(IP, 3129 value.AsRegisterPairLow<Register>(), 3130 ShifterOperand(value.AsRegisterPairHigh<Register>())); 3131 __ b(slow_path->GetEntryLabel(), EQ); 3132 } else { 3133 DCHECK(value.IsConstant()) << value; 3134 if (value.GetConstant()->AsLongConstant()->GetValue() == 0) { 3135 __ b(slow_path->GetEntryLabel()); 3136 } 3137 } 3138 break; 3139 default: 3140 LOG(FATAL) << "Unexpected type for HDivZeroCheck " << instruction->GetType(); 3141 } 3142 } 3143} 3144 3145void InstructionCodeGeneratorARM::HandleIntegerRotate(LocationSummary* locations) { 3146 Register in = locations->InAt(0).AsRegister<Register>(); 3147 Location rhs = locations->InAt(1); 3148 Register out = locations->Out().AsRegister<Register>(); 3149 3150 if (rhs.IsConstant()) { 3151 // Arm32 and Thumb2 assemblers require a rotation on the interval [1,31], 3152 // so map all rotations to a +ve. equivalent in that range. 3153 // (e.g. left *or* right by -2 bits == 30 bits in the same direction.) 3154 uint32_t rot = CodeGenerator::GetInt32ValueOf(rhs.GetConstant()) & 0x1F; 3155 if (rot) { 3156 // Rotate, mapping left rotations to right equivalents if necessary. 3157 // (e.g. left by 2 bits == right by 30.) 3158 __ Ror(out, in, rot); 3159 } else if (out != in) { 3160 __ Mov(out, in); 3161 } 3162 } else { 3163 __ Ror(out, in, rhs.AsRegister<Register>()); 3164 } 3165} 3166 3167// Gain some speed by mapping all Long rotates onto equivalent pairs of Integer 3168// rotates by swapping input regs (effectively rotating by the first 32-bits of 3169// a larger rotation) or flipping direction (thus treating larger right/left 3170// rotations as sub-word sized rotations in the other direction) as appropriate. 3171void InstructionCodeGeneratorARM::HandleLongRotate(LocationSummary* locations) { 3172 Register in_reg_lo = locations->InAt(0).AsRegisterPairLow<Register>(); 3173 Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh<Register>(); 3174 Location rhs = locations->InAt(1); 3175 Register out_reg_lo = locations->Out().AsRegisterPairLow<Register>(); 3176 Register out_reg_hi = locations->Out().AsRegisterPairHigh<Register>(); 3177 3178 if (rhs.IsConstant()) { 3179 uint64_t rot = CodeGenerator::GetInt64ValueOf(rhs.GetConstant()); 3180 // Map all rotations to +ve. equivalents on the interval [0,63]. 3181 rot &= kMaxLongShiftDistance; 3182 // For rotates over a word in size, 'pre-rotate' by 32-bits to keep rotate 3183 // logic below to a simple pair of binary orr. 3184 // (e.g. 34 bits == in_reg swap + 2 bits right.) 3185 if (rot >= kArmBitsPerWord) { 3186 rot -= kArmBitsPerWord; 3187 std::swap(in_reg_hi, in_reg_lo); 3188 } 3189 // Rotate, or mov to out for zero or word size rotations. 3190 if (rot != 0u) { 3191 __ Lsr(out_reg_hi, in_reg_hi, rot); 3192 __ orr(out_reg_hi, out_reg_hi, ShifterOperand(in_reg_lo, arm::LSL, kArmBitsPerWord - rot)); 3193 __ Lsr(out_reg_lo, in_reg_lo, rot); 3194 __ orr(out_reg_lo, out_reg_lo, ShifterOperand(in_reg_hi, arm::LSL, kArmBitsPerWord - rot)); 3195 } else { 3196 __ Mov(out_reg_lo, in_reg_lo); 3197 __ Mov(out_reg_hi, in_reg_hi); 3198 } 3199 } else { 3200 Register shift_right = locations->GetTemp(0).AsRegister<Register>(); 3201 Register shift_left = locations->GetTemp(1).AsRegister<Register>(); 3202 Label end; 3203 Label shift_by_32_plus_shift_right; 3204 3205 __ and_(shift_right, rhs.AsRegister<Register>(), ShifterOperand(0x1F)); 3206 __ Lsrs(shift_left, rhs.AsRegister<Register>(), 6); 3207 __ rsb(shift_left, shift_right, ShifterOperand(kArmBitsPerWord), AL, kCcKeep); 3208 __ b(&shift_by_32_plus_shift_right, CC); 3209 3210 // out_reg_hi = (reg_hi << shift_left) | (reg_lo >> shift_right). 3211 // out_reg_lo = (reg_lo << shift_left) | (reg_hi >> shift_right). 3212 __ Lsl(out_reg_hi, in_reg_hi, shift_left); 3213 __ Lsr(out_reg_lo, in_reg_lo, shift_right); 3214 __ add(out_reg_hi, out_reg_hi, ShifterOperand(out_reg_lo)); 3215 __ Lsl(out_reg_lo, in_reg_lo, shift_left); 3216 __ Lsr(shift_left, in_reg_hi, shift_right); 3217 __ add(out_reg_lo, out_reg_lo, ShifterOperand(shift_left)); 3218 __ b(&end); 3219 3220 __ Bind(&shift_by_32_plus_shift_right); // Shift by 32+shift_right. 3221 // out_reg_hi = (reg_hi >> shift_right) | (reg_lo << shift_left). 3222 // out_reg_lo = (reg_lo >> shift_right) | (reg_hi << shift_left). 3223 __ Lsr(out_reg_hi, in_reg_hi, shift_right); 3224 __ Lsl(out_reg_lo, in_reg_lo, shift_left); 3225 __ add(out_reg_hi, out_reg_hi, ShifterOperand(out_reg_lo)); 3226 __ Lsr(out_reg_lo, in_reg_lo, shift_right); 3227 __ Lsl(shift_right, in_reg_hi, shift_left); 3228 __ add(out_reg_lo, out_reg_lo, ShifterOperand(shift_right)); 3229 3230 __ Bind(&end); 3231 } 3232} 3233 3234void LocationsBuilderARM::VisitRor(HRor* ror) { 3235 LocationSummary* locations = 3236 new (GetGraph()->GetArena()) LocationSummary(ror, LocationSummary::kNoCall); 3237 switch (ror->GetResultType()) { 3238 case Primitive::kPrimInt: { 3239 locations->SetInAt(0, Location::RequiresRegister()); 3240 locations->SetInAt(1, Location::RegisterOrConstant(ror->InputAt(1))); 3241 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 3242 break; 3243 } 3244 case Primitive::kPrimLong: { 3245 locations->SetInAt(0, Location::RequiresRegister()); 3246 if (ror->InputAt(1)->IsConstant()) { 3247 locations->SetInAt(1, Location::ConstantLocation(ror->InputAt(1)->AsConstant())); 3248 } else { 3249 locations->SetInAt(1, Location::RequiresRegister()); 3250 locations->AddTemp(Location::RequiresRegister()); 3251 locations->AddTemp(Location::RequiresRegister()); 3252 } 3253 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 3254 break; 3255 } 3256 default: 3257 LOG(FATAL) << "Unexpected operation type " << ror->GetResultType(); 3258 } 3259} 3260 3261void InstructionCodeGeneratorARM::VisitRor(HRor* ror) { 3262 LocationSummary* locations = ror->GetLocations(); 3263 Primitive::Type type = ror->GetResultType(); 3264 switch (type) { 3265 case Primitive::kPrimInt: { 3266 HandleIntegerRotate(locations); 3267 break; 3268 } 3269 case Primitive::kPrimLong: { 3270 HandleLongRotate(locations); 3271 break; 3272 } 3273 default: 3274 LOG(FATAL) << "Unexpected operation type " << type; 3275 UNREACHABLE(); 3276 } 3277} 3278 3279void LocationsBuilderARM::HandleShift(HBinaryOperation* op) { 3280 DCHECK(op->IsShl() || op->IsShr() || op->IsUShr()); 3281 3282 LocationSummary* locations = 3283 new (GetGraph()->GetArena()) LocationSummary(op, LocationSummary::kNoCall); 3284 3285 switch (op->GetResultType()) { 3286 case Primitive::kPrimInt: { 3287 locations->SetInAt(0, Location::RequiresRegister()); 3288 if (op->InputAt(1)->IsConstant()) { 3289 locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant())); 3290 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 3291 } else { 3292 locations->SetInAt(1, Location::RequiresRegister()); 3293 // Make the output overlap, as it will be used to hold the masked 3294 // second input. 3295 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 3296 } 3297 break; 3298 } 3299 case Primitive::kPrimLong: { 3300 locations->SetInAt(0, Location::RequiresRegister()); 3301 if (op->InputAt(1)->IsConstant()) { 3302 locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant())); 3303 // For simplicity, use kOutputOverlap even though we only require that low registers 3304 // don't clash with high registers which the register allocator currently guarantees. 3305 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 3306 } else { 3307 locations->SetInAt(1, Location::RequiresRegister()); 3308 locations->AddTemp(Location::RequiresRegister()); 3309 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 3310 } 3311 break; 3312 } 3313 default: 3314 LOG(FATAL) << "Unexpected operation type " << op->GetResultType(); 3315 } 3316} 3317 3318void InstructionCodeGeneratorARM::HandleShift(HBinaryOperation* op) { 3319 DCHECK(op->IsShl() || op->IsShr() || op->IsUShr()); 3320 3321 LocationSummary* locations = op->GetLocations(); 3322 Location out = locations->Out(); 3323 Location first = locations->InAt(0); 3324 Location second = locations->InAt(1); 3325 3326 Primitive::Type type = op->GetResultType(); 3327 switch (type) { 3328 case Primitive::kPrimInt: { 3329 Register out_reg = out.AsRegister<Register>(); 3330 Register first_reg = first.AsRegister<Register>(); 3331 if (second.IsRegister()) { 3332 Register second_reg = second.AsRegister<Register>(); 3333 // ARM doesn't mask the shift count so we need to do it ourselves. 3334 __ and_(out_reg, second_reg, ShifterOperand(kMaxIntShiftDistance)); 3335 if (op->IsShl()) { 3336 __ Lsl(out_reg, first_reg, out_reg); 3337 } else if (op->IsShr()) { 3338 __ Asr(out_reg, first_reg, out_reg); 3339 } else { 3340 __ Lsr(out_reg, first_reg, out_reg); 3341 } 3342 } else { 3343 int32_t cst = second.GetConstant()->AsIntConstant()->GetValue(); 3344 uint32_t shift_value = cst & kMaxIntShiftDistance; 3345 if (shift_value == 0) { // ARM does not support shifting with 0 immediate. 3346 __ Mov(out_reg, first_reg); 3347 } else if (op->IsShl()) { 3348 __ Lsl(out_reg, first_reg, shift_value); 3349 } else if (op->IsShr()) { 3350 __ Asr(out_reg, first_reg, shift_value); 3351 } else { 3352 __ Lsr(out_reg, first_reg, shift_value); 3353 } 3354 } 3355 break; 3356 } 3357 case Primitive::kPrimLong: { 3358 Register o_h = out.AsRegisterPairHigh<Register>(); 3359 Register o_l = out.AsRegisterPairLow<Register>(); 3360 3361 Register high = first.AsRegisterPairHigh<Register>(); 3362 Register low = first.AsRegisterPairLow<Register>(); 3363 3364 if (second.IsRegister()) { 3365 Register temp = locations->GetTemp(0).AsRegister<Register>(); 3366 3367 Register second_reg = second.AsRegister<Register>(); 3368 3369 if (op->IsShl()) { 3370 __ and_(o_l, second_reg, ShifterOperand(kMaxLongShiftDistance)); 3371 // Shift the high part 3372 __ Lsl(o_h, high, o_l); 3373 // Shift the low part and `or` what overflew on the high part 3374 __ rsb(temp, o_l, ShifterOperand(kArmBitsPerWord)); 3375 __ Lsr(temp, low, temp); 3376 __ orr(o_h, o_h, ShifterOperand(temp)); 3377 // If the shift is > 32 bits, override the high part 3378 __ subs(temp, o_l, ShifterOperand(kArmBitsPerWord)); 3379 __ it(PL); 3380 __ Lsl(o_h, low, temp, PL); 3381 // Shift the low part 3382 __ Lsl(o_l, low, o_l); 3383 } else if (op->IsShr()) { 3384 __ and_(o_h, second_reg, ShifterOperand(kMaxLongShiftDistance)); 3385 // Shift the low part 3386 __ Lsr(o_l, low, o_h); 3387 // Shift the high part and `or` what underflew on the low part 3388 __ rsb(temp, o_h, ShifterOperand(kArmBitsPerWord)); 3389 __ Lsl(temp, high, temp); 3390 __ orr(o_l, o_l, ShifterOperand(temp)); 3391 // If the shift is > 32 bits, override the low part 3392 __ subs(temp, o_h, ShifterOperand(kArmBitsPerWord)); 3393 __ it(PL); 3394 __ Asr(o_l, high, temp, PL); 3395 // Shift the high part 3396 __ Asr(o_h, high, o_h); 3397 } else { 3398 __ and_(o_h, second_reg, ShifterOperand(kMaxLongShiftDistance)); 3399 // same as Shr except we use `Lsr`s and not `Asr`s 3400 __ Lsr(o_l, low, o_h); 3401 __ rsb(temp, o_h, ShifterOperand(kArmBitsPerWord)); 3402 __ Lsl(temp, high, temp); 3403 __ orr(o_l, o_l, ShifterOperand(temp)); 3404 __ subs(temp, o_h, ShifterOperand(kArmBitsPerWord)); 3405 __ it(PL); 3406 __ Lsr(o_l, high, temp, PL); 3407 __ Lsr(o_h, high, o_h); 3408 } 3409 } else { 3410 // Register allocator doesn't create partial overlap. 3411 DCHECK_NE(o_l, high); 3412 DCHECK_NE(o_h, low); 3413 int32_t cst = second.GetConstant()->AsIntConstant()->GetValue(); 3414 uint32_t shift_value = cst & kMaxLongShiftDistance; 3415 if (shift_value > 32) { 3416 if (op->IsShl()) { 3417 __ Lsl(o_h, low, shift_value - 32); 3418 __ LoadImmediate(o_l, 0); 3419 } else if (op->IsShr()) { 3420 __ Asr(o_l, high, shift_value - 32); 3421 __ Asr(o_h, high, 31); 3422 } else { 3423 __ Lsr(o_l, high, shift_value - 32); 3424 __ LoadImmediate(o_h, 0); 3425 } 3426 } else if (shift_value == 32) { 3427 if (op->IsShl()) { 3428 __ mov(o_h, ShifterOperand(low)); 3429 __ LoadImmediate(o_l, 0); 3430 } else if (op->IsShr()) { 3431 __ mov(o_l, ShifterOperand(high)); 3432 __ Asr(o_h, high, 31); 3433 } else { 3434 __ mov(o_l, ShifterOperand(high)); 3435 __ LoadImmediate(o_h, 0); 3436 } 3437 } else if (shift_value == 1) { 3438 if (op->IsShl()) { 3439 __ Lsls(o_l, low, 1); 3440 __ adc(o_h, high, ShifterOperand(high)); 3441 } else if (op->IsShr()) { 3442 __ Asrs(o_h, high, 1); 3443 __ Rrx(o_l, low); 3444 } else { 3445 __ Lsrs(o_h, high, 1); 3446 __ Rrx(o_l, low); 3447 } 3448 } else { 3449 DCHECK(2 <= shift_value && shift_value < 32) << shift_value; 3450 if (op->IsShl()) { 3451 __ Lsl(o_h, high, shift_value); 3452 __ orr(o_h, o_h, ShifterOperand(low, LSR, 32 - shift_value)); 3453 __ Lsl(o_l, low, shift_value); 3454 } else if (op->IsShr()) { 3455 __ Lsr(o_l, low, shift_value); 3456 __ orr(o_l, o_l, ShifterOperand(high, LSL, 32 - shift_value)); 3457 __ Asr(o_h, high, shift_value); 3458 } else { 3459 __ Lsr(o_l, low, shift_value); 3460 __ orr(o_l, o_l, ShifterOperand(high, LSL, 32 - shift_value)); 3461 __ Lsr(o_h, high, shift_value); 3462 } 3463 } 3464 } 3465 break; 3466 } 3467 default: 3468 LOG(FATAL) << "Unexpected operation type " << type; 3469 UNREACHABLE(); 3470 } 3471} 3472 3473void LocationsBuilderARM::VisitShl(HShl* shl) { 3474 HandleShift(shl); 3475} 3476 3477void InstructionCodeGeneratorARM::VisitShl(HShl* shl) { 3478 HandleShift(shl); 3479} 3480 3481void LocationsBuilderARM::VisitShr(HShr* shr) { 3482 HandleShift(shr); 3483} 3484 3485void InstructionCodeGeneratorARM::VisitShr(HShr* shr) { 3486 HandleShift(shr); 3487} 3488 3489void LocationsBuilderARM::VisitUShr(HUShr* ushr) { 3490 HandleShift(ushr); 3491} 3492 3493void InstructionCodeGeneratorARM::VisitUShr(HUShr* ushr) { 3494 HandleShift(ushr); 3495} 3496 3497void LocationsBuilderARM::VisitNewInstance(HNewInstance* instruction) { 3498 LocationSummary* locations = 3499 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); 3500 if (instruction->IsStringAlloc()) { 3501 locations->AddTemp(Location::RegisterLocation(kMethodRegisterArgument)); 3502 } else { 3503 InvokeRuntimeCallingConvention calling_convention; 3504 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 3505 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); 3506 } 3507 locations->SetOut(Location::RegisterLocation(R0)); 3508} 3509 3510void InstructionCodeGeneratorARM::VisitNewInstance(HNewInstance* instruction) { 3511 // Note: if heap poisoning is enabled, the entry point takes cares 3512 // of poisoning the reference. 3513 if (instruction->IsStringAlloc()) { 3514 // String is allocated through StringFactory. Call NewEmptyString entry point. 3515 Register temp = instruction->GetLocations()->GetTemp(0).AsRegister<Register>(); 3516 MemberOffset code_offset = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmWordSize); 3517 __ LoadFromOffset(kLoadWord, temp, TR, QUICK_ENTRY_POINT(pNewEmptyString)); 3518 __ LoadFromOffset(kLoadWord, LR, temp, code_offset.Int32Value()); 3519 __ blx(LR); 3520 codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); 3521 } else { 3522 codegen_->InvokeRuntime(instruction->GetEntrypoint(), 3523 instruction, 3524 instruction->GetDexPc(), 3525 nullptr); 3526 CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, ArtMethod*>(); 3527 } 3528} 3529 3530void LocationsBuilderARM::VisitNewArray(HNewArray* instruction) { 3531 LocationSummary* locations = 3532 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); 3533 InvokeRuntimeCallingConvention calling_convention; 3534 locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 3535 locations->SetOut(Location::RegisterLocation(R0)); 3536 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); 3537 locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); 3538} 3539 3540void InstructionCodeGeneratorARM::VisitNewArray(HNewArray* instruction) { 3541 InvokeRuntimeCallingConvention calling_convention; 3542 __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex()); 3543 // Note: if heap poisoning is enabled, the entry point takes cares 3544 // of poisoning the reference. 3545 codegen_->InvokeRuntime(instruction->GetEntrypoint(), 3546 instruction, 3547 instruction->GetDexPc(), 3548 nullptr); 3549 CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>(); 3550} 3551 3552void LocationsBuilderARM::VisitParameterValue(HParameterValue* instruction) { 3553 LocationSummary* locations = 3554 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 3555 Location location = parameter_visitor_.GetNextLocation(instruction->GetType()); 3556 if (location.IsStackSlot()) { 3557 location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize()); 3558 } else if (location.IsDoubleStackSlot()) { 3559 location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize()); 3560 } 3561 locations->SetOut(location); 3562} 3563 3564void InstructionCodeGeneratorARM::VisitParameterValue( 3565 HParameterValue* instruction ATTRIBUTE_UNUSED) { 3566 // Nothing to do, the parameter is already at its location. 3567} 3568 3569void LocationsBuilderARM::VisitCurrentMethod(HCurrentMethod* instruction) { 3570 LocationSummary* locations = 3571 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 3572 locations->SetOut(Location::RegisterLocation(kMethodRegisterArgument)); 3573} 3574 3575void InstructionCodeGeneratorARM::VisitCurrentMethod(HCurrentMethod* instruction ATTRIBUTE_UNUSED) { 3576 // Nothing to do, the method is already at its location. 3577} 3578 3579void LocationsBuilderARM::VisitNot(HNot* not_) { 3580 LocationSummary* locations = 3581 new (GetGraph()->GetArena()) LocationSummary(not_, LocationSummary::kNoCall); 3582 locations->SetInAt(0, Location::RequiresRegister()); 3583 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 3584} 3585 3586void InstructionCodeGeneratorARM::VisitNot(HNot* not_) { 3587 LocationSummary* locations = not_->GetLocations(); 3588 Location out = locations->Out(); 3589 Location in = locations->InAt(0); 3590 switch (not_->GetResultType()) { 3591 case Primitive::kPrimInt: 3592 __ mvn(out.AsRegister<Register>(), ShifterOperand(in.AsRegister<Register>())); 3593 break; 3594 3595 case Primitive::kPrimLong: 3596 __ mvn(out.AsRegisterPairLow<Register>(), 3597 ShifterOperand(in.AsRegisterPairLow<Register>())); 3598 __ mvn(out.AsRegisterPairHigh<Register>(), 3599 ShifterOperand(in.AsRegisterPairHigh<Register>())); 3600 break; 3601 3602 default: 3603 LOG(FATAL) << "Unimplemented type for not operation " << not_->GetResultType(); 3604 } 3605} 3606 3607void LocationsBuilderARM::VisitBooleanNot(HBooleanNot* bool_not) { 3608 LocationSummary* locations = 3609 new (GetGraph()->GetArena()) LocationSummary(bool_not, LocationSummary::kNoCall); 3610 locations->SetInAt(0, Location::RequiresRegister()); 3611 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 3612} 3613 3614void InstructionCodeGeneratorARM::VisitBooleanNot(HBooleanNot* bool_not) { 3615 LocationSummary* locations = bool_not->GetLocations(); 3616 Location out = locations->Out(); 3617 Location in = locations->InAt(0); 3618 __ eor(out.AsRegister<Register>(), in.AsRegister<Register>(), ShifterOperand(1)); 3619} 3620 3621void LocationsBuilderARM::VisitCompare(HCompare* compare) { 3622 LocationSummary* locations = 3623 new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall); 3624 switch (compare->InputAt(0)->GetType()) { 3625 case Primitive::kPrimBoolean: 3626 case Primitive::kPrimByte: 3627 case Primitive::kPrimShort: 3628 case Primitive::kPrimChar: 3629 case Primitive::kPrimInt: 3630 case Primitive::kPrimLong: { 3631 locations->SetInAt(0, Location::RequiresRegister()); 3632 locations->SetInAt(1, Location::RequiresRegister()); 3633 // Output overlaps because it is written before doing the low comparison. 3634 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 3635 break; 3636 } 3637 case Primitive::kPrimFloat: 3638 case Primitive::kPrimDouble: { 3639 locations->SetInAt(0, Location::RequiresFpuRegister()); 3640 locations->SetInAt(1, Location::RequiresFpuRegister()); 3641 locations->SetOut(Location::RequiresRegister()); 3642 break; 3643 } 3644 default: 3645 LOG(FATAL) << "Unexpected type for compare operation " << compare->InputAt(0)->GetType(); 3646 } 3647} 3648 3649void InstructionCodeGeneratorARM::VisitCompare(HCompare* compare) { 3650 LocationSummary* locations = compare->GetLocations(); 3651 Register out = locations->Out().AsRegister<Register>(); 3652 Location left = locations->InAt(0); 3653 Location right = locations->InAt(1); 3654 3655 Label less, greater, done; 3656 Primitive::Type type = compare->InputAt(0)->GetType(); 3657 Condition less_cond; 3658 switch (type) { 3659 case Primitive::kPrimBoolean: 3660 case Primitive::kPrimByte: 3661 case Primitive::kPrimShort: 3662 case Primitive::kPrimChar: 3663 case Primitive::kPrimInt: { 3664 __ LoadImmediate(out, 0); 3665 __ cmp(left.AsRegister<Register>(), 3666 ShifterOperand(right.AsRegister<Register>())); // Signed compare. 3667 less_cond = LT; 3668 break; 3669 } 3670 case Primitive::kPrimLong: { 3671 __ cmp(left.AsRegisterPairHigh<Register>(), 3672 ShifterOperand(right.AsRegisterPairHigh<Register>())); // Signed compare. 3673 __ b(&less, LT); 3674 __ b(&greater, GT); 3675 // Do LoadImmediate before the last `cmp`, as LoadImmediate might affect the status flags. 3676 __ LoadImmediate(out, 0); 3677 __ cmp(left.AsRegisterPairLow<Register>(), 3678 ShifterOperand(right.AsRegisterPairLow<Register>())); // Unsigned compare. 3679 less_cond = LO; 3680 break; 3681 } 3682 case Primitive::kPrimFloat: 3683 case Primitive::kPrimDouble: { 3684 __ LoadImmediate(out, 0); 3685 if (type == Primitive::kPrimFloat) { 3686 __ vcmps(left.AsFpuRegister<SRegister>(), right.AsFpuRegister<SRegister>()); 3687 } else { 3688 __ vcmpd(FromLowSToD(left.AsFpuRegisterPairLow<SRegister>()), 3689 FromLowSToD(right.AsFpuRegisterPairLow<SRegister>())); 3690 } 3691 __ vmstat(); // transfer FP status register to ARM APSR. 3692 less_cond = ARMFPCondition(kCondLT, compare->IsGtBias()); 3693 break; 3694 } 3695 default: 3696 LOG(FATAL) << "Unexpected compare type " << type; 3697 UNREACHABLE(); 3698 } 3699 3700 __ b(&done, EQ); 3701 __ b(&less, less_cond); 3702 3703 __ Bind(&greater); 3704 __ LoadImmediate(out, 1); 3705 __ b(&done); 3706 3707 __ Bind(&less); 3708 __ LoadImmediate(out, -1); 3709 3710 __ Bind(&done); 3711} 3712 3713void LocationsBuilderARM::VisitPhi(HPhi* instruction) { 3714 LocationSummary* locations = 3715 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 3716 for (size_t i = 0, e = locations->GetInputCount(); i < e; ++i) { 3717 locations->SetInAt(i, Location::Any()); 3718 } 3719 locations->SetOut(Location::Any()); 3720} 3721 3722void InstructionCodeGeneratorARM::VisitPhi(HPhi* instruction ATTRIBUTE_UNUSED) { 3723 LOG(FATAL) << "Unreachable"; 3724} 3725 3726void CodeGeneratorARM::GenerateMemoryBarrier(MemBarrierKind kind) { 3727 // TODO (ported from quick): revisit ARM barrier kinds. 3728 DmbOptions flavor = DmbOptions::ISH; // Quiet C++ warnings. 3729 switch (kind) { 3730 case MemBarrierKind::kAnyStore: 3731 case MemBarrierKind::kLoadAny: 3732 case MemBarrierKind::kAnyAny: { 3733 flavor = DmbOptions::ISH; 3734 break; 3735 } 3736 case MemBarrierKind::kStoreStore: { 3737 flavor = DmbOptions::ISHST; 3738 break; 3739 } 3740 default: 3741 LOG(FATAL) << "Unexpected memory barrier " << kind; 3742 } 3743 __ dmb(flavor); 3744} 3745 3746void InstructionCodeGeneratorARM::GenerateWideAtomicLoad(Register addr, 3747 uint32_t offset, 3748 Register out_lo, 3749 Register out_hi) { 3750 if (offset != 0) { 3751 // Ensure `out_lo` is different from `addr`, so that loading 3752 // `offset` into `out_lo` does not clutter `addr`. 3753 DCHECK_NE(out_lo, addr); 3754 __ LoadImmediate(out_lo, offset); 3755 __ add(IP, addr, ShifterOperand(out_lo)); 3756 addr = IP; 3757 } 3758 __ ldrexd(out_lo, out_hi, addr); 3759} 3760 3761void InstructionCodeGeneratorARM::GenerateWideAtomicStore(Register addr, 3762 uint32_t offset, 3763 Register value_lo, 3764 Register value_hi, 3765 Register temp1, 3766 Register temp2, 3767 HInstruction* instruction) { 3768 Label fail; 3769 if (offset != 0) { 3770 __ LoadImmediate(temp1, offset); 3771 __ add(IP, addr, ShifterOperand(temp1)); 3772 addr = IP; 3773 } 3774 __ Bind(&fail); 3775 // We need a load followed by store. (The address used in a STREX instruction must 3776 // be the same as the address in the most recently executed LDREX instruction.) 3777 __ ldrexd(temp1, temp2, addr); 3778 codegen_->MaybeRecordImplicitNullCheck(instruction); 3779 __ strexd(temp1, value_lo, value_hi, addr); 3780 __ CompareAndBranchIfNonZero(temp1, &fail); 3781} 3782 3783void LocationsBuilderARM::HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info) { 3784 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet()); 3785 3786 LocationSummary* locations = 3787 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 3788 locations->SetInAt(0, Location::RequiresRegister()); 3789 3790 Primitive::Type field_type = field_info.GetFieldType(); 3791 if (Primitive::IsFloatingPointType(field_type)) { 3792 locations->SetInAt(1, Location::RequiresFpuRegister()); 3793 } else { 3794 locations->SetInAt(1, Location::RequiresRegister()); 3795 } 3796 3797 bool is_wide = field_type == Primitive::kPrimLong || field_type == Primitive::kPrimDouble; 3798 bool generate_volatile = field_info.IsVolatile() 3799 && is_wide 3800 && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd(); 3801 bool needs_write_barrier = 3802 CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1)); 3803 // Temporary registers for the write barrier. 3804 // TODO: consider renaming StoreNeedsWriteBarrier to StoreNeedsGCMark. 3805 if (needs_write_barrier) { 3806 locations->AddTemp(Location::RequiresRegister()); // Possibly used for reference poisoning too. 3807 locations->AddTemp(Location::RequiresRegister()); 3808 } else if (generate_volatile) { 3809 // ARM encoding have some additional constraints for ldrexd/strexd: 3810 // - registers need to be consecutive 3811 // - the first register should be even but not R14. 3812 // We don't test for ARM yet, and the assertion makes sure that we 3813 // revisit this if we ever enable ARM encoding. 3814 DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet()); 3815 3816 locations->AddTemp(Location::RequiresRegister()); 3817 locations->AddTemp(Location::RequiresRegister()); 3818 if (field_type == Primitive::kPrimDouble) { 3819 // For doubles we need two more registers to copy the value. 3820 locations->AddTemp(Location::RegisterLocation(R2)); 3821 locations->AddTemp(Location::RegisterLocation(R3)); 3822 } 3823 } 3824} 3825 3826void InstructionCodeGeneratorARM::HandleFieldSet(HInstruction* instruction, 3827 const FieldInfo& field_info, 3828 bool value_can_be_null) { 3829 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet()); 3830 3831 LocationSummary* locations = instruction->GetLocations(); 3832 Register base = locations->InAt(0).AsRegister<Register>(); 3833 Location value = locations->InAt(1); 3834 3835 bool is_volatile = field_info.IsVolatile(); 3836 bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd(); 3837 Primitive::Type field_type = field_info.GetFieldType(); 3838 uint32_t offset = field_info.GetFieldOffset().Uint32Value(); 3839 bool needs_write_barrier = 3840 CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1)); 3841 3842 if (is_volatile) { 3843 codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyStore); 3844 } 3845 3846 switch (field_type) { 3847 case Primitive::kPrimBoolean: 3848 case Primitive::kPrimByte: { 3849 __ StoreToOffset(kStoreByte, value.AsRegister<Register>(), base, offset); 3850 break; 3851 } 3852 3853 case Primitive::kPrimShort: 3854 case Primitive::kPrimChar: { 3855 __ StoreToOffset(kStoreHalfword, value.AsRegister<Register>(), base, offset); 3856 break; 3857 } 3858 3859 case Primitive::kPrimInt: 3860 case Primitive::kPrimNot: { 3861 if (kPoisonHeapReferences && needs_write_barrier) { 3862 // Note that in the case where `value` is a null reference, 3863 // we do not enter this block, as a null reference does not 3864 // need poisoning. 3865 DCHECK_EQ(field_type, Primitive::kPrimNot); 3866 Register temp = locations->GetTemp(0).AsRegister<Register>(); 3867 __ Mov(temp, value.AsRegister<Register>()); 3868 __ PoisonHeapReference(temp); 3869 __ StoreToOffset(kStoreWord, temp, base, offset); 3870 } else { 3871 __ StoreToOffset(kStoreWord, value.AsRegister<Register>(), base, offset); 3872 } 3873 break; 3874 } 3875 3876 case Primitive::kPrimLong: { 3877 if (is_volatile && !atomic_ldrd_strd) { 3878 GenerateWideAtomicStore(base, offset, 3879 value.AsRegisterPairLow<Register>(), 3880 value.AsRegisterPairHigh<Register>(), 3881 locations->GetTemp(0).AsRegister<Register>(), 3882 locations->GetTemp(1).AsRegister<Register>(), 3883 instruction); 3884 } else { 3885 __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), base, offset); 3886 codegen_->MaybeRecordImplicitNullCheck(instruction); 3887 } 3888 break; 3889 } 3890 3891 case Primitive::kPrimFloat: { 3892 __ StoreSToOffset(value.AsFpuRegister<SRegister>(), base, offset); 3893 break; 3894 } 3895 3896 case Primitive::kPrimDouble: { 3897 DRegister value_reg = FromLowSToD(value.AsFpuRegisterPairLow<SRegister>()); 3898 if (is_volatile && !atomic_ldrd_strd) { 3899 Register value_reg_lo = locations->GetTemp(0).AsRegister<Register>(); 3900 Register value_reg_hi = locations->GetTemp(1).AsRegister<Register>(); 3901 3902 __ vmovrrd(value_reg_lo, value_reg_hi, value_reg); 3903 3904 GenerateWideAtomicStore(base, offset, 3905 value_reg_lo, 3906 value_reg_hi, 3907 locations->GetTemp(2).AsRegister<Register>(), 3908 locations->GetTemp(3).AsRegister<Register>(), 3909 instruction); 3910 } else { 3911 __ StoreDToOffset(value_reg, base, offset); 3912 codegen_->MaybeRecordImplicitNullCheck(instruction); 3913 } 3914 break; 3915 } 3916 3917 case Primitive::kPrimVoid: 3918 LOG(FATAL) << "Unreachable type " << field_type; 3919 UNREACHABLE(); 3920 } 3921 3922 // Longs and doubles are handled in the switch. 3923 if (field_type != Primitive::kPrimLong && field_type != Primitive::kPrimDouble) { 3924 codegen_->MaybeRecordImplicitNullCheck(instruction); 3925 } 3926 3927 if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) { 3928 Register temp = locations->GetTemp(0).AsRegister<Register>(); 3929 Register card = locations->GetTemp(1).AsRegister<Register>(); 3930 codegen_->MarkGCCard( 3931 temp, card, base, value.AsRegister<Register>(), value_can_be_null); 3932 } 3933 3934 if (is_volatile) { 3935 codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyAny); 3936 } 3937} 3938 3939void LocationsBuilderARM::HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info) { 3940 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet()); 3941 3942 bool object_field_get_with_read_barrier = 3943 kEmitCompilerReadBarrier && (field_info.GetFieldType() == Primitive::kPrimNot); 3944 LocationSummary* locations = 3945 new (GetGraph()->GetArena()) LocationSummary(instruction, 3946 object_field_get_with_read_barrier ? 3947 LocationSummary::kCallOnSlowPath : 3948 LocationSummary::kNoCall); 3949 locations->SetInAt(0, Location::RequiresRegister()); 3950 3951 bool volatile_for_double = field_info.IsVolatile() 3952 && (field_info.GetFieldType() == Primitive::kPrimDouble) 3953 && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd(); 3954 // The output overlaps in case of volatile long: we don't want the 3955 // code generated by GenerateWideAtomicLoad to overwrite the 3956 // object's location. Likewise, in the case of an object field get 3957 // with read barriers enabled, we do not want the load to overwrite 3958 // the object's location, as we need it to emit the read barrier. 3959 bool overlap = (field_info.IsVolatile() && (field_info.GetFieldType() == Primitive::kPrimLong)) || 3960 object_field_get_with_read_barrier; 3961 3962 if (Primitive::IsFloatingPointType(instruction->GetType())) { 3963 locations->SetOut(Location::RequiresFpuRegister()); 3964 } else { 3965 locations->SetOut(Location::RequiresRegister(), 3966 (overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap)); 3967 } 3968 if (volatile_for_double) { 3969 // ARM encoding have some additional constraints for ldrexd/strexd: 3970 // - registers need to be consecutive 3971 // - the first register should be even but not R14. 3972 // We don't test for ARM yet, and the assertion makes sure that we 3973 // revisit this if we ever enable ARM encoding. 3974 DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet()); 3975 locations->AddTemp(Location::RequiresRegister()); 3976 locations->AddTemp(Location::RequiresRegister()); 3977 } else if (object_field_get_with_read_barrier && kUseBakerReadBarrier) { 3978 // We need a temporary register for the read barrier marking slow 3979 // path in CodeGeneratorARM::GenerateFieldLoadWithBakerReadBarrier. 3980 locations->AddTemp(Location::RequiresRegister()); 3981 } 3982} 3983 3984Location LocationsBuilderARM::ArmEncodableConstantOrRegister(HInstruction* constant, 3985 Opcode opcode) { 3986 DCHECK(!Primitive::IsFloatingPointType(constant->GetType())); 3987 if (constant->IsConstant() && 3988 CanEncodeConstantAsImmediate(constant->AsConstant(), opcode)) { 3989 return Location::ConstantLocation(constant->AsConstant()); 3990 } 3991 return Location::RequiresRegister(); 3992} 3993 3994bool LocationsBuilderARM::CanEncodeConstantAsImmediate(HConstant* input_cst, 3995 Opcode opcode) { 3996 uint64_t value = static_cast<uint64_t>(Int64FromConstant(input_cst)); 3997 if (Primitive::Is64BitType(input_cst->GetType())) { 3998 return CanEncodeConstantAsImmediate(Low32Bits(value), opcode) && 3999 CanEncodeConstantAsImmediate(High32Bits(value), opcode); 4000 } else { 4001 return CanEncodeConstantAsImmediate(Low32Bits(value), opcode); 4002 } 4003} 4004 4005bool LocationsBuilderARM::CanEncodeConstantAsImmediate(uint32_t value, Opcode opcode) { 4006 ShifterOperand so; 4007 ArmAssembler* assembler = codegen_->GetAssembler(); 4008 if (assembler->ShifterOperandCanHold(kNoRegister, kNoRegister, opcode, value, &so)) { 4009 return true; 4010 } 4011 Opcode neg_opcode = kNoOperand; 4012 switch (opcode) { 4013 case AND: 4014 neg_opcode = BIC; 4015 break; 4016 case ORR: 4017 neg_opcode = ORN; 4018 break; 4019 default: 4020 return false; 4021 } 4022 return assembler->ShifterOperandCanHold(kNoRegister, kNoRegister, neg_opcode, ~value, &so); 4023} 4024 4025void InstructionCodeGeneratorARM::HandleFieldGet(HInstruction* instruction, 4026 const FieldInfo& field_info) { 4027 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet()); 4028 4029 LocationSummary* locations = instruction->GetLocations(); 4030 Location base_loc = locations->InAt(0); 4031 Register base = base_loc.AsRegister<Register>(); 4032 Location out = locations->Out(); 4033 bool is_volatile = field_info.IsVolatile(); 4034 bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd(); 4035 Primitive::Type field_type = field_info.GetFieldType(); 4036 uint32_t offset = field_info.GetFieldOffset().Uint32Value(); 4037 4038 switch (field_type) { 4039 case Primitive::kPrimBoolean: 4040 __ LoadFromOffset(kLoadUnsignedByte, out.AsRegister<Register>(), base, offset); 4041 break; 4042 4043 case Primitive::kPrimByte: 4044 __ LoadFromOffset(kLoadSignedByte, out.AsRegister<Register>(), base, offset); 4045 break; 4046 4047 case Primitive::kPrimShort: 4048 __ LoadFromOffset(kLoadSignedHalfword, out.AsRegister<Register>(), base, offset); 4049 break; 4050 4051 case Primitive::kPrimChar: 4052 __ LoadFromOffset(kLoadUnsignedHalfword, out.AsRegister<Register>(), base, offset); 4053 break; 4054 4055 case Primitive::kPrimInt: 4056 __ LoadFromOffset(kLoadWord, out.AsRegister<Register>(), base, offset); 4057 break; 4058 4059 case Primitive::kPrimNot: { 4060 // /* HeapReference<Object> */ out = *(base + offset) 4061 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) { 4062 Location temp_loc = locations->GetTemp(0); 4063 // Note that a potential implicit null check is handled in this 4064 // CodeGeneratorARM::GenerateFieldLoadWithBakerReadBarrier call. 4065 codegen_->GenerateFieldLoadWithBakerReadBarrier( 4066 instruction, out, base, offset, temp_loc, /* needs_null_check */ true); 4067 if (is_volatile) { 4068 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny); 4069 } 4070 } else { 4071 __ LoadFromOffset(kLoadWord, out.AsRegister<Register>(), base, offset); 4072 codegen_->MaybeRecordImplicitNullCheck(instruction); 4073 if (is_volatile) { 4074 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny); 4075 } 4076 // If read barriers are enabled, emit read barriers other than 4077 // Baker's using a slow path (and also unpoison the loaded 4078 // reference, if heap poisoning is enabled). 4079 codegen_->MaybeGenerateReadBarrierSlow(instruction, out, out, base_loc, offset); 4080 } 4081 break; 4082 } 4083 4084 case Primitive::kPrimLong: 4085 if (is_volatile && !atomic_ldrd_strd) { 4086 GenerateWideAtomicLoad(base, offset, 4087 out.AsRegisterPairLow<Register>(), 4088 out.AsRegisterPairHigh<Register>()); 4089 } else { 4090 __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow<Register>(), base, offset); 4091 } 4092 break; 4093 4094 case Primitive::kPrimFloat: 4095 __ LoadSFromOffset(out.AsFpuRegister<SRegister>(), base, offset); 4096 break; 4097 4098 case Primitive::kPrimDouble: { 4099 DRegister out_reg = FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()); 4100 if (is_volatile && !atomic_ldrd_strd) { 4101 Register lo = locations->GetTemp(0).AsRegister<Register>(); 4102 Register hi = locations->GetTemp(1).AsRegister<Register>(); 4103 GenerateWideAtomicLoad(base, offset, lo, hi); 4104 codegen_->MaybeRecordImplicitNullCheck(instruction); 4105 __ vmovdrr(out_reg, lo, hi); 4106 } else { 4107 __ LoadDFromOffset(out_reg, base, offset); 4108 codegen_->MaybeRecordImplicitNullCheck(instruction); 4109 } 4110 break; 4111 } 4112 4113 case Primitive::kPrimVoid: 4114 LOG(FATAL) << "Unreachable type " << field_type; 4115 UNREACHABLE(); 4116 } 4117 4118 if (field_type == Primitive::kPrimNot || field_type == Primitive::kPrimDouble) { 4119 // Potential implicit null checks, in the case of reference or 4120 // double fields, are handled in the previous switch statement. 4121 } else { 4122 codegen_->MaybeRecordImplicitNullCheck(instruction); 4123 } 4124 4125 if (is_volatile) { 4126 if (field_type == Primitive::kPrimNot) { 4127 // Memory barriers, in the case of references, are also handled 4128 // in the previous switch statement. 4129 } else { 4130 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny); 4131 } 4132 } 4133} 4134 4135void LocationsBuilderARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) { 4136 HandleFieldSet(instruction, instruction->GetFieldInfo()); 4137} 4138 4139void InstructionCodeGeneratorARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) { 4140 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull()); 4141} 4142 4143void LocationsBuilderARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { 4144 HandleFieldGet(instruction, instruction->GetFieldInfo()); 4145} 4146 4147void InstructionCodeGeneratorARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { 4148 HandleFieldGet(instruction, instruction->GetFieldInfo()); 4149} 4150 4151void LocationsBuilderARM::VisitStaticFieldGet(HStaticFieldGet* instruction) { 4152 HandleFieldGet(instruction, instruction->GetFieldInfo()); 4153} 4154 4155void InstructionCodeGeneratorARM::VisitStaticFieldGet(HStaticFieldGet* instruction) { 4156 HandleFieldGet(instruction, instruction->GetFieldInfo()); 4157} 4158 4159void LocationsBuilderARM::VisitStaticFieldSet(HStaticFieldSet* instruction) { 4160 HandleFieldSet(instruction, instruction->GetFieldInfo()); 4161} 4162 4163void InstructionCodeGeneratorARM::VisitStaticFieldSet(HStaticFieldSet* instruction) { 4164 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull()); 4165} 4166 4167void LocationsBuilderARM::VisitUnresolvedInstanceFieldGet( 4168 HUnresolvedInstanceFieldGet* instruction) { 4169 FieldAccessCallingConventionARM calling_convention; 4170 codegen_->CreateUnresolvedFieldLocationSummary( 4171 instruction, instruction->GetFieldType(), calling_convention); 4172} 4173 4174void InstructionCodeGeneratorARM::VisitUnresolvedInstanceFieldGet( 4175 HUnresolvedInstanceFieldGet* instruction) { 4176 FieldAccessCallingConventionARM calling_convention; 4177 codegen_->GenerateUnresolvedFieldAccess(instruction, 4178 instruction->GetFieldType(), 4179 instruction->GetFieldIndex(), 4180 instruction->GetDexPc(), 4181 calling_convention); 4182} 4183 4184void LocationsBuilderARM::VisitUnresolvedInstanceFieldSet( 4185 HUnresolvedInstanceFieldSet* instruction) { 4186 FieldAccessCallingConventionARM calling_convention; 4187 codegen_->CreateUnresolvedFieldLocationSummary( 4188 instruction, instruction->GetFieldType(), calling_convention); 4189} 4190 4191void InstructionCodeGeneratorARM::VisitUnresolvedInstanceFieldSet( 4192 HUnresolvedInstanceFieldSet* instruction) { 4193 FieldAccessCallingConventionARM calling_convention; 4194 codegen_->GenerateUnresolvedFieldAccess(instruction, 4195 instruction->GetFieldType(), 4196 instruction->GetFieldIndex(), 4197 instruction->GetDexPc(), 4198 calling_convention); 4199} 4200 4201void LocationsBuilderARM::VisitUnresolvedStaticFieldGet( 4202 HUnresolvedStaticFieldGet* instruction) { 4203 FieldAccessCallingConventionARM calling_convention; 4204 codegen_->CreateUnresolvedFieldLocationSummary( 4205 instruction, instruction->GetFieldType(), calling_convention); 4206} 4207 4208void InstructionCodeGeneratorARM::VisitUnresolvedStaticFieldGet( 4209 HUnresolvedStaticFieldGet* instruction) { 4210 FieldAccessCallingConventionARM calling_convention; 4211 codegen_->GenerateUnresolvedFieldAccess(instruction, 4212 instruction->GetFieldType(), 4213 instruction->GetFieldIndex(), 4214 instruction->GetDexPc(), 4215 calling_convention); 4216} 4217 4218void LocationsBuilderARM::VisitUnresolvedStaticFieldSet( 4219 HUnresolvedStaticFieldSet* instruction) { 4220 FieldAccessCallingConventionARM calling_convention; 4221 codegen_->CreateUnresolvedFieldLocationSummary( 4222 instruction, instruction->GetFieldType(), calling_convention); 4223} 4224 4225void InstructionCodeGeneratorARM::VisitUnresolvedStaticFieldSet( 4226 HUnresolvedStaticFieldSet* instruction) { 4227 FieldAccessCallingConventionARM calling_convention; 4228 codegen_->GenerateUnresolvedFieldAccess(instruction, 4229 instruction->GetFieldType(), 4230 instruction->GetFieldIndex(), 4231 instruction->GetDexPc(), 4232 calling_convention); 4233} 4234 4235void LocationsBuilderARM::VisitNullCheck(HNullCheck* instruction) { 4236 LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() 4237 ? LocationSummary::kCallOnSlowPath 4238 : LocationSummary::kNoCall; 4239 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); 4240 locations->SetInAt(0, Location::RequiresRegister()); 4241 if (instruction->HasUses()) { 4242 locations->SetOut(Location::SameAsFirstInput()); 4243 } 4244} 4245 4246void CodeGeneratorARM::GenerateImplicitNullCheck(HNullCheck* instruction) { 4247 if (CanMoveNullCheckToUser(instruction)) { 4248 return; 4249 } 4250 Location obj = instruction->GetLocations()->InAt(0); 4251 4252 __ LoadFromOffset(kLoadWord, IP, obj.AsRegister<Register>(), 0); 4253 RecordPcInfo(instruction, instruction->GetDexPc()); 4254} 4255 4256void CodeGeneratorARM::GenerateExplicitNullCheck(HNullCheck* instruction) { 4257 SlowPathCode* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathARM(instruction); 4258 AddSlowPath(slow_path); 4259 4260 LocationSummary* locations = instruction->GetLocations(); 4261 Location obj = locations->InAt(0); 4262 4263 __ CompareAndBranchIfZero(obj.AsRegister<Register>(), slow_path->GetEntryLabel()); 4264} 4265 4266void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) { 4267 codegen_->GenerateNullCheck(instruction); 4268} 4269 4270void LocationsBuilderARM::VisitArrayGet(HArrayGet* instruction) { 4271 bool object_array_get_with_read_barrier = 4272 kEmitCompilerReadBarrier && (instruction->GetType() == Primitive::kPrimNot); 4273 LocationSummary* locations = 4274 new (GetGraph()->GetArena()) LocationSummary(instruction, 4275 object_array_get_with_read_barrier ? 4276 LocationSummary::kCallOnSlowPath : 4277 LocationSummary::kNoCall); 4278 locations->SetInAt(0, Location::RequiresRegister()); 4279 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); 4280 if (Primitive::IsFloatingPointType(instruction->GetType())) { 4281 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 4282 } else { 4283 // The output overlaps in the case of an object array get with 4284 // read barriers enabled: we do not want the move to overwrite the 4285 // array's location, as we need it to emit the read barrier. 4286 locations->SetOut( 4287 Location::RequiresRegister(), 4288 object_array_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap); 4289 } 4290 // We need a temporary register for the read barrier marking slow 4291 // path in CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier. 4292 if (object_array_get_with_read_barrier && kUseBakerReadBarrier) { 4293 locations->AddTemp(Location::RequiresRegister()); 4294 } 4295} 4296 4297void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { 4298 LocationSummary* locations = instruction->GetLocations(); 4299 Location obj_loc = locations->InAt(0); 4300 Register obj = obj_loc.AsRegister<Register>(); 4301 Location index = locations->InAt(1); 4302 Location out_loc = locations->Out(); 4303 uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction); 4304 4305 Primitive::Type type = instruction->GetType(); 4306 switch (type) { 4307 case Primitive::kPrimBoolean: { 4308 Register out = out_loc.AsRegister<Register>(); 4309 if (index.IsConstant()) { 4310 size_t offset = 4311 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset; 4312 __ LoadFromOffset(kLoadUnsignedByte, out, obj, offset); 4313 } else { 4314 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>())); 4315 __ LoadFromOffset(kLoadUnsignedByte, out, IP, data_offset); 4316 } 4317 break; 4318 } 4319 4320 case Primitive::kPrimByte: { 4321 Register out = out_loc.AsRegister<Register>(); 4322 if (index.IsConstant()) { 4323 size_t offset = 4324 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset; 4325 __ LoadFromOffset(kLoadSignedByte, out, obj, offset); 4326 } else { 4327 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>())); 4328 __ LoadFromOffset(kLoadSignedByte, out, IP, data_offset); 4329 } 4330 break; 4331 } 4332 4333 case Primitive::kPrimShort: { 4334 Register out = out_loc.AsRegister<Register>(); 4335 if (index.IsConstant()) { 4336 size_t offset = 4337 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; 4338 __ LoadFromOffset(kLoadSignedHalfword, out, obj, offset); 4339 } else { 4340 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_2)); 4341 __ LoadFromOffset(kLoadSignedHalfword, out, IP, data_offset); 4342 } 4343 break; 4344 } 4345 4346 case Primitive::kPrimChar: { 4347 Register out = out_loc.AsRegister<Register>(); 4348 if (index.IsConstant()) { 4349 size_t offset = 4350 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; 4351 __ LoadFromOffset(kLoadUnsignedHalfword, out, obj, offset); 4352 } else { 4353 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_2)); 4354 __ LoadFromOffset(kLoadUnsignedHalfword, out, IP, data_offset); 4355 } 4356 break; 4357 } 4358 4359 case Primitive::kPrimInt: { 4360 Register out = out_loc.AsRegister<Register>(); 4361 if (index.IsConstant()) { 4362 size_t offset = 4363 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4364 __ LoadFromOffset(kLoadWord, out, obj, offset); 4365 } else { 4366 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4367 __ LoadFromOffset(kLoadWord, out, IP, data_offset); 4368 } 4369 break; 4370 } 4371 4372 case Primitive::kPrimNot: { 4373 static_assert( 4374 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), 4375 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); 4376 // /* HeapReference<Object> */ out = 4377 // *(obj + data_offset + index * sizeof(HeapReference<Object>)) 4378 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) { 4379 Location temp = locations->GetTemp(0); 4380 // Note that a potential implicit null check is handled in this 4381 // CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier call. 4382 codegen_->GenerateArrayLoadWithBakerReadBarrier( 4383 instruction, out_loc, obj, data_offset, index, temp, /* needs_null_check */ true); 4384 } else { 4385 Register out = out_loc.AsRegister<Register>(); 4386 if (index.IsConstant()) { 4387 size_t offset = 4388 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4389 __ LoadFromOffset(kLoadWord, out, obj, offset); 4390 codegen_->MaybeRecordImplicitNullCheck(instruction); 4391 // If read barriers are enabled, emit read barriers other than 4392 // Baker's using a slow path (and also unpoison the loaded 4393 // reference, if heap poisoning is enabled). 4394 codegen_->MaybeGenerateReadBarrierSlow(instruction, out_loc, out_loc, obj_loc, offset); 4395 } else { 4396 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4397 __ LoadFromOffset(kLoadWord, out, IP, data_offset); 4398 codegen_->MaybeRecordImplicitNullCheck(instruction); 4399 // If read barriers are enabled, emit read barriers other than 4400 // Baker's using a slow path (and also unpoison the loaded 4401 // reference, if heap poisoning is enabled). 4402 codegen_->MaybeGenerateReadBarrierSlow( 4403 instruction, out_loc, out_loc, obj_loc, data_offset, index); 4404 } 4405 } 4406 break; 4407 } 4408 4409 case Primitive::kPrimLong: { 4410 if (index.IsConstant()) { 4411 size_t offset = 4412 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; 4413 __ LoadFromOffset(kLoadWordPair, out_loc.AsRegisterPairLow<Register>(), obj, offset); 4414 } else { 4415 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8)); 4416 __ LoadFromOffset(kLoadWordPair, out_loc.AsRegisterPairLow<Register>(), IP, data_offset); 4417 } 4418 break; 4419 } 4420 4421 case Primitive::kPrimFloat: { 4422 SRegister out = out_loc.AsFpuRegister<SRegister>(); 4423 if (index.IsConstant()) { 4424 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4425 __ LoadSFromOffset(out, obj, offset); 4426 } else { 4427 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4428 __ LoadSFromOffset(out, IP, data_offset); 4429 } 4430 break; 4431 } 4432 4433 case Primitive::kPrimDouble: { 4434 SRegister out = out_loc.AsFpuRegisterPairLow<SRegister>(); 4435 if (index.IsConstant()) { 4436 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; 4437 __ LoadDFromOffset(FromLowSToD(out), obj, offset); 4438 } else { 4439 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8)); 4440 __ LoadDFromOffset(FromLowSToD(out), IP, data_offset); 4441 } 4442 break; 4443 } 4444 4445 case Primitive::kPrimVoid: 4446 LOG(FATAL) << "Unreachable type " << type; 4447 UNREACHABLE(); 4448 } 4449 4450 if (type == Primitive::kPrimNot) { 4451 // Potential implicit null checks, in the case of reference 4452 // arrays, are handled in the previous switch statement. 4453 } else { 4454 codegen_->MaybeRecordImplicitNullCheck(instruction); 4455 } 4456} 4457 4458void LocationsBuilderARM::VisitArraySet(HArraySet* instruction) { 4459 Primitive::Type value_type = instruction->GetComponentType(); 4460 4461 bool needs_write_barrier = 4462 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue()); 4463 bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck(); 4464 bool object_array_set_with_read_barrier = 4465 kEmitCompilerReadBarrier && (value_type == Primitive::kPrimNot); 4466 4467 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary( 4468 instruction, 4469 (may_need_runtime_call_for_type_check || object_array_set_with_read_barrier) ? 4470 LocationSummary::kCallOnSlowPath : 4471 LocationSummary::kNoCall); 4472 4473 locations->SetInAt(0, Location::RequiresRegister()); 4474 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); 4475 if (Primitive::IsFloatingPointType(value_type)) { 4476 locations->SetInAt(2, Location::RequiresFpuRegister()); 4477 } else { 4478 locations->SetInAt(2, Location::RequiresRegister()); 4479 } 4480 if (needs_write_barrier) { 4481 // Temporary registers for the write barrier. 4482 locations->AddTemp(Location::RequiresRegister()); // Possibly used for ref. poisoning too. 4483 locations->AddTemp(Location::RequiresRegister()); 4484 } 4485} 4486 4487void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) { 4488 LocationSummary* locations = instruction->GetLocations(); 4489 Location array_loc = locations->InAt(0); 4490 Register array = array_loc.AsRegister<Register>(); 4491 Location index = locations->InAt(1); 4492 Primitive::Type value_type = instruction->GetComponentType(); 4493 bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck(); 4494 bool needs_write_barrier = 4495 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue()); 4496 4497 switch (value_type) { 4498 case Primitive::kPrimBoolean: 4499 case Primitive::kPrimByte: { 4500 uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value(); 4501 Register value = locations->InAt(2).AsRegister<Register>(); 4502 if (index.IsConstant()) { 4503 size_t offset = 4504 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset; 4505 __ StoreToOffset(kStoreByte, value, array, offset); 4506 } else { 4507 __ add(IP, array, ShifterOperand(index.AsRegister<Register>())); 4508 __ StoreToOffset(kStoreByte, value, IP, data_offset); 4509 } 4510 break; 4511 } 4512 4513 case Primitive::kPrimShort: 4514 case Primitive::kPrimChar: { 4515 uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value(); 4516 Register value = locations->InAt(2).AsRegister<Register>(); 4517 if (index.IsConstant()) { 4518 size_t offset = 4519 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; 4520 __ StoreToOffset(kStoreHalfword, value, array, offset); 4521 } else { 4522 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_2)); 4523 __ StoreToOffset(kStoreHalfword, value, IP, data_offset); 4524 } 4525 break; 4526 } 4527 4528 case Primitive::kPrimNot: { 4529 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); 4530 Location value_loc = locations->InAt(2); 4531 Register value = value_loc.AsRegister<Register>(); 4532 Register source = value; 4533 4534 if (instruction->InputAt(2)->IsNullConstant()) { 4535 // Just setting null. 4536 if (index.IsConstant()) { 4537 size_t offset = 4538 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4539 __ StoreToOffset(kStoreWord, source, array, offset); 4540 } else { 4541 DCHECK(index.IsRegister()) << index; 4542 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4543 __ StoreToOffset(kStoreWord, source, IP, data_offset); 4544 } 4545 codegen_->MaybeRecordImplicitNullCheck(instruction); 4546 DCHECK(!needs_write_barrier); 4547 DCHECK(!may_need_runtime_call_for_type_check); 4548 break; 4549 } 4550 4551 DCHECK(needs_write_barrier); 4552 Register temp1 = locations->GetTemp(0).AsRegister<Register>(); 4553 Register temp2 = locations->GetTemp(1).AsRegister<Register>(); 4554 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 4555 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); 4556 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); 4557 Label done; 4558 SlowPathCode* slow_path = nullptr; 4559 4560 if (may_need_runtime_call_for_type_check) { 4561 slow_path = new (GetGraph()->GetArena()) ArraySetSlowPathARM(instruction); 4562 codegen_->AddSlowPath(slow_path); 4563 if (instruction->GetValueCanBeNull()) { 4564 Label non_zero; 4565 __ CompareAndBranchIfNonZero(value, &non_zero); 4566 if (index.IsConstant()) { 4567 size_t offset = 4568 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4569 __ StoreToOffset(kStoreWord, value, array, offset); 4570 } else { 4571 DCHECK(index.IsRegister()) << index; 4572 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4573 __ StoreToOffset(kStoreWord, value, IP, data_offset); 4574 } 4575 codegen_->MaybeRecordImplicitNullCheck(instruction); 4576 __ b(&done); 4577 __ Bind(&non_zero); 4578 } 4579 4580 if (kEmitCompilerReadBarrier) { 4581 // When read barriers are enabled, the type checking 4582 // instrumentation requires two read barriers: 4583 // 4584 // __ Mov(temp2, temp1); 4585 // // /* HeapReference<Class> */ temp1 = temp1->component_type_ 4586 // __ LoadFromOffset(kLoadWord, temp1, temp1, component_offset); 4587 // codegen_->GenerateReadBarrierSlow( 4588 // instruction, temp1_loc, temp1_loc, temp2_loc, component_offset); 4589 // 4590 // // /* HeapReference<Class> */ temp2 = value->klass_ 4591 // __ LoadFromOffset(kLoadWord, temp2, value, class_offset); 4592 // codegen_->GenerateReadBarrierSlow( 4593 // instruction, temp2_loc, temp2_loc, value_loc, class_offset, temp1_loc); 4594 // 4595 // __ cmp(temp1, ShifterOperand(temp2)); 4596 // 4597 // However, the second read barrier may trash `temp`, as it 4598 // is a temporary register, and as such would not be saved 4599 // along with live registers before calling the runtime (nor 4600 // restored afterwards). So in this case, we bail out and 4601 // delegate the work to the array set slow path. 4602 // 4603 // TODO: Extend the register allocator to support a new 4604 // "(locally) live temp" location so as to avoid always 4605 // going into the slow path when read barriers are enabled. 4606 __ b(slow_path->GetEntryLabel()); 4607 } else { 4608 // /* HeapReference<Class> */ temp1 = array->klass_ 4609 __ LoadFromOffset(kLoadWord, temp1, array, class_offset); 4610 codegen_->MaybeRecordImplicitNullCheck(instruction); 4611 __ MaybeUnpoisonHeapReference(temp1); 4612 4613 // /* HeapReference<Class> */ temp1 = temp1->component_type_ 4614 __ LoadFromOffset(kLoadWord, temp1, temp1, component_offset); 4615 // /* HeapReference<Class> */ temp2 = value->klass_ 4616 __ LoadFromOffset(kLoadWord, temp2, value, class_offset); 4617 // If heap poisoning is enabled, no need to unpoison `temp1` 4618 // nor `temp2`, as we are comparing two poisoned references. 4619 __ cmp(temp1, ShifterOperand(temp2)); 4620 4621 if (instruction->StaticTypeOfArrayIsObjectArray()) { 4622 Label do_put; 4623 __ b(&do_put, EQ); 4624 // If heap poisoning is enabled, the `temp1` reference has 4625 // not been unpoisoned yet; unpoison it now. 4626 __ MaybeUnpoisonHeapReference(temp1); 4627 4628 // /* HeapReference<Class> */ temp1 = temp1->super_class_ 4629 __ LoadFromOffset(kLoadWord, temp1, temp1, super_offset); 4630 // If heap poisoning is enabled, no need to unpoison 4631 // `temp1`, as we are comparing against null below. 4632 __ CompareAndBranchIfNonZero(temp1, slow_path->GetEntryLabel()); 4633 __ Bind(&do_put); 4634 } else { 4635 __ b(slow_path->GetEntryLabel(), NE); 4636 } 4637 } 4638 } 4639 4640 if (kPoisonHeapReferences) { 4641 // Note that in the case where `value` is a null reference, 4642 // we do not enter this block, as a null reference does not 4643 // need poisoning. 4644 DCHECK_EQ(value_type, Primitive::kPrimNot); 4645 __ Mov(temp1, value); 4646 __ PoisonHeapReference(temp1); 4647 source = temp1; 4648 } 4649 4650 if (index.IsConstant()) { 4651 size_t offset = 4652 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4653 __ StoreToOffset(kStoreWord, source, array, offset); 4654 } else { 4655 DCHECK(index.IsRegister()) << index; 4656 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4657 __ StoreToOffset(kStoreWord, source, IP, data_offset); 4658 } 4659 4660 if (!may_need_runtime_call_for_type_check) { 4661 codegen_->MaybeRecordImplicitNullCheck(instruction); 4662 } 4663 4664 codegen_->MarkGCCard(temp1, temp2, array, value, instruction->GetValueCanBeNull()); 4665 4666 if (done.IsLinked()) { 4667 __ Bind(&done); 4668 } 4669 4670 if (slow_path != nullptr) { 4671 __ Bind(slow_path->GetExitLabel()); 4672 } 4673 4674 break; 4675 } 4676 4677 case Primitive::kPrimInt: { 4678 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); 4679 Register value = locations->InAt(2).AsRegister<Register>(); 4680 if (index.IsConstant()) { 4681 size_t offset = 4682 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4683 __ StoreToOffset(kStoreWord, value, array, offset); 4684 } else { 4685 DCHECK(index.IsRegister()) << index; 4686 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4687 __ StoreToOffset(kStoreWord, value, IP, data_offset); 4688 } 4689 break; 4690 } 4691 4692 case Primitive::kPrimLong: { 4693 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); 4694 Location value = locations->InAt(2); 4695 if (index.IsConstant()) { 4696 size_t offset = 4697 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; 4698 __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), array, offset); 4699 } else { 4700 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8)); 4701 __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), IP, data_offset); 4702 } 4703 break; 4704 } 4705 4706 case Primitive::kPrimFloat: { 4707 uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value(); 4708 Location value = locations->InAt(2); 4709 DCHECK(value.IsFpuRegister()); 4710 if (index.IsConstant()) { 4711 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4712 __ StoreSToOffset(value.AsFpuRegister<SRegister>(), array, offset); 4713 } else { 4714 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4715 __ StoreSToOffset(value.AsFpuRegister<SRegister>(), IP, data_offset); 4716 } 4717 break; 4718 } 4719 4720 case Primitive::kPrimDouble: { 4721 uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value(); 4722 Location value = locations->InAt(2); 4723 DCHECK(value.IsFpuRegisterPair()); 4724 if (index.IsConstant()) { 4725 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; 4726 __ StoreDToOffset(FromLowSToD(value.AsFpuRegisterPairLow<SRegister>()), array, offset); 4727 } else { 4728 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8)); 4729 __ StoreDToOffset(FromLowSToD(value.AsFpuRegisterPairLow<SRegister>()), IP, data_offset); 4730 } 4731 4732 break; 4733 } 4734 4735 case Primitive::kPrimVoid: 4736 LOG(FATAL) << "Unreachable type " << value_type; 4737 UNREACHABLE(); 4738 } 4739 4740 // Objects are handled in the switch. 4741 if (value_type != Primitive::kPrimNot) { 4742 codegen_->MaybeRecordImplicitNullCheck(instruction); 4743 } 4744} 4745 4746void LocationsBuilderARM::VisitArrayLength(HArrayLength* instruction) { 4747 LocationSummary* locations = 4748 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 4749 locations->SetInAt(0, Location::RequiresRegister()); 4750 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 4751} 4752 4753void InstructionCodeGeneratorARM::VisitArrayLength(HArrayLength* instruction) { 4754 LocationSummary* locations = instruction->GetLocations(); 4755 uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction); 4756 Register obj = locations->InAt(0).AsRegister<Register>(); 4757 Register out = locations->Out().AsRegister<Register>(); 4758 __ LoadFromOffset(kLoadWord, out, obj, offset); 4759 codegen_->MaybeRecordImplicitNullCheck(instruction); 4760} 4761 4762void LocationsBuilderARM::VisitBoundsCheck(HBoundsCheck* instruction) { 4763 LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() 4764 ? LocationSummary::kCallOnSlowPath 4765 : LocationSummary::kNoCall; 4766 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); 4767 locations->SetInAt(0, Location::RequiresRegister()); 4768 locations->SetInAt(1, Location::RequiresRegister()); 4769 if (instruction->HasUses()) { 4770 locations->SetOut(Location::SameAsFirstInput()); 4771 } 4772} 4773 4774void InstructionCodeGeneratorARM::VisitBoundsCheck(HBoundsCheck* instruction) { 4775 LocationSummary* locations = instruction->GetLocations(); 4776 SlowPathCode* slow_path = 4777 new (GetGraph()->GetArena()) BoundsCheckSlowPathARM(instruction); 4778 codegen_->AddSlowPath(slow_path); 4779 4780 Register index = locations->InAt(0).AsRegister<Register>(); 4781 Register length = locations->InAt(1).AsRegister<Register>(); 4782 4783 __ cmp(index, ShifterOperand(length)); 4784 __ b(slow_path->GetEntryLabel(), HS); 4785} 4786 4787void CodeGeneratorARM::MarkGCCard(Register temp, 4788 Register card, 4789 Register object, 4790 Register value, 4791 bool can_be_null) { 4792 Label is_null; 4793 if (can_be_null) { 4794 __ CompareAndBranchIfZero(value, &is_null); 4795 } 4796 __ LoadFromOffset(kLoadWord, card, TR, Thread::CardTableOffset<kArmWordSize>().Int32Value()); 4797 __ Lsr(temp, object, gc::accounting::CardTable::kCardShift); 4798 __ strb(card, Address(card, temp)); 4799 if (can_be_null) { 4800 __ Bind(&is_null); 4801 } 4802} 4803 4804void LocationsBuilderARM::VisitParallelMove(HParallelMove* instruction ATTRIBUTE_UNUSED) { 4805 LOG(FATAL) << "Unreachable"; 4806} 4807 4808void InstructionCodeGeneratorARM::VisitParallelMove(HParallelMove* instruction) { 4809 codegen_->GetMoveResolver()->EmitNativeCode(instruction); 4810} 4811 4812void LocationsBuilderARM::VisitSuspendCheck(HSuspendCheck* instruction) { 4813 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath); 4814} 4815 4816void InstructionCodeGeneratorARM::VisitSuspendCheck(HSuspendCheck* instruction) { 4817 HBasicBlock* block = instruction->GetBlock(); 4818 if (block->GetLoopInformation() != nullptr) { 4819 DCHECK(block->GetLoopInformation()->GetSuspendCheck() == instruction); 4820 // The back edge will generate the suspend check. 4821 return; 4822 } 4823 if (block->IsEntryBlock() && instruction->GetNext()->IsGoto()) { 4824 // The goto will generate the suspend check. 4825 return; 4826 } 4827 GenerateSuspendCheck(instruction, nullptr); 4828} 4829 4830void InstructionCodeGeneratorARM::GenerateSuspendCheck(HSuspendCheck* instruction, 4831 HBasicBlock* successor) { 4832 SuspendCheckSlowPathARM* slow_path = 4833 down_cast<SuspendCheckSlowPathARM*>(instruction->GetSlowPath()); 4834 if (slow_path == nullptr) { 4835 slow_path = new (GetGraph()->GetArena()) SuspendCheckSlowPathARM(instruction, successor); 4836 instruction->SetSlowPath(slow_path); 4837 codegen_->AddSlowPath(slow_path); 4838 if (successor != nullptr) { 4839 DCHECK(successor->IsLoopHeader()); 4840 codegen_->ClearSpillSlotsFromLoopPhisInStackMap(instruction); 4841 } 4842 } else { 4843 DCHECK_EQ(slow_path->GetSuccessor(), successor); 4844 } 4845 4846 __ LoadFromOffset( 4847 kLoadUnsignedHalfword, IP, TR, Thread::ThreadFlagsOffset<kArmWordSize>().Int32Value()); 4848 if (successor == nullptr) { 4849 __ CompareAndBranchIfNonZero(IP, slow_path->GetEntryLabel()); 4850 __ Bind(slow_path->GetReturnLabel()); 4851 } else { 4852 __ CompareAndBranchIfZero(IP, codegen_->GetLabelOf(successor)); 4853 __ b(slow_path->GetEntryLabel()); 4854 } 4855} 4856 4857ArmAssembler* ParallelMoveResolverARM::GetAssembler() const { 4858 return codegen_->GetAssembler(); 4859} 4860 4861void ParallelMoveResolverARM::EmitMove(size_t index) { 4862 MoveOperands* move = moves_[index]; 4863 Location source = move->GetSource(); 4864 Location destination = move->GetDestination(); 4865 4866 if (source.IsRegister()) { 4867 if (destination.IsRegister()) { 4868 __ Mov(destination.AsRegister<Register>(), source.AsRegister<Register>()); 4869 } else if (destination.IsFpuRegister()) { 4870 __ vmovsr(destination.AsFpuRegister<SRegister>(), source.AsRegister<Register>()); 4871 } else { 4872 DCHECK(destination.IsStackSlot()); 4873 __ StoreToOffset(kStoreWord, source.AsRegister<Register>(), 4874 SP, destination.GetStackIndex()); 4875 } 4876 } else if (source.IsStackSlot()) { 4877 if (destination.IsRegister()) { 4878 __ LoadFromOffset(kLoadWord, destination.AsRegister<Register>(), 4879 SP, source.GetStackIndex()); 4880 } else if (destination.IsFpuRegister()) { 4881 __ LoadSFromOffset(destination.AsFpuRegister<SRegister>(), SP, source.GetStackIndex()); 4882 } else { 4883 DCHECK(destination.IsStackSlot()); 4884 __ LoadFromOffset(kLoadWord, IP, SP, source.GetStackIndex()); 4885 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 4886 } 4887 } else if (source.IsFpuRegister()) { 4888 if (destination.IsRegister()) { 4889 __ vmovrs(destination.AsRegister<Register>(), source.AsFpuRegister<SRegister>()); 4890 } else if (destination.IsFpuRegister()) { 4891 __ vmovs(destination.AsFpuRegister<SRegister>(), source.AsFpuRegister<SRegister>()); 4892 } else { 4893 DCHECK(destination.IsStackSlot()); 4894 __ StoreSToOffset(source.AsFpuRegister<SRegister>(), SP, destination.GetStackIndex()); 4895 } 4896 } else if (source.IsDoubleStackSlot()) { 4897 if (destination.IsDoubleStackSlot()) { 4898 __ LoadDFromOffset(DTMP, SP, source.GetStackIndex()); 4899 __ StoreDToOffset(DTMP, SP, destination.GetStackIndex()); 4900 } else if (destination.IsRegisterPair()) { 4901 DCHECK(ExpectedPairLayout(destination)); 4902 __ LoadFromOffset( 4903 kLoadWordPair, destination.AsRegisterPairLow<Register>(), SP, source.GetStackIndex()); 4904 } else { 4905 DCHECK(destination.IsFpuRegisterPair()) << destination; 4906 __ LoadDFromOffset(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), 4907 SP, 4908 source.GetStackIndex()); 4909 } 4910 } else if (source.IsRegisterPair()) { 4911 if (destination.IsRegisterPair()) { 4912 __ Mov(destination.AsRegisterPairLow<Register>(), source.AsRegisterPairLow<Register>()); 4913 __ Mov(destination.AsRegisterPairHigh<Register>(), source.AsRegisterPairHigh<Register>()); 4914 } else if (destination.IsFpuRegisterPair()) { 4915 __ vmovdrr(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), 4916 source.AsRegisterPairLow<Register>(), 4917 source.AsRegisterPairHigh<Register>()); 4918 } else { 4919 DCHECK(destination.IsDoubleStackSlot()) << destination; 4920 DCHECK(ExpectedPairLayout(source)); 4921 __ StoreToOffset( 4922 kStoreWordPair, source.AsRegisterPairLow<Register>(), SP, destination.GetStackIndex()); 4923 } 4924 } else if (source.IsFpuRegisterPair()) { 4925 if (destination.IsRegisterPair()) { 4926 __ vmovrrd(destination.AsRegisterPairLow<Register>(), 4927 destination.AsRegisterPairHigh<Register>(), 4928 FromLowSToD(source.AsFpuRegisterPairLow<SRegister>())); 4929 } else if (destination.IsFpuRegisterPair()) { 4930 __ vmovd(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), 4931 FromLowSToD(source.AsFpuRegisterPairLow<SRegister>())); 4932 } else { 4933 DCHECK(destination.IsDoubleStackSlot()) << destination; 4934 __ StoreDToOffset(FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()), 4935 SP, 4936 destination.GetStackIndex()); 4937 } 4938 } else { 4939 DCHECK(source.IsConstant()) << source; 4940 HConstant* constant = source.GetConstant(); 4941 if (constant->IsIntConstant() || constant->IsNullConstant()) { 4942 int32_t value = CodeGenerator::GetInt32ValueOf(constant); 4943 if (destination.IsRegister()) { 4944 __ LoadImmediate(destination.AsRegister<Register>(), value); 4945 } else { 4946 DCHECK(destination.IsStackSlot()); 4947 __ LoadImmediate(IP, value); 4948 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 4949 } 4950 } else if (constant->IsLongConstant()) { 4951 int64_t value = constant->AsLongConstant()->GetValue(); 4952 if (destination.IsRegisterPair()) { 4953 __ LoadImmediate(destination.AsRegisterPairLow<Register>(), Low32Bits(value)); 4954 __ LoadImmediate(destination.AsRegisterPairHigh<Register>(), High32Bits(value)); 4955 } else { 4956 DCHECK(destination.IsDoubleStackSlot()) << destination; 4957 __ LoadImmediate(IP, Low32Bits(value)); 4958 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 4959 __ LoadImmediate(IP, High32Bits(value)); 4960 __ StoreToOffset(kStoreWord, IP, SP, destination.GetHighStackIndex(kArmWordSize)); 4961 } 4962 } else if (constant->IsDoubleConstant()) { 4963 double value = constant->AsDoubleConstant()->GetValue(); 4964 if (destination.IsFpuRegisterPair()) { 4965 __ LoadDImmediate(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), value); 4966 } else { 4967 DCHECK(destination.IsDoubleStackSlot()) << destination; 4968 uint64_t int_value = bit_cast<uint64_t, double>(value); 4969 __ LoadImmediate(IP, Low32Bits(int_value)); 4970 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 4971 __ LoadImmediate(IP, High32Bits(int_value)); 4972 __ StoreToOffset(kStoreWord, IP, SP, destination.GetHighStackIndex(kArmWordSize)); 4973 } 4974 } else { 4975 DCHECK(constant->IsFloatConstant()) << constant->DebugName(); 4976 float value = constant->AsFloatConstant()->GetValue(); 4977 if (destination.IsFpuRegister()) { 4978 __ LoadSImmediate(destination.AsFpuRegister<SRegister>(), value); 4979 } else { 4980 DCHECK(destination.IsStackSlot()); 4981 __ LoadImmediate(IP, bit_cast<int32_t, float>(value)); 4982 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 4983 } 4984 } 4985 } 4986} 4987 4988void ParallelMoveResolverARM::Exchange(Register reg, int mem) { 4989 __ Mov(IP, reg); 4990 __ LoadFromOffset(kLoadWord, reg, SP, mem); 4991 __ StoreToOffset(kStoreWord, IP, SP, mem); 4992} 4993 4994void ParallelMoveResolverARM::Exchange(int mem1, int mem2) { 4995 ScratchRegisterScope ensure_scratch(this, IP, R0, codegen_->GetNumberOfCoreRegisters()); 4996 int stack_offset = ensure_scratch.IsSpilled() ? kArmWordSize : 0; 4997 __ LoadFromOffset(kLoadWord, static_cast<Register>(ensure_scratch.GetRegister()), 4998 SP, mem1 + stack_offset); 4999 __ LoadFromOffset(kLoadWord, IP, SP, mem2 + stack_offset); 5000 __ StoreToOffset(kStoreWord, static_cast<Register>(ensure_scratch.GetRegister()), 5001 SP, mem2 + stack_offset); 5002 __ StoreToOffset(kStoreWord, IP, SP, mem1 + stack_offset); 5003} 5004 5005void ParallelMoveResolverARM::EmitSwap(size_t index) { 5006 MoveOperands* move = moves_[index]; 5007 Location source = move->GetSource(); 5008 Location destination = move->GetDestination(); 5009 5010 if (source.IsRegister() && destination.IsRegister()) { 5011 DCHECK_NE(source.AsRegister<Register>(), IP); 5012 DCHECK_NE(destination.AsRegister<Register>(), IP); 5013 __ Mov(IP, source.AsRegister<Register>()); 5014 __ Mov(source.AsRegister<Register>(), destination.AsRegister<Register>()); 5015 __ Mov(destination.AsRegister<Register>(), IP); 5016 } else if (source.IsRegister() && destination.IsStackSlot()) { 5017 Exchange(source.AsRegister<Register>(), destination.GetStackIndex()); 5018 } else if (source.IsStackSlot() && destination.IsRegister()) { 5019 Exchange(destination.AsRegister<Register>(), source.GetStackIndex()); 5020 } else if (source.IsStackSlot() && destination.IsStackSlot()) { 5021 Exchange(source.GetStackIndex(), destination.GetStackIndex()); 5022 } else if (source.IsFpuRegister() && destination.IsFpuRegister()) { 5023 __ vmovrs(IP, source.AsFpuRegister<SRegister>()); 5024 __ vmovs(source.AsFpuRegister<SRegister>(), destination.AsFpuRegister<SRegister>()); 5025 __ vmovsr(destination.AsFpuRegister<SRegister>(), IP); 5026 } else if (source.IsRegisterPair() && destination.IsRegisterPair()) { 5027 __ vmovdrr(DTMP, source.AsRegisterPairLow<Register>(), source.AsRegisterPairHigh<Register>()); 5028 __ Mov(source.AsRegisterPairLow<Register>(), destination.AsRegisterPairLow<Register>()); 5029 __ Mov(source.AsRegisterPairHigh<Register>(), destination.AsRegisterPairHigh<Register>()); 5030 __ vmovrrd(destination.AsRegisterPairLow<Register>(), 5031 destination.AsRegisterPairHigh<Register>(), 5032 DTMP); 5033 } else if (source.IsRegisterPair() || destination.IsRegisterPair()) { 5034 Register low_reg = source.IsRegisterPair() 5035 ? source.AsRegisterPairLow<Register>() 5036 : destination.AsRegisterPairLow<Register>(); 5037 int mem = source.IsRegisterPair() 5038 ? destination.GetStackIndex() 5039 : source.GetStackIndex(); 5040 DCHECK(ExpectedPairLayout(source.IsRegisterPair() ? source : destination)); 5041 __ vmovdrr(DTMP, low_reg, static_cast<Register>(low_reg + 1)); 5042 __ LoadFromOffset(kLoadWordPair, low_reg, SP, mem); 5043 __ StoreDToOffset(DTMP, SP, mem); 5044 } else if (source.IsFpuRegisterPair() && destination.IsFpuRegisterPair()) { 5045 DRegister first = FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()); 5046 DRegister second = FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()); 5047 __ vmovd(DTMP, first); 5048 __ vmovd(first, second); 5049 __ vmovd(second, DTMP); 5050 } else if (source.IsFpuRegisterPair() || destination.IsFpuRegisterPair()) { 5051 DRegister reg = source.IsFpuRegisterPair() 5052 ? FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()) 5053 : FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()); 5054 int mem = source.IsFpuRegisterPair() 5055 ? destination.GetStackIndex() 5056 : source.GetStackIndex(); 5057 __ vmovd(DTMP, reg); 5058 __ LoadDFromOffset(reg, SP, mem); 5059 __ StoreDToOffset(DTMP, SP, mem); 5060 } else if (source.IsFpuRegister() || destination.IsFpuRegister()) { 5061 SRegister reg = source.IsFpuRegister() ? source.AsFpuRegister<SRegister>() 5062 : destination.AsFpuRegister<SRegister>(); 5063 int mem = source.IsFpuRegister() 5064 ? destination.GetStackIndex() 5065 : source.GetStackIndex(); 5066 5067 __ vmovrs(IP, reg); 5068 __ LoadSFromOffset(reg, SP, mem); 5069 __ StoreToOffset(kStoreWord, IP, SP, mem); 5070 } else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) { 5071 Exchange(source.GetStackIndex(), destination.GetStackIndex()); 5072 Exchange(source.GetHighStackIndex(kArmWordSize), destination.GetHighStackIndex(kArmWordSize)); 5073 } else { 5074 LOG(FATAL) << "Unimplemented" << source << " <-> " << destination; 5075 } 5076} 5077 5078void ParallelMoveResolverARM::SpillScratch(int reg) { 5079 __ Push(static_cast<Register>(reg)); 5080} 5081 5082void ParallelMoveResolverARM::RestoreScratch(int reg) { 5083 __ Pop(static_cast<Register>(reg)); 5084} 5085 5086HLoadClass::LoadKind CodeGeneratorARM::GetSupportedLoadClassKind( 5087 HLoadClass::LoadKind desired_class_load_kind) { 5088 if (kEmitCompilerReadBarrier) { 5089 switch (desired_class_load_kind) { 5090 case HLoadClass::LoadKind::kBootImageLinkTimeAddress: 5091 case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: 5092 case HLoadClass::LoadKind::kBootImageAddress: 5093 // TODO: Implement for read barrier. 5094 return HLoadClass::LoadKind::kDexCacheViaMethod; 5095 default: 5096 break; 5097 } 5098 } 5099 switch (desired_class_load_kind) { 5100 case HLoadClass::LoadKind::kReferrersClass: 5101 break; 5102 case HLoadClass::LoadKind::kBootImageLinkTimeAddress: 5103 DCHECK(!GetCompilerOptions().GetCompilePic()); 5104 break; 5105 case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: 5106 DCHECK(GetCompilerOptions().GetCompilePic()); 5107 break; 5108 case HLoadClass::LoadKind::kBootImageAddress: 5109 break; 5110 case HLoadClass::LoadKind::kDexCacheAddress: 5111 DCHECK(Runtime::Current()->UseJitCompilation()); 5112 break; 5113 case HLoadClass::LoadKind::kDexCachePcRelative: 5114 DCHECK(!Runtime::Current()->UseJitCompilation()); 5115 // We disable pc-relative load when there is an irreducible loop, as the optimization 5116 // is incompatible with it. 5117 // TODO: Create as many ArmDexCacheArraysBase instructions as needed for methods 5118 // with irreducible loops. 5119 if (GetGraph()->HasIrreducibleLoops()) { 5120 return HLoadClass::LoadKind::kDexCacheViaMethod; 5121 } 5122 break; 5123 case HLoadClass::LoadKind::kDexCacheViaMethod: 5124 break; 5125 } 5126 return desired_class_load_kind; 5127} 5128 5129void LocationsBuilderARM::VisitLoadClass(HLoadClass* cls) { 5130 if (cls->NeedsAccessCheck()) { 5131 InvokeRuntimeCallingConvention calling_convention; 5132 CodeGenerator::CreateLoadClassLocationSummary( 5133 cls, 5134 Location::RegisterLocation(calling_convention.GetRegisterAt(0)), 5135 Location::RegisterLocation(R0), 5136 /* code_generator_supports_read_barrier */ true); 5137 return; 5138 } 5139 5140 LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || kEmitCompilerReadBarrier) 5141 ? LocationSummary::kCallOnSlowPath 5142 : LocationSummary::kNoCall; 5143 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind); 5144 HLoadClass::LoadKind load_kind = cls->GetLoadKind(); 5145 if (load_kind == HLoadClass::LoadKind::kReferrersClass || 5146 load_kind == HLoadClass::LoadKind::kDexCacheViaMethod || 5147 load_kind == HLoadClass::LoadKind::kDexCachePcRelative) { 5148 locations->SetInAt(0, Location::RequiresRegister()); 5149 } 5150 locations->SetOut(Location::RequiresRegister()); 5151} 5152 5153void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) { 5154 LocationSummary* locations = cls->GetLocations(); 5155 if (cls->NeedsAccessCheck()) { 5156 codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex()); 5157 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInitializeTypeAndVerifyAccess), 5158 cls, 5159 cls->GetDexPc(), 5160 nullptr); 5161 CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>(); 5162 return; 5163 } 5164 5165 Location out_loc = locations->Out(); 5166 Register out = out_loc.AsRegister<Register>(); 5167 5168 bool generate_null_check = false; 5169 switch (cls->GetLoadKind()) { 5170 case HLoadClass::LoadKind::kReferrersClass: { 5171 DCHECK(!cls->CanCallRuntime()); 5172 DCHECK(!cls->MustGenerateClinitCheck()); 5173 // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_ 5174 Register current_method = locations->InAt(0).AsRegister<Register>(); 5175 GenerateGcRootFieldLoad( 5176 cls, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value()); 5177 break; 5178 } 5179 case HLoadClass::LoadKind::kBootImageLinkTimeAddress: { 5180 DCHECK(!kEmitCompilerReadBarrier); 5181 __ LoadLiteral(out, codegen_->DeduplicateBootImageTypeLiteral(cls->GetDexFile(), 5182 cls->GetTypeIndex())); 5183 break; 5184 } 5185 case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: { 5186 DCHECK(!kEmitCompilerReadBarrier); 5187 CodeGeneratorARM::PcRelativePatchInfo* labels = 5188 codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex()); 5189 __ BindTrackedLabel(&labels->movw_label); 5190 __ movw(out, /* placeholder */ 0u); 5191 __ BindTrackedLabel(&labels->movt_label); 5192 __ movt(out, /* placeholder */ 0u); 5193 __ BindTrackedLabel(&labels->add_pc_label); 5194 __ add(out, out, ShifterOperand(PC)); 5195 break; 5196 } 5197 case HLoadClass::LoadKind::kBootImageAddress: { 5198 DCHECK(!kEmitCompilerReadBarrier); 5199 DCHECK_NE(cls->GetAddress(), 0u); 5200 uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress()); 5201 __ LoadLiteral(out, codegen_->DeduplicateBootImageAddressLiteral(address)); 5202 break; 5203 } 5204 case HLoadClass::LoadKind::kDexCacheAddress: { 5205 DCHECK_NE(cls->GetAddress(), 0u); 5206 uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress()); 5207 // 16-bit LDR immediate has a 5-bit offset multiplied by the size and that gives 5208 // a 128B range. To try and reduce the number of literals if we load multiple types, 5209 // simply split the dex cache address to a 128B aligned base loaded from a literal 5210 // and the remaining offset embedded in the load. 5211 static_assert(sizeof(GcRoot<mirror::Class>) == 4u, "Expected GC root to be 4 bytes."); 5212 DCHECK_ALIGNED(cls->GetAddress(), 4u); 5213 constexpr size_t offset_bits = /* encoded bits */ 5 + /* scale */ 2; 5214 uint32_t base_address = address & ~MaxInt<uint32_t>(offset_bits); 5215 uint32_t offset = address & MaxInt<uint32_t>(offset_bits); 5216 __ LoadLiteral(out, codegen_->DeduplicateDexCacheAddressLiteral(base_address)); 5217 // /* GcRoot<mirror::Class> */ out = *(base_address + offset) 5218 GenerateGcRootFieldLoad(cls, out_loc, out, offset); 5219 generate_null_check = !cls->IsInDexCache(); 5220 break; 5221 } 5222 case HLoadClass::LoadKind::kDexCachePcRelative: { 5223 Register base_reg = locations->InAt(0).AsRegister<Register>(); 5224 HArmDexCacheArraysBase* base = cls->InputAt(0)->AsArmDexCacheArraysBase(); 5225 int32_t offset = cls->GetDexCacheElementOffset() - base->GetElementOffset(); 5226 // /* GcRoot<mirror::Class> */ out = *(dex_cache_arrays_base + offset) 5227 GenerateGcRootFieldLoad(cls, out_loc, base_reg, offset); 5228 generate_null_check = !cls->IsInDexCache(); 5229 break; 5230 } 5231 case HLoadClass::LoadKind::kDexCacheViaMethod: { 5232 // /* GcRoot<mirror::Class>[] */ out = 5233 // current_method.ptr_sized_fields_->dex_cache_resolved_types_ 5234 Register current_method = locations->InAt(0).AsRegister<Register>(); 5235 __ LoadFromOffset(kLoadWord, 5236 out, 5237 current_method, 5238 ArtMethod::DexCacheResolvedTypesOffset(kArmPointerSize).Int32Value()); 5239 // /* GcRoot<mirror::Class> */ out = out[type_index] 5240 size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex()); 5241 GenerateGcRootFieldLoad(cls, out_loc, out, offset); 5242 generate_null_check = !cls->IsInDexCache(); 5243 } 5244 } 5245 5246 if (generate_null_check || cls->MustGenerateClinitCheck()) { 5247 DCHECK(cls->CanCallRuntime()); 5248 SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM( 5249 cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck()); 5250 codegen_->AddSlowPath(slow_path); 5251 if (generate_null_check) { 5252 __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel()); 5253 } 5254 if (cls->MustGenerateClinitCheck()) { 5255 GenerateClassInitializationCheck(slow_path, out); 5256 } else { 5257 __ Bind(slow_path->GetExitLabel()); 5258 } 5259 } 5260} 5261 5262void LocationsBuilderARM::VisitClinitCheck(HClinitCheck* check) { 5263 LocationSummary* locations = 5264 new (GetGraph()->GetArena()) LocationSummary(check, LocationSummary::kCallOnSlowPath); 5265 locations->SetInAt(0, Location::RequiresRegister()); 5266 if (check->HasUses()) { 5267 locations->SetOut(Location::SameAsFirstInput()); 5268 } 5269} 5270 5271void InstructionCodeGeneratorARM::VisitClinitCheck(HClinitCheck* check) { 5272 // We assume the class is not null. 5273 SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM( 5274 check->GetLoadClass(), check, check->GetDexPc(), true); 5275 codegen_->AddSlowPath(slow_path); 5276 GenerateClassInitializationCheck(slow_path, 5277 check->GetLocations()->InAt(0).AsRegister<Register>()); 5278} 5279 5280void InstructionCodeGeneratorARM::GenerateClassInitializationCheck( 5281 SlowPathCode* slow_path, Register class_reg) { 5282 __ LoadFromOffset(kLoadWord, IP, class_reg, mirror::Class::StatusOffset().Int32Value()); 5283 __ cmp(IP, ShifterOperand(mirror::Class::kStatusInitialized)); 5284 __ b(slow_path->GetEntryLabel(), LT); 5285 // Even if the initialized flag is set, we may be in a situation where caches are not synced 5286 // properly. Therefore, we do a memory fence. 5287 __ dmb(ISH); 5288 __ Bind(slow_path->GetExitLabel()); 5289} 5290 5291HLoadString::LoadKind CodeGeneratorARM::GetSupportedLoadStringKind( 5292 HLoadString::LoadKind desired_string_load_kind) { 5293 if (kEmitCompilerReadBarrier) { 5294 switch (desired_string_load_kind) { 5295 case HLoadString::LoadKind::kBootImageLinkTimeAddress: 5296 case HLoadString::LoadKind::kBootImageLinkTimePcRelative: 5297 case HLoadString::LoadKind::kBootImageAddress: 5298 // TODO: Implement for read barrier. 5299 return HLoadString::LoadKind::kDexCacheViaMethod; 5300 default: 5301 break; 5302 } 5303 } 5304 switch (desired_string_load_kind) { 5305 case HLoadString::LoadKind::kBootImageLinkTimeAddress: 5306 DCHECK(!GetCompilerOptions().GetCompilePic()); 5307 break; 5308 case HLoadString::LoadKind::kBootImageLinkTimePcRelative: 5309 DCHECK(GetCompilerOptions().GetCompilePic()); 5310 break; 5311 case HLoadString::LoadKind::kBootImageAddress: 5312 break; 5313 case HLoadString::LoadKind::kDexCacheAddress: 5314 DCHECK(Runtime::Current()->UseJitCompilation()); 5315 break; 5316 case HLoadString::LoadKind::kDexCachePcRelative: 5317 DCHECK(!Runtime::Current()->UseJitCompilation()); 5318 // We disable pc-relative load when there is an irreducible loop, as the optimization 5319 // is incompatible with it. 5320 // TODO: Create as many ArmDexCacheArraysBase instructions as needed for methods 5321 // with irreducible loops. 5322 if (GetGraph()->HasIrreducibleLoops()) { 5323 return HLoadString::LoadKind::kDexCacheViaMethod; 5324 } 5325 break; 5326 case HLoadString::LoadKind::kDexCacheViaMethod: 5327 break; 5328 } 5329 return desired_string_load_kind; 5330} 5331 5332void LocationsBuilderARM::VisitLoadString(HLoadString* load) { 5333 LocationSummary::CallKind call_kind = (load->NeedsEnvironment() || kEmitCompilerReadBarrier) 5334 ? LocationSummary::kCallOnSlowPath 5335 : LocationSummary::kNoCall; 5336 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind); 5337 HLoadString::LoadKind load_kind = load->GetLoadKind(); 5338 if (load_kind == HLoadString::LoadKind::kDexCacheViaMethod || 5339 load_kind == HLoadString::LoadKind::kDexCachePcRelative) { 5340 locations->SetInAt(0, Location::RequiresRegister()); 5341 } 5342 locations->SetOut(Location::RequiresRegister()); 5343} 5344 5345void InstructionCodeGeneratorARM::VisitLoadString(HLoadString* load) { 5346 LocationSummary* locations = load->GetLocations(); 5347 Location out_loc = locations->Out(); 5348 Register out = out_loc.AsRegister<Register>(); 5349 5350 switch (load->GetLoadKind()) { 5351 case HLoadString::LoadKind::kBootImageLinkTimeAddress: { 5352 DCHECK(!kEmitCompilerReadBarrier); 5353 __ LoadLiteral(out, codegen_->DeduplicateBootImageStringLiteral(load->GetDexFile(), 5354 load->GetStringIndex())); 5355 return; // No dex cache slow path. 5356 } 5357 case HLoadString::LoadKind::kBootImageLinkTimePcRelative: { 5358 DCHECK(!kEmitCompilerReadBarrier); 5359 CodeGeneratorARM::PcRelativePatchInfo* labels = 5360 codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex()); 5361 __ BindTrackedLabel(&labels->movw_label); 5362 __ movw(out, /* placeholder */ 0u); 5363 __ BindTrackedLabel(&labels->movt_label); 5364 __ movt(out, /* placeholder */ 0u); 5365 __ BindTrackedLabel(&labels->add_pc_label); 5366 __ add(out, out, ShifterOperand(PC)); 5367 return; // No dex cache slow path. 5368 } 5369 case HLoadString::LoadKind::kBootImageAddress: { 5370 DCHECK(!kEmitCompilerReadBarrier); 5371 DCHECK_NE(load->GetAddress(), 0u); 5372 uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress()); 5373 __ LoadLiteral(out, codegen_->DeduplicateBootImageAddressLiteral(address)); 5374 return; // No dex cache slow path. 5375 } 5376 case HLoadString::LoadKind::kDexCacheAddress: { 5377 DCHECK_NE(load->GetAddress(), 0u); 5378 uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress()); 5379 // 16-bit LDR immediate has a 5-bit offset multiplied by the size and that gives 5380 // a 128B range. To try and reduce the number of literals if we load multiple strings, 5381 // simply split the dex cache address to a 128B aligned base loaded from a literal 5382 // and the remaining offset embedded in the load. 5383 static_assert(sizeof(GcRoot<mirror::String>) == 4u, "Expected GC root to be 4 bytes."); 5384 DCHECK_ALIGNED(load->GetAddress(), 4u); 5385 constexpr size_t offset_bits = /* encoded bits */ 5 + /* scale */ 2; 5386 uint32_t base_address = address & ~MaxInt<uint32_t>(offset_bits); 5387 uint32_t offset = address & MaxInt<uint32_t>(offset_bits); 5388 __ LoadLiteral(out, codegen_->DeduplicateDexCacheAddressLiteral(base_address)); 5389 // /* GcRoot<mirror::String> */ out = *(base_address + offset) 5390 GenerateGcRootFieldLoad(load, out_loc, out, offset); 5391 break; 5392 } 5393 case HLoadString::LoadKind::kDexCachePcRelative: { 5394 Register base_reg = locations->InAt(0).AsRegister<Register>(); 5395 HArmDexCacheArraysBase* base = load->InputAt(0)->AsArmDexCacheArraysBase(); 5396 int32_t offset = load->GetDexCacheElementOffset() - base->GetElementOffset(); 5397 // /* GcRoot<mirror::String> */ out = *(dex_cache_arrays_base + offset) 5398 GenerateGcRootFieldLoad(load, out_loc, base_reg, offset); 5399 break; 5400 } 5401 case HLoadString::LoadKind::kDexCacheViaMethod: { 5402 Register current_method = locations->InAt(0).AsRegister<Register>(); 5403 5404 // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_ 5405 GenerateGcRootFieldLoad( 5406 load, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value()); 5407 // /* GcRoot<mirror::String>[] */ out = out->dex_cache_strings_ 5408 __ LoadFromOffset(kLoadWord, out, out, mirror::Class::DexCacheStringsOffset().Int32Value()); 5409 // /* GcRoot<mirror::String> */ out = out[string_index] 5410 GenerateGcRootFieldLoad( 5411 load, out_loc, out, CodeGenerator::GetCacheOffset(load->GetStringIndex())); 5412 break; 5413 } 5414 default: 5415 LOG(FATAL) << "Unexpected load kind: " << load->GetLoadKind(); 5416 UNREACHABLE(); 5417 } 5418 5419 if (!load->IsInDexCache()) { 5420 SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathARM(load); 5421 codegen_->AddSlowPath(slow_path); 5422 __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel()); 5423 __ Bind(slow_path->GetExitLabel()); 5424 } 5425} 5426 5427static int32_t GetExceptionTlsOffset() { 5428 return Thread::ExceptionOffset<kArmWordSize>().Int32Value(); 5429} 5430 5431void LocationsBuilderARM::VisitLoadException(HLoadException* load) { 5432 LocationSummary* locations = 5433 new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kNoCall); 5434 locations->SetOut(Location::RequiresRegister()); 5435} 5436 5437void InstructionCodeGeneratorARM::VisitLoadException(HLoadException* load) { 5438 Register out = load->GetLocations()->Out().AsRegister<Register>(); 5439 __ LoadFromOffset(kLoadWord, out, TR, GetExceptionTlsOffset()); 5440} 5441 5442void LocationsBuilderARM::VisitClearException(HClearException* clear) { 5443 new (GetGraph()->GetArena()) LocationSummary(clear, LocationSummary::kNoCall); 5444} 5445 5446void InstructionCodeGeneratorARM::VisitClearException(HClearException* clear ATTRIBUTE_UNUSED) { 5447 __ LoadImmediate(IP, 0); 5448 __ StoreToOffset(kStoreWord, IP, TR, GetExceptionTlsOffset()); 5449} 5450 5451void LocationsBuilderARM::VisitThrow(HThrow* instruction) { 5452 LocationSummary* locations = 5453 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); 5454 InvokeRuntimeCallingConvention calling_convention; 5455 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 5456} 5457 5458void InstructionCodeGeneratorARM::VisitThrow(HThrow* instruction) { 5459 codegen_->InvokeRuntime( 5460 QUICK_ENTRY_POINT(pDeliverException), instruction, instruction->GetDexPc(), nullptr); 5461 CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>(); 5462} 5463 5464static bool TypeCheckNeedsATemporary(TypeCheckKind type_check_kind) { 5465 return kEmitCompilerReadBarrier && 5466 (kUseBakerReadBarrier || 5467 type_check_kind == TypeCheckKind::kAbstractClassCheck || 5468 type_check_kind == TypeCheckKind::kClassHierarchyCheck || 5469 type_check_kind == TypeCheckKind::kArrayObjectCheck); 5470} 5471 5472void LocationsBuilderARM::VisitInstanceOf(HInstanceOf* instruction) { 5473 LocationSummary::CallKind call_kind = LocationSummary::kNoCall; 5474 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind(); 5475 switch (type_check_kind) { 5476 case TypeCheckKind::kExactCheck: 5477 case TypeCheckKind::kAbstractClassCheck: 5478 case TypeCheckKind::kClassHierarchyCheck: 5479 case TypeCheckKind::kArrayObjectCheck: 5480 call_kind = 5481 kEmitCompilerReadBarrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall; 5482 break; 5483 case TypeCheckKind::kArrayCheck: 5484 case TypeCheckKind::kUnresolvedCheck: 5485 case TypeCheckKind::kInterfaceCheck: 5486 call_kind = LocationSummary::kCallOnSlowPath; 5487 break; 5488 } 5489 5490 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); 5491 locations->SetInAt(0, Location::RequiresRegister()); 5492 locations->SetInAt(1, Location::RequiresRegister()); 5493 // The "out" register is used as a temporary, so it overlaps with the inputs. 5494 // Note that TypeCheckSlowPathARM uses this register too. 5495 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 5496 // When read barriers are enabled, we need a temporary register for 5497 // some cases. 5498 if (TypeCheckNeedsATemporary(type_check_kind)) { 5499 locations->AddTemp(Location::RequiresRegister()); 5500 } 5501} 5502 5503void InstructionCodeGeneratorARM::VisitInstanceOf(HInstanceOf* instruction) { 5504 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind(); 5505 LocationSummary* locations = instruction->GetLocations(); 5506 Location obj_loc = locations->InAt(0); 5507 Register obj = obj_loc.AsRegister<Register>(); 5508 Register cls = locations->InAt(1).AsRegister<Register>(); 5509 Location out_loc = locations->Out(); 5510 Register out = out_loc.AsRegister<Register>(); 5511 Location maybe_temp_loc = TypeCheckNeedsATemporary(type_check_kind) ? 5512 locations->GetTemp(0) : 5513 Location::NoLocation(); 5514 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 5515 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); 5516 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); 5517 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value(); 5518 Label done, zero; 5519 SlowPathCode* slow_path = nullptr; 5520 5521 // Return 0 if `obj` is null. 5522 // avoid null check if we know obj is not null. 5523 if (instruction->MustDoNullCheck()) { 5524 __ CompareAndBranchIfZero(obj, &zero); 5525 } 5526 5527 // /* HeapReference<Class> */ out = obj->klass_ 5528 GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, maybe_temp_loc); 5529 5530 switch (type_check_kind) { 5531 case TypeCheckKind::kExactCheck: { 5532 __ cmp(out, ShifterOperand(cls)); 5533 // Classes must be equal for the instanceof to succeed. 5534 __ b(&zero, NE); 5535 __ LoadImmediate(out, 1); 5536 __ b(&done); 5537 break; 5538 } 5539 5540 case TypeCheckKind::kAbstractClassCheck: { 5541 // If the class is abstract, we eagerly fetch the super class of the 5542 // object to avoid doing a comparison we know will fail. 5543 Label loop; 5544 __ Bind(&loop); 5545 // /* HeapReference<Class> */ out = out->super_class_ 5546 GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, maybe_temp_loc); 5547 // If `out` is null, we use it for the result, and jump to `done`. 5548 __ CompareAndBranchIfZero(out, &done); 5549 __ cmp(out, ShifterOperand(cls)); 5550 __ b(&loop, NE); 5551 __ LoadImmediate(out, 1); 5552 if (zero.IsLinked()) { 5553 __ b(&done); 5554 } 5555 break; 5556 } 5557 5558 case TypeCheckKind::kClassHierarchyCheck: { 5559 // Walk over the class hierarchy to find a match. 5560 Label loop, success; 5561 __ Bind(&loop); 5562 __ cmp(out, ShifterOperand(cls)); 5563 __ b(&success, EQ); 5564 // /* HeapReference<Class> */ out = out->super_class_ 5565 GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, maybe_temp_loc); 5566 __ CompareAndBranchIfNonZero(out, &loop); 5567 // If `out` is null, we use it for the result, and jump to `done`. 5568 __ b(&done); 5569 __ Bind(&success); 5570 __ LoadImmediate(out, 1); 5571 if (zero.IsLinked()) { 5572 __ b(&done); 5573 } 5574 break; 5575 } 5576 5577 case TypeCheckKind::kArrayObjectCheck: { 5578 // Do an exact check. 5579 Label exact_check; 5580 __ cmp(out, ShifterOperand(cls)); 5581 __ b(&exact_check, EQ); 5582 // Otherwise, we need to check that the object's class is a non-primitive array. 5583 // /* HeapReference<Class> */ out = out->component_type_ 5584 GenerateReferenceLoadOneRegister(instruction, out_loc, component_offset, maybe_temp_loc); 5585 // If `out` is null, we use it for the result, and jump to `done`. 5586 __ CompareAndBranchIfZero(out, &done); 5587 __ LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset); 5588 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot"); 5589 __ CompareAndBranchIfNonZero(out, &zero); 5590 __ Bind(&exact_check); 5591 __ LoadImmediate(out, 1); 5592 __ b(&done); 5593 break; 5594 } 5595 5596 case TypeCheckKind::kArrayCheck: { 5597 __ cmp(out, ShifterOperand(cls)); 5598 DCHECK(locations->OnlyCallsOnSlowPath()); 5599 slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARM(instruction, 5600 /* is_fatal */ false); 5601 codegen_->AddSlowPath(slow_path); 5602 __ b(slow_path->GetEntryLabel(), NE); 5603 __ LoadImmediate(out, 1); 5604 if (zero.IsLinked()) { 5605 __ b(&done); 5606 } 5607 break; 5608 } 5609 5610 case TypeCheckKind::kUnresolvedCheck: 5611 case TypeCheckKind::kInterfaceCheck: { 5612 // Note that we indeed only call on slow path, but we always go 5613 // into the slow path for the unresolved and interface check 5614 // cases. 5615 // 5616 // We cannot directly call the InstanceofNonTrivial runtime 5617 // entry point without resorting to a type checking slow path 5618 // here (i.e. by calling InvokeRuntime directly), as it would 5619 // require to assign fixed registers for the inputs of this 5620 // HInstanceOf instruction (following the runtime calling 5621 // convention), which might be cluttered by the potential first 5622 // read barrier emission at the beginning of this method. 5623 // 5624 // TODO: Introduce a new runtime entry point taking the object 5625 // to test (instead of its class) as argument, and let it deal 5626 // with the read barrier issues. This will let us refactor this 5627 // case of the `switch` code as it was previously (with a direct 5628 // call to the runtime not using a type checking slow path). 5629 // This should also be beneficial for the other cases above. 5630 DCHECK(locations->OnlyCallsOnSlowPath()); 5631 slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARM(instruction, 5632 /* is_fatal */ false); 5633 codegen_->AddSlowPath(slow_path); 5634 __ b(slow_path->GetEntryLabel()); 5635 if (zero.IsLinked()) { 5636 __ b(&done); 5637 } 5638 break; 5639 } 5640 } 5641 5642 if (zero.IsLinked()) { 5643 __ Bind(&zero); 5644 __ LoadImmediate(out, 0); 5645 } 5646 5647 if (done.IsLinked()) { 5648 __ Bind(&done); 5649 } 5650 5651 if (slow_path != nullptr) { 5652 __ Bind(slow_path->GetExitLabel()); 5653 } 5654} 5655 5656void LocationsBuilderARM::VisitCheckCast(HCheckCast* instruction) { 5657 LocationSummary::CallKind call_kind = LocationSummary::kNoCall; 5658 bool throws_into_catch = instruction->CanThrowIntoCatchBlock(); 5659 5660 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind(); 5661 switch (type_check_kind) { 5662 case TypeCheckKind::kExactCheck: 5663 case TypeCheckKind::kAbstractClassCheck: 5664 case TypeCheckKind::kClassHierarchyCheck: 5665 case TypeCheckKind::kArrayObjectCheck: 5666 call_kind = (throws_into_catch || kEmitCompilerReadBarrier) ? 5667 LocationSummary::kCallOnSlowPath : 5668 LocationSummary::kNoCall; // In fact, call on a fatal (non-returning) slow path. 5669 break; 5670 case TypeCheckKind::kArrayCheck: 5671 case TypeCheckKind::kUnresolvedCheck: 5672 case TypeCheckKind::kInterfaceCheck: 5673 call_kind = LocationSummary::kCallOnSlowPath; 5674 break; 5675 } 5676 5677 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); 5678 locations->SetInAt(0, Location::RequiresRegister()); 5679 locations->SetInAt(1, Location::RequiresRegister()); 5680 // Note that TypeCheckSlowPathARM uses this "temp" register too. 5681 locations->AddTemp(Location::RequiresRegister()); 5682 // When read barriers are enabled, we need an additional temporary 5683 // register for some cases. 5684 if (TypeCheckNeedsATemporary(type_check_kind)) { 5685 locations->AddTemp(Location::RequiresRegister()); 5686 } 5687} 5688 5689void InstructionCodeGeneratorARM::VisitCheckCast(HCheckCast* instruction) { 5690 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind(); 5691 LocationSummary* locations = instruction->GetLocations(); 5692 Location obj_loc = locations->InAt(0); 5693 Register obj = obj_loc.AsRegister<Register>(); 5694 Register cls = locations->InAt(1).AsRegister<Register>(); 5695 Location temp_loc = locations->GetTemp(0); 5696 Register temp = temp_loc.AsRegister<Register>(); 5697 Location maybe_temp2_loc = TypeCheckNeedsATemporary(type_check_kind) ? 5698 locations->GetTemp(1) : 5699 Location::NoLocation(); 5700 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 5701 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); 5702 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); 5703 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value(); 5704 5705 bool is_type_check_slow_path_fatal = 5706 (type_check_kind == TypeCheckKind::kExactCheck || 5707 type_check_kind == TypeCheckKind::kAbstractClassCheck || 5708 type_check_kind == TypeCheckKind::kClassHierarchyCheck || 5709 type_check_kind == TypeCheckKind::kArrayObjectCheck) && 5710 !instruction->CanThrowIntoCatchBlock(); 5711 SlowPathCode* type_check_slow_path = 5712 new (GetGraph()->GetArena()) TypeCheckSlowPathARM(instruction, 5713 is_type_check_slow_path_fatal); 5714 codegen_->AddSlowPath(type_check_slow_path); 5715 5716 Label done; 5717 // Avoid null check if we know obj is not null. 5718 if (instruction->MustDoNullCheck()) { 5719 __ CompareAndBranchIfZero(obj, &done); 5720 } 5721 5722 // /* HeapReference<Class> */ temp = obj->klass_ 5723 GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); 5724 5725 switch (type_check_kind) { 5726 case TypeCheckKind::kExactCheck: 5727 case TypeCheckKind::kArrayCheck: { 5728 __ cmp(temp, ShifterOperand(cls)); 5729 // Jump to slow path for throwing the exception or doing a 5730 // more involved array check. 5731 __ b(type_check_slow_path->GetEntryLabel(), NE); 5732 break; 5733 } 5734 5735 case TypeCheckKind::kAbstractClassCheck: { 5736 // If the class is abstract, we eagerly fetch the super class of the 5737 // object to avoid doing a comparison we know will fail. 5738 Label loop, compare_classes; 5739 __ Bind(&loop); 5740 // /* HeapReference<Class> */ temp = temp->super_class_ 5741 GenerateReferenceLoadOneRegister(instruction, temp_loc, super_offset, maybe_temp2_loc); 5742 5743 // If the class reference currently in `temp` is not null, jump 5744 // to the `compare_classes` label to compare it with the checked 5745 // class. 5746 __ CompareAndBranchIfNonZero(temp, &compare_classes); 5747 // Otherwise, jump to the slow path to throw the exception. 5748 // 5749 // But before, move back the object's class into `temp` before 5750 // going into the slow path, as it has been overwritten in the 5751 // meantime. 5752 // /* HeapReference<Class> */ temp = obj->klass_ 5753 GenerateReferenceLoadTwoRegisters( 5754 instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); 5755 __ b(type_check_slow_path->GetEntryLabel()); 5756 5757 __ Bind(&compare_classes); 5758 __ cmp(temp, ShifterOperand(cls)); 5759 __ b(&loop, NE); 5760 break; 5761 } 5762 5763 case TypeCheckKind::kClassHierarchyCheck: { 5764 // Walk over the class hierarchy to find a match. 5765 Label loop; 5766 __ Bind(&loop); 5767 __ cmp(temp, ShifterOperand(cls)); 5768 __ b(&done, EQ); 5769 5770 // /* HeapReference<Class> */ temp = temp->super_class_ 5771 GenerateReferenceLoadOneRegister(instruction, temp_loc, super_offset, maybe_temp2_loc); 5772 5773 // If the class reference currently in `temp` is not null, jump 5774 // back at the beginning of the loop. 5775 __ CompareAndBranchIfNonZero(temp, &loop); 5776 // Otherwise, jump to the slow path to throw the exception. 5777 // 5778 // But before, move back the object's class into `temp` before 5779 // going into the slow path, as it has been overwritten in the 5780 // meantime. 5781 // /* HeapReference<Class> */ temp = obj->klass_ 5782 GenerateReferenceLoadTwoRegisters( 5783 instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); 5784 __ b(type_check_slow_path->GetEntryLabel()); 5785 break; 5786 } 5787 5788 case TypeCheckKind::kArrayObjectCheck: { 5789 // Do an exact check. 5790 Label check_non_primitive_component_type; 5791 __ cmp(temp, ShifterOperand(cls)); 5792 __ b(&done, EQ); 5793 5794 // Otherwise, we need to check that the object's class is a non-primitive array. 5795 // /* HeapReference<Class> */ temp = temp->component_type_ 5796 GenerateReferenceLoadOneRegister(instruction, temp_loc, component_offset, maybe_temp2_loc); 5797 5798 // If the component type is not null (i.e. the object is indeed 5799 // an array), jump to label `check_non_primitive_component_type` 5800 // to further check that this component type is not a primitive 5801 // type. 5802 __ CompareAndBranchIfNonZero(temp, &check_non_primitive_component_type); 5803 // Otherwise, jump to the slow path to throw the exception. 5804 // 5805 // But before, move back the object's class into `temp` before 5806 // going into the slow path, as it has been overwritten in the 5807 // meantime. 5808 // /* HeapReference<Class> */ temp = obj->klass_ 5809 GenerateReferenceLoadTwoRegisters( 5810 instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); 5811 __ b(type_check_slow_path->GetEntryLabel()); 5812 5813 __ Bind(&check_non_primitive_component_type); 5814 __ LoadFromOffset(kLoadUnsignedHalfword, temp, temp, primitive_offset); 5815 static_assert(Primitive::kPrimNot == 0, "Expected 0 for art::Primitive::kPrimNot"); 5816 __ CompareAndBranchIfZero(temp, &done); 5817 // Same comment as above regarding `temp` and the slow path. 5818 // /* HeapReference<Class> */ temp = obj->klass_ 5819 GenerateReferenceLoadTwoRegisters( 5820 instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); 5821 __ b(type_check_slow_path->GetEntryLabel()); 5822 break; 5823 } 5824 5825 case TypeCheckKind::kUnresolvedCheck: 5826 case TypeCheckKind::kInterfaceCheck: 5827 // We always go into the type check slow path for the unresolved 5828 // and interface check cases. 5829 // 5830 // We cannot directly call the CheckCast runtime entry point 5831 // without resorting to a type checking slow path here (i.e. by 5832 // calling InvokeRuntime directly), as it would require to 5833 // assign fixed registers for the inputs of this HInstanceOf 5834 // instruction (following the runtime calling convention), which 5835 // might be cluttered by the potential first read barrier 5836 // emission at the beginning of this method. 5837 // 5838 // TODO: Introduce a new runtime entry point taking the object 5839 // to test (instead of its class) as argument, and let it deal 5840 // with the read barrier issues. This will let us refactor this 5841 // case of the `switch` code as it was previously (with a direct 5842 // call to the runtime not using a type checking slow path). 5843 // This should also be beneficial for the other cases above. 5844 __ b(type_check_slow_path->GetEntryLabel()); 5845 break; 5846 } 5847 __ Bind(&done); 5848 5849 __ Bind(type_check_slow_path->GetExitLabel()); 5850} 5851 5852void LocationsBuilderARM::VisitMonitorOperation(HMonitorOperation* instruction) { 5853 LocationSummary* locations = 5854 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); 5855 InvokeRuntimeCallingConvention calling_convention; 5856 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 5857} 5858 5859void InstructionCodeGeneratorARM::VisitMonitorOperation(HMonitorOperation* instruction) { 5860 codegen_->InvokeRuntime(instruction->IsEnter() 5861 ? QUICK_ENTRY_POINT(pLockObject) : QUICK_ENTRY_POINT(pUnlockObject), 5862 instruction, 5863 instruction->GetDexPc(), 5864 nullptr); 5865 if (instruction->IsEnter()) { 5866 CheckEntrypointTypes<kQuickLockObject, void, mirror::Object*>(); 5867 } else { 5868 CheckEntrypointTypes<kQuickUnlockObject, void, mirror::Object*>(); 5869 } 5870} 5871 5872void LocationsBuilderARM::VisitAnd(HAnd* instruction) { HandleBitwiseOperation(instruction, AND); } 5873void LocationsBuilderARM::VisitOr(HOr* instruction) { HandleBitwiseOperation(instruction, ORR); } 5874void LocationsBuilderARM::VisitXor(HXor* instruction) { HandleBitwiseOperation(instruction, EOR); } 5875 5876void LocationsBuilderARM::HandleBitwiseOperation(HBinaryOperation* instruction, Opcode opcode) { 5877 LocationSummary* locations = 5878 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 5879 DCHECK(instruction->GetResultType() == Primitive::kPrimInt 5880 || instruction->GetResultType() == Primitive::kPrimLong); 5881 // Note: GVN reorders commutative operations to have the constant on the right hand side. 5882 locations->SetInAt(0, Location::RequiresRegister()); 5883 locations->SetInAt(1, ArmEncodableConstantOrRegister(instruction->InputAt(1), opcode)); 5884 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 5885} 5886 5887void InstructionCodeGeneratorARM::VisitAnd(HAnd* instruction) { 5888 HandleBitwiseOperation(instruction); 5889} 5890 5891void InstructionCodeGeneratorARM::VisitOr(HOr* instruction) { 5892 HandleBitwiseOperation(instruction); 5893} 5894 5895void InstructionCodeGeneratorARM::VisitXor(HXor* instruction) { 5896 HandleBitwiseOperation(instruction); 5897} 5898 5899 5900void LocationsBuilderARM::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) { 5901 LocationSummary* locations = 5902 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 5903 DCHECK(instruction->GetResultType() == Primitive::kPrimInt 5904 || instruction->GetResultType() == Primitive::kPrimLong); 5905 5906 locations->SetInAt(0, Location::RequiresRegister()); 5907 locations->SetInAt(1, Location::RequiresRegister()); 5908 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 5909} 5910 5911void InstructionCodeGeneratorARM::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) { 5912 LocationSummary* locations = instruction->GetLocations(); 5913 Location first = locations->InAt(0); 5914 Location second = locations->InAt(1); 5915 Location out = locations->Out(); 5916 5917 if (instruction->GetResultType() == Primitive::kPrimInt) { 5918 Register first_reg = first.AsRegister<Register>(); 5919 ShifterOperand second_reg(second.AsRegister<Register>()); 5920 Register out_reg = out.AsRegister<Register>(); 5921 5922 switch (instruction->GetOpKind()) { 5923 case HInstruction::kAnd: 5924 __ bic(out_reg, first_reg, second_reg); 5925 break; 5926 case HInstruction::kOr: 5927 __ orn(out_reg, first_reg, second_reg); 5928 break; 5929 // There is no EON on arm. 5930 case HInstruction::kXor: 5931 default: 5932 LOG(FATAL) << "Unexpected instruction " << instruction->DebugName(); 5933 UNREACHABLE(); 5934 } 5935 return; 5936 5937 } else { 5938 DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong); 5939 Register first_low = first.AsRegisterPairLow<Register>(); 5940 Register first_high = first.AsRegisterPairHigh<Register>(); 5941 ShifterOperand second_low(second.AsRegisterPairLow<Register>()); 5942 ShifterOperand second_high(second.AsRegisterPairHigh<Register>()); 5943 Register out_low = out.AsRegisterPairLow<Register>(); 5944 Register out_high = out.AsRegisterPairHigh<Register>(); 5945 5946 switch (instruction->GetOpKind()) { 5947 case HInstruction::kAnd: 5948 __ bic(out_low, first_low, second_low); 5949 __ bic(out_high, first_high, second_high); 5950 break; 5951 case HInstruction::kOr: 5952 __ orn(out_low, first_low, second_low); 5953 __ orn(out_high, first_high, second_high); 5954 break; 5955 // There is no EON on arm. 5956 case HInstruction::kXor: 5957 default: 5958 LOG(FATAL) << "Unexpected instruction " << instruction->DebugName(); 5959 UNREACHABLE(); 5960 } 5961 } 5962} 5963 5964void InstructionCodeGeneratorARM::GenerateAndConst(Register out, Register first, uint32_t value) { 5965 // Optimize special cases for individual halfs of `and-long` (`and` is simplified earlier). 5966 if (value == 0xffffffffu) { 5967 if (out != first) { 5968 __ mov(out, ShifterOperand(first)); 5969 } 5970 return; 5971 } 5972 if (value == 0u) { 5973 __ mov(out, ShifterOperand(0)); 5974 return; 5975 } 5976 ShifterOperand so; 5977 if (__ ShifterOperandCanHold(kNoRegister, kNoRegister, AND, value, &so)) { 5978 __ and_(out, first, so); 5979 } else { 5980 DCHECK(__ ShifterOperandCanHold(kNoRegister, kNoRegister, BIC, ~value, &so)); 5981 __ bic(out, first, ShifterOperand(~value)); 5982 } 5983} 5984 5985void InstructionCodeGeneratorARM::GenerateOrrConst(Register out, Register first, uint32_t value) { 5986 // Optimize special cases for individual halfs of `or-long` (`or` is simplified earlier). 5987 if (value == 0u) { 5988 if (out != first) { 5989 __ mov(out, ShifterOperand(first)); 5990 } 5991 return; 5992 } 5993 if (value == 0xffffffffu) { 5994 __ mvn(out, ShifterOperand(0)); 5995 return; 5996 } 5997 ShifterOperand so; 5998 if (__ ShifterOperandCanHold(kNoRegister, kNoRegister, ORR, value, &so)) { 5999 __ orr(out, first, so); 6000 } else { 6001 DCHECK(__ ShifterOperandCanHold(kNoRegister, kNoRegister, ORN, ~value, &so)); 6002 __ orn(out, first, ShifterOperand(~value)); 6003 } 6004} 6005 6006void InstructionCodeGeneratorARM::GenerateEorConst(Register out, Register first, uint32_t value) { 6007 // Optimize special case for individual halfs of `xor-long` (`xor` is simplified earlier). 6008 if (value == 0u) { 6009 if (out != first) { 6010 __ mov(out, ShifterOperand(first)); 6011 } 6012 return; 6013 } 6014 __ eor(out, first, ShifterOperand(value)); 6015} 6016 6017void InstructionCodeGeneratorARM::HandleBitwiseOperation(HBinaryOperation* instruction) { 6018 LocationSummary* locations = instruction->GetLocations(); 6019 Location first = locations->InAt(0); 6020 Location second = locations->InAt(1); 6021 Location out = locations->Out(); 6022 6023 if (second.IsConstant()) { 6024 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant())); 6025 uint32_t value_low = Low32Bits(value); 6026 if (instruction->GetResultType() == Primitive::kPrimInt) { 6027 Register first_reg = first.AsRegister<Register>(); 6028 Register out_reg = out.AsRegister<Register>(); 6029 if (instruction->IsAnd()) { 6030 GenerateAndConst(out_reg, first_reg, value_low); 6031 } else if (instruction->IsOr()) { 6032 GenerateOrrConst(out_reg, first_reg, value_low); 6033 } else { 6034 DCHECK(instruction->IsXor()); 6035 GenerateEorConst(out_reg, first_reg, value_low); 6036 } 6037 } else { 6038 DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong); 6039 uint32_t value_high = High32Bits(value); 6040 Register first_low = first.AsRegisterPairLow<Register>(); 6041 Register first_high = first.AsRegisterPairHigh<Register>(); 6042 Register out_low = out.AsRegisterPairLow<Register>(); 6043 Register out_high = out.AsRegisterPairHigh<Register>(); 6044 if (instruction->IsAnd()) { 6045 GenerateAndConst(out_low, first_low, value_low); 6046 GenerateAndConst(out_high, first_high, value_high); 6047 } else if (instruction->IsOr()) { 6048 GenerateOrrConst(out_low, first_low, value_low); 6049 GenerateOrrConst(out_high, first_high, value_high); 6050 } else { 6051 DCHECK(instruction->IsXor()); 6052 GenerateEorConst(out_low, first_low, value_low); 6053 GenerateEorConst(out_high, first_high, value_high); 6054 } 6055 } 6056 return; 6057 } 6058 6059 if (instruction->GetResultType() == Primitive::kPrimInt) { 6060 Register first_reg = first.AsRegister<Register>(); 6061 ShifterOperand second_reg(second.AsRegister<Register>()); 6062 Register out_reg = out.AsRegister<Register>(); 6063 if (instruction->IsAnd()) { 6064 __ and_(out_reg, first_reg, second_reg); 6065 } else if (instruction->IsOr()) { 6066 __ orr(out_reg, first_reg, second_reg); 6067 } else { 6068 DCHECK(instruction->IsXor()); 6069 __ eor(out_reg, first_reg, second_reg); 6070 } 6071 } else { 6072 DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong); 6073 Register first_low = first.AsRegisterPairLow<Register>(); 6074 Register first_high = first.AsRegisterPairHigh<Register>(); 6075 ShifterOperand second_low(second.AsRegisterPairLow<Register>()); 6076 ShifterOperand second_high(second.AsRegisterPairHigh<Register>()); 6077 Register out_low = out.AsRegisterPairLow<Register>(); 6078 Register out_high = out.AsRegisterPairHigh<Register>(); 6079 if (instruction->IsAnd()) { 6080 __ and_(out_low, first_low, second_low); 6081 __ and_(out_high, first_high, second_high); 6082 } else if (instruction->IsOr()) { 6083 __ orr(out_low, first_low, second_low); 6084 __ orr(out_high, first_high, second_high); 6085 } else { 6086 DCHECK(instruction->IsXor()); 6087 __ eor(out_low, first_low, second_low); 6088 __ eor(out_high, first_high, second_high); 6089 } 6090 } 6091} 6092 6093void InstructionCodeGeneratorARM::GenerateReferenceLoadOneRegister(HInstruction* instruction, 6094 Location out, 6095 uint32_t offset, 6096 Location maybe_temp) { 6097 Register out_reg = out.AsRegister<Register>(); 6098 if (kEmitCompilerReadBarrier) { 6099 DCHECK(maybe_temp.IsRegister()) << maybe_temp; 6100 if (kUseBakerReadBarrier) { 6101 // Load with fast path based Baker's read barrier. 6102 // /* HeapReference<Object> */ out = *(out + offset) 6103 codegen_->GenerateFieldLoadWithBakerReadBarrier( 6104 instruction, out, out_reg, offset, maybe_temp, /* needs_null_check */ false); 6105 } else { 6106 // Load with slow path based read barrier. 6107 // Save the value of `out` into `maybe_temp` before overwriting it 6108 // in the following move operation, as we will need it for the 6109 // read barrier below. 6110 __ Mov(maybe_temp.AsRegister<Register>(), out_reg); 6111 // /* HeapReference<Object> */ out = *(out + offset) 6112 __ LoadFromOffset(kLoadWord, out_reg, out_reg, offset); 6113 codegen_->GenerateReadBarrierSlow(instruction, out, out, maybe_temp, offset); 6114 } 6115 } else { 6116 // Plain load with no read barrier. 6117 // /* HeapReference<Object> */ out = *(out + offset) 6118 __ LoadFromOffset(kLoadWord, out_reg, out_reg, offset); 6119 __ MaybeUnpoisonHeapReference(out_reg); 6120 } 6121} 6122 6123void InstructionCodeGeneratorARM::GenerateReferenceLoadTwoRegisters(HInstruction* instruction, 6124 Location out, 6125 Location obj, 6126 uint32_t offset, 6127 Location maybe_temp) { 6128 Register out_reg = out.AsRegister<Register>(); 6129 Register obj_reg = obj.AsRegister<Register>(); 6130 if (kEmitCompilerReadBarrier) { 6131 if (kUseBakerReadBarrier) { 6132 DCHECK(maybe_temp.IsRegister()) << maybe_temp; 6133 // Load with fast path based Baker's read barrier. 6134 // /* HeapReference<Object> */ out = *(obj + offset) 6135 codegen_->GenerateFieldLoadWithBakerReadBarrier( 6136 instruction, out, obj_reg, offset, maybe_temp, /* needs_null_check */ false); 6137 } else { 6138 // Load with slow path based read barrier. 6139 // /* HeapReference<Object> */ out = *(obj + offset) 6140 __ LoadFromOffset(kLoadWord, out_reg, obj_reg, offset); 6141 codegen_->GenerateReadBarrierSlow(instruction, out, out, obj, offset); 6142 } 6143 } else { 6144 // Plain load with no read barrier. 6145 // /* HeapReference<Object> */ out = *(obj + offset) 6146 __ LoadFromOffset(kLoadWord, out_reg, obj_reg, offset); 6147 __ MaybeUnpoisonHeapReference(out_reg); 6148 } 6149} 6150 6151void InstructionCodeGeneratorARM::GenerateGcRootFieldLoad(HInstruction* instruction, 6152 Location root, 6153 Register obj, 6154 uint32_t offset) { 6155 Register root_reg = root.AsRegister<Register>(); 6156 if (kEmitCompilerReadBarrier) { 6157 if (kUseBakerReadBarrier) { 6158 // Fast path implementation of art::ReadBarrier::BarrierForRoot when 6159 // Baker's read barrier are used: 6160 // 6161 // root = obj.field; 6162 // if (Thread::Current()->GetIsGcMarking()) { 6163 // root = ReadBarrier::Mark(root) 6164 // } 6165 6166 // /* GcRoot<mirror::Object> */ root = *(obj + offset) 6167 __ LoadFromOffset(kLoadWord, root_reg, obj, offset); 6168 static_assert( 6169 sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(GcRoot<mirror::Object>), 6170 "art::mirror::CompressedReference<mirror::Object> and art::GcRoot<mirror::Object> " 6171 "have different sizes."); 6172 static_assert(sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(int32_t), 6173 "art::mirror::CompressedReference<mirror::Object> and int32_t " 6174 "have different sizes."); 6175 6176 // Slow path used to mark the GC root `root`. 6177 SlowPathCode* slow_path = 6178 new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathARM(instruction, root, root); 6179 codegen_->AddSlowPath(slow_path); 6180 6181 // IP = Thread::Current()->GetIsGcMarking() 6182 __ LoadFromOffset( 6183 kLoadWord, IP, TR, Thread::IsGcMarkingOffset<kArmWordSize>().Int32Value()); 6184 __ CompareAndBranchIfNonZero(IP, slow_path->GetEntryLabel()); 6185 __ Bind(slow_path->GetExitLabel()); 6186 } else { 6187 // GC root loaded through a slow path for read barriers other 6188 // than Baker's. 6189 // /* GcRoot<mirror::Object>* */ root = obj + offset 6190 __ AddConstant(root_reg, obj, offset); 6191 // /* mirror::Object* */ root = root->Read() 6192 codegen_->GenerateReadBarrierForRootSlow(instruction, root, root); 6193 } 6194 } else { 6195 // Plain GC root load with no read barrier. 6196 // /* GcRoot<mirror::Object> */ root = *(obj + offset) 6197 __ LoadFromOffset(kLoadWord, root_reg, obj, offset); 6198 // Note that GC roots are not affected by heap poisoning, thus we 6199 // do not have to unpoison `root_reg` here. 6200 } 6201} 6202 6203void CodeGeneratorARM::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction, 6204 Location ref, 6205 Register obj, 6206 uint32_t offset, 6207 Location temp, 6208 bool needs_null_check) { 6209 DCHECK(kEmitCompilerReadBarrier); 6210 DCHECK(kUseBakerReadBarrier); 6211 6212 // /* HeapReference<Object> */ ref = *(obj + offset) 6213 Location no_index = Location::NoLocation(); 6214 ScaleFactor no_scale_factor = TIMES_1; 6215 GenerateReferenceLoadWithBakerReadBarrier( 6216 instruction, ref, obj, offset, no_index, no_scale_factor, temp, needs_null_check); 6217} 6218 6219void CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction, 6220 Location ref, 6221 Register obj, 6222 uint32_t data_offset, 6223 Location index, 6224 Location temp, 6225 bool needs_null_check) { 6226 DCHECK(kEmitCompilerReadBarrier); 6227 DCHECK(kUseBakerReadBarrier); 6228 6229 static_assert( 6230 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), 6231 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); 6232 // /* HeapReference<Object> */ ref = 6233 // *(obj + data_offset + index * sizeof(HeapReference<Object>)) 6234 ScaleFactor scale_factor = TIMES_4; 6235 GenerateReferenceLoadWithBakerReadBarrier( 6236 instruction, ref, obj, data_offset, index, scale_factor, temp, needs_null_check); 6237} 6238 6239void CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction, 6240 Location ref, 6241 Register obj, 6242 uint32_t offset, 6243 Location index, 6244 ScaleFactor scale_factor, 6245 Location temp, 6246 bool needs_null_check) { 6247 DCHECK(kEmitCompilerReadBarrier); 6248 DCHECK(kUseBakerReadBarrier); 6249 6250 // In slow path based read barriers, the read barrier call is 6251 // inserted after the original load. However, in fast path based 6252 // Baker's read barriers, we need to perform the load of 6253 // mirror::Object::monitor_ *before* the original reference load. 6254 // This load-load ordering is required by the read barrier. 6255 // The fast path/slow path (for Baker's algorithm) should look like: 6256 // 6257 // uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState(); 6258 // lfence; // Load fence or artificial data dependency to prevent load-load reordering 6259 // HeapReference<Object> ref = *src; // Original reference load. 6260 // bool is_gray = (rb_state == ReadBarrier::gray_ptr_); 6261 // if (is_gray) { 6262 // ref = ReadBarrier::Mark(ref); // Performed by runtime entrypoint slow path. 6263 // } 6264 // 6265 // Note: the original implementation in ReadBarrier::Barrier is 6266 // slightly more complex as it performs additional checks that we do 6267 // not do here for performance reasons. 6268 6269 Register ref_reg = ref.AsRegister<Register>(); 6270 Register temp_reg = temp.AsRegister<Register>(); 6271 uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value(); 6272 6273 // /* int32_t */ monitor = obj->monitor_ 6274 __ LoadFromOffset(kLoadWord, temp_reg, obj, monitor_offset); 6275 if (needs_null_check) { 6276 MaybeRecordImplicitNullCheck(instruction); 6277 } 6278 // /* LockWord */ lock_word = LockWord(monitor) 6279 static_assert(sizeof(LockWord) == sizeof(int32_t), 6280 "art::LockWord and int32_t have different sizes."); 6281 // /* uint32_t */ rb_state = lock_word.ReadBarrierState() 6282 __ Lsr(temp_reg, temp_reg, LockWord::kReadBarrierStateShift); 6283 __ and_(temp_reg, temp_reg, ShifterOperand(LockWord::kReadBarrierStateMask)); 6284 static_assert( 6285 LockWord::kReadBarrierStateMask == ReadBarrier::rb_ptr_mask_, 6286 "art::LockWord::kReadBarrierStateMask is not equal to art::ReadBarrier::rb_ptr_mask_."); 6287 6288 // Introduce a dependency on the high bits of rb_state, which shall 6289 // be all zeroes, to prevent load-load reordering, and without using 6290 // a memory barrier (which would be more expensive). 6291 // IP = rb_state & ~LockWord::kReadBarrierStateMask = 0 6292 __ bic(IP, temp_reg, ShifterOperand(LockWord::kReadBarrierStateMask)); 6293 // obj is unchanged by this operation, but its value now depends on 6294 // IP, which depends on temp_reg. 6295 __ add(obj, obj, ShifterOperand(IP)); 6296 6297 // The actual reference load. 6298 if (index.IsValid()) { 6299 // Load types involving an "index": ArrayGet and 6300 // UnsafeGetObject/UnsafeGetObjectVolatile intrinsics. 6301 // /* HeapReference<Object> */ ref = *(obj + offset + (index << scale_factor)) 6302 if (index.IsConstant()) { 6303 size_t computed_offset = 6304 (index.GetConstant()->AsIntConstant()->GetValue() << scale_factor) + offset; 6305 __ LoadFromOffset(kLoadWord, ref_reg, obj, computed_offset); 6306 } else { 6307 // Handle the special case of the 6308 // UnsafeGetObject/UnsafeGetObjectVolatile intrinsics, which use 6309 // a register pair as index ("long offset"), of which only the low 6310 // part contains data. 6311 Register index_reg = index.IsRegisterPair() 6312 ? index.AsRegisterPairLow<Register>() 6313 : index.AsRegister<Register>(); 6314 __ add(IP, obj, ShifterOperand(index_reg, LSL, scale_factor)); 6315 __ LoadFromOffset(kLoadWord, ref_reg, IP, offset); 6316 } 6317 } else { 6318 // /* HeapReference<Object> */ ref = *(obj + offset) 6319 __ LoadFromOffset(kLoadWord, ref_reg, obj, offset); 6320 } 6321 6322 // Object* ref = ref_addr->AsMirrorPtr() 6323 __ MaybeUnpoisonHeapReference(ref_reg); 6324 6325 // Slow path used to mark the object `ref` when it is gray. 6326 SlowPathCode* slow_path = 6327 new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathARM(instruction, ref, ref); 6328 AddSlowPath(slow_path); 6329 6330 // if (rb_state == ReadBarrier::gray_ptr_) 6331 // ref = ReadBarrier::Mark(ref); 6332 __ cmp(temp_reg, ShifterOperand(ReadBarrier::gray_ptr_)); 6333 __ b(slow_path->GetEntryLabel(), EQ); 6334 __ Bind(slow_path->GetExitLabel()); 6335} 6336 6337void CodeGeneratorARM::GenerateReadBarrierSlow(HInstruction* instruction, 6338 Location out, 6339 Location ref, 6340 Location obj, 6341 uint32_t offset, 6342 Location index) { 6343 DCHECK(kEmitCompilerReadBarrier); 6344 6345 // Insert a slow path based read barrier *after* the reference load. 6346 // 6347 // If heap poisoning is enabled, the unpoisoning of the loaded 6348 // reference will be carried out by the runtime within the slow 6349 // path. 6350 // 6351 // Note that `ref` currently does not get unpoisoned (when heap 6352 // poisoning is enabled), which is alright as the `ref` argument is 6353 // not used by the artReadBarrierSlow entry point. 6354 // 6355 // TODO: Unpoison `ref` when it is used by artReadBarrierSlow. 6356 SlowPathCode* slow_path = new (GetGraph()->GetArena()) 6357 ReadBarrierForHeapReferenceSlowPathARM(instruction, out, ref, obj, offset, index); 6358 AddSlowPath(slow_path); 6359 6360 __ b(slow_path->GetEntryLabel()); 6361 __ Bind(slow_path->GetExitLabel()); 6362} 6363 6364void CodeGeneratorARM::MaybeGenerateReadBarrierSlow(HInstruction* instruction, 6365 Location out, 6366 Location ref, 6367 Location obj, 6368 uint32_t offset, 6369 Location index) { 6370 if (kEmitCompilerReadBarrier) { 6371 // Baker's read barriers shall be handled by the fast path 6372 // (CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier). 6373 DCHECK(!kUseBakerReadBarrier); 6374 // If heap poisoning is enabled, unpoisoning will be taken care of 6375 // by the runtime within the slow path. 6376 GenerateReadBarrierSlow(instruction, out, ref, obj, offset, index); 6377 } else if (kPoisonHeapReferences) { 6378 __ UnpoisonHeapReference(out.AsRegister<Register>()); 6379 } 6380} 6381 6382void CodeGeneratorARM::GenerateReadBarrierForRootSlow(HInstruction* instruction, 6383 Location out, 6384 Location root) { 6385 DCHECK(kEmitCompilerReadBarrier); 6386 6387 // Insert a slow path based read barrier *after* the GC root load. 6388 // 6389 // Note that GC roots are not affected by heap poisoning, so we do 6390 // not need to do anything special for this here. 6391 SlowPathCode* slow_path = 6392 new (GetGraph()->GetArena()) ReadBarrierForRootSlowPathARM(instruction, out, root); 6393 AddSlowPath(slow_path); 6394 6395 __ b(slow_path->GetEntryLabel()); 6396 __ Bind(slow_path->GetExitLabel()); 6397} 6398 6399HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARM::GetSupportedInvokeStaticOrDirectDispatch( 6400 const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info, 6401 MethodReference target_method) { 6402 HInvokeStaticOrDirect::DispatchInfo dispatch_info = desired_dispatch_info; 6403 // We disable pc-relative load when there is an irreducible loop, as the optimization 6404 // is incompatible with it. 6405 // TODO: Create as many ArmDexCacheArraysBase instructions as needed for methods 6406 // with irreducible loops. 6407 if (GetGraph()->HasIrreducibleLoops() && 6408 (dispatch_info.method_load_kind == 6409 HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative)) { 6410 dispatch_info.method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod; 6411 } 6412 6413 if (dispatch_info.code_ptr_location == HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative) { 6414 const DexFile& outer_dex_file = GetGraph()->GetDexFile(); 6415 if (&outer_dex_file != target_method.dex_file) { 6416 // Calls across dex files are more likely to exceed the available BL range, 6417 // so use absolute patch with fixup if available and kCallArtMethod otherwise. 6418 HInvokeStaticOrDirect::CodePtrLocation code_ptr_location = 6419 (desired_dispatch_info.method_load_kind == 6420 HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup) 6421 ? HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup 6422 : HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod; 6423 return HInvokeStaticOrDirect::DispatchInfo { 6424 dispatch_info.method_load_kind, 6425 code_ptr_location, 6426 dispatch_info.method_load_data, 6427 0u 6428 }; 6429 } 6430 } 6431 return dispatch_info; 6432} 6433 6434Register CodeGeneratorARM::GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke, 6435 Register temp) { 6436 DCHECK_EQ(invoke->InputCount(), invoke->GetNumberOfArguments() + 1u); 6437 Location location = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex()); 6438 if (!invoke->GetLocations()->Intrinsified()) { 6439 return location.AsRegister<Register>(); 6440 } 6441 // For intrinsics we allow any location, so it may be on the stack. 6442 if (!location.IsRegister()) { 6443 __ LoadFromOffset(kLoadWord, temp, SP, location.GetStackIndex()); 6444 return temp; 6445 } 6446 // For register locations, check if the register was saved. If so, get it from the stack. 6447 // Note: There is a chance that the register was saved but not overwritten, so we could 6448 // save one load. However, since this is just an intrinsic slow path we prefer this 6449 // simple and more robust approach rather that trying to determine if that's the case. 6450 SlowPathCode* slow_path = GetCurrentSlowPath(); 6451 DCHECK(slow_path != nullptr); // For intrinsified invokes the call is emitted on the slow path. 6452 if (slow_path->IsCoreRegisterSaved(location.AsRegister<Register>())) { 6453 int stack_offset = slow_path->GetStackOffsetOfCoreRegister(location.AsRegister<Register>()); 6454 __ LoadFromOffset(kLoadWord, temp, SP, stack_offset); 6455 return temp; 6456 } 6457 return location.AsRegister<Register>(); 6458} 6459 6460void CodeGeneratorARM::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) { 6461 // For better instruction scheduling we load the direct code pointer before the method pointer. 6462 switch (invoke->GetCodePtrLocation()) { 6463 case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup: 6464 // LR = code address from literal pool with link-time patch. 6465 __ LoadLiteral(LR, DeduplicateMethodCodeLiteral(invoke->GetTargetMethod())); 6466 break; 6467 case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect: 6468 // LR = invoke->GetDirectCodePtr(); 6469 __ LoadImmediate(LR, invoke->GetDirectCodePtr()); 6470 break; 6471 default: 6472 break; 6473 } 6474 6475 Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp. 6476 switch (invoke->GetMethodLoadKind()) { 6477 case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: 6478 // temp = thread->string_init_entrypoint 6479 __ LoadFromOffset(kLoadWord, temp.AsRegister<Register>(), TR, invoke->GetStringInitOffset()); 6480 break; 6481 case HInvokeStaticOrDirect::MethodLoadKind::kRecursive: 6482 callee_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex()); 6483 break; 6484 case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress: 6485 __ LoadImmediate(temp.AsRegister<Register>(), invoke->GetMethodAddress()); 6486 break; 6487 case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup: 6488 __ LoadLiteral(temp.AsRegister<Register>(), 6489 DeduplicateMethodAddressLiteral(invoke->GetTargetMethod())); 6490 break; 6491 case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: { 6492 HArmDexCacheArraysBase* base = 6493 invoke->InputAt(invoke->GetSpecialInputIndex())->AsArmDexCacheArraysBase(); 6494 Register base_reg = GetInvokeStaticOrDirectExtraParameter(invoke, 6495 temp.AsRegister<Register>()); 6496 int32_t offset = invoke->GetDexCacheArrayOffset() - base->GetElementOffset(); 6497 __ LoadFromOffset(kLoadWord, temp.AsRegister<Register>(), base_reg, offset); 6498 break; 6499 } 6500 case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: { 6501 Location current_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex()); 6502 Register method_reg; 6503 Register reg = temp.AsRegister<Register>(); 6504 if (current_method.IsRegister()) { 6505 method_reg = current_method.AsRegister<Register>(); 6506 } else { 6507 DCHECK(invoke->GetLocations()->Intrinsified()); 6508 DCHECK(!current_method.IsValid()); 6509 method_reg = reg; 6510 __ LoadFromOffset(kLoadWord, reg, SP, kCurrentMethodStackOffset); 6511 } 6512 // /* ArtMethod*[] */ temp = temp.ptr_sized_fields_->dex_cache_resolved_methods_; 6513 __ LoadFromOffset(kLoadWord, 6514 reg, 6515 method_reg, 6516 ArtMethod::DexCacheResolvedMethodsOffset(kArmPointerSize).Int32Value()); 6517 // temp = temp[index_in_cache]; 6518 // Note: Don't use invoke->GetTargetMethod() as it may point to a different dex file. 6519 uint32_t index_in_cache = invoke->GetDexMethodIndex(); 6520 __ LoadFromOffset(kLoadWord, reg, reg, CodeGenerator::GetCachePointerOffset(index_in_cache)); 6521 break; 6522 } 6523 } 6524 6525 switch (invoke->GetCodePtrLocation()) { 6526 case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf: 6527 __ bl(GetFrameEntryLabel()); 6528 break; 6529 case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative: 6530 relative_call_patches_.emplace_back(invoke->GetTargetMethod()); 6531 __ BindTrackedLabel(&relative_call_patches_.back().label); 6532 // Arbitrarily branch to the BL itself, override at link time. 6533 __ bl(&relative_call_patches_.back().label); 6534 break; 6535 case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup: 6536 case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect: 6537 // LR prepared above for better instruction scheduling. 6538 // LR() 6539 __ blx(LR); 6540 break; 6541 case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod: 6542 // LR = callee_method->entry_point_from_quick_compiled_code_ 6543 __ LoadFromOffset( 6544 kLoadWord, LR, callee_method.AsRegister<Register>(), 6545 ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmWordSize).Int32Value()); 6546 // LR() 6547 __ blx(LR); 6548 break; 6549 } 6550 6551 DCHECK(!IsLeafMethod()); 6552} 6553 6554void CodeGeneratorARM::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp_location) { 6555 Register temp = temp_location.AsRegister<Register>(); 6556 uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset( 6557 invoke->GetVTableIndex(), kArmPointerSize).Uint32Value(); 6558 6559 // Use the calling convention instead of the location of the receiver, as 6560 // intrinsics may have put the receiver in a different register. In the intrinsics 6561 // slow path, the arguments have been moved to the right place, so here we are 6562 // guaranteed that the receiver is the first register of the calling convention. 6563 InvokeDexCallingConvention calling_convention; 6564 Register receiver = calling_convention.GetRegisterAt(0); 6565 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 6566 // /* HeapReference<Class> */ temp = receiver->klass_ 6567 __ LoadFromOffset(kLoadWord, temp, receiver, class_offset); 6568 MaybeRecordImplicitNullCheck(invoke); 6569 // Instead of simply (possibly) unpoisoning `temp` here, we should 6570 // emit a read barrier for the previous class reference load. 6571 // However this is not required in practice, as this is an 6572 // intermediate/temporary reference and because the current 6573 // concurrent copying collector keeps the from-space memory 6574 // intact/accessible until the end of the marking phase (the 6575 // concurrent copying collector may not in the future). 6576 __ MaybeUnpoisonHeapReference(temp); 6577 // temp = temp->GetMethodAt(method_offset); 6578 uint32_t entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset( 6579 kArmWordSize).Int32Value(); 6580 __ LoadFromOffset(kLoadWord, temp, temp, method_offset); 6581 // LR = temp->GetEntryPoint(); 6582 __ LoadFromOffset(kLoadWord, LR, temp, entry_point); 6583 // LR(); 6584 __ blx(LR); 6585} 6586 6587CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativeStringPatch( 6588 const DexFile& dex_file, uint32_t string_index) { 6589 return NewPcRelativePatch(dex_file, string_index, &pc_relative_string_patches_); 6590} 6591 6592CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativeTypePatch( 6593 const DexFile& dex_file, uint32_t type_index) { 6594 return NewPcRelativePatch(dex_file, type_index, &pc_relative_type_patches_); 6595} 6596 6597CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativeDexCacheArrayPatch( 6598 const DexFile& dex_file, uint32_t element_offset) { 6599 return NewPcRelativePatch(dex_file, element_offset, &pc_relative_dex_cache_patches_); 6600} 6601 6602CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativePatch( 6603 const DexFile& dex_file, uint32_t offset_or_index, ArenaDeque<PcRelativePatchInfo>* patches) { 6604 patches->emplace_back(dex_file, offset_or_index); 6605 return &patches->back(); 6606} 6607 6608Literal* CodeGeneratorARM::DeduplicateBootImageStringLiteral(const DexFile& dex_file, 6609 uint32_t string_index) { 6610 return boot_image_string_patches_.GetOrCreate( 6611 StringReference(&dex_file, string_index), 6612 [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); }); 6613} 6614 6615Literal* CodeGeneratorARM::DeduplicateBootImageTypeLiteral(const DexFile& dex_file, 6616 uint32_t type_index) { 6617 return boot_image_type_patches_.GetOrCreate( 6618 TypeReference(&dex_file, type_index), 6619 [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); }); 6620} 6621 6622Literal* CodeGeneratorARM::DeduplicateBootImageAddressLiteral(uint32_t address) { 6623 bool needs_patch = GetCompilerOptions().GetIncludePatchInformation(); 6624 Uint32ToLiteralMap* map = needs_patch ? &boot_image_address_patches_ : &uint32_literals_; 6625 return DeduplicateUint32Literal(dchecked_integral_cast<uint32_t>(address), map); 6626} 6627 6628Literal* CodeGeneratorARM::DeduplicateDexCacheAddressLiteral(uint32_t address) { 6629 return DeduplicateUint32Literal(address, &uint32_literals_); 6630} 6631 6632void CodeGeneratorARM::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) { 6633 DCHECK(linker_patches->empty()); 6634 size_t size = 6635 method_patches_.size() + 6636 call_patches_.size() + 6637 relative_call_patches_.size() + 6638 /* MOVW+MOVT for each base */ 2u * pc_relative_dex_cache_patches_.size() + 6639 boot_image_string_patches_.size() + 6640 /* MOVW+MOVT for each base */ 2u * pc_relative_string_patches_.size() + 6641 boot_image_type_patches_.size() + 6642 /* MOVW+MOVT for each base */ 2u * pc_relative_type_patches_.size() + 6643 boot_image_address_patches_.size(); 6644 linker_patches->reserve(size); 6645 for (const auto& entry : method_patches_) { 6646 const MethodReference& target_method = entry.first; 6647 Literal* literal = entry.second; 6648 DCHECK(literal->GetLabel()->IsBound()); 6649 uint32_t literal_offset = literal->GetLabel()->Position(); 6650 linker_patches->push_back(LinkerPatch::MethodPatch(literal_offset, 6651 target_method.dex_file, 6652 target_method.dex_method_index)); 6653 } 6654 for (const auto& entry : call_patches_) { 6655 const MethodReference& target_method = entry.first; 6656 Literal* literal = entry.second; 6657 DCHECK(literal->GetLabel()->IsBound()); 6658 uint32_t literal_offset = literal->GetLabel()->Position(); 6659 linker_patches->push_back(LinkerPatch::CodePatch(literal_offset, 6660 target_method.dex_file, 6661 target_method.dex_method_index)); 6662 } 6663 for (const MethodPatchInfo<Label>& info : relative_call_patches_) { 6664 uint32_t literal_offset = info.label.Position(); 6665 linker_patches->push_back(LinkerPatch::RelativeCodePatch(literal_offset, 6666 info.target_method.dex_file, 6667 info.target_method.dex_method_index)); 6668 } 6669 for (const PcRelativePatchInfo& info : pc_relative_dex_cache_patches_) { 6670 const DexFile& dex_file = info.target_dex_file; 6671 size_t base_element_offset = info.offset_or_index; 6672 DCHECK(info.add_pc_label.IsBound()); 6673 uint32_t add_pc_offset = dchecked_integral_cast<uint32_t>(info.add_pc_label.Position()); 6674 // Add MOVW patch. 6675 DCHECK(info.movw_label.IsBound()); 6676 uint32_t movw_offset = dchecked_integral_cast<uint32_t>(info.movw_label.Position()); 6677 linker_patches->push_back(LinkerPatch::DexCacheArrayPatch(movw_offset, 6678 &dex_file, 6679 add_pc_offset, 6680 base_element_offset)); 6681 // Add MOVT patch. 6682 DCHECK(info.movt_label.IsBound()); 6683 uint32_t movt_offset = dchecked_integral_cast<uint32_t>(info.movt_label.Position()); 6684 linker_patches->push_back(LinkerPatch::DexCacheArrayPatch(movt_offset, 6685 &dex_file, 6686 add_pc_offset, 6687 base_element_offset)); 6688 } 6689 for (const auto& entry : boot_image_string_patches_) { 6690 const StringReference& target_string = entry.first; 6691 Literal* literal = entry.second; 6692 DCHECK(literal->GetLabel()->IsBound()); 6693 uint32_t literal_offset = literal->GetLabel()->Position(); 6694 linker_patches->push_back(LinkerPatch::StringPatch(literal_offset, 6695 target_string.dex_file, 6696 target_string.string_index)); 6697 } 6698 for (const PcRelativePatchInfo& info : pc_relative_string_patches_) { 6699 const DexFile& dex_file = info.target_dex_file; 6700 uint32_t string_index = info.offset_or_index; 6701 DCHECK(info.add_pc_label.IsBound()); 6702 uint32_t add_pc_offset = dchecked_integral_cast<uint32_t>(info.add_pc_label.Position()); 6703 // Add MOVW patch. 6704 DCHECK(info.movw_label.IsBound()); 6705 uint32_t movw_offset = dchecked_integral_cast<uint32_t>(info.movw_label.Position()); 6706 linker_patches->push_back(LinkerPatch::RelativeStringPatch(movw_offset, 6707 &dex_file, 6708 add_pc_offset, 6709 string_index)); 6710 // Add MOVT patch. 6711 DCHECK(info.movt_label.IsBound()); 6712 uint32_t movt_offset = dchecked_integral_cast<uint32_t>(info.movt_label.Position()); 6713 linker_patches->push_back(LinkerPatch::RelativeStringPatch(movt_offset, 6714 &dex_file, 6715 add_pc_offset, 6716 string_index)); 6717 } 6718 for (const auto& entry : boot_image_type_patches_) { 6719 const TypeReference& target_type = entry.first; 6720 Literal* literal = entry.second; 6721 DCHECK(literal->GetLabel()->IsBound()); 6722 uint32_t literal_offset = literal->GetLabel()->Position(); 6723 linker_patches->push_back(LinkerPatch::TypePatch(literal_offset, 6724 target_type.dex_file, 6725 target_type.type_index)); 6726 } 6727 for (const PcRelativePatchInfo& info : pc_relative_type_patches_) { 6728 const DexFile& dex_file = info.target_dex_file; 6729 uint32_t type_index = info.offset_or_index; 6730 DCHECK(info.add_pc_label.IsBound()); 6731 uint32_t add_pc_offset = dchecked_integral_cast<uint32_t>(info.add_pc_label.Position()); 6732 // Add MOVW patch. 6733 DCHECK(info.movw_label.IsBound()); 6734 uint32_t movw_offset = dchecked_integral_cast<uint32_t>(info.movw_label.Position()); 6735 linker_patches->push_back(LinkerPatch::RelativeTypePatch(movw_offset, 6736 &dex_file, 6737 add_pc_offset, 6738 type_index)); 6739 // Add MOVT patch. 6740 DCHECK(info.movt_label.IsBound()); 6741 uint32_t movt_offset = dchecked_integral_cast<uint32_t>(info.movt_label.Position()); 6742 linker_patches->push_back(LinkerPatch::RelativeTypePatch(movt_offset, 6743 &dex_file, 6744 add_pc_offset, 6745 type_index)); 6746 } 6747 for (const auto& entry : boot_image_address_patches_) { 6748 DCHECK(GetCompilerOptions().GetIncludePatchInformation()); 6749 Literal* literal = entry.second; 6750 DCHECK(literal->GetLabel()->IsBound()); 6751 uint32_t literal_offset = literal->GetLabel()->Position(); 6752 linker_patches->push_back(LinkerPatch::RecordPosition(literal_offset)); 6753 } 6754} 6755 6756Literal* CodeGeneratorARM::DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map) { 6757 return map->GetOrCreate( 6758 value, 6759 [this, value]() { return __ NewLiteral<uint32_t>(value); }); 6760} 6761 6762Literal* CodeGeneratorARM::DeduplicateMethodLiteral(MethodReference target_method, 6763 MethodToLiteralMap* map) { 6764 return map->GetOrCreate( 6765 target_method, 6766 [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); }); 6767} 6768 6769Literal* CodeGeneratorARM::DeduplicateMethodAddressLiteral(MethodReference target_method) { 6770 return DeduplicateMethodLiteral(target_method, &method_patches_); 6771} 6772 6773Literal* CodeGeneratorARM::DeduplicateMethodCodeLiteral(MethodReference target_method) { 6774 return DeduplicateMethodLiteral(target_method, &call_patches_); 6775} 6776 6777void LocationsBuilderARM::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) { 6778 LocationSummary* locations = 6779 new (GetGraph()->GetArena()) LocationSummary(instr, LocationSummary::kNoCall); 6780 locations->SetInAt(HMultiplyAccumulate::kInputAccumulatorIndex, 6781 Location::RequiresRegister()); 6782 locations->SetInAt(HMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister()); 6783 locations->SetInAt(HMultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister()); 6784 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 6785} 6786 6787void InstructionCodeGeneratorARM::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) { 6788 LocationSummary* locations = instr->GetLocations(); 6789 Register res = locations->Out().AsRegister<Register>(); 6790 Register accumulator = 6791 locations->InAt(HMultiplyAccumulate::kInputAccumulatorIndex).AsRegister<Register>(); 6792 Register mul_left = 6793 locations->InAt(HMultiplyAccumulate::kInputMulLeftIndex).AsRegister<Register>(); 6794 Register mul_right = 6795 locations->InAt(HMultiplyAccumulate::kInputMulRightIndex).AsRegister<Register>(); 6796 6797 if (instr->GetOpKind() == HInstruction::kAdd) { 6798 __ mla(res, mul_left, mul_right, accumulator); 6799 } else { 6800 __ mls(res, mul_left, mul_right, accumulator); 6801 } 6802} 6803 6804void LocationsBuilderARM::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) { 6805 // Nothing to do, this should be removed during prepare for register allocator. 6806 LOG(FATAL) << "Unreachable"; 6807} 6808 6809void InstructionCodeGeneratorARM::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) { 6810 // Nothing to do, this should be removed during prepare for register allocator. 6811 LOG(FATAL) << "Unreachable"; 6812} 6813 6814// Simple implementation of packed switch - generate cascaded compare/jumps. 6815void LocationsBuilderARM::VisitPackedSwitch(HPackedSwitch* switch_instr) { 6816 LocationSummary* locations = 6817 new (GetGraph()->GetArena()) LocationSummary(switch_instr, LocationSummary::kNoCall); 6818 locations->SetInAt(0, Location::RequiresRegister()); 6819 if (switch_instr->GetNumEntries() > kPackedSwitchCompareJumpThreshold && 6820 codegen_->GetAssembler()->IsThumb()) { 6821 locations->AddTemp(Location::RequiresRegister()); // We need a temp for the table base. 6822 if (switch_instr->GetStartValue() != 0) { 6823 locations->AddTemp(Location::RequiresRegister()); // We need a temp for the bias. 6824 } 6825 } 6826} 6827 6828void InstructionCodeGeneratorARM::VisitPackedSwitch(HPackedSwitch* switch_instr) { 6829 int32_t lower_bound = switch_instr->GetStartValue(); 6830 uint32_t num_entries = switch_instr->GetNumEntries(); 6831 LocationSummary* locations = switch_instr->GetLocations(); 6832 Register value_reg = locations->InAt(0).AsRegister<Register>(); 6833 HBasicBlock* default_block = switch_instr->GetDefaultBlock(); 6834 6835 if (num_entries <= kPackedSwitchCompareJumpThreshold || !codegen_->GetAssembler()->IsThumb()) { 6836 // Create a series of compare/jumps. 6837 Register temp_reg = IP; 6838 // Note: It is fine for the below AddConstantSetFlags() using IP register to temporarily store 6839 // the immediate, because IP is used as the destination register. For the other 6840 // AddConstantSetFlags() and GenerateCompareWithImmediate(), the immediate values are constant, 6841 // and they can be encoded in the instruction without making use of IP register. 6842 __ AddConstantSetFlags(temp_reg, value_reg, -lower_bound); 6843 6844 const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors(); 6845 // Jump to successors[0] if value == lower_bound. 6846 __ b(codegen_->GetLabelOf(successors[0]), EQ); 6847 int32_t last_index = 0; 6848 for (; num_entries - last_index > 2; last_index += 2) { 6849 __ AddConstantSetFlags(temp_reg, temp_reg, -2); 6850 // Jump to successors[last_index + 1] if value < case_value[last_index + 2]. 6851 __ b(codegen_->GetLabelOf(successors[last_index + 1]), LO); 6852 // Jump to successors[last_index + 2] if value == case_value[last_index + 2]. 6853 __ b(codegen_->GetLabelOf(successors[last_index + 2]), EQ); 6854 } 6855 if (num_entries - last_index == 2) { 6856 // The last missing case_value. 6857 __ CmpConstant(temp_reg, 1); 6858 __ b(codegen_->GetLabelOf(successors[last_index + 1]), EQ); 6859 } 6860 6861 // And the default for any other value. 6862 if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) { 6863 __ b(codegen_->GetLabelOf(default_block)); 6864 } 6865 } else { 6866 // Create a table lookup. 6867 Register temp_reg = locations->GetTemp(0).AsRegister<Register>(); 6868 6869 // Materialize a pointer to the switch table 6870 std::vector<Label*> labels(num_entries); 6871 const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors(); 6872 for (uint32_t i = 0; i < num_entries; i++) { 6873 labels[i] = codegen_->GetLabelOf(successors[i]); 6874 } 6875 JumpTable* table = __ CreateJumpTable(std::move(labels), temp_reg); 6876 6877 // Remove the bias. 6878 Register key_reg; 6879 if (lower_bound != 0) { 6880 key_reg = locations->GetTemp(1).AsRegister<Register>(); 6881 __ AddConstant(key_reg, value_reg, -lower_bound); 6882 } else { 6883 key_reg = value_reg; 6884 } 6885 6886 // Check whether the value is in the table, jump to default block if not. 6887 __ CmpConstant(key_reg, num_entries - 1); 6888 __ b(codegen_->GetLabelOf(default_block), Condition::HI); 6889 6890 // Load the displacement from the table. 6891 __ ldr(temp_reg, Address(temp_reg, key_reg, Shift::LSL, 2)); 6892 6893 // Dispatch is a direct add to the PC (for Thumb2). 6894 __ EmitJumpTableDispatch(table, temp_reg); 6895 } 6896} 6897 6898void LocationsBuilderARM::VisitArmDexCacheArraysBase(HArmDexCacheArraysBase* base) { 6899 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(base); 6900 locations->SetOut(Location::RequiresRegister()); 6901} 6902 6903void InstructionCodeGeneratorARM::VisitArmDexCacheArraysBase(HArmDexCacheArraysBase* base) { 6904 Register base_reg = base->GetLocations()->Out().AsRegister<Register>(); 6905 CodeGeneratorARM::PcRelativePatchInfo* labels = 6906 codegen_->NewPcRelativeDexCacheArrayPatch(base->GetDexFile(), base->GetElementOffset()); 6907 __ BindTrackedLabel(&labels->movw_label); 6908 __ movw(base_reg, /* placeholder */ 0u); 6909 __ BindTrackedLabel(&labels->movt_label); 6910 __ movt(base_reg, /* placeholder */ 0u); 6911 __ BindTrackedLabel(&labels->add_pc_label); 6912 __ add(base_reg, base_reg, ShifterOperand(PC)); 6913} 6914 6915void CodeGeneratorARM::MoveFromReturnRegister(Location trg, Primitive::Type type) { 6916 if (!trg.IsValid()) { 6917 DCHECK_EQ(type, Primitive::kPrimVoid); 6918 return; 6919 } 6920 6921 DCHECK_NE(type, Primitive::kPrimVoid); 6922 6923 Location return_loc = InvokeDexCallingConventionVisitorARM().GetReturnLocation(type); 6924 if (return_loc.Equals(trg)) { 6925 return; 6926 } 6927 6928 // TODO: Consider pairs in the parallel move resolver, then this could be nicely merged 6929 // with the last branch. 6930 if (type == Primitive::kPrimLong) { 6931 HParallelMove parallel_move(GetGraph()->GetArena()); 6932 parallel_move.AddMove(return_loc.ToLow(), trg.ToLow(), Primitive::kPrimInt, nullptr); 6933 parallel_move.AddMove(return_loc.ToHigh(), trg.ToHigh(), Primitive::kPrimInt, nullptr); 6934 GetMoveResolver()->EmitNativeCode(¶llel_move); 6935 } else if (type == Primitive::kPrimDouble) { 6936 HParallelMove parallel_move(GetGraph()->GetArena()); 6937 parallel_move.AddMove(return_loc.ToLow(), trg.ToLow(), Primitive::kPrimFloat, nullptr); 6938 parallel_move.AddMove(return_loc.ToHigh(), trg.ToHigh(), Primitive::kPrimFloat, nullptr); 6939 GetMoveResolver()->EmitNativeCode(¶llel_move); 6940 } else { 6941 // Let the parallel move resolver take care of all of this. 6942 HParallelMove parallel_move(GetGraph()->GetArena()); 6943 parallel_move.AddMove(return_loc, trg, type, nullptr); 6944 GetMoveResolver()->EmitNativeCode(¶llel_move); 6945 } 6946} 6947 6948void LocationsBuilderARM::VisitClassTableGet(HClassTableGet* instruction) { 6949 LocationSummary* locations = 6950 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 6951 locations->SetInAt(0, Location::RequiresRegister()); 6952 locations->SetOut(Location::RequiresRegister()); 6953} 6954 6955void InstructionCodeGeneratorARM::VisitClassTableGet(HClassTableGet* instruction) { 6956 LocationSummary* locations = instruction->GetLocations(); 6957 uint32_t method_offset = 0; 6958 if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) { 6959 method_offset = mirror::Class::EmbeddedVTableEntryOffset( 6960 instruction->GetIndex(), kArmPointerSize).SizeValue(); 6961 } else { 6962 method_offset = mirror::Class::EmbeddedImTableEntryOffset( 6963 instruction->GetIndex() % mirror::Class::kImtSize, kArmPointerSize).Uint32Value(); 6964 } 6965 __ LoadFromOffset(kLoadWord, 6966 locations->Out().AsRegister<Register>(), 6967 locations->InAt(0).AsRegister<Register>(), 6968 method_offset); 6969} 6970 6971#undef __ 6972#undef QUICK_ENTRY_POINT 6973 6974} // namespace arm 6975} // namespace art 6976