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