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