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