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