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