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