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