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