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