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