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