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