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