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