code_generator_arm.cc revision 465ecc86ff65ca546629630c9469deb6d2d8137e
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(), 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 4289void LocationsBuilderARM::VisitArrayGet(HArrayGet* instruction) { 4290 bool object_array_get_with_read_barrier = 4291 kEmitCompilerReadBarrier && (instruction->GetType() == Primitive::kPrimNot); 4292 LocationSummary* locations = 4293 new (GetGraph()->GetArena()) LocationSummary(instruction, 4294 object_array_get_with_read_barrier ? 4295 LocationSummary::kCallOnSlowPath : 4296 LocationSummary::kNoCall); 4297 locations->SetInAt(0, Location::RequiresRegister()); 4298 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); 4299 if (Primitive::IsFloatingPointType(instruction->GetType())) { 4300 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); 4301 } else { 4302 // The output overlaps in the case of an object array get with 4303 // read barriers enabled: we do not want the move to overwrite the 4304 // array's location, as we need it to emit the read barrier. 4305 locations->SetOut( 4306 Location::RequiresRegister(), 4307 object_array_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap); 4308 } 4309 // We need a temporary register for the read barrier marking slow 4310 // path in CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier. 4311 if (object_array_get_with_read_barrier && kUseBakerReadBarrier) { 4312 locations->AddTemp(Location::RequiresRegister()); 4313 } 4314} 4315 4316void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { 4317 LocationSummary* locations = instruction->GetLocations(); 4318 Location obj_loc = locations->InAt(0); 4319 Register obj = obj_loc.AsRegister<Register>(); 4320 Location index = locations->InAt(1); 4321 Location out_loc = locations->Out(); 4322 uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction); 4323 4324 Primitive::Type type = instruction->GetType(); 4325 switch (type) { 4326 case Primitive::kPrimBoolean: { 4327 Register out = out_loc.AsRegister<Register>(); 4328 if (index.IsConstant()) { 4329 size_t offset = 4330 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset; 4331 __ LoadFromOffset(kLoadUnsignedByte, out, obj, offset); 4332 } else { 4333 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>())); 4334 __ LoadFromOffset(kLoadUnsignedByte, out, IP, data_offset); 4335 } 4336 break; 4337 } 4338 4339 case Primitive::kPrimByte: { 4340 Register out = out_loc.AsRegister<Register>(); 4341 if (index.IsConstant()) { 4342 size_t offset = 4343 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset; 4344 __ LoadFromOffset(kLoadSignedByte, out, obj, offset); 4345 } else { 4346 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>())); 4347 __ LoadFromOffset(kLoadSignedByte, out, IP, data_offset); 4348 } 4349 break; 4350 } 4351 4352 case Primitive::kPrimShort: { 4353 Register out = out_loc.AsRegister<Register>(); 4354 if (index.IsConstant()) { 4355 size_t offset = 4356 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; 4357 __ LoadFromOffset(kLoadSignedHalfword, out, obj, offset); 4358 } else { 4359 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_2)); 4360 __ LoadFromOffset(kLoadSignedHalfword, out, IP, data_offset); 4361 } 4362 break; 4363 } 4364 4365 case Primitive::kPrimChar: { 4366 Register out = out_loc.AsRegister<Register>(); 4367 if (index.IsConstant()) { 4368 size_t offset = 4369 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; 4370 __ LoadFromOffset(kLoadUnsignedHalfword, out, obj, offset); 4371 } else { 4372 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_2)); 4373 __ LoadFromOffset(kLoadUnsignedHalfword, out, IP, data_offset); 4374 } 4375 break; 4376 } 4377 4378 case Primitive::kPrimInt: { 4379 Register out = out_loc.AsRegister<Register>(); 4380 if (index.IsConstant()) { 4381 size_t offset = 4382 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4383 __ LoadFromOffset(kLoadWord, out, obj, offset); 4384 } else { 4385 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4386 __ LoadFromOffset(kLoadWord, out, IP, data_offset); 4387 } 4388 break; 4389 } 4390 4391 case Primitive::kPrimNot: { 4392 static_assert( 4393 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), 4394 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); 4395 // /* HeapReference<Object> */ out = 4396 // *(obj + data_offset + index * sizeof(HeapReference<Object>)) 4397 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) { 4398 Location temp = locations->GetTemp(0); 4399 // Note that a potential implicit null check is handled in this 4400 // CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier call. 4401 codegen_->GenerateArrayLoadWithBakerReadBarrier( 4402 instruction, out_loc, obj, data_offset, index, temp, /* needs_null_check */ true); 4403 } else { 4404 Register out = out_loc.AsRegister<Register>(); 4405 if (index.IsConstant()) { 4406 size_t offset = 4407 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4408 __ LoadFromOffset(kLoadWord, out, obj, offset); 4409 codegen_->MaybeRecordImplicitNullCheck(instruction); 4410 // If read barriers are enabled, emit read barriers other than 4411 // Baker's using a slow path (and also unpoison the loaded 4412 // reference, if heap poisoning is enabled). 4413 codegen_->MaybeGenerateReadBarrierSlow(instruction, out_loc, out_loc, obj_loc, offset); 4414 } else { 4415 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4416 __ LoadFromOffset(kLoadWord, out, IP, data_offset); 4417 codegen_->MaybeRecordImplicitNullCheck(instruction); 4418 // If read barriers are enabled, emit read barriers other than 4419 // Baker's using a slow path (and also unpoison the loaded 4420 // reference, if heap poisoning is enabled). 4421 codegen_->MaybeGenerateReadBarrierSlow( 4422 instruction, out_loc, out_loc, obj_loc, data_offset, index); 4423 } 4424 } 4425 break; 4426 } 4427 4428 case Primitive::kPrimLong: { 4429 if (index.IsConstant()) { 4430 size_t offset = 4431 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; 4432 __ LoadFromOffset(kLoadWordPair, out_loc.AsRegisterPairLow<Register>(), obj, offset); 4433 } else { 4434 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8)); 4435 __ LoadFromOffset(kLoadWordPair, out_loc.AsRegisterPairLow<Register>(), IP, data_offset); 4436 } 4437 break; 4438 } 4439 4440 case Primitive::kPrimFloat: { 4441 SRegister out = out_loc.AsFpuRegister<SRegister>(); 4442 if (index.IsConstant()) { 4443 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4444 __ LoadSFromOffset(out, obj, offset); 4445 } else { 4446 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4447 __ LoadSFromOffset(out, IP, data_offset); 4448 } 4449 break; 4450 } 4451 4452 case Primitive::kPrimDouble: { 4453 SRegister out = out_loc.AsFpuRegisterPairLow<SRegister>(); 4454 if (index.IsConstant()) { 4455 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; 4456 __ LoadDFromOffset(FromLowSToD(out), obj, offset); 4457 } else { 4458 __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8)); 4459 __ LoadDFromOffset(FromLowSToD(out), IP, data_offset); 4460 } 4461 break; 4462 } 4463 4464 case Primitive::kPrimVoid: 4465 LOG(FATAL) << "Unreachable type " << type; 4466 UNREACHABLE(); 4467 } 4468 4469 if (type == Primitive::kPrimNot) { 4470 // Potential implicit null checks, in the case of reference 4471 // arrays, are handled in the previous switch statement. 4472 } else { 4473 codegen_->MaybeRecordImplicitNullCheck(instruction); 4474 } 4475} 4476 4477void LocationsBuilderARM::VisitArraySet(HArraySet* instruction) { 4478 Primitive::Type value_type = instruction->GetComponentType(); 4479 4480 bool needs_write_barrier = 4481 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue()); 4482 bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck(); 4483 bool object_array_set_with_read_barrier = 4484 kEmitCompilerReadBarrier && (value_type == Primitive::kPrimNot); 4485 4486 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary( 4487 instruction, 4488 (may_need_runtime_call_for_type_check || object_array_set_with_read_barrier) ? 4489 LocationSummary::kCallOnSlowPath : 4490 LocationSummary::kNoCall); 4491 4492 locations->SetInAt(0, Location::RequiresRegister()); 4493 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); 4494 if (Primitive::IsFloatingPointType(value_type)) { 4495 locations->SetInAt(2, Location::RequiresFpuRegister()); 4496 } else { 4497 locations->SetInAt(2, Location::RequiresRegister()); 4498 } 4499 if (needs_write_barrier) { 4500 // Temporary registers for the write barrier. 4501 locations->AddTemp(Location::RequiresRegister()); // Possibly used for ref. poisoning too. 4502 locations->AddTemp(Location::RequiresRegister()); 4503 } 4504} 4505 4506void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) { 4507 LocationSummary* locations = instruction->GetLocations(); 4508 Location array_loc = locations->InAt(0); 4509 Register array = array_loc.AsRegister<Register>(); 4510 Location index = locations->InAt(1); 4511 Primitive::Type value_type = instruction->GetComponentType(); 4512 bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck(); 4513 bool needs_write_barrier = 4514 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue()); 4515 4516 switch (value_type) { 4517 case Primitive::kPrimBoolean: 4518 case Primitive::kPrimByte: { 4519 uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value(); 4520 Register value = locations->InAt(2).AsRegister<Register>(); 4521 if (index.IsConstant()) { 4522 size_t offset = 4523 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset; 4524 __ StoreToOffset(kStoreByte, value, array, offset); 4525 } else { 4526 __ add(IP, array, ShifterOperand(index.AsRegister<Register>())); 4527 __ StoreToOffset(kStoreByte, value, IP, data_offset); 4528 } 4529 break; 4530 } 4531 4532 case Primitive::kPrimShort: 4533 case Primitive::kPrimChar: { 4534 uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value(); 4535 Register value = locations->InAt(2).AsRegister<Register>(); 4536 if (index.IsConstant()) { 4537 size_t offset = 4538 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; 4539 __ StoreToOffset(kStoreHalfword, value, array, offset); 4540 } else { 4541 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_2)); 4542 __ StoreToOffset(kStoreHalfword, value, IP, data_offset); 4543 } 4544 break; 4545 } 4546 4547 case Primitive::kPrimNot: { 4548 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); 4549 Location value_loc = locations->InAt(2); 4550 Register value = value_loc.AsRegister<Register>(); 4551 Register source = value; 4552 4553 if (instruction->InputAt(2)->IsNullConstant()) { 4554 // Just setting null. 4555 if (index.IsConstant()) { 4556 size_t offset = 4557 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4558 __ StoreToOffset(kStoreWord, source, array, offset); 4559 } else { 4560 DCHECK(index.IsRegister()) << index; 4561 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4562 __ StoreToOffset(kStoreWord, source, IP, data_offset); 4563 } 4564 codegen_->MaybeRecordImplicitNullCheck(instruction); 4565 DCHECK(!needs_write_barrier); 4566 DCHECK(!may_need_runtime_call_for_type_check); 4567 break; 4568 } 4569 4570 DCHECK(needs_write_barrier); 4571 Register temp1 = locations->GetTemp(0).AsRegister<Register>(); 4572 Register temp2 = locations->GetTemp(1).AsRegister<Register>(); 4573 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 4574 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); 4575 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); 4576 Label done; 4577 SlowPathCode* slow_path = nullptr; 4578 4579 if (may_need_runtime_call_for_type_check) { 4580 slow_path = new (GetGraph()->GetArena()) ArraySetSlowPathARM(instruction); 4581 codegen_->AddSlowPath(slow_path); 4582 if (instruction->GetValueCanBeNull()) { 4583 Label non_zero; 4584 __ CompareAndBranchIfNonZero(value, &non_zero); 4585 if (index.IsConstant()) { 4586 size_t offset = 4587 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4588 __ StoreToOffset(kStoreWord, value, array, offset); 4589 } else { 4590 DCHECK(index.IsRegister()) << index; 4591 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4592 __ StoreToOffset(kStoreWord, value, IP, data_offset); 4593 } 4594 codegen_->MaybeRecordImplicitNullCheck(instruction); 4595 __ b(&done); 4596 __ Bind(&non_zero); 4597 } 4598 4599 if (kEmitCompilerReadBarrier) { 4600 // When read barriers are enabled, the type checking 4601 // instrumentation requires two read barriers: 4602 // 4603 // __ Mov(temp2, temp1); 4604 // // /* HeapReference<Class> */ temp1 = temp1->component_type_ 4605 // __ LoadFromOffset(kLoadWord, temp1, temp1, component_offset); 4606 // codegen_->GenerateReadBarrierSlow( 4607 // instruction, temp1_loc, temp1_loc, temp2_loc, component_offset); 4608 // 4609 // // /* HeapReference<Class> */ temp2 = value->klass_ 4610 // __ LoadFromOffset(kLoadWord, temp2, value, class_offset); 4611 // codegen_->GenerateReadBarrierSlow( 4612 // instruction, temp2_loc, temp2_loc, value_loc, class_offset, temp1_loc); 4613 // 4614 // __ cmp(temp1, ShifterOperand(temp2)); 4615 // 4616 // However, the second read barrier may trash `temp`, as it 4617 // is a temporary register, and as such would not be saved 4618 // along with live registers before calling the runtime (nor 4619 // restored afterwards). So in this case, we bail out and 4620 // delegate the work to the array set slow path. 4621 // 4622 // TODO: Extend the register allocator to support a new 4623 // "(locally) live temp" location so as to avoid always 4624 // going into the slow path when read barriers are enabled. 4625 __ b(slow_path->GetEntryLabel()); 4626 } else { 4627 // /* HeapReference<Class> */ temp1 = array->klass_ 4628 __ LoadFromOffset(kLoadWord, temp1, array, class_offset); 4629 codegen_->MaybeRecordImplicitNullCheck(instruction); 4630 __ MaybeUnpoisonHeapReference(temp1); 4631 4632 // /* HeapReference<Class> */ temp1 = temp1->component_type_ 4633 __ LoadFromOffset(kLoadWord, temp1, temp1, component_offset); 4634 // /* HeapReference<Class> */ temp2 = value->klass_ 4635 __ LoadFromOffset(kLoadWord, temp2, value, class_offset); 4636 // If heap poisoning is enabled, no need to unpoison `temp1` 4637 // nor `temp2`, as we are comparing two poisoned references. 4638 __ cmp(temp1, ShifterOperand(temp2)); 4639 4640 if (instruction->StaticTypeOfArrayIsObjectArray()) { 4641 Label do_put; 4642 __ b(&do_put, EQ); 4643 // If heap poisoning is enabled, the `temp1` reference has 4644 // not been unpoisoned yet; unpoison it now. 4645 __ MaybeUnpoisonHeapReference(temp1); 4646 4647 // /* HeapReference<Class> */ temp1 = temp1->super_class_ 4648 __ LoadFromOffset(kLoadWord, temp1, temp1, super_offset); 4649 // If heap poisoning is enabled, no need to unpoison 4650 // `temp1`, as we are comparing against null below. 4651 __ CompareAndBranchIfNonZero(temp1, slow_path->GetEntryLabel()); 4652 __ Bind(&do_put); 4653 } else { 4654 __ b(slow_path->GetEntryLabel(), NE); 4655 } 4656 } 4657 } 4658 4659 if (kPoisonHeapReferences) { 4660 // Note that in the case where `value` is a null reference, 4661 // we do not enter this block, as a null reference does not 4662 // need poisoning. 4663 DCHECK_EQ(value_type, Primitive::kPrimNot); 4664 __ Mov(temp1, value); 4665 __ PoisonHeapReference(temp1); 4666 source = temp1; 4667 } 4668 4669 if (index.IsConstant()) { 4670 size_t offset = 4671 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4672 __ StoreToOffset(kStoreWord, source, array, offset); 4673 } else { 4674 DCHECK(index.IsRegister()) << index; 4675 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4676 __ StoreToOffset(kStoreWord, source, IP, data_offset); 4677 } 4678 4679 if (!may_need_runtime_call_for_type_check) { 4680 codegen_->MaybeRecordImplicitNullCheck(instruction); 4681 } 4682 4683 codegen_->MarkGCCard(temp1, temp2, array, value, instruction->GetValueCanBeNull()); 4684 4685 if (done.IsLinked()) { 4686 __ Bind(&done); 4687 } 4688 4689 if (slow_path != nullptr) { 4690 __ Bind(slow_path->GetExitLabel()); 4691 } 4692 4693 break; 4694 } 4695 4696 case Primitive::kPrimInt: { 4697 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); 4698 Register value = locations->InAt(2).AsRegister<Register>(); 4699 if (index.IsConstant()) { 4700 size_t offset = 4701 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4702 __ StoreToOffset(kStoreWord, value, array, offset); 4703 } else { 4704 DCHECK(index.IsRegister()) << index; 4705 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4706 __ StoreToOffset(kStoreWord, value, IP, data_offset); 4707 } 4708 break; 4709 } 4710 4711 case Primitive::kPrimLong: { 4712 uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); 4713 Location value = locations->InAt(2); 4714 if (index.IsConstant()) { 4715 size_t offset = 4716 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; 4717 __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), array, offset); 4718 } else { 4719 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8)); 4720 __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), IP, data_offset); 4721 } 4722 break; 4723 } 4724 4725 case Primitive::kPrimFloat: { 4726 uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value(); 4727 Location value = locations->InAt(2); 4728 DCHECK(value.IsFpuRegister()); 4729 if (index.IsConstant()) { 4730 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; 4731 __ StoreSToOffset(value.AsFpuRegister<SRegister>(), array, offset); 4732 } else { 4733 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); 4734 __ StoreSToOffset(value.AsFpuRegister<SRegister>(), IP, data_offset); 4735 } 4736 break; 4737 } 4738 4739 case Primitive::kPrimDouble: { 4740 uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value(); 4741 Location value = locations->InAt(2); 4742 DCHECK(value.IsFpuRegisterPair()); 4743 if (index.IsConstant()) { 4744 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; 4745 __ StoreDToOffset(FromLowSToD(value.AsFpuRegisterPairLow<SRegister>()), array, offset); 4746 } else { 4747 __ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8)); 4748 __ StoreDToOffset(FromLowSToD(value.AsFpuRegisterPairLow<SRegister>()), IP, data_offset); 4749 } 4750 4751 break; 4752 } 4753 4754 case Primitive::kPrimVoid: 4755 LOG(FATAL) << "Unreachable type " << value_type; 4756 UNREACHABLE(); 4757 } 4758 4759 // Objects are handled in the switch. 4760 if (value_type != Primitive::kPrimNot) { 4761 codegen_->MaybeRecordImplicitNullCheck(instruction); 4762 } 4763} 4764 4765void LocationsBuilderARM::VisitArrayLength(HArrayLength* instruction) { 4766 LocationSummary* locations = 4767 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 4768 locations->SetInAt(0, Location::RequiresRegister()); 4769 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 4770} 4771 4772void InstructionCodeGeneratorARM::VisitArrayLength(HArrayLength* instruction) { 4773 LocationSummary* locations = instruction->GetLocations(); 4774 uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction); 4775 Register obj = locations->InAt(0).AsRegister<Register>(); 4776 Register out = locations->Out().AsRegister<Register>(); 4777 __ LoadFromOffset(kLoadWord, out, obj, offset); 4778 codegen_->MaybeRecordImplicitNullCheck(instruction); 4779} 4780 4781void LocationsBuilderARM::VisitBoundsCheck(HBoundsCheck* instruction) { 4782 LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() 4783 ? LocationSummary::kCallOnSlowPath 4784 : LocationSummary::kNoCall; 4785 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); 4786 locations->SetInAt(0, Location::RequiresRegister()); 4787 locations->SetInAt(1, Location::RequiresRegister()); 4788 if (instruction->HasUses()) { 4789 locations->SetOut(Location::SameAsFirstInput()); 4790 } 4791} 4792 4793void InstructionCodeGeneratorARM::VisitBoundsCheck(HBoundsCheck* instruction) { 4794 LocationSummary* locations = instruction->GetLocations(); 4795 SlowPathCode* slow_path = 4796 new (GetGraph()->GetArena()) BoundsCheckSlowPathARM(instruction); 4797 codegen_->AddSlowPath(slow_path); 4798 4799 Register index = locations->InAt(0).AsRegister<Register>(); 4800 Register length = locations->InAt(1).AsRegister<Register>(); 4801 4802 __ cmp(index, ShifterOperand(length)); 4803 __ b(slow_path->GetEntryLabel(), HS); 4804} 4805 4806void CodeGeneratorARM::MarkGCCard(Register temp, 4807 Register card, 4808 Register object, 4809 Register value, 4810 bool can_be_null) { 4811 Label is_null; 4812 if (can_be_null) { 4813 __ CompareAndBranchIfZero(value, &is_null); 4814 } 4815 __ LoadFromOffset(kLoadWord, card, TR, Thread::CardTableOffset<kArmWordSize>().Int32Value()); 4816 __ Lsr(temp, object, gc::accounting::CardTable::kCardShift); 4817 __ strb(card, Address(card, temp)); 4818 if (can_be_null) { 4819 __ Bind(&is_null); 4820 } 4821} 4822 4823void LocationsBuilderARM::VisitParallelMove(HParallelMove* instruction ATTRIBUTE_UNUSED) { 4824 LOG(FATAL) << "Unreachable"; 4825} 4826 4827void InstructionCodeGeneratorARM::VisitParallelMove(HParallelMove* instruction) { 4828 codegen_->GetMoveResolver()->EmitNativeCode(instruction); 4829} 4830 4831void LocationsBuilderARM::VisitSuspendCheck(HSuspendCheck* instruction) { 4832 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath); 4833} 4834 4835void InstructionCodeGeneratorARM::VisitSuspendCheck(HSuspendCheck* instruction) { 4836 HBasicBlock* block = instruction->GetBlock(); 4837 if (block->GetLoopInformation() != nullptr) { 4838 DCHECK(block->GetLoopInformation()->GetSuspendCheck() == instruction); 4839 // The back edge will generate the suspend check. 4840 return; 4841 } 4842 if (block->IsEntryBlock() && instruction->GetNext()->IsGoto()) { 4843 // The goto will generate the suspend check. 4844 return; 4845 } 4846 GenerateSuspendCheck(instruction, nullptr); 4847} 4848 4849void InstructionCodeGeneratorARM::GenerateSuspendCheck(HSuspendCheck* instruction, 4850 HBasicBlock* successor) { 4851 SuspendCheckSlowPathARM* slow_path = 4852 down_cast<SuspendCheckSlowPathARM*>(instruction->GetSlowPath()); 4853 if (slow_path == nullptr) { 4854 slow_path = new (GetGraph()->GetArena()) SuspendCheckSlowPathARM(instruction, successor); 4855 instruction->SetSlowPath(slow_path); 4856 codegen_->AddSlowPath(slow_path); 4857 if (successor != nullptr) { 4858 DCHECK(successor->IsLoopHeader()); 4859 codegen_->ClearSpillSlotsFromLoopPhisInStackMap(instruction); 4860 } 4861 } else { 4862 DCHECK_EQ(slow_path->GetSuccessor(), successor); 4863 } 4864 4865 __ LoadFromOffset( 4866 kLoadUnsignedHalfword, IP, TR, Thread::ThreadFlagsOffset<kArmWordSize>().Int32Value()); 4867 if (successor == nullptr) { 4868 __ CompareAndBranchIfNonZero(IP, slow_path->GetEntryLabel()); 4869 __ Bind(slow_path->GetReturnLabel()); 4870 } else { 4871 __ CompareAndBranchIfZero(IP, codegen_->GetLabelOf(successor)); 4872 __ b(slow_path->GetEntryLabel()); 4873 } 4874} 4875 4876ArmAssembler* ParallelMoveResolverARM::GetAssembler() const { 4877 return codegen_->GetAssembler(); 4878} 4879 4880void ParallelMoveResolverARM::EmitMove(size_t index) { 4881 MoveOperands* move = moves_[index]; 4882 Location source = move->GetSource(); 4883 Location destination = move->GetDestination(); 4884 4885 if (source.IsRegister()) { 4886 if (destination.IsRegister()) { 4887 __ Mov(destination.AsRegister<Register>(), source.AsRegister<Register>()); 4888 } else if (destination.IsFpuRegister()) { 4889 __ vmovsr(destination.AsFpuRegister<SRegister>(), source.AsRegister<Register>()); 4890 } else { 4891 DCHECK(destination.IsStackSlot()); 4892 __ StoreToOffset(kStoreWord, source.AsRegister<Register>(), 4893 SP, destination.GetStackIndex()); 4894 } 4895 } else if (source.IsStackSlot()) { 4896 if (destination.IsRegister()) { 4897 __ LoadFromOffset(kLoadWord, destination.AsRegister<Register>(), 4898 SP, source.GetStackIndex()); 4899 } else if (destination.IsFpuRegister()) { 4900 __ LoadSFromOffset(destination.AsFpuRegister<SRegister>(), SP, source.GetStackIndex()); 4901 } else { 4902 DCHECK(destination.IsStackSlot()); 4903 __ LoadFromOffset(kLoadWord, IP, SP, source.GetStackIndex()); 4904 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 4905 } 4906 } else if (source.IsFpuRegister()) { 4907 if (destination.IsRegister()) { 4908 __ vmovrs(destination.AsRegister<Register>(), source.AsFpuRegister<SRegister>()); 4909 } else if (destination.IsFpuRegister()) { 4910 __ vmovs(destination.AsFpuRegister<SRegister>(), source.AsFpuRegister<SRegister>()); 4911 } else { 4912 DCHECK(destination.IsStackSlot()); 4913 __ StoreSToOffset(source.AsFpuRegister<SRegister>(), SP, destination.GetStackIndex()); 4914 } 4915 } else if (source.IsDoubleStackSlot()) { 4916 if (destination.IsDoubleStackSlot()) { 4917 __ LoadDFromOffset(DTMP, SP, source.GetStackIndex()); 4918 __ StoreDToOffset(DTMP, SP, destination.GetStackIndex()); 4919 } else if (destination.IsRegisterPair()) { 4920 DCHECK(ExpectedPairLayout(destination)); 4921 __ LoadFromOffset( 4922 kLoadWordPair, destination.AsRegisterPairLow<Register>(), SP, source.GetStackIndex()); 4923 } else { 4924 DCHECK(destination.IsFpuRegisterPair()) << destination; 4925 __ LoadDFromOffset(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), 4926 SP, 4927 source.GetStackIndex()); 4928 } 4929 } else if (source.IsRegisterPair()) { 4930 if (destination.IsRegisterPair()) { 4931 __ Mov(destination.AsRegisterPairLow<Register>(), source.AsRegisterPairLow<Register>()); 4932 __ Mov(destination.AsRegisterPairHigh<Register>(), source.AsRegisterPairHigh<Register>()); 4933 } else if (destination.IsFpuRegisterPair()) { 4934 __ vmovdrr(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), 4935 source.AsRegisterPairLow<Register>(), 4936 source.AsRegisterPairHigh<Register>()); 4937 } else { 4938 DCHECK(destination.IsDoubleStackSlot()) << destination; 4939 DCHECK(ExpectedPairLayout(source)); 4940 __ StoreToOffset( 4941 kStoreWordPair, source.AsRegisterPairLow<Register>(), SP, destination.GetStackIndex()); 4942 } 4943 } else if (source.IsFpuRegisterPair()) { 4944 if (destination.IsRegisterPair()) { 4945 __ vmovrrd(destination.AsRegisterPairLow<Register>(), 4946 destination.AsRegisterPairHigh<Register>(), 4947 FromLowSToD(source.AsFpuRegisterPairLow<SRegister>())); 4948 } else if (destination.IsFpuRegisterPair()) { 4949 __ vmovd(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), 4950 FromLowSToD(source.AsFpuRegisterPairLow<SRegister>())); 4951 } else { 4952 DCHECK(destination.IsDoubleStackSlot()) << destination; 4953 __ StoreDToOffset(FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()), 4954 SP, 4955 destination.GetStackIndex()); 4956 } 4957 } else { 4958 DCHECK(source.IsConstant()) << source; 4959 HConstant* constant = source.GetConstant(); 4960 if (constant->IsIntConstant() || constant->IsNullConstant()) { 4961 int32_t value = CodeGenerator::GetInt32ValueOf(constant); 4962 if (destination.IsRegister()) { 4963 __ LoadImmediate(destination.AsRegister<Register>(), value); 4964 } else { 4965 DCHECK(destination.IsStackSlot()); 4966 __ LoadImmediate(IP, value); 4967 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 4968 } 4969 } else if (constant->IsLongConstant()) { 4970 int64_t value = constant->AsLongConstant()->GetValue(); 4971 if (destination.IsRegisterPair()) { 4972 __ LoadImmediate(destination.AsRegisterPairLow<Register>(), Low32Bits(value)); 4973 __ LoadImmediate(destination.AsRegisterPairHigh<Register>(), High32Bits(value)); 4974 } else { 4975 DCHECK(destination.IsDoubleStackSlot()) << destination; 4976 __ LoadImmediate(IP, Low32Bits(value)); 4977 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 4978 __ LoadImmediate(IP, High32Bits(value)); 4979 __ StoreToOffset(kStoreWord, IP, SP, destination.GetHighStackIndex(kArmWordSize)); 4980 } 4981 } else if (constant->IsDoubleConstant()) { 4982 double value = constant->AsDoubleConstant()->GetValue(); 4983 if (destination.IsFpuRegisterPair()) { 4984 __ LoadDImmediate(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), value); 4985 } else { 4986 DCHECK(destination.IsDoubleStackSlot()) << destination; 4987 uint64_t int_value = bit_cast<uint64_t, double>(value); 4988 __ LoadImmediate(IP, Low32Bits(int_value)); 4989 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 4990 __ LoadImmediate(IP, High32Bits(int_value)); 4991 __ StoreToOffset(kStoreWord, IP, SP, destination.GetHighStackIndex(kArmWordSize)); 4992 } 4993 } else { 4994 DCHECK(constant->IsFloatConstant()) << constant->DebugName(); 4995 float value = constant->AsFloatConstant()->GetValue(); 4996 if (destination.IsFpuRegister()) { 4997 __ LoadSImmediate(destination.AsFpuRegister<SRegister>(), value); 4998 } else { 4999 DCHECK(destination.IsStackSlot()); 5000 __ LoadImmediate(IP, bit_cast<int32_t, float>(value)); 5001 __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); 5002 } 5003 } 5004 } 5005} 5006 5007void ParallelMoveResolverARM::Exchange(Register reg, int mem) { 5008 __ Mov(IP, reg); 5009 __ LoadFromOffset(kLoadWord, reg, SP, mem); 5010 __ StoreToOffset(kStoreWord, IP, SP, mem); 5011} 5012 5013void ParallelMoveResolverARM::Exchange(int mem1, int mem2) { 5014 ScratchRegisterScope ensure_scratch(this, IP, R0, codegen_->GetNumberOfCoreRegisters()); 5015 int stack_offset = ensure_scratch.IsSpilled() ? kArmWordSize : 0; 5016 __ LoadFromOffset(kLoadWord, static_cast<Register>(ensure_scratch.GetRegister()), 5017 SP, mem1 + stack_offset); 5018 __ LoadFromOffset(kLoadWord, IP, SP, mem2 + stack_offset); 5019 __ StoreToOffset(kStoreWord, static_cast<Register>(ensure_scratch.GetRegister()), 5020 SP, mem2 + stack_offset); 5021 __ StoreToOffset(kStoreWord, IP, SP, mem1 + stack_offset); 5022} 5023 5024void ParallelMoveResolverARM::EmitSwap(size_t index) { 5025 MoveOperands* move = moves_[index]; 5026 Location source = move->GetSource(); 5027 Location destination = move->GetDestination(); 5028 5029 if (source.IsRegister() && destination.IsRegister()) { 5030 DCHECK_NE(source.AsRegister<Register>(), IP); 5031 DCHECK_NE(destination.AsRegister<Register>(), IP); 5032 __ Mov(IP, source.AsRegister<Register>()); 5033 __ Mov(source.AsRegister<Register>(), destination.AsRegister<Register>()); 5034 __ Mov(destination.AsRegister<Register>(), IP); 5035 } else if (source.IsRegister() && destination.IsStackSlot()) { 5036 Exchange(source.AsRegister<Register>(), destination.GetStackIndex()); 5037 } else if (source.IsStackSlot() && destination.IsRegister()) { 5038 Exchange(destination.AsRegister<Register>(), source.GetStackIndex()); 5039 } else if (source.IsStackSlot() && destination.IsStackSlot()) { 5040 Exchange(source.GetStackIndex(), destination.GetStackIndex()); 5041 } else if (source.IsFpuRegister() && destination.IsFpuRegister()) { 5042 __ vmovrs(IP, source.AsFpuRegister<SRegister>()); 5043 __ vmovs(source.AsFpuRegister<SRegister>(), destination.AsFpuRegister<SRegister>()); 5044 __ vmovsr(destination.AsFpuRegister<SRegister>(), IP); 5045 } else if (source.IsRegisterPair() && destination.IsRegisterPair()) { 5046 __ vmovdrr(DTMP, source.AsRegisterPairLow<Register>(), source.AsRegisterPairHigh<Register>()); 5047 __ Mov(source.AsRegisterPairLow<Register>(), destination.AsRegisterPairLow<Register>()); 5048 __ Mov(source.AsRegisterPairHigh<Register>(), destination.AsRegisterPairHigh<Register>()); 5049 __ vmovrrd(destination.AsRegisterPairLow<Register>(), 5050 destination.AsRegisterPairHigh<Register>(), 5051 DTMP); 5052 } else if (source.IsRegisterPair() || destination.IsRegisterPair()) { 5053 Register low_reg = source.IsRegisterPair() 5054 ? source.AsRegisterPairLow<Register>() 5055 : destination.AsRegisterPairLow<Register>(); 5056 int mem = source.IsRegisterPair() 5057 ? destination.GetStackIndex() 5058 : source.GetStackIndex(); 5059 DCHECK(ExpectedPairLayout(source.IsRegisterPair() ? source : destination)); 5060 __ vmovdrr(DTMP, low_reg, static_cast<Register>(low_reg + 1)); 5061 __ LoadFromOffset(kLoadWordPair, low_reg, SP, mem); 5062 __ StoreDToOffset(DTMP, SP, mem); 5063 } else if (source.IsFpuRegisterPair() && destination.IsFpuRegisterPair()) { 5064 DRegister first = FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()); 5065 DRegister second = FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()); 5066 __ vmovd(DTMP, first); 5067 __ vmovd(first, second); 5068 __ vmovd(second, DTMP); 5069 } else if (source.IsFpuRegisterPair() || destination.IsFpuRegisterPair()) { 5070 DRegister reg = source.IsFpuRegisterPair() 5071 ? FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()) 5072 : FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()); 5073 int mem = source.IsFpuRegisterPair() 5074 ? destination.GetStackIndex() 5075 : source.GetStackIndex(); 5076 __ vmovd(DTMP, reg); 5077 __ LoadDFromOffset(reg, SP, mem); 5078 __ StoreDToOffset(DTMP, SP, mem); 5079 } else if (source.IsFpuRegister() || destination.IsFpuRegister()) { 5080 SRegister reg = source.IsFpuRegister() ? source.AsFpuRegister<SRegister>() 5081 : destination.AsFpuRegister<SRegister>(); 5082 int mem = source.IsFpuRegister() 5083 ? destination.GetStackIndex() 5084 : source.GetStackIndex(); 5085 5086 __ vmovrs(IP, reg); 5087 __ LoadSFromOffset(reg, SP, mem); 5088 __ StoreToOffset(kStoreWord, IP, SP, mem); 5089 } else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) { 5090 Exchange(source.GetStackIndex(), destination.GetStackIndex()); 5091 Exchange(source.GetHighStackIndex(kArmWordSize), destination.GetHighStackIndex(kArmWordSize)); 5092 } else { 5093 LOG(FATAL) << "Unimplemented" << source << " <-> " << destination; 5094 } 5095} 5096 5097void ParallelMoveResolverARM::SpillScratch(int reg) { 5098 __ Push(static_cast<Register>(reg)); 5099} 5100 5101void ParallelMoveResolverARM::RestoreScratch(int reg) { 5102 __ Pop(static_cast<Register>(reg)); 5103} 5104 5105HLoadClass::LoadKind CodeGeneratorARM::GetSupportedLoadClassKind( 5106 HLoadClass::LoadKind desired_class_load_kind) { 5107 if (kEmitCompilerReadBarrier) { 5108 switch (desired_class_load_kind) { 5109 case HLoadClass::LoadKind::kBootImageLinkTimeAddress: 5110 case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: 5111 case HLoadClass::LoadKind::kBootImageAddress: 5112 // TODO: Implement for read barrier. 5113 return HLoadClass::LoadKind::kDexCacheViaMethod; 5114 default: 5115 break; 5116 } 5117 } 5118 switch (desired_class_load_kind) { 5119 case HLoadClass::LoadKind::kReferrersClass: 5120 break; 5121 case HLoadClass::LoadKind::kBootImageLinkTimeAddress: 5122 DCHECK(!GetCompilerOptions().GetCompilePic()); 5123 break; 5124 case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: 5125 DCHECK(GetCompilerOptions().GetCompilePic()); 5126 break; 5127 case HLoadClass::LoadKind::kBootImageAddress: 5128 break; 5129 case HLoadClass::LoadKind::kDexCacheAddress: 5130 DCHECK(Runtime::Current()->UseJitCompilation()); 5131 break; 5132 case HLoadClass::LoadKind::kDexCachePcRelative: 5133 DCHECK(!Runtime::Current()->UseJitCompilation()); 5134 // We disable pc-relative load when there is an irreducible loop, as the optimization 5135 // is incompatible with it. 5136 // TODO: Create as many ArmDexCacheArraysBase instructions as needed for methods 5137 // with irreducible loops. 5138 if (GetGraph()->HasIrreducibleLoops()) { 5139 return HLoadClass::LoadKind::kDexCacheViaMethod; 5140 } 5141 break; 5142 case HLoadClass::LoadKind::kDexCacheViaMethod: 5143 break; 5144 } 5145 return desired_class_load_kind; 5146} 5147 5148void LocationsBuilderARM::VisitLoadClass(HLoadClass* cls) { 5149 if (cls->NeedsAccessCheck()) { 5150 InvokeRuntimeCallingConvention calling_convention; 5151 CodeGenerator::CreateLoadClassLocationSummary( 5152 cls, 5153 Location::RegisterLocation(calling_convention.GetRegisterAt(0)), 5154 Location::RegisterLocation(R0), 5155 /* code_generator_supports_read_barrier */ true); 5156 return; 5157 } 5158 5159 LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || kEmitCompilerReadBarrier) 5160 ? LocationSummary::kCallOnSlowPath 5161 : LocationSummary::kNoCall; 5162 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind); 5163 HLoadClass::LoadKind load_kind = cls->GetLoadKind(); 5164 if (load_kind == HLoadClass::LoadKind::kReferrersClass || 5165 load_kind == HLoadClass::LoadKind::kDexCacheViaMethod || 5166 load_kind == HLoadClass::LoadKind::kDexCachePcRelative) { 5167 locations->SetInAt(0, Location::RequiresRegister()); 5168 } 5169 locations->SetOut(Location::RequiresRegister()); 5170} 5171 5172void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) { 5173 LocationSummary* locations = cls->GetLocations(); 5174 if (cls->NeedsAccessCheck()) { 5175 codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex()); 5176 codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInitializeTypeAndVerifyAccess), 5177 cls, 5178 cls->GetDexPc(), 5179 nullptr); 5180 CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>(); 5181 return; 5182 } 5183 5184 Location out_loc = locations->Out(); 5185 Register out = out_loc.AsRegister<Register>(); 5186 5187 bool generate_null_check = false; 5188 switch (cls->GetLoadKind()) { 5189 case HLoadClass::LoadKind::kReferrersClass: { 5190 DCHECK(!cls->CanCallRuntime()); 5191 DCHECK(!cls->MustGenerateClinitCheck()); 5192 // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_ 5193 Register current_method = locations->InAt(0).AsRegister<Register>(); 5194 GenerateGcRootFieldLoad( 5195 cls, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value()); 5196 break; 5197 } 5198 case HLoadClass::LoadKind::kBootImageLinkTimeAddress: { 5199 DCHECK(!kEmitCompilerReadBarrier); 5200 __ LoadLiteral(out, codegen_->DeduplicateBootImageTypeLiteral(cls->GetDexFile(), 5201 cls->GetTypeIndex())); 5202 break; 5203 } 5204 case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: { 5205 DCHECK(!kEmitCompilerReadBarrier); 5206 CodeGeneratorARM::PcRelativePatchInfo* labels = 5207 codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex()); 5208 __ BindTrackedLabel(&labels->movw_label); 5209 __ movw(out, /* placeholder */ 0u); 5210 __ BindTrackedLabel(&labels->movt_label); 5211 __ movt(out, /* placeholder */ 0u); 5212 __ BindTrackedLabel(&labels->add_pc_label); 5213 __ add(out, out, ShifterOperand(PC)); 5214 break; 5215 } 5216 case HLoadClass::LoadKind::kBootImageAddress: { 5217 DCHECK(!kEmitCompilerReadBarrier); 5218 DCHECK_NE(cls->GetAddress(), 0u); 5219 uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress()); 5220 __ LoadLiteral(out, codegen_->DeduplicateBootImageAddressLiteral(address)); 5221 break; 5222 } 5223 case HLoadClass::LoadKind::kDexCacheAddress: { 5224 DCHECK_NE(cls->GetAddress(), 0u); 5225 uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress()); 5226 // 16-bit LDR immediate has a 5-bit offset multiplied by the size and that gives 5227 // a 128B range. To try and reduce the number of literals if we load multiple types, 5228 // simply split the dex cache address to a 128B aligned base loaded from a literal 5229 // and the remaining offset embedded in the load. 5230 static_assert(sizeof(GcRoot<mirror::Class>) == 4u, "Expected GC root to be 4 bytes."); 5231 DCHECK_ALIGNED(cls->GetAddress(), 4u); 5232 constexpr size_t offset_bits = /* encoded bits */ 5 + /* scale */ 2; 5233 uint32_t base_address = address & ~MaxInt<uint32_t>(offset_bits); 5234 uint32_t offset = address & MaxInt<uint32_t>(offset_bits); 5235 __ LoadLiteral(out, codegen_->DeduplicateDexCacheAddressLiteral(base_address)); 5236 // /* GcRoot<mirror::Class> */ out = *(base_address + offset) 5237 GenerateGcRootFieldLoad(cls, out_loc, out, offset); 5238 generate_null_check = !cls->IsInDexCache(); 5239 break; 5240 } 5241 case HLoadClass::LoadKind::kDexCachePcRelative: { 5242 Register base_reg = locations->InAt(0).AsRegister<Register>(); 5243 HArmDexCacheArraysBase* base = cls->InputAt(0)->AsArmDexCacheArraysBase(); 5244 int32_t offset = cls->GetDexCacheElementOffset() - base->GetElementOffset(); 5245 // /* GcRoot<mirror::Class> */ out = *(dex_cache_arrays_base + offset) 5246 GenerateGcRootFieldLoad(cls, out_loc, base_reg, offset); 5247 generate_null_check = !cls->IsInDexCache(); 5248 break; 5249 } 5250 case HLoadClass::LoadKind::kDexCacheViaMethod: { 5251 // /* GcRoot<mirror::Class>[] */ out = 5252 // current_method.ptr_sized_fields_->dex_cache_resolved_types_ 5253 Register current_method = locations->InAt(0).AsRegister<Register>(); 5254 __ LoadFromOffset(kLoadWord, 5255 out, 5256 current_method, 5257 ArtMethod::DexCacheResolvedTypesOffset(kArmPointerSize).Int32Value()); 5258 // /* GcRoot<mirror::Class> */ out = out[type_index] 5259 size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex()); 5260 GenerateGcRootFieldLoad(cls, out_loc, out, offset); 5261 generate_null_check = !cls->IsInDexCache(); 5262 } 5263 } 5264 5265 if (generate_null_check || cls->MustGenerateClinitCheck()) { 5266 DCHECK(cls->CanCallRuntime()); 5267 SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM( 5268 cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck()); 5269 codegen_->AddSlowPath(slow_path); 5270 if (generate_null_check) { 5271 __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel()); 5272 } 5273 if (cls->MustGenerateClinitCheck()) { 5274 GenerateClassInitializationCheck(slow_path, out); 5275 } else { 5276 __ Bind(slow_path->GetExitLabel()); 5277 } 5278 } 5279} 5280 5281void LocationsBuilderARM::VisitClinitCheck(HClinitCheck* check) { 5282 LocationSummary* locations = 5283 new (GetGraph()->GetArena()) LocationSummary(check, LocationSummary::kCallOnSlowPath); 5284 locations->SetInAt(0, Location::RequiresRegister()); 5285 if (check->HasUses()) { 5286 locations->SetOut(Location::SameAsFirstInput()); 5287 } 5288} 5289 5290void InstructionCodeGeneratorARM::VisitClinitCheck(HClinitCheck* check) { 5291 // We assume the class is not null. 5292 SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM( 5293 check->GetLoadClass(), check, check->GetDexPc(), true); 5294 codegen_->AddSlowPath(slow_path); 5295 GenerateClassInitializationCheck(slow_path, 5296 check->GetLocations()->InAt(0).AsRegister<Register>()); 5297} 5298 5299void InstructionCodeGeneratorARM::GenerateClassInitializationCheck( 5300 SlowPathCode* slow_path, Register class_reg) { 5301 __ LoadFromOffset(kLoadWord, IP, class_reg, mirror::Class::StatusOffset().Int32Value()); 5302 __ cmp(IP, ShifterOperand(mirror::Class::kStatusInitialized)); 5303 __ b(slow_path->GetEntryLabel(), LT); 5304 // Even if the initialized flag is set, we may be in a situation where caches are not synced 5305 // properly. Therefore, we do a memory fence. 5306 __ dmb(ISH); 5307 __ Bind(slow_path->GetExitLabel()); 5308} 5309 5310HLoadString::LoadKind CodeGeneratorARM::GetSupportedLoadStringKind( 5311 HLoadString::LoadKind desired_string_load_kind) { 5312 if (kEmitCompilerReadBarrier) { 5313 switch (desired_string_load_kind) { 5314 case HLoadString::LoadKind::kBootImageLinkTimeAddress: 5315 case HLoadString::LoadKind::kBootImageLinkTimePcRelative: 5316 case HLoadString::LoadKind::kBootImageAddress: 5317 // TODO: Implement for read barrier. 5318 return HLoadString::LoadKind::kDexCacheViaMethod; 5319 default: 5320 break; 5321 } 5322 } 5323 switch (desired_string_load_kind) { 5324 case HLoadString::LoadKind::kBootImageLinkTimeAddress: 5325 DCHECK(!GetCompilerOptions().GetCompilePic()); 5326 break; 5327 case HLoadString::LoadKind::kBootImageLinkTimePcRelative: 5328 DCHECK(GetCompilerOptions().GetCompilePic()); 5329 break; 5330 case HLoadString::LoadKind::kBootImageAddress: 5331 break; 5332 case HLoadString::LoadKind::kDexCacheAddress: 5333 DCHECK(Runtime::Current()->UseJitCompilation()); 5334 break; 5335 case HLoadString::LoadKind::kDexCachePcRelative: 5336 DCHECK(!Runtime::Current()->UseJitCompilation()); 5337 // We disable pc-relative load when there is an irreducible loop, as the optimization 5338 // is incompatible with it. 5339 // TODO: Create as many ArmDexCacheArraysBase instructions as needed for methods 5340 // with irreducible loops. 5341 if (GetGraph()->HasIrreducibleLoops()) { 5342 return HLoadString::LoadKind::kDexCacheViaMethod; 5343 } 5344 break; 5345 case HLoadString::LoadKind::kDexCacheViaMethod: 5346 break; 5347 } 5348 return desired_string_load_kind; 5349} 5350 5351void LocationsBuilderARM::VisitLoadString(HLoadString* load) { 5352 LocationSummary::CallKind call_kind = (load->NeedsEnvironment() || kEmitCompilerReadBarrier) 5353 ? LocationSummary::kCallOnSlowPath 5354 : LocationSummary::kNoCall; 5355 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind); 5356 HLoadString::LoadKind load_kind = load->GetLoadKind(); 5357 if (load_kind == HLoadString::LoadKind::kDexCacheViaMethod || 5358 load_kind == HLoadString::LoadKind::kDexCachePcRelative) { 5359 locations->SetInAt(0, Location::RequiresRegister()); 5360 } 5361 locations->SetOut(Location::RequiresRegister()); 5362} 5363 5364void InstructionCodeGeneratorARM::VisitLoadString(HLoadString* load) { 5365 LocationSummary* locations = load->GetLocations(); 5366 Location out_loc = locations->Out(); 5367 Register out = out_loc.AsRegister<Register>(); 5368 5369 switch (load->GetLoadKind()) { 5370 case HLoadString::LoadKind::kBootImageLinkTimeAddress: { 5371 DCHECK(!kEmitCompilerReadBarrier); 5372 __ LoadLiteral(out, codegen_->DeduplicateBootImageStringLiteral(load->GetDexFile(), 5373 load->GetStringIndex())); 5374 return; // No dex cache slow path. 5375 } 5376 case HLoadString::LoadKind::kBootImageLinkTimePcRelative: { 5377 DCHECK(!kEmitCompilerReadBarrier); 5378 CodeGeneratorARM::PcRelativePatchInfo* labels = 5379 codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex()); 5380 __ BindTrackedLabel(&labels->movw_label); 5381 __ movw(out, /* placeholder */ 0u); 5382 __ BindTrackedLabel(&labels->movt_label); 5383 __ movt(out, /* placeholder */ 0u); 5384 __ BindTrackedLabel(&labels->add_pc_label); 5385 __ add(out, out, ShifterOperand(PC)); 5386 return; // No dex cache slow path. 5387 } 5388 case HLoadString::LoadKind::kBootImageAddress: { 5389 DCHECK(!kEmitCompilerReadBarrier); 5390 DCHECK_NE(load->GetAddress(), 0u); 5391 uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress()); 5392 __ LoadLiteral(out, codegen_->DeduplicateBootImageAddressLiteral(address)); 5393 return; // No dex cache slow path. 5394 } 5395 case HLoadString::LoadKind::kDexCacheAddress: { 5396 DCHECK_NE(load->GetAddress(), 0u); 5397 uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress()); 5398 // 16-bit LDR immediate has a 5-bit offset multiplied by the size and that gives 5399 // a 128B range. To try and reduce the number of literals if we load multiple strings, 5400 // simply split the dex cache address to a 128B aligned base loaded from a literal 5401 // and the remaining offset embedded in the load. 5402 static_assert(sizeof(GcRoot<mirror::String>) == 4u, "Expected GC root to be 4 bytes."); 5403 DCHECK_ALIGNED(load->GetAddress(), 4u); 5404 constexpr size_t offset_bits = /* encoded bits */ 5 + /* scale */ 2; 5405 uint32_t base_address = address & ~MaxInt<uint32_t>(offset_bits); 5406 uint32_t offset = address & MaxInt<uint32_t>(offset_bits); 5407 __ LoadLiteral(out, codegen_->DeduplicateDexCacheAddressLiteral(base_address)); 5408 // /* GcRoot<mirror::String> */ out = *(base_address + offset) 5409 GenerateGcRootFieldLoad(load, out_loc, out, offset); 5410 break; 5411 } 5412 case HLoadString::LoadKind::kDexCachePcRelative: { 5413 Register base_reg = locations->InAt(0).AsRegister<Register>(); 5414 HArmDexCacheArraysBase* base = load->InputAt(0)->AsArmDexCacheArraysBase(); 5415 int32_t offset = load->GetDexCacheElementOffset() - base->GetElementOffset(); 5416 // /* GcRoot<mirror::String> */ out = *(dex_cache_arrays_base + offset) 5417 GenerateGcRootFieldLoad(load, out_loc, base_reg, offset); 5418 break; 5419 } 5420 case HLoadString::LoadKind::kDexCacheViaMethod: { 5421 Register current_method = locations->InAt(0).AsRegister<Register>(); 5422 5423 // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_ 5424 GenerateGcRootFieldLoad( 5425 load, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value()); 5426 // /* GcRoot<mirror::String>[] */ out = out->dex_cache_strings_ 5427 __ LoadFromOffset(kLoadWord, out, out, mirror::Class::DexCacheStringsOffset().Int32Value()); 5428 // /* GcRoot<mirror::String> */ out = out[string_index] 5429 GenerateGcRootFieldLoad( 5430 load, out_loc, out, CodeGenerator::GetCacheOffset(load->GetStringIndex())); 5431 break; 5432 } 5433 default: 5434 LOG(FATAL) << "Unexpected load kind: " << load->GetLoadKind(); 5435 UNREACHABLE(); 5436 } 5437 5438 if (!load->IsInDexCache()) { 5439 SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathARM(load); 5440 codegen_->AddSlowPath(slow_path); 5441 __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel()); 5442 __ Bind(slow_path->GetExitLabel()); 5443 } 5444} 5445 5446static int32_t GetExceptionTlsOffset() { 5447 return Thread::ExceptionOffset<kArmWordSize>().Int32Value(); 5448} 5449 5450void LocationsBuilderARM::VisitLoadException(HLoadException* load) { 5451 LocationSummary* locations = 5452 new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kNoCall); 5453 locations->SetOut(Location::RequiresRegister()); 5454} 5455 5456void InstructionCodeGeneratorARM::VisitLoadException(HLoadException* load) { 5457 Register out = load->GetLocations()->Out().AsRegister<Register>(); 5458 __ LoadFromOffset(kLoadWord, out, TR, GetExceptionTlsOffset()); 5459} 5460 5461void LocationsBuilderARM::VisitClearException(HClearException* clear) { 5462 new (GetGraph()->GetArena()) LocationSummary(clear, LocationSummary::kNoCall); 5463} 5464 5465void InstructionCodeGeneratorARM::VisitClearException(HClearException* clear ATTRIBUTE_UNUSED) { 5466 __ LoadImmediate(IP, 0); 5467 __ StoreToOffset(kStoreWord, IP, TR, GetExceptionTlsOffset()); 5468} 5469 5470void LocationsBuilderARM::VisitThrow(HThrow* instruction) { 5471 LocationSummary* locations = 5472 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly); 5473 InvokeRuntimeCallingConvention calling_convention; 5474 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 5475} 5476 5477void InstructionCodeGeneratorARM::VisitThrow(HThrow* instruction) { 5478 codegen_->InvokeRuntime( 5479 QUICK_ENTRY_POINT(pDeliverException), instruction, instruction->GetDexPc(), nullptr); 5480 CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>(); 5481} 5482 5483static bool TypeCheckNeedsATemporary(TypeCheckKind type_check_kind) { 5484 return kEmitCompilerReadBarrier && 5485 (kUseBakerReadBarrier || 5486 type_check_kind == TypeCheckKind::kAbstractClassCheck || 5487 type_check_kind == TypeCheckKind::kClassHierarchyCheck || 5488 type_check_kind == TypeCheckKind::kArrayObjectCheck); 5489} 5490 5491void LocationsBuilderARM::VisitInstanceOf(HInstanceOf* instruction) { 5492 LocationSummary::CallKind call_kind = LocationSummary::kNoCall; 5493 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind(); 5494 switch (type_check_kind) { 5495 case TypeCheckKind::kExactCheck: 5496 case TypeCheckKind::kAbstractClassCheck: 5497 case TypeCheckKind::kClassHierarchyCheck: 5498 case TypeCheckKind::kArrayObjectCheck: 5499 call_kind = 5500 kEmitCompilerReadBarrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall; 5501 break; 5502 case TypeCheckKind::kArrayCheck: 5503 case TypeCheckKind::kUnresolvedCheck: 5504 case TypeCheckKind::kInterfaceCheck: 5505 call_kind = LocationSummary::kCallOnSlowPath; 5506 break; 5507 } 5508 5509 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); 5510 locations->SetInAt(0, Location::RequiresRegister()); 5511 locations->SetInAt(1, Location::RequiresRegister()); 5512 // The "out" register is used as a temporary, so it overlaps with the inputs. 5513 // Note that TypeCheckSlowPathARM uses this register too. 5514 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); 5515 // When read barriers are enabled, we need a temporary register for 5516 // some cases. 5517 if (TypeCheckNeedsATemporary(type_check_kind)) { 5518 locations->AddTemp(Location::RequiresRegister()); 5519 } 5520} 5521 5522void InstructionCodeGeneratorARM::VisitInstanceOf(HInstanceOf* instruction) { 5523 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind(); 5524 LocationSummary* locations = instruction->GetLocations(); 5525 Location obj_loc = locations->InAt(0); 5526 Register obj = obj_loc.AsRegister<Register>(); 5527 Register cls = locations->InAt(1).AsRegister<Register>(); 5528 Location out_loc = locations->Out(); 5529 Register out = out_loc.AsRegister<Register>(); 5530 Location maybe_temp_loc = TypeCheckNeedsATemporary(type_check_kind) ? 5531 locations->GetTemp(0) : 5532 Location::NoLocation(); 5533 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 5534 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); 5535 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); 5536 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value(); 5537 Label done, zero; 5538 SlowPathCode* slow_path = nullptr; 5539 5540 // Return 0 if `obj` is null. 5541 // avoid null check if we know obj is not null. 5542 if (instruction->MustDoNullCheck()) { 5543 __ CompareAndBranchIfZero(obj, &zero); 5544 } 5545 5546 // /* HeapReference<Class> */ out = obj->klass_ 5547 GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, maybe_temp_loc); 5548 5549 switch (type_check_kind) { 5550 case TypeCheckKind::kExactCheck: { 5551 __ cmp(out, ShifterOperand(cls)); 5552 // Classes must be equal for the instanceof to succeed. 5553 __ b(&zero, NE); 5554 __ LoadImmediate(out, 1); 5555 __ b(&done); 5556 break; 5557 } 5558 5559 case TypeCheckKind::kAbstractClassCheck: { 5560 // If the class is abstract, we eagerly fetch the super class of the 5561 // object to avoid doing a comparison we know will fail. 5562 Label loop; 5563 __ Bind(&loop); 5564 // /* HeapReference<Class> */ out = out->super_class_ 5565 GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, maybe_temp_loc); 5566 // If `out` is null, we use it for the result, and jump to `done`. 5567 __ CompareAndBranchIfZero(out, &done); 5568 __ cmp(out, ShifterOperand(cls)); 5569 __ b(&loop, NE); 5570 __ LoadImmediate(out, 1); 5571 if (zero.IsLinked()) { 5572 __ b(&done); 5573 } 5574 break; 5575 } 5576 5577 case TypeCheckKind::kClassHierarchyCheck: { 5578 // Walk over the class hierarchy to find a match. 5579 Label loop, success; 5580 __ Bind(&loop); 5581 __ cmp(out, ShifterOperand(cls)); 5582 __ b(&success, EQ); 5583 // /* HeapReference<Class> */ out = out->super_class_ 5584 GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, maybe_temp_loc); 5585 __ CompareAndBranchIfNonZero(out, &loop); 5586 // If `out` is null, we use it for the result, and jump to `done`. 5587 __ b(&done); 5588 __ Bind(&success); 5589 __ LoadImmediate(out, 1); 5590 if (zero.IsLinked()) { 5591 __ b(&done); 5592 } 5593 break; 5594 } 5595 5596 case TypeCheckKind::kArrayObjectCheck: { 5597 // Do an exact check. 5598 Label exact_check; 5599 __ cmp(out, ShifterOperand(cls)); 5600 __ b(&exact_check, EQ); 5601 // Otherwise, we need to check that the object's class is a non-primitive array. 5602 // /* HeapReference<Class> */ out = out->component_type_ 5603 GenerateReferenceLoadOneRegister(instruction, out_loc, component_offset, maybe_temp_loc); 5604 // If `out` is null, we use it for the result, and jump to `done`. 5605 __ CompareAndBranchIfZero(out, &done); 5606 __ LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset); 5607 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot"); 5608 __ CompareAndBranchIfNonZero(out, &zero); 5609 __ Bind(&exact_check); 5610 __ LoadImmediate(out, 1); 5611 __ b(&done); 5612 break; 5613 } 5614 5615 case TypeCheckKind::kArrayCheck: { 5616 __ cmp(out, ShifterOperand(cls)); 5617 DCHECK(locations->OnlyCallsOnSlowPath()); 5618 slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARM(instruction, 5619 /* is_fatal */ false); 5620 codegen_->AddSlowPath(slow_path); 5621 __ b(slow_path->GetEntryLabel(), NE); 5622 __ LoadImmediate(out, 1); 5623 if (zero.IsLinked()) { 5624 __ b(&done); 5625 } 5626 break; 5627 } 5628 5629 case TypeCheckKind::kUnresolvedCheck: 5630 case TypeCheckKind::kInterfaceCheck: { 5631 // Note that we indeed only call on slow path, but we always go 5632 // into the slow path for the unresolved and interface check 5633 // cases. 5634 // 5635 // We cannot directly call the InstanceofNonTrivial runtime 5636 // entry point without resorting to a type checking slow path 5637 // here (i.e. by calling InvokeRuntime directly), as it would 5638 // require to assign fixed registers for the inputs of this 5639 // HInstanceOf instruction (following the runtime calling 5640 // convention), which might be cluttered by the potential first 5641 // read barrier emission at the beginning of this method. 5642 // 5643 // TODO: Introduce a new runtime entry point taking the object 5644 // to test (instead of its class) as argument, and let it deal 5645 // with the read barrier issues. This will let us refactor this 5646 // case of the `switch` code as it was previously (with a direct 5647 // call to the runtime not using a type checking slow path). 5648 // This should also be beneficial for the other cases above. 5649 DCHECK(locations->OnlyCallsOnSlowPath()); 5650 slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARM(instruction, 5651 /* is_fatal */ false); 5652 codegen_->AddSlowPath(slow_path); 5653 __ b(slow_path->GetEntryLabel()); 5654 if (zero.IsLinked()) { 5655 __ b(&done); 5656 } 5657 break; 5658 } 5659 } 5660 5661 if (zero.IsLinked()) { 5662 __ Bind(&zero); 5663 __ LoadImmediate(out, 0); 5664 } 5665 5666 if (done.IsLinked()) { 5667 __ Bind(&done); 5668 } 5669 5670 if (slow_path != nullptr) { 5671 __ Bind(slow_path->GetExitLabel()); 5672 } 5673} 5674 5675void LocationsBuilderARM::VisitCheckCast(HCheckCast* instruction) { 5676 LocationSummary::CallKind call_kind = LocationSummary::kNoCall; 5677 bool throws_into_catch = instruction->CanThrowIntoCatchBlock(); 5678 5679 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind(); 5680 switch (type_check_kind) { 5681 case TypeCheckKind::kExactCheck: 5682 case TypeCheckKind::kAbstractClassCheck: 5683 case TypeCheckKind::kClassHierarchyCheck: 5684 case TypeCheckKind::kArrayObjectCheck: 5685 call_kind = (throws_into_catch || kEmitCompilerReadBarrier) ? 5686 LocationSummary::kCallOnSlowPath : 5687 LocationSummary::kNoCall; // In fact, call on a fatal (non-returning) slow path. 5688 break; 5689 case TypeCheckKind::kArrayCheck: 5690 case TypeCheckKind::kUnresolvedCheck: 5691 case TypeCheckKind::kInterfaceCheck: 5692 call_kind = LocationSummary::kCallOnSlowPath; 5693 break; 5694 } 5695 5696 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); 5697 locations->SetInAt(0, Location::RequiresRegister()); 5698 locations->SetInAt(1, Location::RequiresRegister()); 5699 // Note that TypeCheckSlowPathARM uses this "temp" register too. 5700 locations->AddTemp(Location::RequiresRegister()); 5701 // When read barriers are enabled, we need an additional temporary 5702 // register for some cases. 5703 if (TypeCheckNeedsATemporary(type_check_kind)) { 5704 locations->AddTemp(Location::RequiresRegister()); 5705 } 5706} 5707 5708void InstructionCodeGeneratorARM::VisitCheckCast(HCheckCast* instruction) { 5709 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind(); 5710 LocationSummary* locations = instruction->GetLocations(); 5711 Location obj_loc = locations->InAt(0); 5712 Register obj = obj_loc.AsRegister<Register>(); 5713 Register cls = locations->InAt(1).AsRegister<Register>(); 5714 Location temp_loc = locations->GetTemp(0); 5715 Register temp = temp_loc.AsRegister<Register>(); 5716 Location maybe_temp2_loc = TypeCheckNeedsATemporary(type_check_kind) ? 5717 locations->GetTemp(1) : 5718 Location::NoLocation(); 5719 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 5720 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); 5721 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); 5722 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value(); 5723 5724 bool is_type_check_slow_path_fatal = 5725 (type_check_kind == TypeCheckKind::kExactCheck || 5726 type_check_kind == TypeCheckKind::kAbstractClassCheck || 5727 type_check_kind == TypeCheckKind::kClassHierarchyCheck || 5728 type_check_kind == TypeCheckKind::kArrayObjectCheck) && 5729 !instruction->CanThrowIntoCatchBlock(); 5730 SlowPathCode* type_check_slow_path = 5731 new (GetGraph()->GetArena()) TypeCheckSlowPathARM(instruction, 5732 is_type_check_slow_path_fatal); 5733 codegen_->AddSlowPath(type_check_slow_path); 5734 5735 Label done; 5736 // Avoid null check if we know obj is not null. 5737 if (instruction->MustDoNullCheck()) { 5738 __ CompareAndBranchIfZero(obj, &done); 5739 } 5740 5741 // /* HeapReference<Class> */ temp = obj->klass_ 5742 GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); 5743 5744 switch (type_check_kind) { 5745 case TypeCheckKind::kExactCheck: 5746 case TypeCheckKind::kArrayCheck: { 5747 __ cmp(temp, ShifterOperand(cls)); 5748 // Jump to slow path for throwing the exception or doing a 5749 // more involved array check. 5750 __ b(type_check_slow_path->GetEntryLabel(), NE); 5751 break; 5752 } 5753 5754 case TypeCheckKind::kAbstractClassCheck: { 5755 // If the class is abstract, we eagerly fetch the super class of the 5756 // object to avoid doing a comparison we know will fail. 5757 Label loop, compare_classes; 5758 __ Bind(&loop); 5759 // /* HeapReference<Class> */ temp = temp->super_class_ 5760 GenerateReferenceLoadOneRegister(instruction, temp_loc, super_offset, maybe_temp2_loc); 5761 5762 // If the class reference currently in `temp` is not null, jump 5763 // to the `compare_classes` label to compare it with the checked 5764 // class. 5765 __ CompareAndBranchIfNonZero(temp, &compare_classes); 5766 // Otherwise, jump to the slow path to throw the exception. 5767 // 5768 // But before, move back the object's class into `temp` before 5769 // going into the slow path, as it has been overwritten in the 5770 // meantime. 5771 // /* HeapReference<Class> */ temp = obj->klass_ 5772 GenerateReferenceLoadTwoRegisters( 5773 instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); 5774 __ b(type_check_slow_path->GetEntryLabel()); 5775 5776 __ Bind(&compare_classes); 5777 __ cmp(temp, ShifterOperand(cls)); 5778 __ b(&loop, NE); 5779 break; 5780 } 5781 5782 case TypeCheckKind::kClassHierarchyCheck: { 5783 // Walk over the class hierarchy to find a match. 5784 Label loop; 5785 __ Bind(&loop); 5786 __ cmp(temp, ShifterOperand(cls)); 5787 __ b(&done, EQ); 5788 5789 // /* HeapReference<Class> */ temp = temp->super_class_ 5790 GenerateReferenceLoadOneRegister(instruction, temp_loc, super_offset, maybe_temp2_loc); 5791 5792 // If the class reference currently in `temp` is not null, jump 5793 // back at the beginning of the loop. 5794 __ CompareAndBranchIfNonZero(temp, &loop); 5795 // Otherwise, jump to the slow path to throw the exception. 5796 // 5797 // But before, move back the object's class into `temp` before 5798 // going into the slow path, as it has been overwritten in the 5799 // meantime. 5800 // /* HeapReference<Class> */ temp = obj->klass_ 5801 GenerateReferenceLoadTwoRegisters( 5802 instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); 5803 __ b(type_check_slow_path->GetEntryLabel()); 5804 break; 5805 } 5806 5807 case TypeCheckKind::kArrayObjectCheck: { 5808 // Do an exact check. 5809 Label check_non_primitive_component_type; 5810 __ cmp(temp, ShifterOperand(cls)); 5811 __ b(&done, EQ); 5812 5813 // Otherwise, we need to check that the object's class is a non-primitive array. 5814 // /* HeapReference<Class> */ temp = temp->component_type_ 5815 GenerateReferenceLoadOneRegister(instruction, temp_loc, component_offset, maybe_temp2_loc); 5816 5817 // If the component type is not null (i.e. the object is indeed 5818 // an array), jump to label `check_non_primitive_component_type` 5819 // to further check that this component type is not a primitive 5820 // type. 5821 __ CompareAndBranchIfNonZero(temp, &check_non_primitive_component_type); 5822 // Otherwise, jump to the slow path to throw the exception. 5823 // 5824 // But before, move back the object's class into `temp` before 5825 // going into the slow path, as it has been overwritten in the 5826 // meantime. 5827 // /* HeapReference<Class> */ temp = obj->klass_ 5828 GenerateReferenceLoadTwoRegisters( 5829 instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); 5830 __ b(type_check_slow_path->GetEntryLabel()); 5831 5832 __ Bind(&check_non_primitive_component_type); 5833 __ LoadFromOffset(kLoadUnsignedHalfword, temp, temp, primitive_offset); 5834 static_assert(Primitive::kPrimNot == 0, "Expected 0 for art::Primitive::kPrimNot"); 5835 __ CompareAndBranchIfZero(temp, &done); 5836 // Same comment as above regarding `temp` and the slow path. 5837 // /* HeapReference<Class> */ temp = obj->klass_ 5838 GenerateReferenceLoadTwoRegisters( 5839 instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); 5840 __ b(type_check_slow_path->GetEntryLabel()); 5841 break; 5842 } 5843 5844 case TypeCheckKind::kUnresolvedCheck: 5845 case TypeCheckKind::kInterfaceCheck: 5846 // We always go into the type check slow path for the unresolved 5847 // and interface check cases. 5848 // 5849 // We cannot directly call the CheckCast runtime entry point 5850 // without resorting to a type checking slow path here (i.e. by 5851 // calling InvokeRuntime directly), as it would require to 5852 // assign fixed registers for the inputs of this HInstanceOf 5853 // instruction (following the runtime calling convention), which 5854 // might be cluttered by the potential first read barrier 5855 // emission at the beginning of this method. 5856 // 5857 // TODO: Introduce a new runtime entry point taking the object 5858 // to test (instead of its class) as argument, and let it deal 5859 // with the read barrier issues. This will let us refactor this 5860 // case of the `switch` code as it was previously (with a direct 5861 // call to the runtime not using a type checking slow path). 5862 // This should also be beneficial for the other cases above. 5863 __ b(type_check_slow_path->GetEntryLabel()); 5864 break; 5865 } 5866 __ Bind(&done); 5867 5868 __ Bind(type_check_slow_path->GetExitLabel()); 5869} 5870 5871void LocationsBuilderARM::VisitMonitorOperation(HMonitorOperation* instruction) { 5872 LocationSummary* locations = 5873 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly); 5874 InvokeRuntimeCallingConvention calling_convention; 5875 locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); 5876} 5877 5878void InstructionCodeGeneratorARM::VisitMonitorOperation(HMonitorOperation* instruction) { 5879 codegen_->InvokeRuntime(instruction->IsEnter() 5880 ? QUICK_ENTRY_POINT(pLockObject) : QUICK_ENTRY_POINT(pUnlockObject), 5881 instruction, 5882 instruction->GetDexPc(), 5883 nullptr); 5884 if (instruction->IsEnter()) { 5885 CheckEntrypointTypes<kQuickLockObject, void, mirror::Object*>(); 5886 } else { 5887 CheckEntrypointTypes<kQuickUnlockObject, void, mirror::Object*>(); 5888 } 5889} 5890 5891void LocationsBuilderARM::VisitAnd(HAnd* instruction) { HandleBitwiseOperation(instruction, AND); } 5892void LocationsBuilderARM::VisitOr(HOr* instruction) { HandleBitwiseOperation(instruction, ORR); } 5893void LocationsBuilderARM::VisitXor(HXor* instruction) { HandleBitwiseOperation(instruction, EOR); } 5894 5895void LocationsBuilderARM::HandleBitwiseOperation(HBinaryOperation* instruction, Opcode opcode) { 5896 LocationSummary* locations = 5897 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 5898 DCHECK(instruction->GetResultType() == Primitive::kPrimInt 5899 || instruction->GetResultType() == Primitive::kPrimLong); 5900 // Note: GVN reorders commutative operations to have the constant on the right hand side. 5901 locations->SetInAt(0, Location::RequiresRegister()); 5902 locations->SetInAt(1, ArmEncodableConstantOrRegister(instruction->InputAt(1), opcode)); 5903 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 5904} 5905 5906void InstructionCodeGeneratorARM::VisitAnd(HAnd* instruction) { 5907 HandleBitwiseOperation(instruction); 5908} 5909 5910void InstructionCodeGeneratorARM::VisitOr(HOr* instruction) { 5911 HandleBitwiseOperation(instruction); 5912} 5913 5914void InstructionCodeGeneratorARM::VisitXor(HXor* instruction) { 5915 HandleBitwiseOperation(instruction); 5916} 5917 5918 5919void LocationsBuilderARM::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) { 5920 LocationSummary* locations = 5921 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 5922 DCHECK(instruction->GetResultType() == Primitive::kPrimInt 5923 || instruction->GetResultType() == Primitive::kPrimLong); 5924 5925 locations->SetInAt(0, Location::RequiresRegister()); 5926 locations->SetInAt(1, Location::RequiresRegister()); 5927 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 5928} 5929 5930void InstructionCodeGeneratorARM::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) { 5931 LocationSummary* locations = instruction->GetLocations(); 5932 Location first = locations->InAt(0); 5933 Location second = locations->InAt(1); 5934 Location out = locations->Out(); 5935 5936 if (instruction->GetResultType() == Primitive::kPrimInt) { 5937 Register first_reg = first.AsRegister<Register>(); 5938 ShifterOperand second_reg(second.AsRegister<Register>()); 5939 Register out_reg = out.AsRegister<Register>(); 5940 5941 switch (instruction->GetOpKind()) { 5942 case HInstruction::kAnd: 5943 __ bic(out_reg, first_reg, second_reg); 5944 break; 5945 case HInstruction::kOr: 5946 __ orn(out_reg, first_reg, second_reg); 5947 break; 5948 // There is no EON on arm. 5949 case HInstruction::kXor: 5950 default: 5951 LOG(FATAL) << "Unexpected instruction " << instruction->DebugName(); 5952 UNREACHABLE(); 5953 } 5954 return; 5955 5956 } else { 5957 DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong); 5958 Register first_low = first.AsRegisterPairLow<Register>(); 5959 Register first_high = first.AsRegisterPairHigh<Register>(); 5960 ShifterOperand second_low(second.AsRegisterPairLow<Register>()); 5961 ShifterOperand second_high(second.AsRegisterPairHigh<Register>()); 5962 Register out_low = out.AsRegisterPairLow<Register>(); 5963 Register out_high = out.AsRegisterPairHigh<Register>(); 5964 5965 switch (instruction->GetOpKind()) { 5966 case HInstruction::kAnd: 5967 __ bic(out_low, first_low, second_low); 5968 __ bic(out_high, first_high, second_high); 5969 break; 5970 case HInstruction::kOr: 5971 __ orn(out_low, first_low, second_low); 5972 __ orn(out_high, first_high, second_high); 5973 break; 5974 // There is no EON on arm. 5975 case HInstruction::kXor: 5976 default: 5977 LOG(FATAL) << "Unexpected instruction " << instruction->DebugName(); 5978 UNREACHABLE(); 5979 } 5980 } 5981} 5982 5983void InstructionCodeGeneratorARM::GenerateAndConst(Register out, Register first, uint32_t value) { 5984 // Optimize special cases for individual halfs of `and-long` (`and` is simplified earlier). 5985 if (value == 0xffffffffu) { 5986 if (out != first) { 5987 __ mov(out, ShifterOperand(first)); 5988 } 5989 return; 5990 } 5991 if (value == 0u) { 5992 __ mov(out, ShifterOperand(0)); 5993 return; 5994 } 5995 ShifterOperand so; 5996 if (__ ShifterOperandCanHold(kNoRegister, kNoRegister, AND, value, &so)) { 5997 __ and_(out, first, so); 5998 } else { 5999 DCHECK(__ ShifterOperandCanHold(kNoRegister, kNoRegister, BIC, ~value, &so)); 6000 __ bic(out, first, ShifterOperand(~value)); 6001 } 6002} 6003 6004void InstructionCodeGeneratorARM::GenerateOrrConst(Register out, Register first, uint32_t value) { 6005 // Optimize special cases for individual halfs of `or-long` (`or` is simplified earlier). 6006 if (value == 0u) { 6007 if (out != first) { 6008 __ mov(out, ShifterOperand(first)); 6009 } 6010 return; 6011 } 6012 if (value == 0xffffffffu) { 6013 __ mvn(out, ShifterOperand(0)); 6014 return; 6015 } 6016 ShifterOperand so; 6017 if (__ ShifterOperandCanHold(kNoRegister, kNoRegister, ORR, value, &so)) { 6018 __ orr(out, first, so); 6019 } else { 6020 DCHECK(__ ShifterOperandCanHold(kNoRegister, kNoRegister, ORN, ~value, &so)); 6021 __ orn(out, first, ShifterOperand(~value)); 6022 } 6023} 6024 6025void InstructionCodeGeneratorARM::GenerateEorConst(Register out, Register first, uint32_t value) { 6026 // Optimize special case for individual halfs of `xor-long` (`xor` is simplified earlier). 6027 if (value == 0u) { 6028 if (out != first) { 6029 __ mov(out, ShifterOperand(first)); 6030 } 6031 return; 6032 } 6033 __ eor(out, first, ShifterOperand(value)); 6034} 6035 6036void InstructionCodeGeneratorARM::HandleBitwiseOperation(HBinaryOperation* instruction) { 6037 LocationSummary* locations = instruction->GetLocations(); 6038 Location first = locations->InAt(0); 6039 Location second = locations->InAt(1); 6040 Location out = locations->Out(); 6041 6042 if (second.IsConstant()) { 6043 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant())); 6044 uint32_t value_low = Low32Bits(value); 6045 if (instruction->GetResultType() == Primitive::kPrimInt) { 6046 Register first_reg = first.AsRegister<Register>(); 6047 Register out_reg = out.AsRegister<Register>(); 6048 if (instruction->IsAnd()) { 6049 GenerateAndConst(out_reg, first_reg, value_low); 6050 } else if (instruction->IsOr()) { 6051 GenerateOrrConst(out_reg, first_reg, value_low); 6052 } else { 6053 DCHECK(instruction->IsXor()); 6054 GenerateEorConst(out_reg, first_reg, value_low); 6055 } 6056 } else { 6057 DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong); 6058 uint32_t value_high = High32Bits(value); 6059 Register first_low = first.AsRegisterPairLow<Register>(); 6060 Register first_high = first.AsRegisterPairHigh<Register>(); 6061 Register out_low = out.AsRegisterPairLow<Register>(); 6062 Register out_high = out.AsRegisterPairHigh<Register>(); 6063 if (instruction->IsAnd()) { 6064 GenerateAndConst(out_low, first_low, value_low); 6065 GenerateAndConst(out_high, first_high, value_high); 6066 } else if (instruction->IsOr()) { 6067 GenerateOrrConst(out_low, first_low, value_low); 6068 GenerateOrrConst(out_high, first_high, value_high); 6069 } else { 6070 DCHECK(instruction->IsXor()); 6071 GenerateEorConst(out_low, first_low, value_low); 6072 GenerateEorConst(out_high, first_high, value_high); 6073 } 6074 } 6075 return; 6076 } 6077 6078 if (instruction->GetResultType() == Primitive::kPrimInt) { 6079 Register first_reg = first.AsRegister<Register>(); 6080 ShifterOperand second_reg(second.AsRegister<Register>()); 6081 Register out_reg = out.AsRegister<Register>(); 6082 if (instruction->IsAnd()) { 6083 __ and_(out_reg, first_reg, second_reg); 6084 } else if (instruction->IsOr()) { 6085 __ orr(out_reg, first_reg, second_reg); 6086 } else { 6087 DCHECK(instruction->IsXor()); 6088 __ eor(out_reg, first_reg, second_reg); 6089 } 6090 } else { 6091 DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong); 6092 Register first_low = first.AsRegisterPairLow<Register>(); 6093 Register first_high = first.AsRegisterPairHigh<Register>(); 6094 ShifterOperand second_low(second.AsRegisterPairLow<Register>()); 6095 ShifterOperand second_high(second.AsRegisterPairHigh<Register>()); 6096 Register out_low = out.AsRegisterPairLow<Register>(); 6097 Register out_high = out.AsRegisterPairHigh<Register>(); 6098 if (instruction->IsAnd()) { 6099 __ and_(out_low, first_low, second_low); 6100 __ and_(out_high, first_high, second_high); 6101 } else if (instruction->IsOr()) { 6102 __ orr(out_low, first_low, second_low); 6103 __ orr(out_high, first_high, second_high); 6104 } else { 6105 DCHECK(instruction->IsXor()); 6106 __ eor(out_low, first_low, second_low); 6107 __ eor(out_high, first_high, second_high); 6108 } 6109 } 6110} 6111 6112void InstructionCodeGeneratorARM::GenerateReferenceLoadOneRegister(HInstruction* instruction, 6113 Location out, 6114 uint32_t offset, 6115 Location maybe_temp) { 6116 Register out_reg = out.AsRegister<Register>(); 6117 if (kEmitCompilerReadBarrier) { 6118 DCHECK(maybe_temp.IsRegister()) << maybe_temp; 6119 if (kUseBakerReadBarrier) { 6120 // Load with fast path based Baker's read barrier. 6121 // /* HeapReference<Object> */ out = *(out + offset) 6122 codegen_->GenerateFieldLoadWithBakerReadBarrier( 6123 instruction, out, out_reg, offset, maybe_temp, /* needs_null_check */ false); 6124 } else { 6125 // Load with slow path based read barrier. 6126 // Save the value of `out` into `maybe_temp` before overwriting it 6127 // in the following move operation, as we will need it for the 6128 // read barrier below. 6129 __ Mov(maybe_temp.AsRegister<Register>(), out_reg); 6130 // /* HeapReference<Object> */ out = *(out + offset) 6131 __ LoadFromOffset(kLoadWord, out_reg, out_reg, offset); 6132 codegen_->GenerateReadBarrierSlow(instruction, out, out, maybe_temp, offset); 6133 } 6134 } else { 6135 // Plain load with no read barrier. 6136 // /* HeapReference<Object> */ out = *(out + offset) 6137 __ LoadFromOffset(kLoadWord, out_reg, out_reg, offset); 6138 __ MaybeUnpoisonHeapReference(out_reg); 6139 } 6140} 6141 6142void InstructionCodeGeneratorARM::GenerateReferenceLoadTwoRegisters(HInstruction* instruction, 6143 Location out, 6144 Location obj, 6145 uint32_t offset, 6146 Location maybe_temp) { 6147 Register out_reg = out.AsRegister<Register>(); 6148 Register obj_reg = obj.AsRegister<Register>(); 6149 if (kEmitCompilerReadBarrier) { 6150 if (kUseBakerReadBarrier) { 6151 DCHECK(maybe_temp.IsRegister()) << maybe_temp; 6152 // Load with fast path based Baker's read barrier. 6153 // /* HeapReference<Object> */ out = *(obj + offset) 6154 codegen_->GenerateFieldLoadWithBakerReadBarrier( 6155 instruction, out, obj_reg, offset, maybe_temp, /* needs_null_check */ false); 6156 } else { 6157 // Load with slow path based read barrier. 6158 // /* HeapReference<Object> */ out = *(obj + offset) 6159 __ LoadFromOffset(kLoadWord, out_reg, obj_reg, offset); 6160 codegen_->GenerateReadBarrierSlow(instruction, out, out, obj, offset); 6161 } 6162 } else { 6163 // Plain load with no read barrier. 6164 // /* HeapReference<Object> */ out = *(obj + offset) 6165 __ LoadFromOffset(kLoadWord, out_reg, obj_reg, offset); 6166 __ MaybeUnpoisonHeapReference(out_reg); 6167 } 6168} 6169 6170void InstructionCodeGeneratorARM::GenerateGcRootFieldLoad(HInstruction* instruction, 6171 Location root, 6172 Register obj, 6173 uint32_t offset) { 6174 Register root_reg = root.AsRegister<Register>(); 6175 if (kEmitCompilerReadBarrier) { 6176 if (kUseBakerReadBarrier) { 6177 // Fast path implementation of art::ReadBarrier::BarrierForRoot when 6178 // Baker's read barrier are used: 6179 // 6180 // root = obj.field; 6181 // if (Thread::Current()->GetIsGcMarking()) { 6182 // root = ReadBarrier::Mark(root) 6183 // } 6184 6185 // /* GcRoot<mirror::Object> */ root = *(obj + offset) 6186 __ LoadFromOffset(kLoadWord, root_reg, obj, offset); 6187 static_assert( 6188 sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(GcRoot<mirror::Object>), 6189 "art::mirror::CompressedReference<mirror::Object> and art::GcRoot<mirror::Object> " 6190 "have different sizes."); 6191 static_assert(sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(int32_t), 6192 "art::mirror::CompressedReference<mirror::Object> and int32_t " 6193 "have different sizes."); 6194 6195 // Slow path used to mark the GC root `root`. 6196 SlowPathCode* slow_path = 6197 new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathARM(instruction, root); 6198 codegen_->AddSlowPath(slow_path); 6199 6200 // IP = Thread::Current()->GetIsGcMarking() 6201 __ LoadFromOffset( 6202 kLoadWord, IP, TR, Thread::IsGcMarkingOffset<kArmWordSize>().Int32Value()); 6203 __ CompareAndBranchIfNonZero(IP, slow_path->GetEntryLabel()); 6204 __ Bind(slow_path->GetExitLabel()); 6205 } else { 6206 // GC root loaded through a slow path for read barriers other 6207 // than Baker's. 6208 // /* GcRoot<mirror::Object>* */ root = obj + offset 6209 __ AddConstant(root_reg, obj, offset); 6210 // /* mirror::Object* */ root = root->Read() 6211 codegen_->GenerateReadBarrierForRootSlow(instruction, root, root); 6212 } 6213 } else { 6214 // Plain GC root load with no read barrier. 6215 // /* GcRoot<mirror::Object> */ root = *(obj + offset) 6216 __ LoadFromOffset(kLoadWord, root_reg, obj, offset); 6217 // Note that GC roots are not affected by heap poisoning, thus we 6218 // do not have to unpoison `root_reg` here. 6219 } 6220} 6221 6222void CodeGeneratorARM::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction, 6223 Location ref, 6224 Register obj, 6225 uint32_t offset, 6226 Location temp, 6227 bool needs_null_check) { 6228 DCHECK(kEmitCompilerReadBarrier); 6229 DCHECK(kUseBakerReadBarrier); 6230 6231 // /* HeapReference<Object> */ ref = *(obj + offset) 6232 Location no_index = Location::NoLocation(); 6233 ScaleFactor no_scale_factor = TIMES_1; 6234 GenerateReferenceLoadWithBakerReadBarrier( 6235 instruction, ref, obj, offset, no_index, no_scale_factor, temp, needs_null_check); 6236} 6237 6238void CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction, 6239 Location ref, 6240 Register obj, 6241 uint32_t data_offset, 6242 Location index, 6243 Location temp, 6244 bool needs_null_check) { 6245 DCHECK(kEmitCompilerReadBarrier); 6246 DCHECK(kUseBakerReadBarrier); 6247 6248 static_assert( 6249 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), 6250 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); 6251 // /* HeapReference<Object> */ ref = 6252 // *(obj + data_offset + index * sizeof(HeapReference<Object>)) 6253 ScaleFactor scale_factor = TIMES_4; 6254 GenerateReferenceLoadWithBakerReadBarrier( 6255 instruction, ref, obj, data_offset, index, scale_factor, temp, needs_null_check); 6256} 6257 6258void CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction, 6259 Location ref, 6260 Register obj, 6261 uint32_t offset, 6262 Location index, 6263 ScaleFactor scale_factor, 6264 Location temp, 6265 bool needs_null_check) { 6266 DCHECK(kEmitCompilerReadBarrier); 6267 DCHECK(kUseBakerReadBarrier); 6268 6269 // In slow path based read barriers, the read barrier call is 6270 // inserted after the original load. However, in fast path based 6271 // Baker's read barriers, we need to perform the load of 6272 // mirror::Object::monitor_ *before* the original reference load. 6273 // This load-load ordering is required by the read barrier. 6274 // The fast path/slow path (for Baker's algorithm) should look like: 6275 // 6276 // uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState(); 6277 // lfence; // Load fence or artificial data dependency to prevent load-load reordering 6278 // HeapReference<Object> ref = *src; // Original reference load. 6279 // bool is_gray = (rb_state == ReadBarrier::gray_ptr_); 6280 // if (is_gray) { 6281 // ref = ReadBarrier::Mark(ref); // Performed by runtime entrypoint slow path. 6282 // } 6283 // 6284 // Note: the original implementation in ReadBarrier::Barrier is 6285 // slightly more complex as it performs additional checks that we do 6286 // not do here for performance reasons. 6287 6288 Register ref_reg = ref.AsRegister<Register>(); 6289 Register temp_reg = temp.AsRegister<Register>(); 6290 uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value(); 6291 6292 // /* int32_t */ monitor = obj->monitor_ 6293 __ LoadFromOffset(kLoadWord, temp_reg, obj, monitor_offset); 6294 if (needs_null_check) { 6295 MaybeRecordImplicitNullCheck(instruction); 6296 } 6297 // /* LockWord */ lock_word = LockWord(monitor) 6298 static_assert(sizeof(LockWord) == sizeof(int32_t), 6299 "art::LockWord and int32_t have different sizes."); 6300 6301 // Introduce a dependency on the lock_word including the rb_state, 6302 // which shall prevent load-load reordering without using 6303 // a memory barrier (which would be more expensive). 6304 // obj is unchanged by this operation, but its value now depends on temp_reg. 6305 __ add(obj, obj, ShifterOperand(temp_reg, LSR, 32)); 6306 6307 // The actual reference load. 6308 if (index.IsValid()) { 6309 // Load types involving an "index": ArrayGet and 6310 // UnsafeGetObject/UnsafeGetObjectVolatile intrinsics. 6311 // /* HeapReference<Object> */ ref = *(obj + offset + (index << scale_factor)) 6312 if (index.IsConstant()) { 6313 size_t computed_offset = 6314 (index.GetConstant()->AsIntConstant()->GetValue() << scale_factor) + offset; 6315 __ LoadFromOffset(kLoadWord, ref_reg, obj, computed_offset); 6316 } else { 6317 // Handle the special case of the 6318 // UnsafeGetObject/UnsafeGetObjectVolatile intrinsics, which use 6319 // a register pair as index ("long offset"), of which only the low 6320 // part contains data. 6321 Register index_reg = index.IsRegisterPair() 6322 ? index.AsRegisterPairLow<Register>() 6323 : index.AsRegister<Register>(); 6324 __ add(IP, obj, ShifterOperand(index_reg, LSL, scale_factor)); 6325 __ LoadFromOffset(kLoadWord, ref_reg, IP, offset); 6326 } 6327 } else { 6328 // /* HeapReference<Object> */ ref = *(obj + offset) 6329 __ LoadFromOffset(kLoadWord, ref_reg, obj, offset); 6330 } 6331 6332 // Object* ref = ref_addr->AsMirrorPtr() 6333 __ MaybeUnpoisonHeapReference(ref_reg); 6334 6335 // Slow path used to mark the object `ref` when it is gray. 6336 SlowPathCode* slow_path = 6337 new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathARM(instruction, ref); 6338 AddSlowPath(slow_path); 6339 6340 // if (rb_state == ReadBarrier::gray_ptr_) 6341 // ref = ReadBarrier::Mark(ref); 6342 // Given the numeric representation, it's enough to check the low bit of the 6343 // rb_state. We do that by shifting the bit out of the lock word with LSRS 6344 // which can be a 16-bit instruction unlike the TST immediate. 6345 static_assert(ReadBarrier::white_ptr_ == 0, "Expecting white to have value 0"); 6346 static_assert(ReadBarrier::gray_ptr_ == 1, "Expecting gray to have value 1"); 6347 static_assert(ReadBarrier::black_ptr_ == 2, "Expecting black to have value 2"); 6348 __ Lsrs(temp_reg, temp_reg, LockWord::kReadBarrierStateShift + 1); 6349 __ b(slow_path->GetEntryLabel(), CS); // Carry flag is the last bit shifted out by LSRS. 6350 __ Bind(slow_path->GetExitLabel()); 6351} 6352 6353void CodeGeneratorARM::GenerateReadBarrierSlow(HInstruction* instruction, 6354 Location out, 6355 Location ref, 6356 Location obj, 6357 uint32_t offset, 6358 Location index) { 6359 DCHECK(kEmitCompilerReadBarrier); 6360 6361 // Insert a slow path based read barrier *after* the reference load. 6362 // 6363 // If heap poisoning is enabled, the unpoisoning of the loaded 6364 // reference will be carried out by the runtime within the slow 6365 // path. 6366 // 6367 // Note that `ref` currently does not get unpoisoned (when heap 6368 // poisoning is enabled), which is alright as the `ref` argument is 6369 // not used by the artReadBarrierSlow entry point. 6370 // 6371 // TODO: Unpoison `ref` when it is used by artReadBarrierSlow. 6372 SlowPathCode* slow_path = new (GetGraph()->GetArena()) 6373 ReadBarrierForHeapReferenceSlowPathARM(instruction, out, ref, obj, offset, index); 6374 AddSlowPath(slow_path); 6375 6376 __ b(slow_path->GetEntryLabel()); 6377 __ Bind(slow_path->GetExitLabel()); 6378} 6379 6380void CodeGeneratorARM::MaybeGenerateReadBarrierSlow(HInstruction* instruction, 6381 Location out, 6382 Location ref, 6383 Location obj, 6384 uint32_t offset, 6385 Location index) { 6386 if (kEmitCompilerReadBarrier) { 6387 // Baker's read barriers shall be handled by the fast path 6388 // (CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier). 6389 DCHECK(!kUseBakerReadBarrier); 6390 // If heap poisoning is enabled, unpoisoning will be taken care of 6391 // by the runtime within the slow path. 6392 GenerateReadBarrierSlow(instruction, out, ref, obj, offset, index); 6393 } else if (kPoisonHeapReferences) { 6394 __ UnpoisonHeapReference(out.AsRegister<Register>()); 6395 } 6396} 6397 6398void CodeGeneratorARM::GenerateReadBarrierForRootSlow(HInstruction* instruction, 6399 Location out, 6400 Location root) { 6401 DCHECK(kEmitCompilerReadBarrier); 6402 6403 // Insert a slow path based read barrier *after* the GC root load. 6404 // 6405 // Note that GC roots are not affected by heap poisoning, so we do 6406 // not need to do anything special for this here. 6407 SlowPathCode* slow_path = 6408 new (GetGraph()->GetArena()) ReadBarrierForRootSlowPathARM(instruction, out, root); 6409 AddSlowPath(slow_path); 6410 6411 __ b(slow_path->GetEntryLabel()); 6412 __ Bind(slow_path->GetExitLabel()); 6413} 6414 6415HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARM::GetSupportedInvokeStaticOrDirectDispatch( 6416 const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info, 6417 MethodReference target_method) { 6418 HInvokeStaticOrDirect::DispatchInfo dispatch_info = desired_dispatch_info; 6419 // We disable pc-relative load when there is an irreducible loop, as the optimization 6420 // is incompatible with it. 6421 // TODO: Create as many ArmDexCacheArraysBase instructions as needed for methods 6422 // with irreducible loops. 6423 if (GetGraph()->HasIrreducibleLoops() && 6424 (dispatch_info.method_load_kind == 6425 HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative)) { 6426 dispatch_info.method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod; 6427 } 6428 6429 if (dispatch_info.code_ptr_location == HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative) { 6430 const DexFile& outer_dex_file = GetGraph()->GetDexFile(); 6431 if (&outer_dex_file != target_method.dex_file) { 6432 // Calls across dex files are more likely to exceed the available BL range, 6433 // so use absolute patch with fixup if available and kCallArtMethod otherwise. 6434 HInvokeStaticOrDirect::CodePtrLocation code_ptr_location = 6435 (desired_dispatch_info.method_load_kind == 6436 HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup) 6437 ? HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup 6438 : HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod; 6439 return HInvokeStaticOrDirect::DispatchInfo { 6440 dispatch_info.method_load_kind, 6441 code_ptr_location, 6442 dispatch_info.method_load_data, 6443 0u 6444 }; 6445 } 6446 } 6447 return dispatch_info; 6448} 6449 6450Register CodeGeneratorARM::GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke, 6451 Register temp) { 6452 DCHECK_EQ(invoke->InputCount(), invoke->GetNumberOfArguments() + 1u); 6453 Location location = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex()); 6454 if (!invoke->GetLocations()->Intrinsified()) { 6455 return location.AsRegister<Register>(); 6456 } 6457 // For intrinsics we allow any location, so it may be on the stack. 6458 if (!location.IsRegister()) { 6459 __ LoadFromOffset(kLoadWord, temp, SP, location.GetStackIndex()); 6460 return temp; 6461 } 6462 // For register locations, check if the register was saved. If so, get it from the stack. 6463 // Note: There is a chance that the register was saved but not overwritten, so we could 6464 // save one load. However, since this is just an intrinsic slow path we prefer this 6465 // simple and more robust approach rather that trying to determine if that's the case. 6466 SlowPathCode* slow_path = GetCurrentSlowPath(); 6467 DCHECK(slow_path != nullptr); // For intrinsified invokes the call is emitted on the slow path. 6468 if (slow_path->IsCoreRegisterSaved(location.AsRegister<Register>())) { 6469 int stack_offset = slow_path->GetStackOffsetOfCoreRegister(location.AsRegister<Register>()); 6470 __ LoadFromOffset(kLoadWord, temp, SP, stack_offset); 6471 return temp; 6472 } 6473 return location.AsRegister<Register>(); 6474} 6475 6476void CodeGeneratorARM::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) { 6477 // For better instruction scheduling we load the direct code pointer before the method pointer. 6478 switch (invoke->GetCodePtrLocation()) { 6479 case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup: 6480 // LR = code address from literal pool with link-time patch. 6481 __ LoadLiteral(LR, DeduplicateMethodCodeLiteral(invoke->GetTargetMethod())); 6482 break; 6483 case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect: 6484 // LR = invoke->GetDirectCodePtr(); 6485 __ LoadImmediate(LR, invoke->GetDirectCodePtr()); 6486 break; 6487 default: 6488 break; 6489 } 6490 6491 Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp. 6492 switch (invoke->GetMethodLoadKind()) { 6493 case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: 6494 // temp = thread->string_init_entrypoint 6495 __ LoadFromOffset(kLoadWord, temp.AsRegister<Register>(), TR, invoke->GetStringInitOffset()); 6496 break; 6497 case HInvokeStaticOrDirect::MethodLoadKind::kRecursive: 6498 callee_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex()); 6499 break; 6500 case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress: 6501 __ LoadImmediate(temp.AsRegister<Register>(), invoke->GetMethodAddress()); 6502 break; 6503 case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup: 6504 __ LoadLiteral(temp.AsRegister<Register>(), 6505 DeduplicateMethodAddressLiteral(invoke->GetTargetMethod())); 6506 break; 6507 case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: { 6508 HArmDexCacheArraysBase* base = 6509 invoke->InputAt(invoke->GetSpecialInputIndex())->AsArmDexCacheArraysBase(); 6510 Register base_reg = GetInvokeStaticOrDirectExtraParameter(invoke, 6511 temp.AsRegister<Register>()); 6512 int32_t offset = invoke->GetDexCacheArrayOffset() - base->GetElementOffset(); 6513 __ LoadFromOffset(kLoadWord, temp.AsRegister<Register>(), base_reg, offset); 6514 break; 6515 } 6516 case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: { 6517 Location current_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex()); 6518 Register method_reg; 6519 Register reg = temp.AsRegister<Register>(); 6520 if (current_method.IsRegister()) { 6521 method_reg = current_method.AsRegister<Register>(); 6522 } else { 6523 DCHECK(invoke->GetLocations()->Intrinsified()); 6524 DCHECK(!current_method.IsValid()); 6525 method_reg = reg; 6526 __ LoadFromOffset(kLoadWord, reg, SP, kCurrentMethodStackOffset); 6527 } 6528 // /* ArtMethod*[] */ temp = temp.ptr_sized_fields_->dex_cache_resolved_methods_; 6529 __ LoadFromOffset(kLoadWord, 6530 reg, 6531 method_reg, 6532 ArtMethod::DexCacheResolvedMethodsOffset(kArmPointerSize).Int32Value()); 6533 // temp = temp[index_in_cache]; 6534 // Note: Don't use invoke->GetTargetMethod() as it may point to a different dex file. 6535 uint32_t index_in_cache = invoke->GetDexMethodIndex(); 6536 __ LoadFromOffset(kLoadWord, reg, reg, CodeGenerator::GetCachePointerOffset(index_in_cache)); 6537 break; 6538 } 6539 } 6540 6541 switch (invoke->GetCodePtrLocation()) { 6542 case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf: 6543 __ bl(GetFrameEntryLabel()); 6544 break; 6545 case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative: 6546 relative_call_patches_.emplace_back(invoke->GetTargetMethod()); 6547 __ BindTrackedLabel(&relative_call_patches_.back().label); 6548 // Arbitrarily branch to the BL itself, override at link time. 6549 __ bl(&relative_call_patches_.back().label); 6550 break; 6551 case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup: 6552 case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect: 6553 // LR prepared above for better instruction scheduling. 6554 // LR() 6555 __ blx(LR); 6556 break; 6557 case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod: 6558 // LR = callee_method->entry_point_from_quick_compiled_code_ 6559 __ LoadFromOffset( 6560 kLoadWord, LR, callee_method.AsRegister<Register>(), 6561 ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmWordSize).Int32Value()); 6562 // LR() 6563 __ blx(LR); 6564 break; 6565 } 6566 6567 DCHECK(!IsLeafMethod()); 6568} 6569 6570void CodeGeneratorARM::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp_location) { 6571 Register temp = temp_location.AsRegister<Register>(); 6572 uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset( 6573 invoke->GetVTableIndex(), kArmPointerSize).Uint32Value(); 6574 6575 // Use the calling convention instead of the location of the receiver, as 6576 // intrinsics may have put the receiver in a different register. In the intrinsics 6577 // slow path, the arguments have been moved to the right place, so here we are 6578 // guaranteed that the receiver is the first register of the calling convention. 6579 InvokeDexCallingConvention calling_convention; 6580 Register receiver = calling_convention.GetRegisterAt(0); 6581 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); 6582 // /* HeapReference<Class> */ temp = receiver->klass_ 6583 __ LoadFromOffset(kLoadWord, temp, receiver, class_offset); 6584 MaybeRecordImplicitNullCheck(invoke); 6585 // Instead of simply (possibly) unpoisoning `temp` here, we should 6586 // emit a read barrier for the previous class reference load. 6587 // However this is not required in practice, as this is an 6588 // intermediate/temporary reference and because the current 6589 // concurrent copying collector keeps the from-space memory 6590 // intact/accessible until the end of the marking phase (the 6591 // concurrent copying collector may not in the future). 6592 __ MaybeUnpoisonHeapReference(temp); 6593 // temp = temp->GetMethodAt(method_offset); 6594 uint32_t entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset( 6595 kArmWordSize).Int32Value(); 6596 __ LoadFromOffset(kLoadWord, temp, temp, method_offset); 6597 // LR = temp->GetEntryPoint(); 6598 __ LoadFromOffset(kLoadWord, LR, temp, entry_point); 6599 // LR(); 6600 __ blx(LR); 6601} 6602 6603CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativeStringPatch( 6604 const DexFile& dex_file, uint32_t string_index) { 6605 return NewPcRelativePatch(dex_file, string_index, &pc_relative_string_patches_); 6606} 6607 6608CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativeTypePatch( 6609 const DexFile& dex_file, uint32_t type_index) { 6610 return NewPcRelativePatch(dex_file, type_index, &pc_relative_type_patches_); 6611} 6612 6613CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativeDexCacheArrayPatch( 6614 const DexFile& dex_file, uint32_t element_offset) { 6615 return NewPcRelativePatch(dex_file, element_offset, &pc_relative_dex_cache_patches_); 6616} 6617 6618CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativePatch( 6619 const DexFile& dex_file, uint32_t offset_or_index, ArenaDeque<PcRelativePatchInfo>* patches) { 6620 patches->emplace_back(dex_file, offset_or_index); 6621 return &patches->back(); 6622} 6623 6624Literal* CodeGeneratorARM::DeduplicateBootImageStringLiteral(const DexFile& dex_file, 6625 uint32_t string_index) { 6626 return boot_image_string_patches_.GetOrCreate( 6627 StringReference(&dex_file, string_index), 6628 [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); }); 6629} 6630 6631Literal* CodeGeneratorARM::DeduplicateBootImageTypeLiteral(const DexFile& dex_file, 6632 uint32_t type_index) { 6633 return boot_image_type_patches_.GetOrCreate( 6634 TypeReference(&dex_file, type_index), 6635 [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); }); 6636} 6637 6638Literal* CodeGeneratorARM::DeduplicateBootImageAddressLiteral(uint32_t address) { 6639 bool needs_patch = GetCompilerOptions().GetIncludePatchInformation(); 6640 Uint32ToLiteralMap* map = needs_patch ? &boot_image_address_patches_ : &uint32_literals_; 6641 return DeduplicateUint32Literal(dchecked_integral_cast<uint32_t>(address), map); 6642} 6643 6644Literal* CodeGeneratorARM::DeduplicateDexCacheAddressLiteral(uint32_t address) { 6645 return DeduplicateUint32Literal(address, &uint32_literals_); 6646} 6647 6648void CodeGeneratorARM::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) { 6649 DCHECK(linker_patches->empty()); 6650 size_t size = 6651 method_patches_.size() + 6652 call_patches_.size() + 6653 relative_call_patches_.size() + 6654 /* MOVW+MOVT for each base */ 2u * pc_relative_dex_cache_patches_.size() + 6655 boot_image_string_patches_.size() + 6656 /* MOVW+MOVT for each base */ 2u * pc_relative_string_patches_.size() + 6657 boot_image_type_patches_.size() + 6658 /* MOVW+MOVT for each base */ 2u * pc_relative_type_patches_.size() + 6659 boot_image_address_patches_.size(); 6660 linker_patches->reserve(size); 6661 for (const auto& entry : method_patches_) { 6662 const MethodReference& target_method = entry.first; 6663 Literal* literal = entry.second; 6664 DCHECK(literal->GetLabel()->IsBound()); 6665 uint32_t literal_offset = literal->GetLabel()->Position(); 6666 linker_patches->push_back(LinkerPatch::MethodPatch(literal_offset, 6667 target_method.dex_file, 6668 target_method.dex_method_index)); 6669 } 6670 for (const auto& entry : call_patches_) { 6671 const MethodReference& target_method = entry.first; 6672 Literal* literal = entry.second; 6673 DCHECK(literal->GetLabel()->IsBound()); 6674 uint32_t literal_offset = literal->GetLabel()->Position(); 6675 linker_patches->push_back(LinkerPatch::CodePatch(literal_offset, 6676 target_method.dex_file, 6677 target_method.dex_method_index)); 6678 } 6679 for (const MethodPatchInfo<Label>& info : relative_call_patches_) { 6680 uint32_t literal_offset = info.label.Position(); 6681 linker_patches->push_back(LinkerPatch::RelativeCodePatch(literal_offset, 6682 info.target_method.dex_file, 6683 info.target_method.dex_method_index)); 6684 } 6685 for (const PcRelativePatchInfo& info : pc_relative_dex_cache_patches_) { 6686 const DexFile& dex_file = info.target_dex_file; 6687 size_t base_element_offset = info.offset_or_index; 6688 DCHECK(info.add_pc_label.IsBound()); 6689 uint32_t add_pc_offset = dchecked_integral_cast<uint32_t>(info.add_pc_label.Position()); 6690 // Add MOVW patch. 6691 DCHECK(info.movw_label.IsBound()); 6692 uint32_t movw_offset = dchecked_integral_cast<uint32_t>(info.movw_label.Position()); 6693 linker_patches->push_back(LinkerPatch::DexCacheArrayPatch(movw_offset, 6694 &dex_file, 6695 add_pc_offset, 6696 base_element_offset)); 6697 // Add MOVT patch. 6698 DCHECK(info.movt_label.IsBound()); 6699 uint32_t movt_offset = dchecked_integral_cast<uint32_t>(info.movt_label.Position()); 6700 linker_patches->push_back(LinkerPatch::DexCacheArrayPatch(movt_offset, 6701 &dex_file, 6702 add_pc_offset, 6703 base_element_offset)); 6704 } 6705 for (const auto& entry : boot_image_string_patches_) { 6706 const StringReference& target_string = entry.first; 6707 Literal* literal = entry.second; 6708 DCHECK(literal->GetLabel()->IsBound()); 6709 uint32_t literal_offset = literal->GetLabel()->Position(); 6710 linker_patches->push_back(LinkerPatch::StringPatch(literal_offset, 6711 target_string.dex_file, 6712 target_string.string_index)); 6713 } 6714 for (const PcRelativePatchInfo& info : pc_relative_string_patches_) { 6715 const DexFile& dex_file = info.target_dex_file; 6716 uint32_t string_index = info.offset_or_index; 6717 DCHECK(info.add_pc_label.IsBound()); 6718 uint32_t add_pc_offset = dchecked_integral_cast<uint32_t>(info.add_pc_label.Position()); 6719 // Add MOVW patch. 6720 DCHECK(info.movw_label.IsBound()); 6721 uint32_t movw_offset = dchecked_integral_cast<uint32_t>(info.movw_label.Position()); 6722 linker_patches->push_back(LinkerPatch::RelativeStringPatch(movw_offset, 6723 &dex_file, 6724 add_pc_offset, 6725 string_index)); 6726 // Add MOVT patch. 6727 DCHECK(info.movt_label.IsBound()); 6728 uint32_t movt_offset = dchecked_integral_cast<uint32_t>(info.movt_label.Position()); 6729 linker_patches->push_back(LinkerPatch::RelativeStringPatch(movt_offset, 6730 &dex_file, 6731 add_pc_offset, 6732 string_index)); 6733 } 6734 for (const auto& entry : boot_image_type_patches_) { 6735 const TypeReference& target_type = entry.first; 6736 Literal* literal = entry.second; 6737 DCHECK(literal->GetLabel()->IsBound()); 6738 uint32_t literal_offset = literal->GetLabel()->Position(); 6739 linker_patches->push_back(LinkerPatch::TypePatch(literal_offset, 6740 target_type.dex_file, 6741 target_type.type_index)); 6742 } 6743 for (const PcRelativePatchInfo& info : pc_relative_type_patches_) { 6744 const DexFile& dex_file = info.target_dex_file; 6745 uint32_t type_index = info.offset_or_index; 6746 DCHECK(info.add_pc_label.IsBound()); 6747 uint32_t add_pc_offset = dchecked_integral_cast<uint32_t>(info.add_pc_label.Position()); 6748 // Add MOVW patch. 6749 DCHECK(info.movw_label.IsBound()); 6750 uint32_t movw_offset = dchecked_integral_cast<uint32_t>(info.movw_label.Position()); 6751 linker_patches->push_back(LinkerPatch::RelativeTypePatch(movw_offset, 6752 &dex_file, 6753 add_pc_offset, 6754 type_index)); 6755 // Add MOVT patch. 6756 DCHECK(info.movt_label.IsBound()); 6757 uint32_t movt_offset = dchecked_integral_cast<uint32_t>(info.movt_label.Position()); 6758 linker_patches->push_back(LinkerPatch::RelativeTypePatch(movt_offset, 6759 &dex_file, 6760 add_pc_offset, 6761 type_index)); 6762 } 6763 for (const auto& entry : boot_image_address_patches_) { 6764 DCHECK(GetCompilerOptions().GetIncludePatchInformation()); 6765 Literal* literal = entry.second; 6766 DCHECK(literal->GetLabel()->IsBound()); 6767 uint32_t literal_offset = literal->GetLabel()->Position(); 6768 linker_patches->push_back(LinkerPatch::RecordPosition(literal_offset)); 6769 } 6770} 6771 6772Literal* CodeGeneratorARM::DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map) { 6773 return map->GetOrCreate( 6774 value, 6775 [this, value]() { return __ NewLiteral<uint32_t>(value); }); 6776} 6777 6778Literal* CodeGeneratorARM::DeduplicateMethodLiteral(MethodReference target_method, 6779 MethodToLiteralMap* map) { 6780 return map->GetOrCreate( 6781 target_method, 6782 [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); }); 6783} 6784 6785Literal* CodeGeneratorARM::DeduplicateMethodAddressLiteral(MethodReference target_method) { 6786 return DeduplicateMethodLiteral(target_method, &method_patches_); 6787} 6788 6789Literal* CodeGeneratorARM::DeduplicateMethodCodeLiteral(MethodReference target_method) { 6790 return DeduplicateMethodLiteral(target_method, &call_patches_); 6791} 6792 6793void LocationsBuilderARM::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) { 6794 LocationSummary* locations = 6795 new (GetGraph()->GetArena()) LocationSummary(instr, LocationSummary::kNoCall); 6796 locations->SetInAt(HMultiplyAccumulate::kInputAccumulatorIndex, 6797 Location::RequiresRegister()); 6798 locations->SetInAt(HMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister()); 6799 locations->SetInAt(HMultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister()); 6800 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); 6801} 6802 6803void InstructionCodeGeneratorARM::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) { 6804 LocationSummary* locations = instr->GetLocations(); 6805 Register res = locations->Out().AsRegister<Register>(); 6806 Register accumulator = 6807 locations->InAt(HMultiplyAccumulate::kInputAccumulatorIndex).AsRegister<Register>(); 6808 Register mul_left = 6809 locations->InAt(HMultiplyAccumulate::kInputMulLeftIndex).AsRegister<Register>(); 6810 Register mul_right = 6811 locations->InAt(HMultiplyAccumulate::kInputMulRightIndex).AsRegister<Register>(); 6812 6813 if (instr->GetOpKind() == HInstruction::kAdd) { 6814 __ mla(res, mul_left, mul_right, accumulator); 6815 } else { 6816 __ mls(res, mul_left, mul_right, accumulator); 6817 } 6818} 6819 6820void LocationsBuilderARM::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) { 6821 // Nothing to do, this should be removed during prepare for register allocator. 6822 LOG(FATAL) << "Unreachable"; 6823} 6824 6825void InstructionCodeGeneratorARM::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) { 6826 // Nothing to do, this should be removed during prepare for register allocator. 6827 LOG(FATAL) << "Unreachable"; 6828} 6829 6830// Simple implementation of packed switch - generate cascaded compare/jumps. 6831void LocationsBuilderARM::VisitPackedSwitch(HPackedSwitch* switch_instr) { 6832 LocationSummary* locations = 6833 new (GetGraph()->GetArena()) LocationSummary(switch_instr, LocationSummary::kNoCall); 6834 locations->SetInAt(0, Location::RequiresRegister()); 6835 if (switch_instr->GetNumEntries() > kPackedSwitchCompareJumpThreshold && 6836 codegen_->GetAssembler()->IsThumb()) { 6837 locations->AddTemp(Location::RequiresRegister()); // We need a temp for the table base. 6838 if (switch_instr->GetStartValue() != 0) { 6839 locations->AddTemp(Location::RequiresRegister()); // We need a temp for the bias. 6840 } 6841 } 6842} 6843 6844void InstructionCodeGeneratorARM::VisitPackedSwitch(HPackedSwitch* switch_instr) { 6845 int32_t lower_bound = switch_instr->GetStartValue(); 6846 uint32_t num_entries = switch_instr->GetNumEntries(); 6847 LocationSummary* locations = switch_instr->GetLocations(); 6848 Register value_reg = locations->InAt(0).AsRegister<Register>(); 6849 HBasicBlock* default_block = switch_instr->GetDefaultBlock(); 6850 6851 if (num_entries <= kPackedSwitchCompareJumpThreshold || !codegen_->GetAssembler()->IsThumb()) { 6852 // Create a series of compare/jumps. 6853 Register temp_reg = IP; 6854 // Note: It is fine for the below AddConstantSetFlags() using IP register to temporarily store 6855 // the immediate, because IP is used as the destination register. For the other 6856 // AddConstantSetFlags() and GenerateCompareWithImmediate(), the immediate values are constant, 6857 // and they can be encoded in the instruction without making use of IP register. 6858 __ AddConstantSetFlags(temp_reg, value_reg, -lower_bound); 6859 6860 const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors(); 6861 // Jump to successors[0] if value == lower_bound. 6862 __ b(codegen_->GetLabelOf(successors[0]), EQ); 6863 int32_t last_index = 0; 6864 for (; num_entries - last_index > 2; last_index += 2) { 6865 __ AddConstantSetFlags(temp_reg, temp_reg, -2); 6866 // Jump to successors[last_index + 1] if value < case_value[last_index + 2]. 6867 __ b(codegen_->GetLabelOf(successors[last_index + 1]), LO); 6868 // Jump to successors[last_index + 2] if value == case_value[last_index + 2]. 6869 __ b(codegen_->GetLabelOf(successors[last_index + 2]), EQ); 6870 } 6871 if (num_entries - last_index == 2) { 6872 // The last missing case_value. 6873 __ CmpConstant(temp_reg, 1); 6874 __ b(codegen_->GetLabelOf(successors[last_index + 1]), EQ); 6875 } 6876 6877 // And the default for any other value. 6878 if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) { 6879 __ b(codegen_->GetLabelOf(default_block)); 6880 } 6881 } else { 6882 // Create a table lookup. 6883 Register temp_reg = locations->GetTemp(0).AsRegister<Register>(); 6884 6885 // Materialize a pointer to the switch table 6886 std::vector<Label*> labels(num_entries); 6887 const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors(); 6888 for (uint32_t i = 0; i < num_entries; i++) { 6889 labels[i] = codegen_->GetLabelOf(successors[i]); 6890 } 6891 JumpTable* table = __ CreateJumpTable(std::move(labels), temp_reg); 6892 6893 // Remove the bias. 6894 Register key_reg; 6895 if (lower_bound != 0) { 6896 key_reg = locations->GetTemp(1).AsRegister<Register>(); 6897 __ AddConstant(key_reg, value_reg, -lower_bound); 6898 } else { 6899 key_reg = value_reg; 6900 } 6901 6902 // Check whether the value is in the table, jump to default block if not. 6903 __ CmpConstant(key_reg, num_entries - 1); 6904 __ b(codegen_->GetLabelOf(default_block), Condition::HI); 6905 6906 // Load the displacement from the table. 6907 __ ldr(temp_reg, Address(temp_reg, key_reg, Shift::LSL, 2)); 6908 6909 // Dispatch is a direct add to the PC (for Thumb2). 6910 __ EmitJumpTableDispatch(table, temp_reg); 6911 } 6912} 6913 6914void LocationsBuilderARM::VisitArmDexCacheArraysBase(HArmDexCacheArraysBase* base) { 6915 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(base); 6916 locations->SetOut(Location::RequiresRegister()); 6917} 6918 6919void InstructionCodeGeneratorARM::VisitArmDexCacheArraysBase(HArmDexCacheArraysBase* base) { 6920 Register base_reg = base->GetLocations()->Out().AsRegister<Register>(); 6921 CodeGeneratorARM::PcRelativePatchInfo* labels = 6922 codegen_->NewPcRelativeDexCacheArrayPatch(base->GetDexFile(), base->GetElementOffset()); 6923 __ BindTrackedLabel(&labels->movw_label); 6924 __ movw(base_reg, /* placeholder */ 0u); 6925 __ BindTrackedLabel(&labels->movt_label); 6926 __ movt(base_reg, /* placeholder */ 0u); 6927 __ BindTrackedLabel(&labels->add_pc_label); 6928 __ add(base_reg, base_reg, ShifterOperand(PC)); 6929} 6930 6931void CodeGeneratorARM::MoveFromReturnRegister(Location trg, Primitive::Type type) { 6932 if (!trg.IsValid()) { 6933 DCHECK_EQ(type, Primitive::kPrimVoid); 6934 return; 6935 } 6936 6937 DCHECK_NE(type, Primitive::kPrimVoid); 6938 6939 Location return_loc = InvokeDexCallingConventionVisitorARM().GetReturnLocation(type); 6940 if (return_loc.Equals(trg)) { 6941 return; 6942 } 6943 6944 // TODO: Consider pairs in the parallel move resolver, then this could be nicely merged 6945 // with the last branch. 6946 if (type == Primitive::kPrimLong) { 6947 HParallelMove parallel_move(GetGraph()->GetArena()); 6948 parallel_move.AddMove(return_loc.ToLow(), trg.ToLow(), Primitive::kPrimInt, nullptr); 6949 parallel_move.AddMove(return_loc.ToHigh(), trg.ToHigh(), Primitive::kPrimInt, nullptr); 6950 GetMoveResolver()->EmitNativeCode(¶llel_move); 6951 } else if (type == Primitive::kPrimDouble) { 6952 HParallelMove parallel_move(GetGraph()->GetArena()); 6953 parallel_move.AddMove(return_loc.ToLow(), trg.ToLow(), Primitive::kPrimFloat, nullptr); 6954 parallel_move.AddMove(return_loc.ToHigh(), trg.ToHigh(), Primitive::kPrimFloat, nullptr); 6955 GetMoveResolver()->EmitNativeCode(¶llel_move); 6956 } else { 6957 // Let the parallel move resolver take care of all of this. 6958 HParallelMove parallel_move(GetGraph()->GetArena()); 6959 parallel_move.AddMove(return_loc, trg, type, nullptr); 6960 GetMoveResolver()->EmitNativeCode(¶llel_move); 6961 } 6962} 6963 6964void LocationsBuilderARM::VisitClassTableGet(HClassTableGet* instruction) { 6965 LocationSummary* locations = 6966 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); 6967 locations->SetInAt(0, Location::RequiresRegister()); 6968 locations->SetOut(Location::RequiresRegister()); 6969} 6970 6971void InstructionCodeGeneratorARM::VisitClassTableGet(HClassTableGet* instruction) { 6972 LocationSummary* locations = instruction->GetLocations(); 6973 if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) { 6974 uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset( 6975 instruction->GetIndex(), kArmPointerSize).SizeValue(); 6976 __ LoadFromOffset(kLoadWord, 6977 locations->Out().AsRegister<Register>(), 6978 locations->InAt(0).AsRegister<Register>(), 6979 method_offset); 6980 } else { 6981 uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement( 6982 instruction->GetIndex(), kArmPointerSize)); 6983 __ LoadFromOffset(kLoadWord, 6984 locations->Out().AsRegister<Register>(), 6985 locations->InAt(0).AsRegister<Register>(), 6986 mirror::Class::ImtPtrOffset(kArmPointerSize).Uint32Value()); 6987 __ LoadFromOffset(kLoadWord, 6988 locations->Out().AsRegister<Register>(), 6989 locations->Out().AsRegister<Register>(), 6990 method_offset); 6991 } 6992} 6993 6994#undef __ 6995#undef QUICK_ENTRY_POINT 6996 6997} // namespace arm 6998} // namespace art 6999