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