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