• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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_vixl.h"
18 
19 #include "arch/arm/asm_support_arm.h"
20 #include "arch/arm/instruction_set_features_arm.h"
21 #include "arch/arm/jni_frame_arm.h"
22 #include "art_method-inl.h"
23 #include "base/bit_utils.h"
24 #include "base/bit_utils_iterator.h"
25 #include "class_root-inl.h"
26 #include "class_table.h"
27 #include "code_generator_utils.h"
28 #include "common_arm.h"
29 #include "compiled_method.h"
30 #include "entrypoints/quick/quick_entrypoints.h"
31 #include "gc/accounting/card_table.h"
32 #include "gc/space/image_space.h"
33 #include "heap_poisoning.h"
34 #include "interpreter/mterp/nterp.h"
35 #include "intrinsics.h"
36 #include "intrinsics_arm_vixl.h"
37 #include "intrinsics_utils.h"
38 #include "linker/linker_patch.h"
39 #include "mirror/array-inl.h"
40 #include "mirror/class-inl.h"
41 #include "mirror/var_handle.h"
42 #include "scoped_thread_state_change-inl.h"
43 #include "thread.h"
44 #include "utils/arm/assembler_arm_vixl.h"
45 #include "utils/arm/managed_register_arm.h"
46 #include "utils/assembler.h"
47 #include "utils/stack_checks.h"
48 
49 namespace art {
50 namespace arm {
51 
52 namespace vixl32 = vixl::aarch32;
53 using namespace vixl32;  // NOLINT(build/namespaces)
54 
55 using helpers::DRegisterFrom;
56 using helpers::HighRegisterFrom;
57 using helpers::InputDRegisterAt;
58 using helpers::InputOperandAt;
59 using helpers::InputRegister;
60 using helpers::InputRegisterAt;
61 using helpers::InputSRegisterAt;
62 using helpers::InputVRegister;
63 using helpers::InputVRegisterAt;
64 using helpers::Int32ConstantFrom;
65 using helpers::Int64ConstantFrom;
66 using helpers::LocationFrom;
67 using helpers::LowRegisterFrom;
68 using helpers::LowSRegisterFrom;
69 using helpers::OperandFrom;
70 using helpers::OutputRegister;
71 using helpers::OutputSRegister;
72 using helpers::OutputVRegister;
73 using helpers::RegisterFrom;
74 using helpers::SRegisterFrom;
75 using helpers::Uint64ConstantFrom;
76 
77 using vixl::EmissionCheckScope;
78 using vixl::ExactAssemblyScope;
79 using vixl::CodeBufferCheckScope;
80 
81 using RegisterList = vixl32::RegisterList;
82 
ExpectedPairLayout(Location location)83 static bool ExpectedPairLayout(Location location) {
84   // We expected this for both core and fpu register pairs.
85   return ((location.low() & 1) == 0) && (location.low() + 1 == location.high());
86 }
87 // Use a local definition to prevent copying mistakes.
88 static constexpr size_t kArmWordSize = static_cast<size_t>(kArmPointerSize);
89 static constexpr size_t kArmBitsPerWord = kArmWordSize * kBitsPerByte;
90 static constexpr uint32_t kPackedSwitchCompareJumpThreshold = 7;
91 
92 // Reference load (except object array loads) is using LDR Rt, [Rn, #offset] which can handle
93 // offset < 4KiB. For offsets >= 4KiB, the load shall be emitted as two or more instructions.
94 // For the Baker read barrier implementation using link-time generated thunks we need to split
95 // the offset explicitly.
96 constexpr uint32_t kReferenceLoadMinFarOffset = 4 * KB;
97 
98 // Using a base helps identify when we hit Marking Register check breakpoints.
99 constexpr int kMarkingRegisterCheckBreakCodeBaseCode = 0x10;
100 
101 #ifdef __
102 #error "ARM Codegen VIXL macro-assembler macro already defined."
103 #endif
104 
105 // NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
106 #define __ down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler()->  // NOLINT
107 #define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArmPointerSize, x).Int32Value()
108 
109 // Marker that code is yet to be, and must, be implemented.
110 #define TODO_VIXL32(level) LOG(level) << __PRETTY_FUNCTION__ << " unimplemented "
111 
CanEmitNarrowLdr(vixl32::Register rt,vixl32::Register rn,uint32_t offset)112 static inline bool CanEmitNarrowLdr(vixl32::Register rt, vixl32::Register rn, uint32_t offset) {
113   return rt.IsLow() && rn.IsLow() && offset < 32u;
114 }
115 
116 class EmitAdrCode {
117  public:
EmitAdrCode(ArmVIXLMacroAssembler * assembler,vixl32::Register rd,vixl32::Label * label)118   EmitAdrCode(ArmVIXLMacroAssembler* assembler, vixl32::Register rd, vixl32::Label* label)
119       : assembler_(assembler), rd_(rd), label_(label) {
120     DCHECK(!assembler->AllowMacroInstructions());  // In ExactAssemblyScope.
121     adr_location_ = assembler->GetCursorOffset();
122     assembler->adr(EncodingSize(Wide), rd, label);
123   }
124 
~EmitAdrCode()125   ~EmitAdrCode() {
126     DCHECK(label_->IsBound());
127     // The ADR emitted by the assembler does not set the Thumb mode bit we need.
128     // TODO: Maybe extend VIXL to allow ADR for return address?
129     uint8_t* raw_adr = assembler_->GetBuffer()->GetOffsetAddress<uint8_t*>(adr_location_);
130     // Expecting ADR encoding T3 with `(offset & 1) == 0`.
131     DCHECK_EQ(raw_adr[1] & 0xfbu, 0xf2u);           // Check bits 24-31, except 26.
132     DCHECK_EQ(raw_adr[0] & 0xffu, 0x0fu);           // Check bits 16-23.
133     DCHECK_EQ(raw_adr[3] & 0x8fu, rd_.GetCode());   // Check bits 8-11 and 15.
134     DCHECK_EQ(raw_adr[2] & 0x01u, 0x00u);           // Check bit 0, i.e. the `offset & 1`.
135     // Add the Thumb mode bit.
136     raw_adr[2] |= 0x01u;
137   }
138 
139  private:
140   ArmVIXLMacroAssembler* const assembler_;
141   vixl32::Register rd_;
142   vixl32::Label* const label_;
143   int32_t adr_location_;
144 };
145 
OneRegInReferenceOutSaveEverythingCallerSaves()146 static RegisterSet OneRegInReferenceOutSaveEverythingCallerSaves() {
147   InvokeRuntimeCallingConventionARMVIXL calling_convention;
148   RegisterSet caller_saves = RegisterSet::Empty();
149   caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0)));
150   // TODO: Add GetReturnLocation() to the calling convention so that we can DCHECK()
151   // that the the kPrimNot result register is the same as the first argument register.
152   return caller_saves;
153 }
154 
155 // SaveLiveRegisters and RestoreLiveRegisters from SlowPathCodeARM operate on sets of S registers,
156 // for each live D registers they treat two corresponding S registers as live ones.
157 //
158 // Two following functions (SaveContiguousSRegisterList, RestoreContiguousSRegisterList) build
159 // from a list of contiguous S registers a list of contiguous D registers (processing first/last
160 // S registers corner cases) and save/restore this new list treating them as D registers.
161 // - decreasing code size
162 // - avoiding hazards on Cortex-A57, when a pair of S registers for an actual live D register is
163 //   restored and then used in regular non SlowPath code as D register.
164 //
165 // For the following example (v means the S register is live):
166 //   D names: |    D0   |    D1   |    D2   |    D4   | ...
167 //   S names: | S0 | S1 | S2 | S3 | S4 | S5 | S6 | S7 | ...
168 //   Live?    |    |  v |  v |  v |  v |  v |  v |    | ...
169 //
170 // S1 and S6 will be saved/restored independently; D registers list (D1, D2) will be processed
171 // as D registers.
172 //
173 // TODO(VIXL): All this code should be unnecessary once the VIXL AArch32 backend provides helpers
174 // for lists of floating-point registers.
SaveContiguousSRegisterList(size_t first,size_t last,CodeGenerator * codegen,size_t stack_offset)175 static size_t SaveContiguousSRegisterList(size_t first,
176                                           size_t last,
177                                           CodeGenerator* codegen,
178                                           size_t stack_offset) {
179   static_assert(kSRegSizeInBytes == kArmWordSize, "Broken assumption on reg/word sizes.");
180   static_assert(kDRegSizeInBytes == 2 * kArmWordSize, "Broken assumption on reg/word sizes.");
181   DCHECK_LE(first, last);
182   if ((first == last) && (first == 0)) {
183     __ Vstr(vixl32::SRegister(first), MemOperand(sp, stack_offset));
184     return stack_offset + kSRegSizeInBytes;
185   }
186   if (first % 2 == 1) {
187     __ Vstr(vixl32::SRegister(first++), MemOperand(sp, stack_offset));
188     stack_offset += kSRegSizeInBytes;
189   }
190 
191   bool save_last = false;
192   if (last % 2 == 0) {
193     save_last = true;
194     --last;
195   }
196 
197   if (first < last) {
198     vixl32::DRegister d_reg = vixl32::DRegister(first / 2);
199     DCHECK_EQ((last - first + 1) % 2, 0u);
200     size_t number_of_d_regs = (last - first + 1) / 2;
201 
202     if (number_of_d_regs == 1) {
203       __ Vstr(d_reg, MemOperand(sp, stack_offset));
204     } else if (number_of_d_regs > 1) {
205       UseScratchRegisterScope temps(down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler());
206       vixl32::Register base = sp;
207       if (stack_offset != 0) {
208         base = temps.Acquire();
209         __ Add(base, sp, Operand::From(stack_offset));
210       }
211       __ Vstm(F64, base, NO_WRITE_BACK, DRegisterList(d_reg, number_of_d_regs));
212     }
213     stack_offset += number_of_d_regs * kDRegSizeInBytes;
214   }
215 
216   if (save_last) {
217     __ Vstr(vixl32::SRegister(last + 1), MemOperand(sp, stack_offset));
218     stack_offset += kSRegSizeInBytes;
219   }
220 
221   return stack_offset;
222 }
223 
RestoreContiguousSRegisterList(size_t first,size_t last,CodeGenerator * codegen,size_t stack_offset)224 static size_t RestoreContiguousSRegisterList(size_t first,
225                                              size_t last,
226                                              CodeGenerator* codegen,
227                                              size_t stack_offset) {
228   static_assert(kSRegSizeInBytes == kArmWordSize, "Broken assumption on reg/word sizes.");
229   static_assert(kDRegSizeInBytes == 2 * kArmWordSize, "Broken assumption on reg/word sizes.");
230   DCHECK_LE(first, last);
231   if ((first == last) && (first == 0)) {
232     __ Vldr(vixl32::SRegister(first), MemOperand(sp, stack_offset));
233     return stack_offset + kSRegSizeInBytes;
234   }
235   if (first % 2 == 1) {
236     __ Vldr(vixl32::SRegister(first++), MemOperand(sp, stack_offset));
237     stack_offset += kSRegSizeInBytes;
238   }
239 
240   bool restore_last = false;
241   if (last % 2 == 0) {
242     restore_last = true;
243     --last;
244   }
245 
246   if (first < last) {
247     vixl32::DRegister d_reg = vixl32::DRegister(first / 2);
248     DCHECK_EQ((last - first + 1) % 2, 0u);
249     size_t number_of_d_regs = (last - first + 1) / 2;
250     if (number_of_d_regs == 1) {
251       __ Vldr(d_reg, MemOperand(sp, stack_offset));
252     } else if (number_of_d_regs > 1) {
253       UseScratchRegisterScope temps(down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler());
254       vixl32::Register base = sp;
255       if (stack_offset != 0) {
256         base = temps.Acquire();
257         __ Add(base, sp, Operand::From(stack_offset));
258       }
259       __ Vldm(F64, base, NO_WRITE_BACK, DRegisterList(d_reg, number_of_d_regs));
260     }
261     stack_offset += number_of_d_regs * kDRegSizeInBytes;
262   }
263 
264   if (restore_last) {
265     __ Vldr(vixl32::SRegister(last + 1), MemOperand(sp, stack_offset));
266     stack_offset += kSRegSizeInBytes;
267   }
268 
269   return stack_offset;
270 }
271 
GetLoadOperandType(DataType::Type type)272 static LoadOperandType GetLoadOperandType(DataType::Type type) {
273   switch (type) {
274     case DataType::Type::kReference:
275       return kLoadWord;
276     case DataType::Type::kBool:
277     case DataType::Type::kUint8:
278       return kLoadUnsignedByte;
279     case DataType::Type::kInt8:
280       return kLoadSignedByte;
281     case DataType::Type::kUint16:
282       return kLoadUnsignedHalfword;
283     case DataType::Type::kInt16:
284       return kLoadSignedHalfword;
285     case DataType::Type::kInt32:
286       return kLoadWord;
287     case DataType::Type::kInt64:
288       return kLoadWordPair;
289     case DataType::Type::kFloat32:
290       return kLoadSWord;
291     case DataType::Type::kFloat64:
292       return kLoadDWord;
293     default:
294       LOG(FATAL) << "Unreachable type " << type;
295       UNREACHABLE();
296   }
297 }
298 
GetStoreOperandType(DataType::Type type)299 static StoreOperandType GetStoreOperandType(DataType::Type type) {
300   switch (type) {
301     case DataType::Type::kReference:
302       return kStoreWord;
303     case DataType::Type::kBool:
304     case DataType::Type::kUint8:
305     case DataType::Type::kInt8:
306       return kStoreByte;
307     case DataType::Type::kUint16:
308     case DataType::Type::kInt16:
309       return kStoreHalfword;
310     case DataType::Type::kInt32:
311       return kStoreWord;
312     case DataType::Type::kInt64:
313       return kStoreWordPair;
314     case DataType::Type::kFloat32:
315       return kStoreSWord;
316     case DataType::Type::kFloat64:
317       return kStoreDWord;
318     default:
319       LOG(FATAL) << "Unreachable type " << type;
320       UNREACHABLE();
321   }
322 }
323 
SaveLiveRegisters(CodeGenerator * codegen,LocationSummary * locations)324 void SlowPathCodeARMVIXL::SaveLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
325   size_t stack_offset = codegen->GetFirstRegisterSlotInSlowPath();
326   size_t orig_offset = stack_offset;
327 
328   const uint32_t core_spills = codegen->GetSlowPathSpills(locations, /* core_registers= */ true);
329   for (uint32_t i : LowToHighBits(core_spills)) {
330     // If the register holds an object, update the stack mask.
331     if (locations->RegisterContainsObject(i)) {
332       locations->SetStackBit(stack_offset / kVRegSize);
333     }
334     DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
335     DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
336     saved_core_stack_offsets_[i] = stack_offset;
337     stack_offset += kArmWordSize;
338   }
339 
340   CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
341   arm_codegen->GetAssembler()->StoreRegisterList(core_spills, orig_offset);
342 
343   uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers= */ false);
344   orig_offset = stack_offset;
345   for (uint32_t i : LowToHighBits(fp_spills)) {
346     DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
347     saved_fpu_stack_offsets_[i] = stack_offset;
348     stack_offset += kArmWordSize;
349   }
350 
351   stack_offset = orig_offset;
352   while (fp_spills != 0u) {
353     uint32_t begin = CTZ(fp_spills);
354     uint32_t tmp = fp_spills + (1u << begin);
355     fp_spills &= tmp;  // Clear the contiguous range of 1s.
356     uint32_t end = (tmp == 0u) ? 32u : CTZ(tmp);  // CTZ(0) is undefined.
357     stack_offset = SaveContiguousSRegisterList(begin, end - 1, codegen, stack_offset);
358   }
359   DCHECK_LE(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
360 }
361 
RestoreLiveRegisters(CodeGenerator * codegen,LocationSummary * locations)362 void SlowPathCodeARMVIXL::RestoreLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
363   size_t stack_offset = codegen->GetFirstRegisterSlotInSlowPath();
364   size_t orig_offset = stack_offset;
365 
366   const uint32_t core_spills = codegen->GetSlowPathSpills(locations, /* core_registers= */ true);
367   for (uint32_t i : LowToHighBits(core_spills)) {
368     DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
369     DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
370     stack_offset += kArmWordSize;
371   }
372 
373   // TODO(VIXL): Check the coherency of stack_offset after this with a test.
374   CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
375   arm_codegen->GetAssembler()->LoadRegisterList(core_spills, orig_offset);
376 
377   uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers= */ false);
378   while (fp_spills != 0u) {
379     uint32_t begin = CTZ(fp_spills);
380     uint32_t tmp = fp_spills + (1u << begin);
381     fp_spills &= tmp;  // Clear the contiguous range of 1s.
382     uint32_t end = (tmp == 0u) ? 32u : CTZ(tmp);  // CTZ(0) is undefined.
383     stack_offset = RestoreContiguousSRegisterList(begin, end - 1, codegen, stack_offset);
384   }
385   DCHECK_LE(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
386 }
387 
388 class NullCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
389  public:
NullCheckSlowPathARMVIXL(HNullCheck * instruction)390   explicit NullCheckSlowPathARMVIXL(HNullCheck* instruction) : SlowPathCodeARMVIXL(instruction) {}
391 
EmitNativeCode(CodeGenerator * codegen)392   void EmitNativeCode(CodeGenerator* codegen) override {
393     CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
394     __ Bind(GetEntryLabel());
395     if (instruction_->CanThrowIntoCatchBlock()) {
396       // Live registers will be restored in the catch block if caught.
397       SaveLiveRegisters(codegen, instruction_->GetLocations());
398     }
399     arm_codegen->InvokeRuntime(kQuickThrowNullPointer,
400                                instruction_,
401                                instruction_->GetDexPc(),
402                                this);
403     CheckEntrypointTypes<kQuickThrowNullPointer, void, void>();
404   }
405 
IsFatal() const406   bool IsFatal() const override { return true; }
407 
GetDescription() const408   const char* GetDescription() const override { return "NullCheckSlowPathARMVIXL"; }
409 
410  private:
411   DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARMVIXL);
412 };
413 
414 class DivZeroCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
415  public:
DivZeroCheckSlowPathARMVIXL(HDivZeroCheck * instruction)416   explicit DivZeroCheckSlowPathARMVIXL(HDivZeroCheck* instruction)
417       : SlowPathCodeARMVIXL(instruction) {}
418 
EmitNativeCode(CodeGenerator * codegen)419   void EmitNativeCode(CodeGenerator* codegen) override {
420     CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
421     __ Bind(GetEntryLabel());
422     arm_codegen->InvokeRuntime(kQuickThrowDivZero, instruction_, instruction_->GetDexPc(), this);
423     CheckEntrypointTypes<kQuickThrowDivZero, void, void>();
424   }
425 
IsFatal() const426   bool IsFatal() const override { return true; }
427 
GetDescription() const428   const char* GetDescription() const override { return "DivZeroCheckSlowPathARMVIXL"; }
429 
430  private:
431   DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathARMVIXL);
432 };
433 
434 class SuspendCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
435  public:
SuspendCheckSlowPathARMVIXL(HSuspendCheck * instruction,HBasicBlock * successor)436   SuspendCheckSlowPathARMVIXL(HSuspendCheck* instruction, HBasicBlock* successor)
437       : SlowPathCodeARMVIXL(instruction), successor_(successor) {}
438 
EmitNativeCode(CodeGenerator * codegen)439   void EmitNativeCode(CodeGenerator* codegen) override {
440     CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
441     __ Bind(GetEntryLabel());
442     arm_codegen->InvokeRuntime(kQuickTestSuspend, instruction_, instruction_->GetDexPc(), this);
443     CheckEntrypointTypes<kQuickTestSuspend, void, void>();
444     if (successor_ == nullptr) {
445       __ B(GetReturnLabel());
446     } else {
447       __ B(arm_codegen->GetLabelOf(successor_));
448     }
449   }
450 
GetReturnLabel()451   vixl32::Label* GetReturnLabel() {
452     DCHECK(successor_ == nullptr);
453     return &return_label_;
454   }
455 
GetSuccessor() const456   HBasicBlock* GetSuccessor() const {
457     return successor_;
458   }
459 
GetDescription() const460   const char* GetDescription() const override { return "SuspendCheckSlowPathARMVIXL"; }
461 
462  private:
463   // If not null, the block to branch to after the suspend check.
464   HBasicBlock* const successor_;
465 
466   // If `successor_` is null, the label to branch to after the suspend check.
467   vixl32::Label return_label_;
468 
469   DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathARMVIXL);
470 };
471 
472 class BoundsCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
473  public:
BoundsCheckSlowPathARMVIXL(HBoundsCheck * instruction)474   explicit BoundsCheckSlowPathARMVIXL(HBoundsCheck* instruction)
475       : SlowPathCodeARMVIXL(instruction) {}
476 
EmitNativeCode(CodeGenerator * codegen)477   void EmitNativeCode(CodeGenerator* codegen) override {
478     CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
479     LocationSummary* locations = instruction_->GetLocations();
480 
481     __ Bind(GetEntryLabel());
482     if (instruction_->CanThrowIntoCatchBlock()) {
483       // Live registers will be restored in the catch block if caught.
484       SaveLiveRegisters(codegen, instruction_->GetLocations());
485     }
486     // We're moving two locations to locations that could overlap, so we need a parallel
487     // move resolver.
488     InvokeRuntimeCallingConventionARMVIXL calling_convention;
489     codegen->EmitParallelMoves(
490         locations->InAt(0),
491         LocationFrom(calling_convention.GetRegisterAt(0)),
492         DataType::Type::kInt32,
493         locations->InAt(1),
494         LocationFrom(calling_convention.GetRegisterAt(1)),
495         DataType::Type::kInt32);
496     QuickEntrypointEnum entrypoint = instruction_->AsBoundsCheck()->IsStringCharAt()
497         ? kQuickThrowStringBounds
498         : kQuickThrowArrayBounds;
499     arm_codegen->InvokeRuntime(entrypoint, instruction_, instruction_->GetDexPc(), this);
500     CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>();
501     CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>();
502   }
503 
IsFatal() const504   bool IsFatal() const override { return true; }
505 
GetDescription() const506   const char* GetDescription() const override { return "BoundsCheckSlowPathARMVIXL"; }
507 
508  private:
509   DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARMVIXL);
510 };
511 
512 class LoadClassSlowPathARMVIXL : public SlowPathCodeARMVIXL {
513  public:
LoadClassSlowPathARMVIXL(HLoadClass * cls,HInstruction * at)514   LoadClassSlowPathARMVIXL(HLoadClass* cls, HInstruction* at)
515       : SlowPathCodeARMVIXL(at), cls_(cls) {
516     DCHECK(at->IsLoadClass() || at->IsClinitCheck());
517     DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_);
518   }
519 
EmitNativeCode(CodeGenerator * codegen)520   void EmitNativeCode(CodeGenerator* codegen) override {
521     LocationSummary* locations = instruction_->GetLocations();
522     Location out = locations->Out();
523     const uint32_t dex_pc = instruction_->GetDexPc();
524     bool must_resolve_type = instruction_->IsLoadClass() && cls_->MustResolveTypeOnSlowPath();
525     bool must_do_clinit = instruction_->IsClinitCheck() || cls_->MustGenerateClinitCheck();
526 
527     CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
528     __ Bind(GetEntryLabel());
529     SaveLiveRegisters(codegen, locations);
530 
531     InvokeRuntimeCallingConventionARMVIXL calling_convention;
532     if (must_resolve_type) {
533       DCHECK(IsSameDexFile(cls_->GetDexFile(), arm_codegen->GetGraph()->GetDexFile()) ||
534              arm_codegen->GetCompilerOptions().WithinOatFile(&cls_->GetDexFile()) ||
535              ContainsElement(Runtime::Current()->GetClassLinker()->GetBootClassPath(),
536                              &cls_->GetDexFile()));
537       dex::TypeIndex type_index = cls_->GetTypeIndex();
538       __ Mov(calling_convention.GetRegisterAt(0), type_index.index_);
539       if (cls_->NeedsAccessCheck()) {
540         CheckEntrypointTypes<kQuickResolveTypeAndVerifyAccess, void*, uint32_t>();
541         arm_codegen->InvokeRuntime(kQuickResolveTypeAndVerifyAccess, instruction_, dex_pc, this);
542       } else {
543         CheckEntrypointTypes<kQuickResolveType, void*, uint32_t>();
544         arm_codegen->InvokeRuntime(kQuickResolveType, instruction_, dex_pc, this);
545       }
546       // If we also must_do_clinit, the resolved type is now in the correct register.
547     } else {
548       DCHECK(must_do_clinit);
549       Location source = instruction_->IsLoadClass() ? out : locations->InAt(0);
550       arm_codegen->Move32(LocationFrom(calling_convention.GetRegisterAt(0)), source);
551     }
552     if (must_do_clinit) {
553       arm_codegen->InvokeRuntime(kQuickInitializeStaticStorage, instruction_, dex_pc, this);
554       CheckEntrypointTypes<kQuickInitializeStaticStorage, void*, mirror::Class*>();
555     }
556 
557     // Move the class to the desired location.
558     if (out.IsValid()) {
559       DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
560       arm_codegen->Move32(locations->Out(), LocationFrom(r0));
561     }
562     RestoreLiveRegisters(codegen, locations);
563     __ B(GetExitLabel());
564   }
565 
GetDescription() const566   const char* GetDescription() const override { return "LoadClassSlowPathARMVIXL"; }
567 
568  private:
569   // The class this slow path will load.
570   HLoadClass* const cls_;
571 
572   DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathARMVIXL);
573 };
574 
575 class LoadStringSlowPathARMVIXL : public SlowPathCodeARMVIXL {
576  public:
LoadStringSlowPathARMVIXL(HLoadString * instruction)577   explicit LoadStringSlowPathARMVIXL(HLoadString* instruction)
578       : SlowPathCodeARMVIXL(instruction) {}
579 
EmitNativeCode(CodeGenerator * codegen)580   void EmitNativeCode(CodeGenerator* codegen) override {
581     DCHECK(instruction_->IsLoadString());
582     DCHECK_EQ(instruction_->AsLoadString()->GetLoadKind(), HLoadString::LoadKind::kBssEntry);
583     LocationSummary* locations = instruction_->GetLocations();
584     DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
585     const dex::StringIndex string_index = instruction_->AsLoadString()->GetStringIndex();
586 
587     CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
588     __ Bind(GetEntryLabel());
589     SaveLiveRegisters(codegen, locations);
590 
591     InvokeRuntimeCallingConventionARMVIXL calling_convention;
592     __ Mov(calling_convention.GetRegisterAt(0), string_index.index_);
593     arm_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), this);
594     CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
595 
596     arm_codegen->Move32(locations->Out(), LocationFrom(r0));
597     RestoreLiveRegisters(codegen, locations);
598 
599     __ B(GetExitLabel());
600   }
601 
GetDescription() const602   const char* GetDescription() const override { return "LoadStringSlowPathARMVIXL"; }
603 
604  private:
605   DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathARMVIXL);
606 };
607 
608 class TypeCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
609  public:
TypeCheckSlowPathARMVIXL(HInstruction * instruction,bool is_fatal)610   TypeCheckSlowPathARMVIXL(HInstruction* instruction, bool is_fatal)
611       : SlowPathCodeARMVIXL(instruction), is_fatal_(is_fatal) {}
612 
EmitNativeCode(CodeGenerator * codegen)613   void EmitNativeCode(CodeGenerator* codegen) override {
614     LocationSummary* locations = instruction_->GetLocations();
615     DCHECK(instruction_->IsCheckCast()
616            || !locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
617 
618     CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
619     __ Bind(GetEntryLabel());
620 
621     if (!is_fatal_ || instruction_->CanThrowIntoCatchBlock()) {
622       SaveLiveRegisters(codegen, locations);
623     }
624 
625     // We're moving two locations to locations that could overlap, so we need a parallel
626     // move resolver.
627     InvokeRuntimeCallingConventionARMVIXL calling_convention;
628 
629     codegen->EmitParallelMoves(locations->InAt(0),
630                                LocationFrom(calling_convention.GetRegisterAt(0)),
631                                DataType::Type::kReference,
632                                locations->InAt(1),
633                                LocationFrom(calling_convention.GetRegisterAt(1)),
634                                DataType::Type::kReference);
635     if (instruction_->IsInstanceOf()) {
636       arm_codegen->InvokeRuntime(kQuickInstanceofNonTrivial,
637                                  instruction_,
638                                  instruction_->GetDexPc(),
639                                  this);
640       CheckEntrypointTypes<kQuickInstanceofNonTrivial, size_t, mirror::Object*, mirror::Class*>();
641       arm_codegen->Move32(locations->Out(), LocationFrom(r0));
642     } else {
643       DCHECK(instruction_->IsCheckCast());
644       arm_codegen->InvokeRuntime(kQuickCheckInstanceOf,
645                                  instruction_,
646                                  instruction_->GetDexPc(),
647                                  this);
648       CheckEntrypointTypes<kQuickCheckInstanceOf, void, mirror::Object*, mirror::Class*>();
649     }
650 
651     if (!is_fatal_) {
652       RestoreLiveRegisters(codegen, locations);
653       __ B(GetExitLabel());
654     }
655   }
656 
GetDescription() const657   const char* GetDescription() const override { return "TypeCheckSlowPathARMVIXL"; }
658 
IsFatal() const659   bool IsFatal() const override { return is_fatal_; }
660 
661  private:
662   const bool is_fatal_;
663 
664   DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathARMVIXL);
665 };
666 
667 class DeoptimizationSlowPathARMVIXL : public SlowPathCodeARMVIXL {
668  public:
DeoptimizationSlowPathARMVIXL(HDeoptimize * instruction)669   explicit DeoptimizationSlowPathARMVIXL(HDeoptimize* instruction)
670       : SlowPathCodeARMVIXL(instruction) {}
671 
EmitNativeCode(CodeGenerator * codegen)672   void EmitNativeCode(CodeGenerator* codegen) override {
673     CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
674     __ Bind(GetEntryLabel());
675         LocationSummary* locations = instruction_->GetLocations();
676     SaveLiveRegisters(codegen, locations);
677     InvokeRuntimeCallingConventionARMVIXL calling_convention;
678     __ Mov(calling_convention.GetRegisterAt(0),
679            static_cast<uint32_t>(instruction_->AsDeoptimize()->GetDeoptimizationKind()));
680 
681     arm_codegen->InvokeRuntime(kQuickDeoptimize, instruction_, instruction_->GetDexPc(), this);
682     CheckEntrypointTypes<kQuickDeoptimize, void, DeoptimizationKind>();
683   }
684 
GetDescription() const685   const char* GetDescription() const override { return "DeoptimizationSlowPathARMVIXL"; }
686 
687  private:
688   DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathARMVIXL);
689 };
690 
691 class ArraySetSlowPathARMVIXL : public SlowPathCodeARMVIXL {
692  public:
ArraySetSlowPathARMVIXL(HInstruction * instruction)693   explicit ArraySetSlowPathARMVIXL(HInstruction* instruction) : SlowPathCodeARMVIXL(instruction) {}
694 
EmitNativeCode(CodeGenerator * codegen)695   void EmitNativeCode(CodeGenerator* codegen) override {
696     LocationSummary* locations = instruction_->GetLocations();
697     __ Bind(GetEntryLabel());
698     SaveLiveRegisters(codegen, locations);
699 
700     InvokeRuntimeCallingConventionARMVIXL calling_convention;
701     HParallelMove parallel_move(codegen->GetGraph()->GetAllocator());
702     parallel_move.AddMove(
703         locations->InAt(0),
704         LocationFrom(calling_convention.GetRegisterAt(0)),
705         DataType::Type::kReference,
706         nullptr);
707     parallel_move.AddMove(
708         locations->InAt(1),
709         LocationFrom(calling_convention.GetRegisterAt(1)),
710         DataType::Type::kInt32,
711         nullptr);
712     parallel_move.AddMove(
713         locations->InAt(2),
714         LocationFrom(calling_convention.GetRegisterAt(2)),
715         DataType::Type::kReference,
716         nullptr);
717     codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
718 
719     CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
720     arm_codegen->InvokeRuntime(kQuickAputObject, instruction_, instruction_->GetDexPc(), this);
721     CheckEntrypointTypes<kQuickAputObject, void, mirror::Array*, int32_t, mirror::Object*>();
722     RestoreLiveRegisters(codegen, locations);
723     __ B(GetExitLabel());
724   }
725 
GetDescription() const726   const char* GetDescription() const override { return "ArraySetSlowPathARMVIXL"; }
727 
728  private:
729   DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathARMVIXL);
730 };
731 
732 // Slow path generating a read barrier for a heap reference.
733 class ReadBarrierForHeapReferenceSlowPathARMVIXL : public SlowPathCodeARMVIXL {
734  public:
ReadBarrierForHeapReferenceSlowPathARMVIXL(HInstruction * instruction,Location out,Location ref,Location obj,uint32_t offset,Location index)735   ReadBarrierForHeapReferenceSlowPathARMVIXL(HInstruction* instruction,
736                                              Location out,
737                                              Location ref,
738                                              Location obj,
739                                              uint32_t offset,
740                                              Location index)
741       : SlowPathCodeARMVIXL(instruction),
742         out_(out),
743         ref_(ref),
744         obj_(obj),
745         offset_(offset),
746         index_(index) {
747     DCHECK(kEmitCompilerReadBarrier);
748     // If `obj` is equal to `out` or `ref`, it means the initial object
749     // has been overwritten by (or after) the heap object reference load
750     // to be instrumented, e.g.:
751     //
752     //   __ LoadFromOffset(kLoadWord, out, out, offset);
753     //   codegen_->GenerateReadBarrierSlow(instruction, out_loc, out_loc, out_loc, offset);
754     //
755     // In that case, we have lost the information about the original
756     // object, and the emitted read barrier cannot work properly.
757     DCHECK(!obj.Equals(out)) << "obj=" << obj << " out=" << out;
758     DCHECK(!obj.Equals(ref)) << "obj=" << obj << " ref=" << ref;
759   }
760 
EmitNativeCode(CodeGenerator * codegen)761   void EmitNativeCode(CodeGenerator* codegen) override {
762     CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
763     LocationSummary* locations = instruction_->GetLocations();
764     vixl32::Register reg_out = RegisterFrom(out_);
765     DCHECK(locations->CanCall());
766     DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out.GetCode()));
767     DCHECK(instruction_->IsInstanceFieldGet() ||
768            instruction_->IsPredicatedInstanceFieldGet() ||
769            instruction_->IsStaticFieldGet() ||
770            instruction_->IsArrayGet() ||
771            instruction_->IsInstanceOf() ||
772            instruction_->IsCheckCast() ||
773            (instruction_->IsInvoke() && instruction_->GetLocations()->Intrinsified()))
774         << "Unexpected instruction in read barrier for heap reference slow path: "
775         << instruction_->DebugName();
776     // The read barrier instrumentation of object ArrayGet
777     // instructions does not support the HIntermediateAddress
778     // instruction.
779     DCHECK(!(instruction_->IsArrayGet() &&
780              instruction_->AsArrayGet()->GetArray()->IsIntermediateAddress()));
781 
782     __ Bind(GetEntryLabel());
783     SaveLiveRegisters(codegen, locations);
784 
785     // We may have to change the index's value, but as `index_` is a
786     // constant member (like other "inputs" of this slow path),
787     // introduce a copy of it, `index`.
788     Location index = index_;
789     if (index_.IsValid()) {
790       // Handle `index_` for HArrayGet and UnsafeGetObject/UnsafeGetObjectVolatile intrinsics.
791       if (instruction_->IsArrayGet()) {
792         // Compute the actual memory offset and store it in `index`.
793         vixl32::Register index_reg = RegisterFrom(index_);
794         DCHECK(locations->GetLiveRegisters()->ContainsCoreRegister(index_reg.GetCode()));
795         if (codegen->IsCoreCalleeSaveRegister(index_reg.GetCode())) {
796           // We are about to change the value of `index_reg` (see the
797           // calls to art::arm::ArmVIXLMacroAssembler::Lsl and
798           // art::arm::ArmVIXLMacroAssembler::Add below), but it has
799           // not been saved by the previous call to
800           // art::SlowPathCode::SaveLiveRegisters, as it is a
801           // callee-save register --
802           // art::SlowPathCode::SaveLiveRegisters does not consider
803           // callee-save registers, as it has been designed with the
804           // assumption that callee-save registers are supposed to be
805           // handled by the called function.  So, as a callee-save
806           // register, `index_reg` _would_ eventually be saved onto
807           // the stack, but it would be too late: we would have
808           // changed its value earlier.  Therefore, we manually save
809           // it here into another freely available register,
810           // `free_reg`, chosen of course among the caller-save
811           // registers (as a callee-save `free_reg` register would
812           // exhibit the same problem).
813           //
814           // Note we could have requested a temporary register from
815           // the register allocator instead; but we prefer not to, as
816           // this is a slow path, and we know we can find a
817           // caller-save register that is available.
818           vixl32::Register free_reg = FindAvailableCallerSaveRegister(codegen);
819           __ Mov(free_reg, index_reg);
820           index_reg = free_reg;
821           index = LocationFrom(index_reg);
822         } else {
823           // The initial register stored in `index_` has already been
824           // saved in the call to art::SlowPathCode::SaveLiveRegisters
825           // (as it is not a callee-save register), so we can freely
826           // use it.
827         }
828         // Shifting the index value contained in `index_reg` by the scale
829         // factor (2) cannot overflow in practice, as the runtime is
830         // unable to allocate object arrays with a size larger than
831         // 2^26 - 1 (that is, 2^28 - 4 bytes).
832         __ Lsl(index_reg, index_reg, TIMES_4);
833         static_assert(
834             sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
835             "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
836         __ Add(index_reg, index_reg, offset_);
837       } else {
838         // In the case of the following intrinsics `index_` is not shifted by a scale factor of 2
839         // (as in the case of ArrayGet), as it is actually an offset to an object field within an
840         // object.
841         DCHECK(instruction_->IsInvoke()) << instruction_->DebugName();
842         DCHECK(instruction_->GetLocations()->Intrinsified());
843         HInvoke* invoke = instruction_->AsInvoke();
844         DCHECK(IsUnsafeGetObject(invoke) || IsVarHandleGet(invoke) || IsVarHandleCASFamily(invoke))
845             << invoke->GetIntrinsic();
846         DCHECK_EQ(offset_, 0U);
847         // Though UnsafeGet's offset location is a register pair, we only pass the low
848         // part (high part is irrelevant for 32-bit addresses) to the slow path.
849         // For VarHandle intrinsics, the index is always just a register.
850         DCHECK(index_.IsRegister());
851         index = index_;
852       }
853     }
854 
855     // We're moving two or three locations to locations that could
856     // overlap, so we need a parallel move resolver.
857     InvokeRuntimeCallingConventionARMVIXL calling_convention;
858     HParallelMove parallel_move(codegen->GetGraph()->GetAllocator());
859     parallel_move.AddMove(ref_,
860                           LocationFrom(calling_convention.GetRegisterAt(0)),
861                           DataType::Type::kReference,
862                           nullptr);
863     parallel_move.AddMove(obj_,
864                           LocationFrom(calling_convention.GetRegisterAt(1)),
865                           DataType::Type::kReference,
866                           nullptr);
867     if (index.IsValid()) {
868       parallel_move.AddMove(index,
869                             LocationFrom(calling_convention.GetRegisterAt(2)),
870                             DataType::Type::kInt32,
871                             nullptr);
872       codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
873     } else {
874       codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
875       __ Mov(calling_convention.GetRegisterAt(2), offset_);
876     }
877     arm_codegen->InvokeRuntime(kQuickReadBarrierSlow, instruction_, instruction_->GetDexPc(), this);
878     CheckEntrypointTypes<
879         kQuickReadBarrierSlow, mirror::Object*, mirror::Object*, mirror::Object*, uint32_t>();
880     arm_codegen->Move32(out_, LocationFrom(r0));
881 
882     RestoreLiveRegisters(codegen, locations);
883     __ B(GetExitLabel());
884   }
885 
GetDescription() const886   const char* GetDescription() const override {
887     return "ReadBarrierForHeapReferenceSlowPathARMVIXL";
888   }
889 
890  private:
FindAvailableCallerSaveRegister(CodeGenerator * codegen)891   vixl32::Register FindAvailableCallerSaveRegister(CodeGenerator* codegen) {
892     uint32_t ref = RegisterFrom(ref_).GetCode();
893     uint32_t obj = RegisterFrom(obj_).GetCode();
894     for (uint32_t i = 0, e = codegen->GetNumberOfCoreRegisters(); i < e; ++i) {
895       if (i != ref && i != obj && !codegen->IsCoreCalleeSaveRegister(i)) {
896         return vixl32::Register(i);
897       }
898     }
899     // We shall never fail to find a free caller-save register, as
900     // there are more than two core caller-save registers on ARM
901     // (meaning it is possible to find one which is different from
902     // `ref` and `obj`).
903     DCHECK_GT(codegen->GetNumberOfCoreCallerSaveRegisters(), 2u);
904     LOG(FATAL) << "Could not find a free caller-save register";
905     UNREACHABLE();
906   }
907 
908   const Location out_;
909   const Location ref_;
910   const Location obj_;
911   const uint32_t offset_;
912   // An additional location containing an index to an array.
913   // Only used for HArrayGet and the UnsafeGetObject &
914   // UnsafeGetObjectVolatile intrinsics.
915   const Location index_;
916 
917   DISALLOW_COPY_AND_ASSIGN(ReadBarrierForHeapReferenceSlowPathARMVIXL);
918 };
919 
920 // Slow path generating a read barrier for a GC root.
921 class ReadBarrierForRootSlowPathARMVIXL : public SlowPathCodeARMVIXL {
922  public:
ReadBarrierForRootSlowPathARMVIXL(HInstruction * instruction,Location out,Location root)923   ReadBarrierForRootSlowPathARMVIXL(HInstruction* instruction, Location out, Location root)
924       : SlowPathCodeARMVIXL(instruction), out_(out), root_(root) {
925     DCHECK(kEmitCompilerReadBarrier);
926   }
927 
EmitNativeCode(CodeGenerator * codegen)928   void EmitNativeCode(CodeGenerator* codegen) override {
929     LocationSummary* locations = instruction_->GetLocations();
930     vixl32::Register reg_out = RegisterFrom(out_);
931     DCHECK(locations->CanCall());
932     DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out.GetCode()));
933     DCHECK(instruction_->IsLoadClass() ||
934            instruction_->IsLoadString() ||
935            (instruction_->IsInvoke() && instruction_->GetLocations()->Intrinsified()))
936         << "Unexpected instruction in read barrier for GC root slow path: "
937         << instruction_->DebugName();
938 
939     __ Bind(GetEntryLabel());
940     SaveLiveRegisters(codegen, locations);
941 
942     InvokeRuntimeCallingConventionARMVIXL calling_convention;
943     CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
944     arm_codegen->Move32(LocationFrom(calling_convention.GetRegisterAt(0)), root_);
945     arm_codegen->InvokeRuntime(kQuickReadBarrierForRootSlow,
946                                instruction_,
947                                instruction_->GetDexPc(),
948                                this);
949     CheckEntrypointTypes<kQuickReadBarrierForRootSlow, mirror::Object*, GcRoot<mirror::Object>*>();
950     arm_codegen->Move32(out_, LocationFrom(r0));
951 
952     RestoreLiveRegisters(codegen, locations);
953     __ B(GetExitLabel());
954   }
955 
GetDescription() const956   const char* GetDescription() const override { return "ReadBarrierForRootSlowPathARMVIXL"; }
957 
958  private:
959   const Location out_;
960   const Location root_;
961 
962   DISALLOW_COPY_AND_ASSIGN(ReadBarrierForRootSlowPathARMVIXL);
963 };
964 
965 class MethodEntryExitHooksSlowPathARMVIXL : public SlowPathCodeARMVIXL {
966  public:
MethodEntryExitHooksSlowPathARMVIXL(HInstruction * instruction)967   explicit MethodEntryExitHooksSlowPathARMVIXL(HInstruction* instruction)
968       : SlowPathCodeARMVIXL(instruction) {}
969 
EmitNativeCode(CodeGenerator * codegen)970   void EmitNativeCode(CodeGenerator* codegen) override {
971     LocationSummary* locations = instruction_->GetLocations();
972     CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
973     QuickEntrypointEnum entry_point =
974         (instruction_->IsMethodEntryHook()) ? kQuickMethodEntryHook : kQuickMethodExitHook;
975     __ Bind(GetEntryLabel());
976     SaveLiveRegisters(codegen, locations);
977     arm_codegen->InvokeRuntime(entry_point, instruction_, instruction_->GetDexPc(), this);
978     RestoreLiveRegisters(codegen, locations);
979     __ B(GetExitLabel());
980   }
981 
GetDescription() const982   const char* GetDescription() const override {
983     return "MethodEntryExitHooksSlowPath";
984   }
985 
986  private:
987   DISALLOW_COPY_AND_ASSIGN(MethodEntryExitHooksSlowPathARMVIXL);
988 };
989 
990 class CompileOptimizedSlowPathARMVIXL : public SlowPathCodeARMVIXL {
991  public:
CompileOptimizedSlowPathARMVIXL()992   CompileOptimizedSlowPathARMVIXL() : SlowPathCodeARMVIXL(/* instruction= */ nullptr) {}
993 
EmitNativeCode(CodeGenerator * codegen)994   void EmitNativeCode(CodeGenerator* codegen) override {
995     uint32_t entry_point_offset =
996         GetThreadOffset<kArmPointerSize>(kQuickCompileOptimized).Int32Value();
997     __ Bind(GetEntryLabel());
998     __ Ldr(lr, MemOperand(tr, entry_point_offset));
999     // Note: we don't record the call here (and therefore don't generate a stack
1000     // map), as the entrypoint should never be suspended.
1001     __ Blx(lr);
1002     __ B(GetExitLabel());
1003   }
1004 
GetDescription() const1005   const char* GetDescription() const override {
1006     return "CompileOptimizedSlowPath";
1007   }
1008 
1009  private:
1010   DISALLOW_COPY_AND_ASSIGN(CompileOptimizedSlowPathARMVIXL);
1011 };
1012 
ARMCondition(IfCondition cond)1013 inline vixl32::Condition ARMCondition(IfCondition cond) {
1014   switch (cond) {
1015     case kCondEQ: return eq;
1016     case kCondNE: return ne;
1017     case kCondLT: return lt;
1018     case kCondLE: return le;
1019     case kCondGT: return gt;
1020     case kCondGE: return ge;
1021     case kCondB:  return lo;
1022     case kCondBE: return ls;
1023     case kCondA:  return hi;
1024     case kCondAE: return hs;
1025   }
1026   LOG(FATAL) << "Unreachable";
1027   UNREACHABLE();
1028 }
1029 
1030 // Maps signed condition to unsigned condition.
ARMUnsignedCondition(IfCondition cond)1031 inline vixl32::Condition ARMUnsignedCondition(IfCondition cond) {
1032   switch (cond) {
1033     case kCondEQ: return eq;
1034     case kCondNE: return ne;
1035     // Signed to unsigned.
1036     case kCondLT: return lo;
1037     case kCondLE: return ls;
1038     case kCondGT: return hi;
1039     case kCondGE: return hs;
1040     // Unsigned remain unchanged.
1041     case kCondB:  return lo;
1042     case kCondBE: return ls;
1043     case kCondA:  return hi;
1044     case kCondAE: return hs;
1045   }
1046   LOG(FATAL) << "Unreachable";
1047   UNREACHABLE();
1048 }
1049 
ARMFPCondition(IfCondition cond,bool gt_bias)1050 inline vixl32::Condition ARMFPCondition(IfCondition cond, bool gt_bias) {
1051   // The ARM condition codes can express all the necessary branches, see the
1052   // "Meaning (floating-point)" column in the table A8-1 of the ARMv7 reference manual.
1053   // There is no dex instruction or HIR that would need the missing conditions
1054   // "equal or unordered" or "not equal".
1055   switch (cond) {
1056     case kCondEQ: return eq;
1057     case kCondNE: return ne /* unordered */;
1058     case kCondLT: return gt_bias ? cc : lt /* unordered */;
1059     case kCondLE: return gt_bias ? ls : le /* unordered */;
1060     case kCondGT: return gt_bias ? hi /* unordered */ : gt;
1061     case kCondGE: return gt_bias ? cs /* unordered */ : ge;
1062     default:
1063       LOG(FATAL) << "UNREACHABLE";
1064       UNREACHABLE();
1065   }
1066 }
1067 
ShiftFromOpKind(HDataProcWithShifterOp::OpKind op_kind)1068 inline ShiftType ShiftFromOpKind(HDataProcWithShifterOp::OpKind op_kind) {
1069   switch (op_kind) {
1070     case HDataProcWithShifterOp::kASR: return ShiftType::ASR;
1071     case HDataProcWithShifterOp::kLSL: return ShiftType::LSL;
1072     case HDataProcWithShifterOp::kLSR: return ShiftType::LSR;
1073     default:
1074       LOG(FATAL) << "Unexpected op kind " << op_kind;
1075       UNREACHABLE();
1076   }
1077 }
1078 
DumpCoreRegister(std::ostream & stream,int reg) const1079 void CodeGeneratorARMVIXL::DumpCoreRegister(std::ostream& stream, int reg) const {
1080   stream << vixl32::Register(reg);
1081 }
1082 
DumpFloatingPointRegister(std::ostream & stream,int reg) const1083 void CodeGeneratorARMVIXL::DumpFloatingPointRegister(std::ostream& stream, int reg) const {
1084   stream << vixl32::SRegister(reg);
1085 }
1086 
GetInstructionSetFeatures() const1087 const ArmInstructionSetFeatures& CodeGeneratorARMVIXL::GetInstructionSetFeatures() const {
1088   return *GetCompilerOptions().GetInstructionSetFeatures()->AsArmInstructionSetFeatures();
1089 }
1090 
ComputeSRegisterListMask(const SRegisterList & regs)1091 static uint32_t ComputeSRegisterListMask(const SRegisterList& regs) {
1092   uint32_t mask = 0;
1093   for (uint32_t i = regs.GetFirstSRegister().GetCode();
1094        i <= regs.GetLastSRegister().GetCode();
1095        ++i) {
1096     mask |= (1 << i);
1097   }
1098   return mask;
1099 }
1100 
1101 // Saves the register in the stack. Returns the size taken on stack.
SaveCoreRegister(size_t stack_index ATTRIBUTE_UNUSED,uint32_t reg_id ATTRIBUTE_UNUSED)1102 size_t CodeGeneratorARMVIXL::SaveCoreRegister(size_t stack_index ATTRIBUTE_UNUSED,
1103                                               uint32_t reg_id ATTRIBUTE_UNUSED) {
1104   TODO_VIXL32(FATAL);
1105   UNREACHABLE();
1106 }
1107 
1108 // Restores the register from the stack. Returns the size taken on stack.
RestoreCoreRegister(size_t stack_index ATTRIBUTE_UNUSED,uint32_t reg_id ATTRIBUTE_UNUSED)1109 size_t CodeGeneratorARMVIXL::RestoreCoreRegister(size_t stack_index ATTRIBUTE_UNUSED,
1110                                                  uint32_t reg_id ATTRIBUTE_UNUSED) {
1111   TODO_VIXL32(FATAL);
1112   UNREACHABLE();
1113 }
1114 
SaveFloatingPointRegister(size_t stack_index ATTRIBUTE_UNUSED,uint32_t reg_id ATTRIBUTE_UNUSED)1115 size_t CodeGeneratorARMVIXL::SaveFloatingPointRegister(size_t stack_index ATTRIBUTE_UNUSED,
1116                                                        uint32_t reg_id ATTRIBUTE_UNUSED) {
1117   TODO_VIXL32(FATAL);
1118   UNREACHABLE();
1119 }
1120 
RestoreFloatingPointRegister(size_t stack_index ATTRIBUTE_UNUSED,uint32_t reg_id ATTRIBUTE_UNUSED)1121 size_t CodeGeneratorARMVIXL::RestoreFloatingPointRegister(size_t stack_index ATTRIBUTE_UNUSED,
1122                                                           uint32_t reg_id ATTRIBUTE_UNUSED) {
1123   TODO_VIXL32(FATAL);
1124   UNREACHABLE();
1125 }
1126 
GenerateDataProcInstruction(HInstruction::InstructionKind kind,vixl32::Register out,vixl32::Register first,const Operand & second,CodeGeneratorARMVIXL * codegen)1127 static void GenerateDataProcInstruction(HInstruction::InstructionKind kind,
1128                                         vixl32::Register out,
1129                                         vixl32::Register first,
1130                                         const Operand& second,
1131                                         CodeGeneratorARMVIXL* codegen) {
1132   if (second.IsImmediate() && second.GetImmediate() == 0) {
1133     const Operand in = kind == HInstruction::kAnd
1134         ? Operand(0)
1135         : Operand(first);
1136 
1137     __ Mov(out, in);
1138   } else {
1139     switch (kind) {
1140       case HInstruction::kAdd:
1141         __ Add(out, first, second);
1142         break;
1143       case HInstruction::kAnd:
1144         __ And(out, first, second);
1145         break;
1146       case HInstruction::kOr:
1147         __ Orr(out, first, second);
1148         break;
1149       case HInstruction::kSub:
1150         __ Sub(out, first, second);
1151         break;
1152       case HInstruction::kXor:
1153         __ Eor(out, first, second);
1154         break;
1155       default:
1156         LOG(FATAL) << "Unexpected instruction kind: " << kind;
1157         UNREACHABLE();
1158     }
1159   }
1160 }
1161 
GenerateDataProc(HInstruction::InstructionKind kind,const Location & out,const Location & first,const Operand & second_lo,const Operand & second_hi,CodeGeneratorARMVIXL * codegen)1162 static void GenerateDataProc(HInstruction::InstructionKind kind,
1163                              const Location& out,
1164                              const Location& first,
1165                              const Operand& second_lo,
1166                              const Operand& second_hi,
1167                              CodeGeneratorARMVIXL* codegen) {
1168   const vixl32::Register first_hi = HighRegisterFrom(first);
1169   const vixl32::Register first_lo = LowRegisterFrom(first);
1170   const vixl32::Register out_hi = HighRegisterFrom(out);
1171   const vixl32::Register out_lo = LowRegisterFrom(out);
1172 
1173   if (kind == HInstruction::kAdd) {
1174     __ Adds(out_lo, first_lo, second_lo);
1175     __ Adc(out_hi, first_hi, second_hi);
1176   } else if (kind == HInstruction::kSub) {
1177     __ Subs(out_lo, first_lo, second_lo);
1178     __ Sbc(out_hi, first_hi, second_hi);
1179   } else {
1180     GenerateDataProcInstruction(kind, out_lo, first_lo, second_lo, codegen);
1181     GenerateDataProcInstruction(kind, out_hi, first_hi, second_hi, codegen);
1182   }
1183 }
1184 
GetShifterOperand(vixl32::Register rm,ShiftType shift,uint32_t shift_imm)1185 static Operand GetShifterOperand(vixl32::Register rm, ShiftType shift, uint32_t shift_imm) {
1186   return shift_imm == 0 ? Operand(rm) : Operand(rm, shift, shift_imm);
1187 }
1188 
GenerateLongDataProc(HDataProcWithShifterOp * instruction,CodeGeneratorARMVIXL * codegen)1189 static void GenerateLongDataProc(HDataProcWithShifterOp* instruction,
1190                                  CodeGeneratorARMVIXL* codegen) {
1191   DCHECK_EQ(instruction->GetType(), DataType::Type::kInt64);
1192   DCHECK(HDataProcWithShifterOp::IsShiftOp(instruction->GetOpKind()));
1193 
1194   const LocationSummary* const locations = instruction->GetLocations();
1195   const uint32_t shift_value = instruction->GetShiftAmount();
1196   const HInstruction::InstructionKind kind = instruction->GetInstrKind();
1197   const Location first = locations->InAt(0);
1198   const Location second = locations->InAt(1);
1199   const Location out = locations->Out();
1200   const vixl32::Register first_hi = HighRegisterFrom(first);
1201   const vixl32::Register first_lo = LowRegisterFrom(first);
1202   const vixl32::Register out_hi = HighRegisterFrom(out);
1203   const vixl32::Register out_lo = LowRegisterFrom(out);
1204   const vixl32::Register second_hi = HighRegisterFrom(second);
1205   const vixl32::Register second_lo = LowRegisterFrom(second);
1206   const ShiftType shift = ShiftFromOpKind(instruction->GetOpKind());
1207 
1208   if (shift_value >= 32) {
1209     if (shift == ShiftType::LSL) {
1210       GenerateDataProcInstruction(kind,
1211                                   out_hi,
1212                                   first_hi,
1213                                   Operand(second_lo, ShiftType::LSL, shift_value - 32),
1214                                   codegen);
1215       GenerateDataProcInstruction(kind, out_lo, first_lo, 0, codegen);
1216     } else if (shift == ShiftType::ASR) {
1217       GenerateDataProc(kind,
1218                        out,
1219                        first,
1220                        GetShifterOperand(second_hi, ShiftType::ASR, shift_value - 32),
1221                        Operand(second_hi, ShiftType::ASR, 31),
1222                        codegen);
1223     } else {
1224       DCHECK_EQ(shift, ShiftType::LSR);
1225       GenerateDataProc(kind,
1226                        out,
1227                        first,
1228                        GetShifterOperand(second_hi, ShiftType::LSR, shift_value - 32),
1229                        0,
1230                        codegen);
1231     }
1232   } else {
1233     DCHECK_GT(shift_value, 1U);
1234     DCHECK_LT(shift_value, 32U);
1235 
1236     UseScratchRegisterScope temps(codegen->GetVIXLAssembler());
1237 
1238     if (shift == ShiftType::LSL) {
1239       // We are not doing this for HInstruction::kAdd because the output will require
1240       // Location::kOutputOverlap; not applicable to other cases.
1241       if (kind == HInstruction::kOr || kind == HInstruction::kXor) {
1242         GenerateDataProcInstruction(kind,
1243                                     out_hi,
1244                                     first_hi,
1245                                     Operand(second_hi, ShiftType::LSL, shift_value),
1246                                     codegen);
1247         GenerateDataProcInstruction(kind,
1248                                     out_hi,
1249                                     out_hi,
1250                                     Operand(second_lo, ShiftType::LSR, 32 - shift_value),
1251                                     codegen);
1252         GenerateDataProcInstruction(kind,
1253                                     out_lo,
1254                                     first_lo,
1255                                     Operand(second_lo, ShiftType::LSL, shift_value),
1256                                     codegen);
1257       } else {
1258         const vixl32::Register temp = temps.Acquire();
1259 
1260         __ Lsl(temp, second_hi, shift_value);
1261         __ Orr(temp, temp, Operand(second_lo, ShiftType::LSR, 32 - shift_value));
1262         GenerateDataProc(kind,
1263                          out,
1264                          first,
1265                          Operand(second_lo, ShiftType::LSL, shift_value),
1266                          temp,
1267                          codegen);
1268       }
1269     } else {
1270       DCHECK(shift == ShiftType::ASR || shift == ShiftType::LSR);
1271 
1272       // We are not doing this for HInstruction::kAdd because the output will require
1273       // Location::kOutputOverlap; not applicable to other cases.
1274       if (kind == HInstruction::kOr || kind == HInstruction::kXor) {
1275         GenerateDataProcInstruction(kind,
1276                                     out_lo,
1277                                     first_lo,
1278                                     Operand(second_lo, ShiftType::LSR, shift_value),
1279                                     codegen);
1280         GenerateDataProcInstruction(kind,
1281                                     out_lo,
1282                                     out_lo,
1283                                     Operand(second_hi, ShiftType::LSL, 32 - shift_value),
1284                                     codegen);
1285         GenerateDataProcInstruction(kind,
1286                                     out_hi,
1287                                     first_hi,
1288                                     Operand(second_hi, shift, shift_value),
1289                                     codegen);
1290       } else {
1291         const vixl32::Register temp = temps.Acquire();
1292 
1293         __ Lsr(temp, second_lo, shift_value);
1294         __ Orr(temp, temp, Operand(second_hi, ShiftType::LSL, 32 - shift_value));
1295         GenerateDataProc(kind,
1296                          out,
1297                          first,
1298                          temp,
1299                          Operand(second_hi, shift, shift_value),
1300                          codegen);
1301       }
1302     }
1303   }
1304 }
1305 
GenerateVcmp(HInstruction * instruction,CodeGeneratorARMVIXL * codegen)1306 static void GenerateVcmp(HInstruction* instruction, CodeGeneratorARMVIXL* codegen) {
1307   const Location rhs_loc = instruction->GetLocations()->InAt(1);
1308   if (rhs_loc.IsConstant()) {
1309     // 0.0 is the only immediate that can be encoded directly in
1310     // a VCMP instruction.
1311     //
1312     // Both the JLS (section 15.20.1) and the JVMS (section 6.5)
1313     // specify that in a floating-point comparison, positive zero
1314     // and negative zero are considered equal, so we can use the
1315     // literal 0.0 for both cases here.
1316     //
1317     // Note however that some methods (Float.equal, Float.compare,
1318     // Float.compareTo, Double.equal, Double.compare,
1319     // Double.compareTo, Math.max, Math.min, StrictMath.max,
1320     // StrictMath.min) consider 0.0 to be (strictly) greater than
1321     // -0.0. So if we ever translate calls to these methods into a
1322     // HCompare instruction, we must handle the -0.0 case with
1323     // care here.
1324     DCHECK(rhs_loc.GetConstant()->IsArithmeticZero());
1325 
1326     const DataType::Type type = instruction->InputAt(0)->GetType();
1327 
1328     if (type == DataType::Type::kFloat32) {
1329       __ Vcmp(F32, InputSRegisterAt(instruction, 0), 0.0);
1330     } else {
1331       DCHECK_EQ(type, DataType::Type::kFloat64);
1332       __ Vcmp(F64, InputDRegisterAt(instruction, 0), 0.0);
1333     }
1334   } else {
1335     __ Vcmp(InputVRegisterAt(instruction, 0), InputVRegisterAt(instruction, 1));
1336   }
1337 }
1338 
AdjustConstantForCondition(int64_t value,IfCondition * condition,IfCondition * opposite)1339 static int64_t AdjustConstantForCondition(int64_t value,
1340                                           IfCondition* condition,
1341                                           IfCondition* opposite) {
1342   if (value == 1) {
1343     if (*condition == kCondB) {
1344       value = 0;
1345       *condition = kCondEQ;
1346       *opposite = kCondNE;
1347     } else if (*condition == kCondAE) {
1348       value = 0;
1349       *condition = kCondNE;
1350       *opposite = kCondEQ;
1351     }
1352   } else if (value == -1) {
1353     if (*condition == kCondGT) {
1354       value = 0;
1355       *condition = kCondGE;
1356       *opposite = kCondLT;
1357     } else if (*condition == kCondLE) {
1358       value = 0;
1359       *condition = kCondLT;
1360       *opposite = kCondGE;
1361     }
1362   }
1363 
1364   return value;
1365 }
1366 
GenerateLongTestConstant(HCondition * condition,bool invert,CodeGeneratorARMVIXL * codegen)1367 static std::pair<vixl32::Condition, vixl32::Condition> GenerateLongTestConstant(
1368     HCondition* condition,
1369     bool invert,
1370     CodeGeneratorARMVIXL* codegen) {
1371   DCHECK_EQ(condition->GetLeft()->GetType(), DataType::Type::kInt64);
1372 
1373   const LocationSummary* const locations = condition->GetLocations();
1374   IfCondition cond = condition->GetCondition();
1375   IfCondition opposite = condition->GetOppositeCondition();
1376 
1377   if (invert) {
1378     std::swap(cond, opposite);
1379   }
1380 
1381   std::pair<vixl32::Condition, vixl32::Condition> ret(eq, ne);
1382   const Location left = locations->InAt(0);
1383   const Location right = locations->InAt(1);
1384 
1385   DCHECK(right.IsConstant());
1386 
1387   const vixl32::Register left_high = HighRegisterFrom(left);
1388   const vixl32::Register left_low = LowRegisterFrom(left);
1389   int64_t value = AdjustConstantForCondition(Int64ConstantFrom(right), &cond, &opposite);
1390   UseScratchRegisterScope temps(codegen->GetVIXLAssembler());
1391 
1392   // Comparisons against 0 are common enough to deserve special attention.
1393   if (value == 0) {
1394     switch (cond) {
1395       case kCondNE:
1396       // x > 0 iff x != 0 when the comparison is unsigned.
1397       case kCondA:
1398         ret = std::make_pair(ne, eq);
1399         FALLTHROUGH_INTENDED;
1400       case kCondEQ:
1401       // x <= 0 iff x == 0 when the comparison is unsigned.
1402       case kCondBE:
1403         __ Orrs(temps.Acquire(), left_low, left_high);
1404         return ret;
1405       case kCondLT:
1406       case kCondGE:
1407         __ Cmp(left_high, 0);
1408         return std::make_pair(ARMCondition(cond), ARMCondition(opposite));
1409       // Trivially true or false.
1410       case kCondB:
1411         ret = std::make_pair(ne, eq);
1412         FALLTHROUGH_INTENDED;
1413       case kCondAE:
1414         __ Cmp(left_low, left_low);
1415         return ret;
1416       default:
1417         break;
1418     }
1419   }
1420 
1421   switch (cond) {
1422     case kCondEQ:
1423     case kCondNE:
1424     case kCondB:
1425     case kCondBE:
1426     case kCondA:
1427     case kCondAE: {
1428       const uint32_t value_low = Low32Bits(value);
1429       Operand operand_low(value_low);
1430 
1431       __ Cmp(left_high, High32Bits(value));
1432 
1433       // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
1434       // we must ensure that the operands corresponding to the least significant
1435       // halves of the inputs fit into a 16-bit CMP encoding.
1436       if (!left_low.IsLow() || !IsUint<8>(value_low)) {
1437         operand_low = Operand(temps.Acquire());
1438         __ Mov(LeaveFlags, operand_low.GetBaseRegister(), value_low);
1439       }
1440 
1441       // We use the scope because of the IT block that follows.
1442       ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
1443                                2 * vixl32::k16BitT32InstructionSizeInBytes,
1444                                CodeBufferCheckScope::kExactSize);
1445 
1446       __ it(eq);
1447       __ cmp(eq, left_low, operand_low);
1448       ret = std::make_pair(ARMUnsignedCondition(cond), ARMUnsignedCondition(opposite));
1449       break;
1450     }
1451     case kCondLE:
1452     case kCondGT:
1453       // Trivially true or false.
1454       if (value == std::numeric_limits<int64_t>::max()) {
1455         __ Cmp(left_low, left_low);
1456         ret = cond == kCondLE ? std::make_pair(eq, ne) : std::make_pair(ne, eq);
1457         break;
1458       }
1459 
1460       if (cond == kCondLE) {
1461         DCHECK_EQ(opposite, kCondGT);
1462         cond = kCondLT;
1463         opposite = kCondGE;
1464       } else {
1465         DCHECK_EQ(cond, kCondGT);
1466         DCHECK_EQ(opposite, kCondLE);
1467         cond = kCondGE;
1468         opposite = kCondLT;
1469       }
1470 
1471       value++;
1472       FALLTHROUGH_INTENDED;
1473     case kCondGE:
1474     case kCondLT: {
1475       __ Cmp(left_low, Low32Bits(value));
1476       __ Sbcs(temps.Acquire(), left_high, High32Bits(value));
1477       ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite));
1478       break;
1479     }
1480     default:
1481       LOG(FATAL) << "Unreachable";
1482       UNREACHABLE();
1483   }
1484 
1485   return ret;
1486 }
1487 
GenerateLongTest(HCondition * condition,bool invert,CodeGeneratorARMVIXL * codegen)1488 static std::pair<vixl32::Condition, vixl32::Condition> GenerateLongTest(
1489     HCondition* condition,
1490     bool invert,
1491     CodeGeneratorARMVIXL* codegen) {
1492   DCHECK_EQ(condition->GetLeft()->GetType(), DataType::Type::kInt64);
1493 
1494   const LocationSummary* const locations = condition->GetLocations();
1495   IfCondition cond = condition->GetCondition();
1496   IfCondition opposite = condition->GetOppositeCondition();
1497 
1498   if (invert) {
1499     std::swap(cond, opposite);
1500   }
1501 
1502   std::pair<vixl32::Condition, vixl32::Condition> ret(eq, ne);
1503   Location left = locations->InAt(0);
1504   Location right = locations->InAt(1);
1505 
1506   DCHECK(right.IsRegisterPair());
1507 
1508   switch (cond) {
1509     case kCondEQ:
1510     case kCondNE:
1511     case kCondB:
1512     case kCondBE:
1513     case kCondA:
1514     case kCondAE: {
1515       __ Cmp(HighRegisterFrom(left), HighRegisterFrom(right));
1516 
1517       // We use the scope because of the IT block that follows.
1518       ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
1519                                2 * vixl32::k16BitT32InstructionSizeInBytes,
1520                                CodeBufferCheckScope::kExactSize);
1521 
1522       __ it(eq);
1523       __ cmp(eq, LowRegisterFrom(left), LowRegisterFrom(right));
1524       ret = std::make_pair(ARMUnsignedCondition(cond), ARMUnsignedCondition(opposite));
1525       break;
1526     }
1527     case kCondLE:
1528     case kCondGT:
1529       if (cond == kCondLE) {
1530         DCHECK_EQ(opposite, kCondGT);
1531         cond = kCondGE;
1532         opposite = kCondLT;
1533       } else {
1534         DCHECK_EQ(cond, kCondGT);
1535         DCHECK_EQ(opposite, kCondLE);
1536         cond = kCondLT;
1537         opposite = kCondGE;
1538       }
1539 
1540       std::swap(left, right);
1541       FALLTHROUGH_INTENDED;
1542     case kCondGE:
1543     case kCondLT: {
1544       UseScratchRegisterScope temps(codegen->GetVIXLAssembler());
1545 
1546       __ Cmp(LowRegisterFrom(left), LowRegisterFrom(right));
1547       __ Sbcs(temps.Acquire(), HighRegisterFrom(left), HighRegisterFrom(right));
1548       ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite));
1549       break;
1550     }
1551     default:
1552       LOG(FATAL) << "Unreachable";
1553       UNREACHABLE();
1554   }
1555 
1556   return ret;
1557 }
1558 
GenerateTest(HCondition * condition,bool invert,CodeGeneratorARMVIXL * codegen)1559 static std::pair<vixl32::Condition, vixl32::Condition> GenerateTest(HCondition* condition,
1560                                                                     bool invert,
1561                                                                     CodeGeneratorARMVIXL* codegen) {
1562   const DataType::Type type = condition->GetLeft()->GetType();
1563   IfCondition cond = condition->GetCondition();
1564   IfCondition opposite = condition->GetOppositeCondition();
1565   std::pair<vixl32::Condition, vixl32::Condition> ret(eq, ne);
1566 
1567   if (invert) {
1568     std::swap(cond, opposite);
1569   }
1570 
1571   if (type == DataType::Type::kInt64) {
1572     ret = condition->GetLocations()->InAt(1).IsConstant()
1573         ? GenerateLongTestConstant(condition, invert, codegen)
1574         : GenerateLongTest(condition, invert, codegen);
1575   } else if (DataType::IsFloatingPointType(type)) {
1576     GenerateVcmp(condition, codegen);
1577     __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
1578     ret = std::make_pair(ARMFPCondition(cond, condition->IsGtBias()),
1579                          ARMFPCondition(opposite, condition->IsGtBias()));
1580   } else {
1581     DCHECK(DataType::IsIntegralType(type) || type == DataType::Type::kReference) << type;
1582     __ Cmp(InputRegisterAt(condition, 0), InputOperandAt(condition, 1));
1583     ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite));
1584   }
1585 
1586   return ret;
1587 }
1588 
GenerateConditionGeneric(HCondition * cond,CodeGeneratorARMVIXL * codegen)1589 static void GenerateConditionGeneric(HCondition* cond, CodeGeneratorARMVIXL* codegen) {
1590   const vixl32::Register out = OutputRegister(cond);
1591   const auto condition = GenerateTest(cond, false, codegen);
1592 
1593   __ Mov(LeaveFlags, out, 0);
1594 
1595   if (out.IsLow()) {
1596     // We use the scope because of the IT block that follows.
1597     ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
1598                              2 * vixl32::k16BitT32InstructionSizeInBytes,
1599                              CodeBufferCheckScope::kExactSize);
1600 
1601     __ it(condition.first);
1602     __ mov(condition.first, out, 1);
1603   } else {
1604     vixl32::Label done_label;
1605     vixl32::Label* const final_label = codegen->GetFinalLabel(cond, &done_label);
1606 
1607     __ B(condition.second, final_label, /* is_far_target= */ false);
1608     __ Mov(out, 1);
1609 
1610     if (done_label.IsReferenced()) {
1611       __ Bind(&done_label);
1612     }
1613   }
1614 }
1615 
GenerateEqualLong(HCondition * cond,CodeGeneratorARMVIXL * codegen)1616 static void GenerateEqualLong(HCondition* cond, CodeGeneratorARMVIXL* codegen) {
1617   DCHECK_EQ(cond->GetLeft()->GetType(), DataType::Type::kInt64);
1618 
1619   const LocationSummary* const locations = cond->GetLocations();
1620   IfCondition condition = cond->GetCondition();
1621   const vixl32::Register out = OutputRegister(cond);
1622   const Location left = locations->InAt(0);
1623   const Location right = locations->InAt(1);
1624   vixl32::Register left_high = HighRegisterFrom(left);
1625   vixl32::Register left_low = LowRegisterFrom(left);
1626   vixl32::Register temp;
1627   UseScratchRegisterScope temps(codegen->GetVIXLAssembler());
1628 
1629   if (right.IsConstant()) {
1630     IfCondition opposite = cond->GetOppositeCondition();
1631     const int64_t value = AdjustConstantForCondition(Int64ConstantFrom(right),
1632                                                      &condition,
1633                                                      &opposite);
1634     Operand right_high = High32Bits(value);
1635     Operand right_low = Low32Bits(value);
1636 
1637     // The output uses Location::kNoOutputOverlap.
1638     if (out.Is(left_high)) {
1639       std::swap(left_low, left_high);
1640       std::swap(right_low, right_high);
1641     }
1642 
1643     __ Sub(out, left_low, right_low);
1644     temp = temps.Acquire();
1645     __ Sub(temp, left_high, right_high);
1646   } else {
1647     DCHECK(right.IsRegisterPair());
1648     temp = temps.Acquire();
1649     __ Sub(temp, left_high, HighRegisterFrom(right));
1650     __ Sub(out, left_low, LowRegisterFrom(right));
1651   }
1652 
1653   // Need to check after calling AdjustConstantForCondition().
1654   DCHECK(condition == kCondEQ || condition == kCondNE) << condition;
1655 
1656   if (condition == kCondNE && out.IsLow()) {
1657     __ Orrs(out, out, temp);
1658 
1659     // We use the scope because of the IT block that follows.
1660     ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
1661                              2 * vixl32::k16BitT32InstructionSizeInBytes,
1662                              CodeBufferCheckScope::kExactSize);
1663 
1664     __ it(ne);
1665     __ mov(ne, out, 1);
1666   } else {
1667     __ Orr(out, out, temp);
1668     codegen->GenerateConditionWithZero(condition, out, out, temp);
1669   }
1670 }
1671 
GenerateConditionLong(HCondition * cond,CodeGeneratorARMVIXL * codegen)1672 static void GenerateConditionLong(HCondition* cond, CodeGeneratorARMVIXL* codegen) {
1673   DCHECK_EQ(cond->GetLeft()->GetType(), DataType::Type::kInt64);
1674 
1675   const LocationSummary* const locations = cond->GetLocations();
1676   IfCondition condition = cond->GetCondition();
1677   const vixl32::Register out = OutputRegister(cond);
1678   const Location left = locations->InAt(0);
1679   const Location right = locations->InAt(1);
1680 
1681   if (right.IsConstant()) {
1682     IfCondition opposite = cond->GetOppositeCondition();
1683 
1684     // Comparisons against 0 are common enough to deserve special attention.
1685     if (AdjustConstantForCondition(Int64ConstantFrom(right), &condition, &opposite) == 0) {
1686       switch (condition) {
1687         case kCondNE:
1688         case kCondA:
1689           if (out.IsLow()) {
1690             // We only care if both input registers are 0 or not.
1691             __ Orrs(out, LowRegisterFrom(left), HighRegisterFrom(left));
1692 
1693             // We use the scope because of the IT block that follows.
1694             ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
1695                                      2 * vixl32::k16BitT32InstructionSizeInBytes,
1696                                      CodeBufferCheckScope::kExactSize);
1697 
1698             __ it(ne);
1699             __ mov(ne, out, 1);
1700             return;
1701           }
1702 
1703           FALLTHROUGH_INTENDED;
1704         case kCondEQ:
1705         case kCondBE:
1706           // We only care if both input registers are 0 or not.
1707           __ Orr(out, LowRegisterFrom(left), HighRegisterFrom(left));
1708           codegen->GenerateConditionWithZero(condition, out, out);
1709           return;
1710         case kCondLT:
1711         case kCondGE:
1712           // We only care about the sign bit.
1713           FALLTHROUGH_INTENDED;
1714         case kCondAE:
1715         case kCondB:
1716           codegen->GenerateConditionWithZero(condition, out, HighRegisterFrom(left));
1717           return;
1718         case kCondLE:
1719         case kCondGT:
1720         default:
1721           break;
1722       }
1723     }
1724   }
1725 
1726   // If `out` is a low register, then the GenerateConditionGeneric()
1727   // function generates a shorter code sequence that is still branchless.
1728   if ((condition == kCondEQ || condition == kCondNE) && !out.IsLow()) {
1729     GenerateEqualLong(cond, codegen);
1730     return;
1731   }
1732 
1733   GenerateConditionGeneric(cond, codegen);
1734 }
1735 
GenerateConditionIntegralOrNonPrimitive(HCondition * cond,CodeGeneratorARMVIXL * codegen)1736 static void GenerateConditionIntegralOrNonPrimitive(HCondition* cond,
1737                                                     CodeGeneratorARMVIXL* codegen) {
1738   const DataType::Type type = cond->GetLeft()->GetType();
1739 
1740   DCHECK(DataType::IsIntegralType(type) || type == DataType::Type::kReference) << type;
1741 
1742   if (type == DataType::Type::kInt64) {
1743     GenerateConditionLong(cond, codegen);
1744     return;
1745   }
1746 
1747   IfCondition condition = cond->GetCondition();
1748   vixl32::Register in = InputRegisterAt(cond, 0);
1749   const vixl32::Register out = OutputRegister(cond);
1750   const Location right = cond->GetLocations()->InAt(1);
1751   int64_t value;
1752 
1753   if (right.IsConstant()) {
1754     IfCondition opposite = cond->GetOppositeCondition();
1755 
1756     value = AdjustConstantForCondition(Int64ConstantFrom(right), &condition, &opposite);
1757 
1758     // Comparisons against 0 are common enough to deserve special attention.
1759     if (value == 0) {
1760       switch (condition) {
1761         case kCondNE:
1762         case kCondA:
1763           if (out.IsLow() && out.Is(in)) {
1764             __ Cmp(out, 0);
1765 
1766             // We use the scope because of the IT block that follows.
1767             ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
1768                                      2 * vixl32::k16BitT32InstructionSizeInBytes,
1769                                      CodeBufferCheckScope::kExactSize);
1770 
1771             __ it(ne);
1772             __ mov(ne, out, 1);
1773             return;
1774           }
1775 
1776           FALLTHROUGH_INTENDED;
1777         case kCondEQ:
1778         case kCondBE:
1779         case kCondLT:
1780         case kCondGE:
1781         case kCondAE:
1782         case kCondB:
1783           codegen->GenerateConditionWithZero(condition, out, in);
1784           return;
1785         case kCondLE:
1786         case kCondGT:
1787         default:
1788           break;
1789       }
1790     }
1791   }
1792 
1793   if (condition == kCondEQ || condition == kCondNE) {
1794     Operand operand(0);
1795 
1796     if (right.IsConstant()) {
1797       operand = Operand::From(value);
1798     } else if (out.Is(RegisterFrom(right))) {
1799       // Avoid 32-bit instructions if possible.
1800       operand = InputOperandAt(cond, 0);
1801       in = RegisterFrom(right);
1802     } else {
1803       operand = InputOperandAt(cond, 1);
1804     }
1805 
1806     if (condition == kCondNE && out.IsLow()) {
1807       __ Subs(out, in, operand);
1808 
1809       // We use the scope because of the IT block that follows.
1810       ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
1811                                2 * vixl32::k16BitT32InstructionSizeInBytes,
1812                                CodeBufferCheckScope::kExactSize);
1813 
1814       __ it(ne);
1815       __ mov(ne, out, 1);
1816     } else {
1817       __ Sub(out, in, operand);
1818       codegen->GenerateConditionWithZero(condition, out, out);
1819     }
1820 
1821     return;
1822   }
1823 
1824   GenerateConditionGeneric(cond, codegen);
1825 }
1826 
CanEncodeConstantAs8BitImmediate(HConstant * constant)1827 static bool CanEncodeConstantAs8BitImmediate(HConstant* constant) {
1828   const DataType::Type type = constant->GetType();
1829   bool ret = false;
1830 
1831   DCHECK(DataType::IsIntegralType(type) || type == DataType::Type::kReference) << type;
1832 
1833   if (type == DataType::Type::kInt64) {
1834     const uint64_t value = Uint64ConstantFrom(constant);
1835 
1836     ret = IsUint<8>(Low32Bits(value)) && IsUint<8>(High32Bits(value));
1837   } else {
1838     ret = IsUint<8>(Int32ConstantFrom(constant));
1839   }
1840 
1841   return ret;
1842 }
1843 
Arm8BitEncodableConstantOrRegister(HInstruction * constant)1844 static Location Arm8BitEncodableConstantOrRegister(HInstruction* constant) {
1845   DCHECK(!DataType::IsFloatingPointType(constant->GetType()));
1846 
1847   if (constant->IsConstant() && CanEncodeConstantAs8BitImmediate(constant->AsConstant())) {
1848     return Location::ConstantLocation(constant->AsConstant());
1849   }
1850 
1851   return Location::RequiresRegister();
1852 }
1853 
CanGenerateConditionalMove(const Location & out,const Location & src)1854 static bool CanGenerateConditionalMove(const Location& out, const Location& src) {
1855   // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
1856   // we check that we are not dealing with floating-point output (there is no
1857   // 16-bit VMOV encoding).
1858   if (!out.IsRegister() && !out.IsRegisterPair()) {
1859     return false;
1860   }
1861 
1862   // For constants, we also check that the output is in one or two low registers,
1863   // and that the constants fit in an 8-bit unsigned integer, so that a 16-bit
1864   // MOV encoding can be used.
1865   if (src.IsConstant()) {
1866     if (!CanEncodeConstantAs8BitImmediate(src.GetConstant())) {
1867       return false;
1868     }
1869 
1870     if (out.IsRegister()) {
1871       if (!RegisterFrom(out).IsLow()) {
1872         return false;
1873       }
1874     } else {
1875       DCHECK(out.IsRegisterPair());
1876 
1877       if (!HighRegisterFrom(out).IsLow()) {
1878         return false;
1879       }
1880     }
1881   }
1882 
1883   return true;
1884 }
1885 
1886 #undef __
1887 
GetFinalLabel(HInstruction * instruction,vixl32::Label * final_label)1888 vixl32::Label* CodeGeneratorARMVIXL::GetFinalLabel(HInstruction* instruction,
1889                                                    vixl32::Label* final_label) {
1890   DCHECK(!instruction->IsControlFlow() && !instruction->IsSuspendCheck());
1891   DCHECK_IMPLIES(instruction->IsInvoke(), !instruction->GetLocations()->CanCall());
1892 
1893   const HBasicBlock* const block = instruction->GetBlock();
1894   const HLoopInformation* const info = block->GetLoopInformation();
1895   HInstruction* const next = instruction->GetNext();
1896 
1897   // Avoid a branch to a branch.
1898   if (next->IsGoto() && (info == nullptr ||
1899                          !info->IsBackEdge(*block) ||
1900                          !info->HasSuspendCheck())) {
1901     final_label = GetLabelOf(next->AsGoto()->GetSuccessor());
1902   }
1903 
1904   return final_label;
1905 }
1906 
CodeGeneratorARMVIXL(HGraph * graph,const CompilerOptions & compiler_options,OptimizingCompilerStats * stats)1907 CodeGeneratorARMVIXL::CodeGeneratorARMVIXL(HGraph* graph,
1908                                            const CompilerOptions& compiler_options,
1909                                            OptimizingCompilerStats* stats)
1910     : CodeGenerator(graph,
1911                     kNumberOfCoreRegisters,
1912                     kNumberOfSRegisters,
1913                     kNumberOfRegisterPairs,
1914                     kCoreCalleeSaves.GetList(),
1915                     ComputeSRegisterListMask(kFpuCalleeSaves),
1916                     compiler_options,
1917                     stats),
1918       block_labels_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1919       jump_tables_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1920       location_builder_(graph, this),
1921       instruction_visitor_(graph, this),
1922       move_resolver_(graph->GetAllocator(), this),
1923       assembler_(graph->GetAllocator()),
1924       boot_image_method_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1925       method_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1926       boot_image_type_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1927       type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1928       public_type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1929       package_type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1930       boot_image_string_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1931       string_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1932       boot_image_other_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1933       call_entrypoint_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1934       baker_read_barrier_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1935       uint32_literals_(std::less<uint32_t>(),
1936                        graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1937       jit_string_patches_(StringReferenceValueComparator(),
1938                           graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1939       jit_class_patches_(TypeReferenceValueComparator(),
1940                          graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1941       jit_baker_read_barrier_slow_paths_(std::less<uint32_t>(),
1942                                          graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)) {
1943   // Always save the LR register to mimic Quick.
1944   AddAllocatedRegister(Location::RegisterLocation(LR));
1945   // Give D30 and D31 as scratch register to VIXL. The register allocator only works on
1946   // S0-S31, which alias to D0-D15.
1947   GetVIXLAssembler()->GetScratchVRegisterList()->Combine(d31);
1948   GetVIXLAssembler()->GetScratchVRegisterList()->Combine(d30);
1949 }
1950 
EmitTable(CodeGeneratorARMVIXL * codegen)1951 void JumpTableARMVIXL::EmitTable(CodeGeneratorARMVIXL* codegen) {
1952   uint32_t num_entries = switch_instr_->GetNumEntries();
1953   DCHECK_GE(num_entries, kPackedSwitchCompareJumpThreshold);
1954 
1955   // We are about to use the assembler to place literals directly. Make sure we have enough
1956   // underlying code buffer and we have generated a jump table of the right size, using
1957   // codegen->GetVIXLAssembler()->GetBuffer().Align();
1958   ExactAssemblyScope aas(codegen->GetVIXLAssembler(),
1959                          num_entries * sizeof(int32_t),
1960                          CodeBufferCheckScope::kMaximumSize);
1961   // TODO(VIXL): Check that using lower case bind is fine here.
1962   codegen->GetVIXLAssembler()->bind(&table_start_);
1963   for (uint32_t i = 0; i < num_entries; i++) {
1964     codegen->GetVIXLAssembler()->place(bb_addresses_[i].get());
1965   }
1966 }
1967 
FixTable(CodeGeneratorARMVIXL * codegen)1968 void JumpTableARMVIXL::FixTable(CodeGeneratorARMVIXL* codegen) {
1969   uint32_t num_entries = switch_instr_->GetNumEntries();
1970   DCHECK_GE(num_entries, kPackedSwitchCompareJumpThreshold);
1971 
1972   const ArenaVector<HBasicBlock*>& successors = switch_instr_->GetBlock()->GetSuccessors();
1973   for (uint32_t i = 0; i < num_entries; i++) {
1974     vixl32::Label* target_label = codegen->GetLabelOf(successors[i]);
1975     DCHECK(target_label->IsBound());
1976     int32_t jump_offset = target_label->GetLocation() - table_start_.GetLocation();
1977     // When doing BX to address we need to have lower bit set to 1 in T32.
1978     if (codegen->GetVIXLAssembler()->IsUsingT32()) {
1979       jump_offset++;
1980     }
1981     DCHECK_GT(jump_offset, std::numeric_limits<int32_t>::min());
1982     DCHECK_LE(jump_offset, std::numeric_limits<int32_t>::max());
1983 
1984     bb_addresses_[i].get()->UpdateValue(jump_offset, codegen->GetVIXLAssembler()->GetBuffer());
1985   }
1986 }
1987 
FixJumpTables()1988 void CodeGeneratorARMVIXL::FixJumpTables() {
1989   for (auto&& jump_table : jump_tables_) {
1990     jump_table->FixTable(this);
1991   }
1992 }
1993 
1994 #define __ reinterpret_cast<ArmVIXLAssembler*>(GetAssembler())->GetVIXLAssembler()->  // NOLINT
1995 
Finalize(CodeAllocator * allocator)1996 void CodeGeneratorARMVIXL::Finalize(CodeAllocator* allocator) {
1997   FixJumpTables();
1998 
1999   // Emit JIT baker read barrier slow paths.
2000   DCHECK(GetCompilerOptions().IsJitCompiler() || jit_baker_read_barrier_slow_paths_.empty());
2001   for (auto& entry : jit_baker_read_barrier_slow_paths_) {
2002     uint32_t encoded_data = entry.first;
2003     vixl::aarch32::Label* slow_path_entry = &entry.second.label;
2004     __ Bind(slow_path_entry);
2005     CompileBakerReadBarrierThunk(*GetAssembler(), encoded_data, /* debug_name= */ nullptr);
2006   }
2007 
2008   GetAssembler()->FinalizeCode();
2009   CodeGenerator::Finalize(allocator);
2010 
2011   // Verify Baker read barrier linker patches.
2012   if (kIsDebugBuild) {
2013     ArrayRef<const uint8_t> code = allocator->GetMemory();
2014     for (const BakerReadBarrierPatchInfo& info : baker_read_barrier_patches_) {
2015       DCHECK(info.label.IsBound());
2016       uint32_t literal_offset = info.label.GetLocation();
2017       DCHECK_ALIGNED(literal_offset, 2u);
2018 
2019       auto GetInsn16 = [&code](uint32_t offset) {
2020         DCHECK_ALIGNED(offset, 2u);
2021         return (static_cast<uint32_t>(code[offset + 0]) << 0) +
2022                (static_cast<uint32_t>(code[offset + 1]) << 8);
2023       };
2024       auto GetInsn32 = [=](uint32_t offset) {
2025         return (GetInsn16(offset) << 16) + (GetInsn16(offset + 2u) << 0);
2026       };
2027 
2028       uint32_t encoded_data = info.custom_data;
2029       BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data);
2030       // Check that the next instruction matches the expected LDR.
2031       switch (kind) {
2032         case BakerReadBarrierKind::kField: {
2033           BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data);
2034           if (width == BakerReadBarrierWidth::kWide) {
2035             DCHECK_GE(code.size() - literal_offset, 8u);
2036             uint32_t next_insn = GetInsn32(literal_offset + 4u);
2037             // LDR (immediate), encoding T3, with correct base_reg.
2038             CheckValidReg((next_insn >> 12) & 0xfu);  // Check destination register.
2039             const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
2040             CHECK_EQ(next_insn & 0xffff0000u, 0xf8d00000u | (base_reg << 16));
2041           } else {
2042             DCHECK_GE(code.size() - literal_offset, 6u);
2043             uint32_t next_insn = GetInsn16(literal_offset + 4u);
2044             // LDR (immediate), encoding T1, with correct base_reg.
2045             CheckValidReg(next_insn & 0x7u);  // Check destination register.
2046             const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
2047             CHECK_EQ(next_insn & 0xf838u, 0x6800u | (base_reg << 3));
2048           }
2049           break;
2050         }
2051         case BakerReadBarrierKind::kArray: {
2052           DCHECK_GE(code.size() - literal_offset, 8u);
2053           uint32_t next_insn = GetInsn32(literal_offset + 4u);
2054           // LDR (register) with correct base_reg, S=1 and option=011 (LDR Wt, [Xn, Xm, LSL #2]).
2055           CheckValidReg((next_insn >> 12) & 0xfu);  // Check destination register.
2056           const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
2057           CHECK_EQ(next_insn & 0xffff0ff0u, 0xf8500020u | (base_reg << 16));
2058           CheckValidReg(next_insn & 0xf);  // Check index register
2059           break;
2060         }
2061         case BakerReadBarrierKind::kGcRoot: {
2062           BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data);
2063           if (width == BakerReadBarrierWidth::kWide) {
2064             DCHECK_GE(literal_offset, 4u);
2065             uint32_t prev_insn = GetInsn32(literal_offset - 4u);
2066             // LDR (immediate), encoding T3, with correct root_reg.
2067             const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
2068             CHECK_EQ(prev_insn & 0xfff0f000u, 0xf8d00000u | (root_reg << 12));
2069           } else {
2070             DCHECK_GE(literal_offset, 2u);
2071             uint32_t prev_insn = GetInsn16(literal_offset - 2u);
2072             const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
2073             // Usually LDR (immediate), encoding T1, with correct root_reg but we may have
2074             // a `MOV marked, old_value` for intrinsic CAS where `marked` is a low register.
2075             if ((prev_insn & 0xff87u) != (0x4600 | root_reg)) {
2076               CHECK_EQ(prev_insn & 0xf807u, 0x6800u | root_reg);
2077             }
2078           }
2079           break;
2080         }
2081         case BakerReadBarrierKind::kIntrinsicCas: {
2082           DCHECK_GE(literal_offset, 4u);
2083           uint32_t prev_insn = GetInsn32(literal_offset - 4u);
2084           // MOV (register), encoding T3, with correct root_reg.
2085           const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
2086           DCHECK_GE(root_reg, 8u);  // Used only for high registers.
2087           CHECK_EQ(prev_insn & 0xfffffff0u, 0xea4f0000u | (root_reg << 8));
2088           break;
2089         }
2090         default:
2091           LOG(FATAL) << "Unexpected kind: " << static_cast<uint32_t>(kind);
2092           UNREACHABLE();
2093       }
2094     }
2095   }
2096 }
2097 
SetupBlockedRegisters() const2098 void CodeGeneratorARMVIXL::SetupBlockedRegisters() const {
2099   // Stack register, LR and PC are always reserved.
2100   blocked_core_registers_[SP] = true;
2101   blocked_core_registers_[LR] = true;
2102   blocked_core_registers_[PC] = true;
2103 
2104   if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2105     // Reserve marking register.
2106     blocked_core_registers_[MR] = true;
2107   }
2108 
2109   // Reserve thread register.
2110   blocked_core_registers_[TR] = true;
2111 
2112   // Reserve temp register.
2113   blocked_core_registers_[IP] = true;
2114 
2115   if (GetGraph()->IsDebuggable()) {
2116     // Stubs do not save callee-save floating point registers. If the graph
2117     // is debuggable, we need to deal with these registers differently. For
2118     // now, just block them.
2119     for (uint32_t i = kFpuCalleeSaves.GetFirstSRegister().GetCode();
2120          i <= kFpuCalleeSaves.GetLastSRegister().GetCode();
2121          ++i) {
2122       blocked_fpu_registers_[i] = true;
2123     }
2124   }
2125 }
2126 
InstructionCodeGeneratorARMVIXL(HGraph * graph,CodeGeneratorARMVIXL * codegen)2127 InstructionCodeGeneratorARMVIXL::InstructionCodeGeneratorARMVIXL(HGraph* graph,
2128                                                                  CodeGeneratorARMVIXL* codegen)
2129       : InstructionCodeGenerator(graph, codegen),
2130         assembler_(codegen->GetAssembler()),
2131         codegen_(codegen) {}
2132 
ComputeSpillMask()2133 void CodeGeneratorARMVIXL::ComputeSpillMask() {
2134   core_spill_mask_ = allocated_registers_.GetCoreRegisters() & core_callee_save_mask_;
2135   DCHECK_NE(core_spill_mask_ & (1u << kLrCode), 0u)
2136       << "At least the return address register must be saved";
2137   // 16-bit PUSH/POP (T1) can save/restore just the LR/PC.
2138   DCHECK(GetVIXLAssembler()->IsUsingT32());
2139   fpu_spill_mask_ = allocated_registers_.GetFloatingPointRegisters() & fpu_callee_save_mask_;
2140   // We use vpush and vpop for saving and restoring floating point registers, which take
2141   // a SRegister and the number of registers to save/restore after that SRegister. We
2142   // therefore update the `fpu_spill_mask_` to also contain those registers not allocated,
2143   // but in the range.
2144   if (fpu_spill_mask_ != 0) {
2145     uint32_t least_significant_bit = LeastSignificantBit(fpu_spill_mask_);
2146     uint32_t most_significant_bit = MostSignificantBit(fpu_spill_mask_);
2147     for (uint32_t i = least_significant_bit + 1 ; i < most_significant_bit; ++i) {
2148       fpu_spill_mask_ |= (1 << i);
2149     }
2150   }
2151 }
2152 
VisitMethodExitHook(HMethodExitHook * method_hook)2153 void LocationsBuilderARMVIXL::VisitMethodExitHook(HMethodExitHook* method_hook) {
2154   LocationSummary* locations = new (GetGraph()->GetAllocator())
2155       LocationSummary(method_hook, LocationSummary::kCallOnSlowPath);
2156   locations->SetInAt(0, parameter_visitor_.GetReturnLocation(method_hook->InputAt(0)->GetType()));
2157 }
2158 
GenerateMethodEntryExitHook(HInstruction * instruction)2159 void InstructionCodeGeneratorARMVIXL::GenerateMethodEntryExitHook(HInstruction* instruction) {
2160   UseScratchRegisterScope temps(GetVIXLAssembler());
2161   vixl32::Register temp = temps.Acquire();
2162 
2163   SlowPathCodeARMVIXL* slow_path =
2164       new (codegen_->GetScopedAllocator()) MethodEntryExitHooksSlowPathARMVIXL(instruction);
2165   codegen_->AddSlowPath(slow_path);
2166 
2167   int offset = instrumentation::Instrumentation::NeedsEntryExitHooksOffset().Int32Value();
2168   uint32_t address = reinterpret_cast32<uint32_t>(Runtime::Current()->GetInstrumentation());
2169   __ Mov(temp, address + offset);
2170   __ Ldrb(temp, MemOperand(temp, 0));
2171   __ CompareAndBranchIfNonZero(temp, slow_path->GetEntryLabel());
2172   __ Bind(slow_path->GetExitLabel());
2173 }
2174 
VisitMethodExitHook(HMethodExitHook * instruction)2175 void InstructionCodeGeneratorARMVIXL::VisitMethodExitHook(HMethodExitHook* instruction) {
2176   DCHECK(codegen_->GetCompilerOptions().IsJitCompiler() && GetGraph()->IsDebuggable());
2177   DCHECK(codegen_->RequiresCurrentMethod());
2178   GenerateMethodEntryExitHook(instruction);
2179 }
2180 
VisitMethodEntryHook(HMethodEntryHook * method_hook)2181 void LocationsBuilderARMVIXL::VisitMethodEntryHook(HMethodEntryHook* method_hook) {
2182   new (GetGraph()->GetAllocator()) LocationSummary(method_hook, LocationSummary::kCallOnSlowPath);
2183 }
2184 
VisitMethodEntryHook(HMethodEntryHook * instruction)2185 void InstructionCodeGeneratorARMVIXL::VisitMethodEntryHook(HMethodEntryHook* instruction) {
2186   DCHECK(codegen_->GetCompilerOptions().IsJitCompiler() && GetGraph()->IsDebuggable());
2187   DCHECK(codegen_->RequiresCurrentMethod());
2188   GenerateMethodEntryExitHook(instruction);
2189 }
2190 
MaybeIncrementHotness(bool is_frame_entry)2191 void CodeGeneratorARMVIXL::MaybeIncrementHotness(bool is_frame_entry) {
2192   if (GetCompilerOptions().CountHotnessInCompiledCode()) {
2193     UseScratchRegisterScope temps(GetVIXLAssembler());
2194     vixl32::Register temp = temps.Acquire();
2195     static_assert(ArtMethod::MaxCounter() == 0xFFFF, "asm is probably wrong");
2196     if (!is_frame_entry) {
2197       __ Push(vixl32::Register(kMethodRegister));
2198       GetAssembler()->cfi().AdjustCFAOffset(kArmWordSize);
2199       GetAssembler()->LoadFromOffset(kLoadWord, kMethodRegister, sp, kArmWordSize);
2200     }
2201     // Load with zero extend to clear the high bits for integer overflow check.
2202     __ Ldrh(temp, MemOperand(kMethodRegister, ArtMethod::HotnessCountOffset().Int32Value()));
2203     vixl::aarch32::Label done;
2204     DCHECK_EQ(0u, interpreter::kNterpHotnessValue);
2205     __ CompareAndBranchIfZero(temp, &done, /* is_far_target= */ false);
2206     __ Add(temp, temp, -1);
2207     __ Strh(temp, MemOperand(kMethodRegister, ArtMethod::HotnessCountOffset().Int32Value()));
2208     __ Bind(&done);
2209     if (!is_frame_entry) {
2210       __ Pop(vixl32::Register(kMethodRegister));
2211       GetAssembler()->cfi().AdjustCFAOffset(-static_cast<int>(kArmWordSize));
2212     }
2213   }
2214 
2215   if (GetGraph()->IsCompilingBaseline() && !Runtime::Current()->IsAotCompiler()) {
2216     SlowPathCodeARMVIXL* slow_path = new (GetScopedAllocator()) CompileOptimizedSlowPathARMVIXL();
2217     AddSlowPath(slow_path);
2218     ProfilingInfo* info = GetGraph()->GetProfilingInfo();
2219     DCHECK(info != nullptr);
2220     DCHECK(!HasEmptyFrame());
2221     uint32_t address = reinterpret_cast32<uint32_t>(info);
2222     UseScratchRegisterScope temps(GetVIXLAssembler());
2223     vixl32::Register tmp = temps.Acquire();
2224     __ Mov(lr, address);
2225     __ Ldrh(tmp, MemOperand(lr, ProfilingInfo::BaselineHotnessCountOffset().Int32Value()));
2226     __ Adds(tmp, tmp, -1);
2227     __ B(cc, slow_path->GetEntryLabel());
2228     __ Strh(tmp, MemOperand(lr, ProfilingInfo::BaselineHotnessCountOffset().Int32Value()));
2229     __ Bind(slow_path->GetExitLabel());
2230   }
2231 }
2232 
GenerateFrameEntry()2233 void CodeGeneratorARMVIXL::GenerateFrameEntry() {
2234   bool skip_overflow_check =
2235       IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kArm);
2236   DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks());
2237   __ Bind(&frame_entry_label_);
2238 
2239   if (HasEmptyFrame()) {
2240     // Ensure that the CFI opcode list is not empty.
2241     GetAssembler()->cfi().Nop();
2242     MaybeIncrementHotness(/* is_frame_entry= */ true);
2243     return;
2244   }
2245 
2246   if (!skip_overflow_check) {
2247     // Using r4 instead of IP saves 2 bytes.
2248     UseScratchRegisterScope temps(GetVIXLAssembler());
2249     vixl32::Register temp;
2250     // TODO: Remove this check when R4 is made a callee-save register
2251     // in ART compiled code (b/72801708). Currently we need to make
2252     // sure r4 is not blocked, e.g. in special purpose
2253     // TestCodeGeneratorARMVIXL; also asserting that r4 is available
2254     // here.
2255     if (!blocked_core_registers_[R4]) {
2256       for (vixl32::Register reg : kParameterCoreRegistersVIXL) {
2257         DCHECK(!reg.Is(r4));
2258       }
2259       DCHECK(!kCoreCalleeSaves.Includes(r4));
2260       temp = r4;
2261     } else {
2262       temp = temps.Acquire();
2263     }
2264     __ Sub(temp, sp, Operand::From(GetStackOverflowReservedBytes(InstructionSet::kArm)));
2265     // The load must immediately precede RecordPcInfo.
2266     ExactAssemblyScope aas(GetVIXLAssembler(),
2267                            vixl32::kMaxInstructionSizeInBytes,
2268                            CodeBufferCheckScope::kMaximumSize);
2269     __ ldr(temp, MemOperand(temp));
2270     RecordPcInfo(nullptr, 0);
2271   }
2272 
2273   uint32_t frame_size = GetFrameSize();
2274   uint32_t core_spills_offset = frame_size - GetCoreSpillSize();
2275   uint32_t fp_spills_offset = frame_size - FrameEntrySpillSize();
2276   if ((fpu_spill_mask_ == 0u || IsPowerOfTwo(fpu_spill_mask_)) &&
2277       core_spills_offset <= 3u * kArmWordSize) {
2278     // Do a single PUSH for core registers including the method and up to two
2279     // filler registers. Then store the single FP spill if any.
2280     // (The worst case is when the method is not required and we actually
2281     // store 3 extra registers but they are stored in the same properly
2282     // aligned 16-byte chunk where we're already writing anyway.)
2283     DCHECK_EQ(kMethodRegister.GetCode(), 0u);
2284     uint32_t extra_regs = MaxInt<uint32_t>(core_spills_offset / kArmWordSize);
2285     DCHECK_LT(MostSignificantBit(extra_regs), LeastSignificantBit(core_spill_mask_));
2286     __ Push(RegisterList(core_spill_mask_ | extra_regs));
2287     GetAssembler()->cfi().AdjustCFAOffset(frame_size);
2288     GetAssembler()->cfi().RelOffsetForMany(DWARFReg(kMethodRegister),
2289                                            core_spills_offset,
2290                                            core_spill_mask_,
2291                                            kArmWordSize);
2292     if (fpu_spill_mask_ != 0u) {
2293       DCHECK(IsPowerOfTwo(fpu_spill_mask_));
2294       vixl::aarch32::SRegister sreg(LeastSignificantBit(fpu_spill_mask_));
2295       GetAssembler()->StoreSToOffset(sreg, sp, fp_spills_offset);
2296       GetAssembler()->cfi().RelOffset(DWARFReg(sreg), /*offset=*/ fp_spills_offset);
2297     }
2298   } else {
2299     __ Push(RegisterList(core_spill_mask_));
2300     GetAssembler()->cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(core_spill_mask_));
2301     GetAssembler()->cfi().RelOffsetForMany(DWARFReg(kMethodRegister),
2302                                            /*offset=*/ 0,
2303                                            core_spill_mask_,
2304                                            kArmWordSize);
2305     if (fpu_spill_mask_ != 0) {
2306       uint32_t first = LeastSignificantBit(fpu_spill_mask_);
2307 
2308       // Check that list is contiguous.
2309       DCHECK_EQ(fpu_spill_mask_ >> CTZ(fpu_spill_mask_), ~0u >> (32 - POPCOUNT(fpu_spill_mask_)));
2310 
2311       __ Vpush(SRegisterList(vixl32::SRegister(first), POPCOUNT(fpu_spill_mask_)));
2312       GetAssembler()->cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(fpu_spill_mask_));
2313       GetAssembler()->cfi().RelOffsetForMany(DWARFReg(s0),
2314                                              /*offset=*/ 0,
2315                                              fpu_spill_mask_,
2316                                              kArmWordSize);
2317     }
2318 
2319     // Adjust SP and save the current method if we need it. Note that we do
2320     // not save the method in HCurrentMethod, as the instruction might have
2321     // been removed in the SSA graph.
2322     if (RequiresCurrentMethod() && fp_spills_offset <= 3 * kArmWordSize) {
2323       DCHECK_EQ(kMethodRegister.GetCode(), 0u);
2324       __ Push(RegisterList(MaxInt<uint32_t>(fp_spills_offset / kArmWordSize)));
2325       GetAssembler()->cfi().AdjustCFAOffset(fp_spills_offset);
2326     } else {
2327       IncreaseFrame(fp_spills_offset);
2328       if (RequiresCurrentMethod()) {
2329         GetAssembler()->StoreToOffset(kStoreWord, kMethodRegister, sp, 0);
2330       }
2331     }
2332   }
2333 
2334   if (GetGraph()->HasShouldDeoptimizeFlag()) {
2335     UseScratchRegisterScope temps(GetVIXLAssembler());
2336     vixl32::Register temp = temps.Acquire();
2337     // Initialize should_deoptimize flag to 0.
2338     __ Mov(temp, 0);
2339     GetAssembler()->StoreToOffset(kStoreWord, temp, sp, GetStackOffsetOfShouldDeoptimizeFlag());
2340   }
2341 
2342   MaybeIncrementHotness(/* is_frame_entry= */ true);
2343   MaybeGenerateMarkingRegisterCheck(/* code= */ 1);
2344 }
2345 
GenerateFrameExit()2346 void CodeGeneratorARMVIXL::GenerateFrameExit() {
2347   if (HasEmptyFrame()) {
2348     __ Bx(lr);
2349     return;
2350   }
2351 
2352   // Pop LR into PC to return.
2353   DCHECK_NE(core_spill_mask_ & (1 << kLrCode), 0U);
2354   uint32_t pop_mask = (core_spill_mask_ & (~(1 << kLrCode))) | 1 << kPcCode;
2355 
2356   uint32_t frame_size = GetFrameSize();
2357   uint32_t core_spills_offset = frame_size - GetCoreSpillSize();
2358   uint32_t fp_spills_offset = frame_size - FrameEntrySpillSize();
2359   if ((fpu_spill_mask_ == 0u || IsPowerOfTwo(fpu_spill_mask_)) &&
2360       // r4 is blocked by TestCodeGeneratorARMVIXL used by some tests.
2361       core_spills_offset <= (blocked_core_registers_[r4.GetCode()] ? 2u : 3u) * kArmWordSize) {
2362     // Load the FP spill if any and then do a single POP including the method
2363     // and up to two filler registers. If we have no FP spills, this also has
2364     // the advantage that we do not need to emit CFI directives.
2365     if (fpu_spill_mask_ != 0u) {
2366       DCHECK(IsPowerOfTwo(fpu_spill_mask_));
2367       vixl::aarch32::SRegister sreg(LeastSignificantBit(fpu_spill_mask_));
2368       GetAssembler()->cfi().RememberState();
2369       GetAssembler()->LoadSFromOffset(sreg, sp, fp_spills_offset);
2370       GetAssembler()->cfi().Restore(DWARFReg(sreg));
2371     }
2372     // Clobber registers r2-r4 as they are caller-save in ART managed ABI and
2373     // never hold the return value.
2374     uint32_t extra_regs = MaxInt<uint32_t>(core_spills_offset / kArmWordSize) << r2.GetCode();
2375     DCHECK_EQ(extra_regs & kCoreCalleeSaves.GetList(), 0u);
2376     DCHECK_LT(MostSignificantBit(extra_regs), LeastSignificantBit(pop_mask));
2377     __ Pop(RegisterList(pop_mask | extra_regs));
2378     if (fpu_spill_mask_ != 0u) {
2379       GetAssembler()->cfi().RestoreState();
2380     }
2381   } else {
2382     GetAssembler()->cfi().RememberState();
2383     DecreaseFrame(fp_spills_offset);
2384     if (fpu_spill_mask_ != 0) {
2385       uint32_t first = LeastSignificantBit(fpu_spill_mask_);
2386 
2387       // Check that list is contiguous.
2388       DCHECK_EQ(fpu_spill_mask_ >> CTZ(fpu_spill_mask_), ~0u >> (32 - POPCOUNT(fpu_spill_mask_)));
2389 
2390       __ Vpop(SRegisterList(vixl32::SRegister(first), POPCOUNT(fpu_spill_mask_)));
2391       GetAssembler()->cfi().AdjustCFAOffset(
2392           -static_cast<int>(kArmWordSize) * POPCOUNT(fpu_spill_mask_));
2393       GetAssembler()->cfi().RestoreMany(DWARFReg(vixl32::SRegister(0)), fpu_spill_mask_);
2394     }
2395     __ Pop(RegisterList(pop_mask));
2396     GetAssembler()->cfi().RestoreState();
2397     GetAssembler()->cfi().DefCFAOffset(GetFrameSize());
2398   }
2399 }
2400 
Bind(HBasicBlock * block)2401 void CodeGeneratorARMVIXL::Bind(HBasicBlock* block) {
2402   __ Bind(GetLabelOf(block));
2403 }
2404 
GetNextLocation(DataType::Type type)2405 Location InvokeDexCallingConventionVisitorARMVIXL::GetNextLocation(DataType::Type type) {
2406   switch (type) {
2407     case DataType::Type::kReference:
2408     case DataType::Type::kBool:
2409     case DataType::Type::kUint8:
2410     case DataType::Type::kInt8:
2411     case DataType::Type::kUint16:
2412     case DataType::Type::kInt16:
2413     case DataType::Type::kInt32: {
2414       uint32_t index = gp_index_++;
2415       uint32_t stack_index = stack_index_++;
2416       if (index < calling_convention.GetNumberOfRegisters()) {
2417         return LocationFrom(calling_convention.GetRegisterAt(index));
2418       } else {
2419         return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index));
2420       }
2421     }
2422 
2423     case DataType::Type::kInt64: {
2424       uint32_t index = gp_index_;
2425       uint32_t stack_index = stack_index_;
2426       gp_index_ += 2;
2427       stack_index_ += 2;
2428       if (index + 1 < calling_convention.GetNumberOfRegisters()) {
2429         if (calling_convention.GetRegisterAt(index).Is(r1)) {
2430           // Skip R1, and use R2_R3 instead.
2431           gp_index_++;
2432           index++;
2433         }
2434       }
2435       if (index + 1 < calling_convention.GetNumberOfRegisters()) {
2436         DCHECK_EQ(calling_convention.GetRegisterAt(index).GetCode() + 1,
2437                   calling_convention.GetRegisterAt(index + 1).GetCode());
2438 
2439         return LocationFrom(calling_convention.GetRegisterAt(index),
2440                             calling_convention.GetRegisterAt(index + 1));
2441       } else {
2442         return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index));
2443       }
2444     }
2445 
2446     case DataType::Type::kFloat32: {
2447       uint32_t stack_index = stack_index_++;
2448       if (float_index_ % 2 == 0) {
2449         float_index_ = std::max(double_index_, float_index_);
2450       }
2451       if (float_index_ < calling_convention.GetNumberOfFpuRegisters()) {
2452         return LocationFrom(calling_convention.GetFpuRegisterAt(float_index_++));
2453       } else {
2454         return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index));
2455       }
2456     }
2457 
2458     case DataType::Type::kFloat64: {
2459       double_index_ = std::max(double_index_, RoundUp(float_index_, 2));
2460       uint32_t stack_index = stack_index_;
2461       stack_index_ += 2;
2462       if (double_index_ + 1 < calling_convention.GetNumberOfFpuRegisters()) {
2463         uint32_t index = double_index_;
2464         double_index_ += 2;
2465         Location result = LocationFrom(
2466           calling_convention.GetFpuRegisterAt(index),
2467           calling_convention.GetFpuRegisterAt(index + 1));
2468         DCHECK(ExpectedPairLayout(result));
2469         return result;
2470       } else {
2471         return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index));
2472       }
2473     }
2474 
2475     case DataType::Type::kUint32:
2476     case DataType::Type::kUint64:
2477     case DataType::Type::kVoid:
2478       LOG(FATAL) << "Unexpected parameter type " << type;
2479       UNREACHABLE();
2480   }
2481   return Location::NoLocation();
2482 }
2483 
GetReturnLocation(DataType::Type type) const2484 Location InvokeDexCallingConventionVisitorARMVIXL::GetReturnLocation(DataType::Type type) const {
2485   switch (type) {
2486     case DataType::Type::kReference:
2487     case DataType::Type::kBool:
2488     case DataType::Type::kUint8:
2489     case DataType::Type::kInt8:
2490     case DataType::Type::kUint16:
2491     case DataType::Type::kInt16:
2492     case DataType::Type::kUint32:
2493     case DataType::Type::kInt32: {
2494       return LocationFrom(r0);
2495     }
2496 
2497     case DataType::Type::kFloat32: {
2498       return LocationFrom(s0);
2499     }
2500 
2501     case DataType::Type::kUint64:
2502     case DataType::Type::kInt64: {
2503       return LocationFrom(r0, r1);
2504     }
2505 
2506     case DataType::Type::kFloat64: {
2507       return LocationFrom(s0, s1);
2508     }
2509 
2510     case DataType::Type::kVoid:
2511       return Location::NoLocation();
2512   }
2513 
2514   UNREACHABLE();
2515 }
2516 
GetMethodLocation() const2517 Location InvokeDexCallingConventionVisitorARMVIXL::GetMethodLocation() const {
2518   return LocationFrom(kMethodRegister);
2519 }
2520 
GetNextLocation(DataType::Type type)2521 Location CriticalNativeCallingConventionVisitorARMVIXL::GetNextLocation(DataType::Type type) {
2522   DCHECK_NE(type, DataType::Type::kReference);
2523 
2524   // Native ABI uses the same registers as managed, except that the method register r0
2525   // is a normal argument.
2526   Location location = Location::NoLocation();
2527   if (DataType::Is64BitType(type)) {
2528     gpr_index_ = RoundUp(gpr_index_, 2u);
2529     stack_offset_ = RoundUp(stack_offset_, 2 * kFramePointerSize);
2530     if (gpr_index_ < 1u + kParameterCoreRegistersLengthVIXL) {
2531       location = LocationFrom(gpr_index_ == 0u ? r0 : kParameterCoreRegistersVIXL[gpr_index_ - 1u],
2532                               kParameterCoreRegistersVIXL[gpr_index_]);
2533       gpr_index_ += 2u;
2534     }
2535   } else {
2536     if (gpr_index_ < 1u + kParameterCoreRegistersLengthVIXL) {
2537       location = LocationFrom(gpr_index_ == 0u ? r0 : kParameterCoreRegistersVIXL[gpr_index_ - 1u]);
2538       ++gpr_index_;
2539     }
2540   }
2541   if (location.IsInvalid()) {
2542     if (DataType::Is64BitType(type)) {
2543       location = Location::DoubleStackSlot(stack_offset_);
2544       stack_offset_ += 2 * kFramePointerSize;
2545     } else {
2546       location = Location::StackSlot(stack_offset_);
2547       stack_offset_ += kFramePointerSize;
2548     }
2549 
2550     if (for_register_allocation_) {
2551       location = Location::Any();
2552     }
2553   }
2554   return location;
2555 }
2556 
GetReturnLocation(DataType::Type type) const2557 Location CriticalNativeCallingConventionVisitorARMVIXL::GetReturnLocation(DataType::Type type)
2558     const {
2559   // We perform conversion to the managed ABI return register after the call if needed.
2560   InvokeDexCallingConventionVisitorARMVIXL dex_calling_convention;
2561   return dex_calling_convention.GetReturnLocation(type);
2562 }
2563 
GetMethodLocation() const2564 Location CriticalNativeCallingConventionVisitorARMVIXL::GetMethodLocation() const {
2565   // Pass the method in the hidden argument R4.
2566   return Location::RegisterLocation(R4);
2567 }
2568 
Move32(Location destination,Location source)2569 void CodeGeneratorARMVIXL::Move32(Location destination, Location source) {
2570   if (source.Equals(destination)) {
2571     return;
2572   }
2573   if (destination.IsRegister()) {
2574     if (source.IsRegister()) {
2575       __ Mov(RegisterFrom(destination), RegisterFrom(source));
2576     } else if (source.IsFpuRegister()) {
2577       __ Vmov(RegisterFrom(destination), SRegisterFrom(source));
2578     } else {
2579       GetAssembler()->LoadFromOffset(kLoadWord,
2580                                      RegisterFrom(destination),
2581                                      sp,
2582                                      source.GetStackIndex());
2583     }
2584   } else if (destination.IsFpuRegister()) {
2585     if (source.IsRegister()) {
2586       __ Vmov(SRegisterFrom(destination), RegisterFrom(source));
2587     } else if (source.IsFpuRegister()) {
2588       __ Vmov(SRegisterFrom(destination), SRegisterFrom(source));
2589     } else {
2590       GetAssembler()->LoadSFromOffset(SRegisterFrom(destination), sp, source.GetStackIndex());
2591     }
2592   } else {
2593     DCHECK(destination.IsStackSlot()) << destination;
2594     if (source.IsRegister()) {
2595       GetAssembler()->StoreToOffset(kStoreWord,
2596                                     RegisterFrom(source),
2597                                     sp,
2598                                     destination.GetStackIndex());
2599     } else if (source.IsFpuRegister()) {
2600       GetAssembler()->StoreSToOffset(SRegisterFrom(source), sp, destination.GetStackIndex());
2601     } else {
2602       DCHECK(source.IsStackSlot()) << source;
2603       UseScratchRegisterScope temps(GetVIXLAssembler());
2604       vixl32::Register temp = temps.Acquire();
2605       GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, source.GetStackIndex());
2606       GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
2607     }
2608   }
2609 }
2610 
MoveConstant(Location location,int32_t value)2611 void CodeGeneratorARMVIXL::MoveConstant(Location location, int32_t value) {
2612   DCHECK(location.IsRegister());
2613   __ Mov(RegisterFrom(location), value);
2614 }
2615 
MoveLocation(Location dst,Location src,DataType::Type dst_type)2616 void CodeGeneratorARMVIXL::MoveLocation(Location dst, Location src, DataType::Type dst_type) {
2617   // TODO(VIXL): Maybe refactor to have the 'move' implementation here and use it in
2618   // `ParallelMoveResolverARMVIXL::EmitMove`, as is done in the `arm64` backend.
2619   HParallelMove move(GetGraph()->GetAllocator());
2620   move.AddMove(src, dst, dst_type, nullptr);
2621   GetMoveResolver()->EmitNativeCode(&move);
2622 }
2623 
AddLocationAsTemp(Location location,LocationSummary * locations)2624 void CodeGeneratorARMVIXL::AddLocationAsTemp(Location location, LocationSummary* locations) {
2625   if (location.IsRegister()) {
2626     locations->AddTemp(location);
2627   } else if (location.IsRegisterPair()) {
2628     locations->AddTemp(LocationFrom(LowRegisterFrom(location)));
2629     locations->AddTemp(LocationFrom(HighRegisterFrom(location)));
2630   } else {
2631     UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location;
2632   }
2633 }
2634 
InvokeRuntime(QuickEntrypointEnum entrypoint,HInstruction * instruction,uint32_t dex_pc,SlowPathCode * slow_path)2635 void CodeGeneratorARMVIXL::InvokeRuntime(QuickEntrypointEnum entrypoint,
2636                                          HInstruction* instruction,
2637                                          uint32_t dex_pc,
2638                                          SlowPathCode* slow_path) {
2639   ValidateInvokeRuntime(entrypoint, instruction, slow_path);
2640 
2641   ThreadOffset32 entrypoint_offset = GetThreadOffset<kArmPointerSize>(entrypoint);
2642   // Reduce code size for AOT by using shared trampolines for slow path runtime calls across the
2643   // entire oat file. This adds an extra branch and we do not want to slow down the main path.
2644   // For JIT, thunk sharing is per-method, so the gains would be smaller or even negative.
2645   if (slow_path == nullptr || GetCompilerOptions().IsJitCompiler()) {
2646     __ Ldr(lr, MemOperand(tr, entrypoint_offset.Int32Value()));
2647     // Ensure the pc position is recorded immediately after the `blx` instruction.
2648     // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
2649     ExactAssemblyScope aas(GetVIXLAssembler(),
2650                            vixl32::k16BitT32InstructionSizeInBytes,
2651                            CodeBufferCheckScope::kExactSize);
2652     __ blx(lr);
2653     if (EntrypointRequiresStackMap(entrypoint)) {
2654       RecordPcInfo(instruction, dex_pc, slow_path);
2655     }
2656   } else {
2657     // Ensure the pc position is recorded immediately after the `bl` instruction.
2658     ExactAssemblyScope aas(GetVIXLAssembler(),
2659                            vixl32::k32BitT32InstructionSizeInBytes,
2660                            CodeBufferCheckScope::kExactSize);
2661     EmitEntrypointThunkCall(entrypoint_offset);
2662     if (EntrypointRequiresStackMap(entrypoint)) {
2663       RecordPcInfo(instruction, dex_pc, slow_path);
2664     }
2665   }
2666 }
2667 
InvokeRuntimeWithoutRecordingPcInfo(int32_t entry_point_offset,HInstruction * instruction,SlowPathCode * slow_path)2668 void CodeGeneratorARMVIXL::InvokeRuntimeWithoutRecordingPcInfo(int32_t entry_point_offset,
2669                                                                HInstruction* instruction,
2670                                                                SlowPathCode* slow_path) {
2671   ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction, slow_path);
2672   __ Ldr(lr, MemOperand(tr, entry_point_offset));
2673   __ Blx(lr);
2674 }
2675 
HandleGoto(HInstruction * got,HBasicBlock * successor)2676 void InstructionCodeGeneratorARMVIXL::HandleGoto(HInstruction* got, HBasicBlock* successor) {
2677   if (successor->IsExitBlock()) {
2678     DCHECK(got->GetPrevious()->AlwaysThrows());
2679     return;  // no code needed
2680   }
2681 
2682   HBasicBlock* block = got->GetBlock();
2683   HInstruction* previous = got->GetPrevious();
2684   HLoopInformation* info = block->GetLoopInformation();
2685 
2686   if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) {
2687     codegen_->MaybeIncrementHotness(/* is_frame_entry= */ false);
2688     GenerateSuspendCheck(info->GetSuspendCheck(), successor);
2689     return;
2690   }
2691   if (block->IsEntryBlock() && (previous != nullptr) && previous->IsSuspendCheck()) {
2692     GenerateSuspendCheck(previous->AsSuspendCheck(), nullptr);
2693     codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 2);
2694   }
2695   if (!codegen_->GoesToNextBlock(block, successor)) {
2696     __ B(codegen_->GetLabelOf(successor));
2697   }
2698 }
2699 
VisitGoto(HGoto * got)2700 void LocationsBuilderARMVIXL::VisitGoto(HGoto* got) {
2701   got->SetLocations(nullptr);
2702 }
2703 
VisitGoto(HGoto * got)2704 void InstructionCodeGeneratorARMVIXL::VisitGoto(HGoto* got) {
2705   HandleGoto(got, got->GetSuccessor());
2706 }
2707 
VisitTryBoundary(HTryBoundary * try_boundary)2708 void LocationsBuilderARMVIXL::VisitTryBoundary(HTryBoundary* try_boundary) {
2709   try_boundary->SetLocations(nullptr);
2710 }
2711 
VisitTryBoundary(HTryBoundary * try_boundary)2712 void InstructionCodeGeneratorARMVIXL::VisitTryBoundary(HTryBoundary* try_boundary) {
2713   HBasicBlock* successor = try_boundary->GetNormalFlowSuccessor();
2714   if (!successor->IsExitBlock()) {
2715     HandleGoto(try_boundary, successor);
2716   }
2717 }
2718 
VisitExit(HExit * exit)2719 void LocationsBuilderARMVIXL::VisitExit(HExit* exit) {
2720   exit->SetLocations(nullptr);
2721 }
2722 
VisitExit(HExit * exit ATTRIBUTE_UNUSED)2723 void InstructionCodeGeneratorARMVIXL::VisitExit(HExit* exit ATTRIBUTE_UNUSED) {
2724 }
2725 
GenerateCompareTestAndBranch(HCondition * condition,vixl32::Label * true_target,vixl32::Label * false_target,bool is_far_target)2726 void InstructionCodeGeneratorARMVIXL::GenerateCompareTestAndBranch(HCondition* condition,
2727                                                                    vixl32::Label* true_target,
2728                                                                    vixl32::Label* false_target,
2729                                                                    bool is_far_target) {
2730   if (true_target == false_target) {
2731     DCHECK(true_target != nullptr);
2732     __ B(true_target);
2733     return;
2734   }
2735 
2736   vixl32::Label* non_fallthrough_target;
2737   bool invert;
2738   bool emit_both_branches;
2739 
2740   if (true_target == nullptr) {
2741     // The true target is fallthrough.
2742     DCHECK(false_target != nullptr);
2743     non_fallthrough_target = false_target;
2744     invert = true;
2745     emit_both_branches = false;
2746   } else {
2747     non_fallthrough_target = true_target;
2748     invert = false;
2749     // Either the false target is fallthrough, or there is no fallthrough
2750     // and both branches must be emitted.
2751     emit_both_branches = (false_target != nullptr);
2752   }
2753 
2754   const auto cond = GenerateTest(condition, invert, codegen_);
2755 
2756   __ B(cond.first, non_fallthrough_target, is_far_target);
2757 
2758   if (emit_both_branches) {
2759     // No target falls through, we need to branch.
2760     __ B(false_target);
2761   }
2762 }
2763 
GenerateTestAndBranch(HInstruction * instruction,size_t condition_input_index,vixl32::Label * true_target,vixl32::Label * false_target,bool far_target)2764 void InstructionCodeGeneratorARMVIXL::GenerateTestAndBranch(HInstruction* instruction,
2765                                                             size_t condition_input_index,
2766                                                             vixl32::Label* true_target,
2767                                                             vixl32::Label* false_target,
2768                                                             bool far_target) {
2769   HInstruction* cond = instruction->InputAt(condition_input_index);
2770 
2771   if (true_target == nullptr && false_target == nullptr) {
2772     // Nothing to do. The code always falls through.
2773     return;
2774   } else if (cond->IsIntConstant()) {
2775     // Constant condition, statically compared against "true" (integer value 1).
2776     if (cond->AsIntConstant()->IsTrue()) {
2777       if (true_target != nullptr) {
2778         __ B(true_target);
2779       }
2780     } else {
2781       DCHECK(cond->AsIntConstant()->IsFalse()) << Int32ConstantFrom(cond);
2782       if (false_target != nullptr) {
2783         __ B(false_target);
2784       }
2785     }
2786     return;
2787   }
2788 
2789   // The following code generates these patterns:
2790   //  (1) true_target == nullptr && false_target != nullptr
2791   //        - opposite condition true => branch to false_target
2792   //  (2) true_target != nullptr && false_target == nullptr
2793   //        - condition true => branch to true_target
2794   //  (3) true_target != nullptr && false_target != nullptr
2795   //        - condition true => branch to true_target
2796   //        - branch to false_target
2797   if (IsBooleanValueOrMaterializedCondition(cond)) {
2798     // Condition has been materialized, compare the output to 0.
2799     if (kIsDebugBuild) {
2800       Location cond_val = instruction->GetLocations()->InAt(condition_input_index);
2801       DCHECK(cond_val.IsRegister());
2802     }
2803     if (true_target == nullptr) {
2804       __ CompareAndBranchIfZero(InputRegisterAt(instruction, condition_input_index),
2805                                 false_target,
2806                                 far_target);
2807     } else {
2808       __ CompareAndBranchIfNonZero(InputRegisterAt(instruction, condition_input_index),
2809                                    true_target,
2810                                    far_target);
2811     }
2812   } else {
2813     // Condition has not been materialized. Use its inputs as the comparison and
2814     // its condition as the branch condition.
2815     HCondition* condition = cond->AsCondition();
2816 
2817     // If this is a long or FP comparison that has been folded into
2818     // the HCondition, generate the comparison directly.
2819     DataType::Type type = condition->InputAt(0)->GetType();
2820     if (type == DataType::Type::kInt64 || DataType::IsFloatingPointType(type)) {
2821       GenerateCompareTestAndBranch(condition, true_target, false_target, far_target);
2822       return;
2823     }
2824 
2825     vixl32::Label* non_fallthrough_target;
2826     vixl32::Condition arm_cond = vixl32::Condition::None();
2827     const vixl32::Register left = InputRegisterAt(cond, 0);
2828     const Operand right = InputOperandAt(cond, 1);
2829 
2830     if (true_target == nullptr) {
2831       arm_cond = ARMCondition(condition->GetOppositeCondition());
2832       non_fallthrough_target = false_target;
2833     } else {
2834       arm_cond = ARMCondition(condition->GetCondition());
2835       non_fallthrough_target = true_target;
2836     }
2837 
2838     if (right.IsImmediate() && right.GetImmediate() == 0 && (arm_cond.Is(ne) || arm_cond.Is(eq))) {
2839       if (arm_cond.Is(eq)) {
2840         __ CompareAndBranchIfZero(left, non_fallthrough_target, far_target);
2841       } else {
2842         DCHECK(arm_cond.Is(ne));
2843         __ CompareAndBranchIfNonZero(left, non_fallthrough_target, far_target);
2844       }
2845     } else {
2846       __ Cmp(left, right);
2847       __ B(arm_cond, non_fallthrough_target, far_target);
2848     }
2849   }
2850 
2851   // If neither branch falls through (case 3), the conditional branch to `true_target`
2852   // was already emitted (case 2) and we need to emit a jump to `false_target`.
2853   if (true_target != nullptr && false_target != nullptr) {
2854     __ B(false_target);
2855   }
2856 }
2857 
VisitIf(HIf * if_instr)2858 void LocationsBuilderARMVIXL::VisitIf(HIf* if_instr) {
2859   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(if_instr);
2860   if (IsBooleanValueOrMaterializedCondition(if_instr->InputAt(0))) {
2861     locations->SetInAt(0, Location::RequiresRegister());
2862   }
2863 }
2864 
VisitIf(HIf * if_instr)2865 void InstructionCodeGeneratorARMVIXL::VisitIf(HIf* if_instr) {
2866   HBasicBlock* true_successor = if_instr->IfTrueSuccessor();
2867   HBasicBlock* false_successor = if_instr->IfFalseSuccessor();
2868   vixl32::Label* true_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), true_successor) ?
2869       nullptr : codegen_->GetLabelOf(true_successor);
2870   vixl32::Label* false_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), false_successor) ?
2871       nullptr : codegen_->GetLabelOf(false_successor);
2872   GenerateTestAndBranch(if_instr, /* condition_input_index= */ 0, true_target, false_target);
2873 }
2874 
VisitDeoptimize(HDeoptimize * deoptimize)2875 void LocationsBuilderARMVIXL::VisitDeoptimize(HDeoptimize* deoptimize) {
2876   LocationSummary* locations = new (GetGraph()->GetAllocator())
2877       LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath);
2878   InvokeRuntimeCallingConventionARMVIXL calling_convention;
2879   RegisterSet caller_saves = RegisterSet::Empty();
2880   caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0)));
2881   locations->SetCustomSlowPathCallerSaves(caller_saves);
2882   if (IsBooleanValueOrMaterializedCondition(deoptimize->InputAt(0))) {
2883     locations->SetInAt(0, Location::RequiresRegister());
2884   }
2885 }
2886 
VisitDeoptimize(HDeoptimize * deoptimize)2887 void InstructionCodeGeneratorARMVIXL::VisitDeoptimize(HDeoptimize* deoptimize) {
2888   SlowPathCodeARMVIXL* slow_path =
2889       deopt_slow_paths_.NewSlowPath<DeoptimizationSlowPathARMVIXL>(deoptimize);
2890   GenerateTestAndBranch(deoptimize,
2891                         /* condition_input_index= */ 0,
2892                         slow_path->GetEntryLabel(),
2893                         /* false_target= */ nullptr);
2894 }
2895 
VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag * flag)2896 void LocationsBuilderARMVIXL::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
2897   LocationSummary* locations = new (GetGraph()->GetAllocator())
2898       LocationSummary(flag, LocationSummary::kNoCall);
2899   locations->SetOut(Location::RequiresRegister());
2900 }
2901 
VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag * flag)2902 void InstructionCodeGeneratorARMVIXL::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
2903   GetAssembler()->LoadFromOffset(kLoadWord,
2904                                  OutputRegister(flag),
2905                                  sp,
2906                                  codegen_->GetStackOffsetOfShouldDeoptimizeFlag());
2907 }
2908 
VisitSelect(HSelect * select)2909 void LocationsBuilderARMVIXL::VisitSelect(HSelect* select) {
2910   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(select);
2911   const bool is_floating_point = DataType::IsFloatingPointType(select->GetType());
2912 
2913   if (is_floating_point) {
2914     locations->SetInAt(0, Location::RequiresFpuRegister());
2915     locations->SetInAt(1, Location::FpuRegisterOrConstant(select->GetTrueValue()));
2916   } else {
2917     locations->SetInAt(0, Location::RequiresRegister());
2918     locations->SetInAt(1, Arm8BitEncodableConstantOrRegister(select->GetTrueValue()));
2919   }
2920 
2921   if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) {
2922     locations->SetInAt(2, Location::RegisterOrConstant(select->GetCondition()));
2923     // The code generator handles overlap with the values, but not with the condition.
2924     locations->SetOut(Location::SameAsFirstInput());
2925   } else if (is_floating_point) {
2926     locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2927   } else {
2928     if (!locations->InAt(1).IsConstant()) {
2929       locations->SetInAt(0, Arm8BitEncodableConstantOrRegister(select->GetFalseValue()));
2930     }
2931 
2932     locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2933   }
2934 }
2935 
VisitSelect(HSelect * select)2936 void InstructionCodeGeneratorARMVIXL::VisitSelect(HSelect* select) {
2937   HInstruction* const condition = select->GetCondition();
2938   const LocationSummary* const locations = select->GetLocations();
2939   const DataType::Type type = select->GetType();
2940   const Location first = locations->InAt(0);
2941   const Location out = locations->Out();
2942   const Location second = locations->InAt(1);
2943 
2944   // In the unlucky case the output of this instruction overlaps
2945   // with an input of an "emitted-at-use-site" condition, and
2946   // the output of this instruction is not one of its inputs, we'll
2947   // need to fallback to branches instead of conditional ARM instructions.
2948   bool output_overlaps_with_condition_inputs =
2949       !IsBooleanValueOrMaterializedCondition(condition) &&
2950       !out.Equals(first) &&
2951       !out.Equals(second) &&
2952       (condition->GetLocations()->InAt(0).Equals(out) ||
2953        condition->GetLocations()->InAt(1).Equals(out));
2954   DCHECK_IMPLIES(output_overlaps_with_condition_inputs, condition->IsCondition());
2955   Location src;
2956 
2957   if (condition->IsIntConstant()) {
2958     if (condition->AsIntConstant()->IsFalse()) {
2959       src = first;
2960     } else {
2961       src = second;
2962     }
2963 
2964     codegen_->MoveLocation(out, src, type);
2965     return;
2966   }
2967 
2968   if (!DataType::IsFloatingPointType(type) && !output_overlaps_with_condition_inputs) {
2969     bool invert = false;
2970 
2971     if (out.Equals(second)) {
2972       src = first;
2973       invert = true;
2974     } else if (out.Equals(first)) {
2975       src = second;
2976     } else if (second.IsConstant()) {
2977       DCHECK(CanEncodeConstantAs8BitImmediate(second.GetConstant()));
2978       src = second;
2979     } else if (first.IsConstant()) {
2980       DCHECK(CanEncodeConstantAs8BitImmediate(first.GetConstant()));
2981       src = first;
2982       invert = true;
2983     } else {
2984       src = second;
2985     }
2986 
2987     if (CanGenerateConditionalMove(out, src)) {
2988       if (!out.Equals(first) && !out.Equals(second)) {
2989         codegen_->MoveLocation(out, src.Equals(first) ? second : first, type);
2990       }
2991 
2992       std::pair<vixl32::Condition, vixl32::Condition> cond(eq, ne);
2993 
2994       if (IsBooleanValueOrMaterializedCondition(condition)) {
2995         __ Cmp(InputRegisterAt(select, 2), 0);
2996         cond = invert ? std::make_pair(eq, ne) : std::make_pair(ne, eq);
2997       } else {
2998         cond = GenerateTest(condition->AsCondition(), invert, codegen_);
2999       }
3000 
3001       const size_t instr_count = out.IsRegisterPair() ? 4 : 2;
3002       // We use the scope because of the IT block that follows.
3003       ExactAssemblyScope guard(GetVIXLAssembler(),
3004                                instr_count * vixl32::k16BitT32InstructionSizeInBytes,
3005                                CodeBufferCheckScope::kExactSize);
3006 
3007       if (out.IsRegister()) {
3008         __ it(cond.first);
3009         __ mov(cond.first, RegisterFrom(out), OperandFrom(src, type));
3010       } else {
3011         DCHECK(out.IsRegisterPair());
3012 
3013         Operand operand_high(0);
3014         Operand operand_low(0);
3015 
3016         if (src.IsConstant()) {
3017           const int64_t value = Int64ConstantFrom(src);
3018 
3019           operand_high = High32Bits(value);
3020           operand_low = Low32Bits(value);
3021         } else {
3022           DCHECK(src.IsRegisterPair());
3023           operand_high = HighRegisterFrom(src);
3024           operand_low = LowRegisterFrom(src);
3025         }
3026 
3027         __ it(cond.first);
3028         __ mov(cond.first, LowRegisterFrom(out), operand_low);
3029         __ it(cond.first);
3030         __ mov(cond.first, HighRegisterFrom(out), operand_high);
3031       }
3032 
3033       return;
3034     }
3035   }
3036 
3037   vixl32::Label* false_target = nullptr;
3038   vixl32::Label* true_target = nullptr;
3039   vixl32::Label select_end;
3040   vixl32::Label other_case;
3041   vixl32::Label* const target = codegen_->GetFinalLabel(select, &select_end);
3042 
3043   if (out.Equals(second)) {
3044     true_target = target;
3045     src = first;
3046   } else {
3047     false_target = target;
3048     src = second;
3049 
3050     if (!out.Equals(first)) {
3051       if (output_overlaps_with_condition_inputs) {
3052         false_target = &other_case;
3053       } else {
3054         codegen_->MoveLocation(out, first, type);
3055       }
3056     }
3057   }
3058 
3059   GenerateTestAndBranch(select, 2, true_target, false_target, /* far_target= */ false);
3060   codegen_->MoveLocation(out, src, type);
3061   if (output_overlaps_with_condition_inputs) {
3062     __ B(target);
3063     __ Bind(&other_case);
3064     codegen_->MoveLocation(out, first, type);
3065   }
3066 
3067   if (select_end.IsReferenced()) {
3068     __ Bind(&select_end);
3069   }
3070 }
3071 
VisitNativeDebugInfo(HNativeDebugInfo * info)3072 void LocationsBuilderARMVIXL::VisitNativeDebugInfo(HNativeDebugInfo* info) {
3073   new (GetGraph()->GetAllocator()) LocationSummary(info);
3074 }
3075 
VisitNativeDebugInfo(HNativeDebugInfo *)3076 void InstructionCodeGeneratorARMVIXL::VisitNativeDebugInfo(HNativeDebugInfo*) {
3077   // MaybeRecordNativeDebugInfo is already called implicitly in CodeGenerator::Compile.
3078 }
3079 
IncreaseFrame(size_t adjustment)3080 void CodeGeneratorARMVIXL::IncreaseFrame(size_t adjustment) {
3081   __ Claim(adjustment);
3082   GetAssembler()->cfi().AdjustCFAOffset(adjustment);
3083 }
3084 
DecreaseFrame(size_t adjustment)3085 void CodeGeneratorARMVIXL::DecreaseFrame(size_t adjustment) {
3086   __ Drop(adjustment);
3087   GetAssembler()->cfi().AdjustCFAOffset(-adjustment);
3088 }
3089 
GenerateNop()3090 void CodeGeneratorARMVIXL::GenerateNop() {
3091   __ Nop();
3092 }
3093 
3094 // `temp` is an extra temporary register that is used for some conditions;
3095 // callers may not specify it, in which case the method will use a scratch
3096 // register instead.
GenerateConditionWithZero(IfCondition condition,vixl32::Register out,vixl32::Register in,vixl32::Register temp)3097 void CodeGeneratorARMVIXL::GenerateConditionWithZero(IfCondition condition,
3098                                                      vixl32::Register out,
3099                                                      vixl32::Register in,
3100                                                      vixl32::Register temp) {
3101   switch (condition) {
3102     case kCondEQ:
3103     // x <= 0 iff x == 0 when the comparison is unsigned.
3104     case kCondBE:
3105       if (!temp.IsValid() || (out.IsLow() && !out.Is(in))) {
3106         temp = out;
3107       }
3108 
3109       // Avoid 32-bit instructions if possible; note that `in` and `temp` must be
3110       // different as well.
3111       if (in.IsLow() && temp.IsLow() && !in.Is(temp)) {
3112         // temp = - in; only 0 sets the carry flag.
3113         __ Rsbs(temp, in, 0);
3114 
3115         if (out.Is(in)) {
3116           std::swap(in, temp);
3117         }
3118 
3119         // out = - in + in + carry = carry
3120         __ Adc(out, temp, in);
3121       } else {
3122         // If `in` is 0, then it has 32 leading zeros, and less than that otherwise.
3123         __ Clz(out, in);
3124         // Any number less than 32 logically shifted right by 5 bits results in 0;
3125         // the same operation on 32 yields 1.
3126         __ Lsr(out, out, 5);
3127       }
3128 
3129       break;
3130     case kCondNE:
3131     // x > 0 iff x != 0 when the comparison is unsigned.
3132     case kCondA: {
3133       UseScratchRegisterScope temps(GetVIXLAssembler());
3134 
3135       if (out.Is(in)) {
3136         if (!temp.IsValid() || in.Is(temp)) {
3137           temp = temps.Acquire();
3138         }
3139       } else if (!temp.IsValid() || !temp.IsLow()) {
3140         temp = out;
3141       }
3142 
3143       // temp = in - 1; only 0 does not set the carry flag.
3144       __ Subs(temp, in, 1);
3145       // out = in + ~temp + carry = in + (-(in - 1) - 1) + carry = in - in + 1 - 1 + carry = carry
3146       __ Sbc(out, in, temp);
3147       break;
3148     }
3149     case kCondGE:
3150       __ Mvn(out, in);
3151       in = out;
3152       FALLTHROUGH_INTENDED;
3153     case kCondLT:
3154       // We only care about the sign bit.
3155       __ Lsr(out, in, 31);
3156       break;
3157     case kCondAE:
3158       // Trivially true.
3159       __ Mov(out, 1);
3160       break;
3161     case kCondB:
3162       // Trivially false.
3163       __ Mov(out, 0);
3164       break;
3165     default:
3166       LOG(FATAL) << "Unexpected condition " << condition;
3167       UNREACHABLE();
3168   }
3169 }
3170 
HandleCondition(HCondition * cond)3171 void LocationsBuilderARMVIXL::HandleCondition(HCondition* cond) {
3172   LocationSummary* locations =
3173       new (GetGraph()->GetAllocator()) LocationSummary(cond, LocationSummary::kNoCall);
3174   const DataType::Type type = cond->InputAt(0)->GetType();
3175   if (DataType::IsFloatingPointType(type)) {
3176     locations->SetInAt(0, Location::RequiresFpuRegister());
3177     locations->SetInAt(1, ArithmeticZeroOrFpuRegister(cond->InputAt(1)));
3178   } else {
3179     locations->SetInAt(0, Location::RequiresRegister());
3180     locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
3181   }
3182   if (!cond->IsEmittedAtUseSite()) {
3183     locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3184   }
3185 }
3186 
HandleCondition(HCondition * cond)3187 void InstructionCodeGeneratorARMVIXL::HandleCondition(HCondition* cond) {
3188   if (cond->IsEmittedAtUseSite()) {
3189     return;
3190   }
3191 
3192   const DataType::Type type = cond->GetLeft()->GetType();
3193 
3194   if (DataType::IsFloatingPointType(type)) {
3195     GenerateConditionGeneric(cond, codegen_);
3196     return;
3197   }
3198 
3199   DCHECK(DataType::IsIntegralType(type) || type == DataType::Type::kReference) << type;
3200 
3201   const IfCondition condition = cond->GetCondition();
3202 
3203   // A condition with only one boolean input, or two boolean inputs without being equality or
3204   // inequality results from transformations done by the instruction simplifier, and is handled
3205   // as a regular condition with integral inputs.
3206   if (type == DataType::Type::kBool &&
3207       cond->GetRight()->GetType() == DataType::Type::kBool &&
3208       (condition == kCondEQ || condition == kCondNE)) {
3209     vixl32::Register left = InputRegisterAt(cond, 0);
3210     const vixl32::Register out = OutputRegister(cond);
3211     const Location right_loc = cond->GetLocations()->InAt(1);
3212 
3213     // The constant case is handled by the instruction simplifier.
3214     DCHECK(!right_loc.IsConstant());
3215 
3216     vixl32::Register right = RegisterFrom(right_loc);
3217 
3218     // Avoid 32-bit instructions if possible.
3219     if (out.Is(right)) {
3220       std::swap(left, right);
3221     }
3222 
3223     __ Eor(out, left, right);
3224 
3225     if (condition == kCondEQ) {
3226       __ Eor(out, out, 1);
3227     }
3228 
3229     return;
3230   }
3231 
3232   GenerateConditionIntegralOrNonPrimitive(cond, codegen_);
3233 }
3234 
VisitEqual(HEqual * comp)3235 void LocationsBuilderARMVIXL::VisitEqual(HEqual* comp) {
3236   HandleCondition(comp);
3237 }
3238 
VisitEqual(HEqual * comp)3239 void InstructionCodeGeneratorARMVIXL::VisitEqual(HEqual* comp) {
3240   HandleCondition(comp);
3241 }
3242 
VisitNotEqual(HNotEqual * comp)3243 void LocationsBuilderARMVIXL::VisitNotEqual(HNotEqual* comp) {
3244   HandleCondition(comp);
3245 }
3246 
VisitNotEqual(HNotEqual * comp)3247 void InstructionCodeGeneratorARMVIXL::VisitNotEqual(HNotEqual* comp) {
3248   HandleCondition(comp);
3249 }
3250 
VisitLessThan(HLessThan * comp)3251 void LocationsBuilderARMVIXL::VisitLessThan(HLessThan* comp) {
3252   HandleCondition(comp);
3253 }
3254 
VisitLessThan(HLessThan * comp)3255 void InstructionCodeGeneratorARMVIXL::VisitLessThan(HLessThan* comp) {
3256   HandleCondition(comp);
3257 }
3258 
VisitLessThanOrEqual(HLessThanOrEqual * comp)3259 void LocationsBuilderARMVIXL::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
3260   HandleCondition(comp);
3261 }
3262 
VisitLessThanOrEqual(HLessThanOrEqual * comp)3263 void InstructionCodeGeneratorARMVIXL::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
3264   HandleCondition(comp);
3265 }
3266 
VisitGreaterThan(HGreaterThan * comp)3267 void LocationsBuilderARMVIXL::VisitGreaterThan(HGreaterThan* comp) {
3268   HandleCondition(comp);
3269 }
3270 
VisitGreaterThan(HGreaterThan * comp)3271 void InstructionCodeGeneratorARMVIXL::VisitGreaterThan(HGreaterThan* comp) {
3272   HandleCondition(comp);
3273 }
3274 
VisitGreaterThanOrEqual(HGreaterThanOrEqual * comp)3275 void LocationsBuilderARMVIXL::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
3276   HandleCondition(comp);
3277 }
3278 
VisitGreaterThanOrEqual(HGreaterThanOrEqual * comp)3279 void InstructionCodeGeneratorARMVIXL::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
3280   HandleCondition(comp);
3281 }
3282 
VisitBelow(HBelow * comp)3283 void LocationsBuilderARMVIXL::VisitBelow(HBelow* comp) {
3284   HandleCondition(comp);
3285 }
3286 
VisitBelow(HBelow * comp)3287 void InstructionCodeGeneratorARMVIXL::VisitBelow(HBelow* comp) {
3288   HandleCondition(comp);
3289 }
3290 
VisitBelowOrEqual(HBelowOrEqual * comp)3291 void LocationsBuilderARMVIXL::VisitBelowOrEqual(HBelowOrEqual* comp) {
3292   HandleCondition(comp);
3293 }
3294 
VisitBelowOrEqual(HBelowOrEqual * comp)3295 void InstructionCodeGeneratorARMVIXL::VisitBelowOrEqual(HBelowOrEqual* comp) {
3296   HandleCondition(comp);
3297 }
3298 
VisitAbove(HAbove * comp)3299 void LocationsBuilderARMVIXL::VisitAbove(HAbove* comp) {
3300   HandleCondition(comp);
3301 }
3302 
VisitAbove(HAbove * comp)3303 void InstructionCodeGeneratorARMVIXL::VisitAbove(HAbove* comp) {
3304   HandleCondition(comp);
3305 }
3306 
VisitAboveOrEqual(HAboveOrEqual * comp)3307 void LocationsBuilderARMVIXL::VisitAboveOrEqual(HAboveOrEqual* comp) {
3308   HandleCondition(comp);
3309 }
3310 
VisitAboveOrEqual(HAboveOrEqual * comp)3311 void InstructionCodeGeneratorARMVIXL::VisitAboveOrEqual(HAboveOrEqual* comp) {
3312   HandleCondition(comp);
3313 }
3314 
VisitIntConstant(HIntConstant * constant)3315 void LocationsBuilderARMVIXL::VisitIntConstant(HIntConstant* constant) {
3316   LocationSummary* locations =
3317       new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
3318   locations->SetOut(Location::ConstantLocation(constant));
3319 }
3320 
VisitIntConstant(HIntConstant * constant ATTRIBUTE_UNUSED)3321 void InstructionCodeGeneratorARMVIXL::VisitIntConstant(HIntConstant* constant ATTRIBUTE_UNUSED) {
3322   // Will be generated at use site.
3323 }
3324 
VisitNullConstant(HNullConstant * constant)3325 void LocationsBuilderARMVIXL::VisitNullConstant(HNullConstant* constant) {
3326   LocationSummary* locations =
3327       new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
3328   locations->SetOut(Location::ConstantLocation(constant));
3329 }
3330 
VisitNullConstant(HNullConstant * constant ATTRIBUTE_UNUSED)3331 void InstructionCodeGeneratorARMVIXL::VisitNullConstant(HNullConstant* constant ATTRIBUTE_UNUSED) {
3332   // Will be generated at use site.
3333 }
3334 
VisitLongConstant(HLongConstant * constant)3335 void LocationsBuilderARMVIXL::VisitLongConstant(HLongConstant* constant) {
3336   LocationSummary* locations =
3337       new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
3338   locations->SetOut(Location::ConstantLocation(constant));
3339 }
3340 
VisitLongConstant(HLongConstant * constant ATTRIBUTE_UNUSED)3341 void InstructionCodeGeneratorARMVIXL::VisitLongConstant(HLongConstant* constant ATTRIBUTE_UNUSED) {
3342   // Will be generated at use site.
3343 }
3344 
VisitFloatConstant(HFloatConstant * constant)3345 void LocationsBuilderARMVIXL::VisitFloatConstant(HFloatConstant* constant) {
3346   LocationSummary* locations =
3347       new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
3348   locations->SetOut(Location::ConstantLocation(constant));
3349 }
3350 
VisitFloatConstant(HFloatConstant * constant ATTRIBUTE_UNUSED)3351 void InstructionCodeGeneratorARMVIXL::VisitFloatConstant(
3352     HFloatConstant* constant ATTRIBUTE_UNUSED) {
3353   // Will be generated at use site.
3354 }
3355 
VisitDoubleConstant(HDoubleConstant * constant)3356 void LocationsBuilderARMVIXL::VisitDoubleConstant(HDoubleConstant* constant) {
3357   LocationSummary* locations =
3358       new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
3359   locations->SetOut(Location::ConstantLocation(constant));
3360 }
3361 
VisitDoubleConstant(HDoubleConstant * constant ATTRIBUTE_UNUSED)3362 void InstructionCodeGeneratorARMVIXL::VisitDoubleConstant(
3363     HDoubleConstant* constant ATTRIBUTE_UNUSED) {
3364   // Will be generated at use site.
3365 }
3366 
VisitConstructorFence(HConstructorFence * constructor_fence)3367 void LocationsBuilderARMVIXL::VisitConstructorFence(HConstructorFence* constructor_fence) {
3368   constructor_fence->SetLocations(nullptr);
3369 }
3370 
VisitConstructorFence(HConstructorFence * constructor_fence ATTRIBUTE_UNUSED)3371 void InstructionCodeGeneratorARMVIXL::VisitConstructorFence(
3372     HConstructorFence* constructor_fence ATTRIBUTE_UNUSED) {
3373   codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
3374 }
3375 
VisitMemoryBarrier(HMemoryBarrier * memory_barrier)3376 void LocationsBuilderARMVIXL::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
3377   memory_barrier->SetLocations(nullptr);
3378 }
3379 
VisitMemoryBarrier(HMemoryBarrier * memory_barrier)3380 void InstructionCodeGeneratorARMVIXL::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
3381   codegen_->GenerateMemoryBarrier(memory_barrier->GetBarrierKind());
3382 }
3383 
VisitReturnVoid(HReturnVoid * ret)3384 void LocationsBuilderARMVIXL::VisitReturnVoid(HReturnVoid* ret) {
3385   ret->SetLocations(nullptr);
3386 }
3387 
VisitReturnVoid(HReturnVoid * ret ATTRIBUTE_UNUSED)3388 void InstructionCodeGeneratorARMVIXL::VisitReturnVoid(HReturnVoid* ret ATTRIBUTE_UNUSED) {
3389   codegen_->GenerateFrameExit();
3390 }
3391 
VisitReturn(HReturn * ret)3392 void LocationsBuilderARMVIXL::VisitReturn(HReturn* ret) {
3393   LocationSummary* locations =
3394       new (GetGraph()->GetAllocator()) LocationSummary(ret, LocationSummary::kNoCall);
3395   locations->SetInAt(0, parameter_visitor_.GetReturnLocation(ret->InputAt(0)->GetType()));
3396 }
3397 
VisitReturn(HReturn * ret)3398 void InstructionCodeGeneratorARMVIXL::VisitReturn(HReturn* ret) {
3399   if (GetGraph()->IsCompilingOsr()) {
3400     // To simplify callers of an OSR method, we put the return value in both
3401     // floating point and core registers.
3402     switch (ret->InputAt(0)->GetType()) {
3403       case DataType::Type::kFloat32:
3404         __ Vmov(r0, s0);
3405         break;
3406       case DataType::Type::kFloat64:
3407         __ Vmov(r0, r1, d0);
3408         break;
3409       default:
3410         break;
3411     }
3412   }
3413   codegen_->GenerateFrameExit();
3414 }
3415 
VisitInvokeUnresolved(HInvokeUnresolved * invoke)3416 void LocationsBuilderARMVIXL::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
3417   // The trampoline uses the same calling convention as dex calling conventions,
3418   // except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain
3419   // the method_idx.
3420   HandleInvoke(invoke);
3421 }
3422 
VisitInvokeUnresolved(HInvokeUnresolved * invoke)3423 void InstructionCodeGeneratorARMVIXL::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
3424   codegen_->GenerateInvokeUnresolvedRuntimeCall(invoke);
3425   codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 3);
3426 }
3427 
VisitInvokeStaticOrDirect(HInvokeStaticOrDirect * invoke)3428 void LocationsBuilderARMVIXL::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
3429   // Explicit clinit checks triggered by static invokes must have been pruned by
3430   // art::PrepareForRegisterAllocation.
3431   DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
3432 
3433   IntrinsicLocationsBuilderARMVIXL intrinsic(codegen_);
3434   if (intrinsic.TryDispatch(invoke)) {
3435     return;
3436   }
3437 
3438   if (invoke->GetCodePtrLocation() == CodePtrLocation::kCallCriticalNative) {
3439     CriticalNativeCallingConventionVisitorARMVIXL calling_convention_visitor(
3440         /*for_register_allocation=*/ true);
3441     CodeGenerator::CreateCommonInvokeLocationSummary(invoke, &calling_convention_visitor);
3442   } else {
3443     HandleInvoke(invoke);
3444   }
3445 }
3446 
TryGenerateIntrinsicCode(HInvoke * invoke,CodeGeneratorARMVIXL * codegen)3447 static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorARMVIXL* codegen) {
3448   if (invoke->GetLocations()->Intrinsified()) {
3449     IntrinsicCodeGeneratorARMVIXL intrinsic(codegen);
3450     intrinsic.Dispatch(invoke);
3451     return true;
3452   }
3453   return false;
3454 }
3455 
VisitInvokeStaticOrDirect(HInvokeStaticOrDirect * invoke)3456 void InstructionCodeGeneratorARMVIXL::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
3457   // Explicit clinit checks triggered by static invokes must have been pruned by
3458   // art::PrepareForRegisterAllocation.
3459   DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
3460 
3461   if (TryGenerateIntrinsicCode(invoke, codegen_)) {
3462     codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 4);
3463     return;
3464   }
3465 
3466   LocationSummary* locations = invoke->GetLocations();
3467   codegen_->GenerateStaticOrDirectCall(
3468       invoke, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation());
3469 
3470   codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 5);
3471 }
3472 
HandleInvoke(HInvoke * invoke)3473 void LocationsBuilderARMVIXL::HandleInvoke(HInvoke* invoke) {
3474   InvokeDexCallingConventionVisitorARMVIXL calling_convention_visitor;
3475   CodeGenerator::CreateCommonInvokeLocationSummary(invoke, &calling_convention_visitor);
3476 }
3477 
VisitInvokeVirtual(HInvokeVirtual * invoke)3478 void LocationsBuilderARMVIXL::VisitInvokeVirtual(HInvokeVirtual* invoke) {
3479   IntrinsicLocationsBuilderARMVIXL intrinsic(codegen_);
3480   if (intrinsic.TryDispatch(invoke)) {
3481     return;
3482   }
3483 
3484   HandleInvoke(invoke);
3485 }
3486 
VisitInvokeVirtual(HInvokeVirtual * invoke)3487 void InstructionCodeGeneratorARMVIXL::VisitInvokeVirtual(HInvokeVirtual* invoke) {
3488   if (TryGenerateIntrinsicCode(invoke, codegen_)) {
3489     codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 6);
3490     return;
3491   }
3492 
3493   codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0));
3494   DCHECK(!codegen_->IsLeafMethod());
3495 
3496   codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 7);
3497 }
3498 
VisitInvokeInterface(HInvokeInterface * invoke)3499 void LocationsBuilderARMVIXL::VisitInvokeInterface(HInvokeInterface* invoke) {
3500   HandleInvoke(invoke);
3501   // Add the hidden argument.
3502   if (invoke->GetHiddenArgumentLoadKind() == MethodLoadKind::kRecursive) {
3503     // We cannot request r12 as it's blocked by the register allocator.
3504     invoke->GetLocations()->SetInAt(invoke->GetNumberOfArguments() - 1, Location::Any());
3505   }
3506 }
3507 
MaybeGenerateInlineCacheCheck(HInstruction * instruction,vixl32::Register klass)3508 void CodeGeneratorARMVIXL::MaybeGenerateInlineCacheCheck(HInstruction* instruction,
3509                                                          vixl32::Register klass) {
3510   DCHECK_EQ(r0.GetCode(), klass.GetCode());
3511   // We know the destination of an intrinsic, so no need to record inline
3512   // caches.
3513   if (!instruction->GetLocations()->Intrinsified() &&
3514       GetGraph()->IsCompilingBaseline() &&
3515       !Runtime::Current()->IsAotCompiler()) {
3516     DCHECK(!instruction->GetEnvironment()->IsFromInlinedInvoke());
3517     ProfilingInfo* info = GetGraph()->GetProfilingInfo();
3518     DCHECK(info != nullptr);
3519     InlineCache* cache = info->GetInlineCache(instruction->GetDexPc());
3520     uint32_t address = reinterpret_cast32<uint32_t>(cache);
3521     vixl32::Label done;
3522     UseScratchRegisterScope temps(GetVIXLAssembler());
3523     temps.Exclude(ip);
3524     __ Mov(r4, address);
3525     __ Ldr(ip, MemOperand(r4, InlineCache::ClassesOffset().Int32Value()));
3526     // Fast path for a monomorphic cache.
3527     __ Cmp(klass, ip);
3528     __ B(eq, &done, /* is_far_target= */ false);
3529     InvokeRuntime(kQuickUpdateInlineCache, instruction, instruction->GetDexPc());
3530     __ Bind(&done);
3531   }
3532 }
3533 
VisitInvokeInterface(HInvokeInterface * invoke)3534 void InstructionCodeGeneratorARMVIXL::VisitInvokeInterface(HInvokeInterface* invoke) {
3535   // TODO: b/18116999, our IMTs can miss an IncompatibleClassChangeError.
3536   LocationSummary* locations = invoke->GetLocations();
3537   vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
3538   Location receiver = locations->InAt(0);
3539   uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
3540 
3541   DCHECK(!receiver.IsStackSlot());
3542 
3543   // Ensure the pc position is recorded immediately after the `ldr` instruction.
3544   {
3545     ExactAssemblyScope aas(GetVIXLAssembler(),
3546                            vixl32::kMaxInstructionSizeInBytes,
3547                            CodeBufferCheckScope::kMaximumSize);
3548     // /* HeapReference<Class> */ temp = receiver->klass_
3549     __ ldr(temp, MemOperand(RegisterFrom(receiver), class_offset));
3550     codegen_->MaybeRecordImplicitNullCheck(invoke);
3551   }
3552   // Instead of simply (possibly) unpoisoning `temp` here, we should
3553   // emit a read barrier for the previous class reference load.
3554   // However this is not required in practice, as this is an
3555   // intermediate/temporary reference and because the current
3556   // concurrent copying collector keeps the from-space memory
3557   // intact/accessible until the end of the marking phase (the
3558   // concurrent copying collector may not in the future).
3559   GetAssembler()->MaybeUnpoisonHeapReference(temp);
3560 
3561   // If we're compiling baseline, update the inline cache.
3562   codegen_->MaybeGenerateInlineCacheCheck(invoke, temp);
3563 
3564   GetAssembler()->LoadFromOffset(kLoadWord,
3565                                  temp,
3566                                  temp,
3567                                  mirror::Class::ImtPtrOffset(kArmPointerSize).Uint32Value());
3568 
3569   uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement(
3570       invoke->GetImtIndex(), kArmPointerSize));
3571   // temp = temp->GetImtEntryAt(method_offset);
3572   GetAssembler()->LoadFromOffset(kLoadWord, temp, temp, method_offset);
3573   uint32_t entry_point =
3574       ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value();
3575   // LR = temp->GetEntryPoint();
3576   GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, entry_point);
3577 
3578   {
3579     // Set the hidden (in r12) argument. It is done here, right before a BLX to prevent other
3580     // instruction from clobbering it as they might use r12 as a scratch register.
3581     Location hidden_reg = Location::RegisterLocation(r12.GetCode());
3582     // The VIXL macro assembler may clobber any of the scratch registers that are available to it,
3583     // so it checks if the application is using them (by passing them to the macro assembler
3584     // methods). The following application of UseScratchRegisterScope corrects VIXL's notion of
3585     // what is available, and is the opposite of the standard usage: Instead of requesting a
3586     // temporary location, it imposes an external constraint (i.e. a specific register is reserved
3587     // for the hidden argument). Note that this works even if VIXL needs a scratch register itself
3588     // (to materialize the constant), since the destination register becomes available for such use
3589     // internally for the duration of the macro instruction.
3590     UseScratchRegisterScope temps(GetVIXLAssembler());
3591     temps.Exclude(RegisterFrom(hidden_reg));
3592     if (invoke->GetHiddenArgumentLoadKind() == MethodLoadKind::kRecursive) {
3593       Location current_method = locations->InAt(invoke->GetNumberOfArguments() - 1);
3594       if (current_method.IsStackSlot()) {
3595         GetAssembler()->LoadFromOffset(
3596             kLoadWord, RegisterFrom(hidden_reg), sp, current_method.GetStackIndex());
3597       } else {
3598         __ Mov(RegisterFrom(hidden_reg), RegisterFrom(current_method));
3599       }
3600     } else if (invoke->GetHiddenArgumentLoadKind() == MethodLoadKind::kRuntimeCall) {
3601       // We pass the method from the IMT in case of a conflict. This will ensure
3602       // we go into the runtime to resolve the actual method.
3603       CHECK_NE(temp.GetCode(), lr.GetCode());
3604       __ Mov(RegisterFrom(hidden_reg), temp);
3605     } else {
3606       codegen_->LoadMethod(invoke->GetHiddenArgumentLoadKind(), hidden_reg, invoke);
3607     }
3608   }
3609   {
3610     // Ensure the pc position is recorded immediately after the `blx` instruction.
3611     // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
3612     ExactAssemblyScope aas(GetVIXLAssembler(),
3613                            vixl32::k16BitT32InstructionSizeInBytes,
3614                            CodeBufferCheckScope::kExactSize);
3615     // LR();
3616     __ blx(lr);
3617     codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
3618     DCHECK(!codegen_->IsLeafMethod());
3619   }
3620 
3621   codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 8);
3622 }
3623 
VisitInvokePolymorphic(HInvokePolymorphic * invoke)3624 void LocationsBuilderARMVIXL::VisitInvokePolymorphic(HInvokePolymorphic* invoke) {
3625   IntrinsicLocationsBuilderARMVIXL intrinsic(codegen_);
3626   if (intrinsic.TryDispatch(invoke)) {
3627     return;
3628   }
3629   HandleInvoke(invoke);
3630 }
3631 
VisitInvokePolymorphic(HInvokePolymorphic * invoke)3632 void InstructionCodeGeneratorARMVIXL::VisitInvokePolymorphic(HInvokePolymorphic* invoke) {
3633   if (TryGenerateIntrinsicCode(invoke, codegen_)) {
3634     codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 9);
3635     return;
3636   }
3637   codegen_->GenerateInvokePolymorphicCall(invoke);
3638   codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 10);
3639 }
3640 
VisitInvokeCustom(HInvokeCustom * invoke)3641 void LocationsBuilderARMVIXL::VisitInvokeCustom(HInvokeCustom* invoke) {
3642   HandleInvoke(invoke);
3643 }
3644 
VisitInvokeCustom(HInvokeCustom * invoke)3645 void InstructionCodeGeneratorARMVIXL::VisitInvokeCustom(HInvokeCustom* invoke) {
3646   codegen_->GenerateInvokeCustomCall(invoke);
3647   codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 11);
3648 }
3649 
VisitNeg(HNeg * neg)3650 void LocationsBuilderARMVIXL::VisitNeg(HNeg* neg) {
3651   LocationSummary* locations =
3652       new (GetGraph()->GetAllocator()) LocationSummary(neg, LocationSummary::kNoCall);
3653   switch (neg->GetResultType()) {
3654     case DataType::Type::kInt32: {
3655       locations->SetInAt(0, Location::RequiresRegister());
3656       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3657       break;
3658     }
3659     case DataType::Type::kInt64: {
3660       locations->SetInAt(0, Location::RequiresRegister());
3661       locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
3662       break;
3663     }
3664 
3665     case DataType::Type::kFloat32:
3666     case DataType::Type::kFloat64:
3667       locations->SetInAt(0, Location::RequiresFpuRegister());
3668       locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3669       break;
3670 
3671     default:
3672       LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
3673   }
3674 }
3675 
VisitNeg(HNeg * neg)3676 void InstructionCodeGeneratorARMVIXL::VisitNeg(HNeg* neg) {
3677   LocationSummary* locations = neg->GetLocations();
3678   Location out = locations->Out();
3679   Location in = locations->InAt(0);
3680   switch (neg->GetResultType()) {
3681     case DataType::Type::kInt32:
3682       __ Rsb(OutputRegister(neg), InputRegisterAt(neg, 0), 0);
3683       break;
3684 
3685     case DataType::Type::kInt64:
3686       // out.lo = 0 - in.lo (and update the carry/borrow (C) flag)
3687       __ Rsbs(LowRegisterFrom(out), LowRegisterFrom(in), 0);
3688       // We cannot emit an RSC (Reverse Subtract with Carry)
3689       // instruction here, as it does not exist in the Thumb-2
3690       // instruction set.  We use the following approach
3691       // using SBC and SUB instead.
3692       //
3693       // out.hi = -C
3694       __ Sbc(HighRegisterFrom(out), HighRegisterFrom(out), HighRegisterFrom(out));
3695       // out.hi = out.hi - in.hi
3696       __ Sub(HighRegisterFrom(out), HighRegisterFrom(out), HighRegisterFrom(in));
3697       break;
3698 
3699     case DataType::Type::kFloat32:
3700     case DataType::Type::kFloat64:
3701       __ Vneg(OutputVRegister(neg), InputVRegister(neg));
3702       break;
3703 
3704     default:
3705       LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
3706   }
3707 }
3708 
VisitTypeConversion(HTypeConversion * conversion)3709 void LocationsBuilderARMVIXL::VisitTypeConversion(HTypeConversion* conversion) {
3710   DataType::Type result_type = conversion->GetResultType();
3711   DataType::Type input_type = conversion->GetInputType();
3712   DCHECK(!DataType::IsTypeConversionImplicit(input_type, result_type))
3713       << input_type << " -> " << result_type;
3714 
3715   // The float-to-long, double-to-long and long-to-float type conversions
3716   // rely on a call to the runtime.
3717   LocationSummary::CallKind call_kind =
3718       (((input_type == DataType::Type::kFloat32 || input_type == DataType::Type::kFloat64)
3719         && result_type == DataType::Type::kInt64)
3720        || (input_type == DataType::Type::kInt64 && result_type == DataType::Type::kFloat32))
3721       ? LocationSummary::kCallOnMainOnly
3722       : LocationSummary::kNoCall;
3723   LocationSummary* locations =
3724       new (GetGraph()->GetAllocator()) LocationSummary(conversion, call_kind);
3725 
3726   switch (result_type) {
3727     case DataType::Type::kUint8:
3728     case DataType::Type::kInt8:
3729     case DataType::Type::kUint16:
3730     case DataType::Type::kInt16:
3731       DCHECK(DataType::IsIntegralType(input_type)) << input_type;
3732       locations->SetInAt(0, Location::RequiresRegister());
3733       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3734       break;
3735 
3736     case DataType::Type::kInt32:
3737       switch (input_type) {
3738         case DataType::Type::kInt64:
3739           locations->SetInAt(0, Location::Any());
3740           locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3741           break;
3742 
3743         case DataType::Type::kFloat32:
3744           locations->SetInAt(0, Location::RequiresFpuRegister());
3745           locations->SetOut(Location::RequiresRegister());
3746           locations->AddTemp(Location::RequiresFpuRegister());
3747           break;
3748 
3749         case DataType::Type::kFloat64:
3750           locations->SetInAt(0, Location::RequiresFpuRegister());
3751           locations->SetOut(Location::RequiresRegister());
3752           locations->AddTemp(Location::RequiresFpuRegister());
3753           break;
3754 
3755         default:
3756           LOG(FATAL) << "Unexpected type conversion from " << input_type
3757                      << " to " << result_type;
3758       }
3759       break;
3760 
3761     case DataType::Type::kInt64:
3762       switch (input_type) {
3763         case DataType::Type::kBool:
3764         case DataType::Type::kUint8:
3765         case DataType::Type::kInt8:
3766         case DataType::Type::kUint16:
3767         case DataType::Type::kInt16:
3768         case DataType::Type::kInt32:
3769           locations->SetInAt(0, Location::RequiresRegister());
3770           locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3771           break;
3772 
3773         case DataType::Type::kFloat32: {
3774           InvokeRuntimeCallingConventionARMVIXL calling_convention;
3775           locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
3776           locations->SetOut(LocationFrom(r0, r1));
3777           break;
3778         }
3779 
3780         case DataType::Type::kFloat64: {
3781           InvokeRuntimeCallingConventionARMVIXL calling_convention;
3782           locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0),
3783                                              calling_convention.GetFpuRegisterAt(1)));
3784           locations->SetOut(LocationFrom(r0, r1));
3785           break;
3786         }
3787 
3788         default:
3789           LOG(FATAL) << "Unexpected type conversion from " << input_type
3790                      << " to " << result_type;
3791       }
3792       break;
3793 
3794     case DataType::Type::kFloat32:
3795       switch (input_type) {
3796         case DataType::Type::kBool:
3797         case DataType::Type::kUint8:
3798         case DataType::Type::kInt8:
3799         case DataType::Type::kUint16:
3800         case DataType::Type::kInt16:
3801         case DataType::Type::kInt32:
3802           locations->SetInAt(0, Location::RequiresRegister());
3803           locations->SetOut(Location::RequiresFpuRegister());
3804           break;
3805 
3806         case DataType::Type::kInt64: {
3807           InvokeRuntimeCallingConventionARMVIXL calling_convention;
3808           locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0),
3809                                              calling_convention.GetRegisterAt(1)));
3810           locations->SetOut(LocationFrom(calling_convention.GetFpuRegisterAt(0)));
3811           break;
3812         }
3813 
3814         case DataType::Type::kFloat64:
3815           locations->SetInAt(0, Location::RequiresFpuRegister());
3816           locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3817           break;
3818 
3819         default:
3820           LOG(FATAL) << "Unexpected type conversion from " << input_type
3821                      << " to " << result_type;
3822       }
3823       break;
3824 
3825     case DataType::Type::kFloat64:
3826       switch (input_type) {
3827         case DataType::Type::kBool:
3828         case DataType::Type::kUint8:
3829         case DataType::Type::kInt8:
3830         case DataType::Type::kUint16:
3831         case DataType::Type::kInt16:
3832         case DataType::Type::kInt32:
3833           locations->SetInAt(0, Location::RequiresRegister());
3834           locations->SetOut(Location::RequiresFpuRegister());
3835           break;
3836 
3837         case DataType::Type::kInt64:
3838           locations->SetInAt(0, Location::RequiresRegister());
3839           locations->SetOut(Location::RequiresFpuRegister());
3840           locations->AddTemp(Location::RequiresFpuRegister());
3841           locations->AddTemp(Location::RequiresFpuRegister());
3842           break;
3843 
3844         case DataType::Type::kFloat32:
3845           locations->SetInAt(0, Location::RequiresFpuRegister());
3846           locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3847           break;
3848 
3849         default:
3850           LOG(FATAL) << "Unexpected type conversion from " << input_type
3851                      << " to " << result_type;
3852       }
3853       break;
3854 
3855     default:
3856       LOG(FATAL) << "Unexpected type conversion from " << input_type
3857                  << " to " << result_type;
3858   }
3859 }
3860 
VisitTypeConversion(HTypeConversion * conversion)3861 void InstructionCodeGeneratorARMVIXL::VisitTypeConversion(HTypeConversion* conversion) {
3862   LocationSummary* locations = conversion->GetLocations();
3863   Location out = locations->Out();
3864   Location in = locations->InAt(0);
3865   DataType::Type result_type = conversion->GetResultType();
3866   DataType::Type input_type = conversion->GetInputType();
3867   DCHECK(!DataType::IsTypeConversionImplicit(input_type, result_type))
3868       << input_type << " -> " << result_type;
3869   switch (result_type) {
3870     case DataType::Type::kUint8:
3871       switch (input_type) {
3872         case DataType::Type::kInt8:
3873         case DataType::Type::kUint16:
3874         case DataType::Type::kInt16:
3875         case DataType::Type::kInt32:
3876           __ Ubfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 8);
3877           break;
3878         case DataType::Type::kInt64:
3879           __ Ubfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 8);
3880           break;
3881 
3882         default:
3883           LOG(FATAL) << "Unexpected type conversion from " << input_type
3884                      << " to " << result_type;
3885       }
3886       break;
3887 
3888     case DataType::Type::kInt8:
3889       switch (input_type) {
3890         case DataType::Type::kUint8:
3891         case DataType::Type::kUint16:
3892         case DataType::Type::kInt16:
3893         case DataType::Type::kInt32:
3894           __ Sbfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 8);
3895           break;
3896         case DataType::Type::kInt64:
3897           __ Sbfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 8);
3898           break;
3899 
3900         default:
3901           LOG(FATAL) << "Unexpected type conversion from " << input_type
3902                      << " to " << result_type;
3903       }
3904       break;
3905 
3906     case DataType::Type::kUint16:
3907       switch (input_type) {
3908         case DataType::Type::kInt8:
3909         case DataType::Type::kInt16:
3910         case DataType::Type::kInt32:
3911           __ Ubfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 16);
3912           break;
3913         case DataType::Type::kInt64:
3914           __ Ubfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 16);
3915           break;
3916 
3917         default:
3918           LOG(FATAL) << "Unexpected type conversion from " << input_type
3919                      << " to " << result_type;
3920       }
3921       break;
3922 
3923     case DataType::Type::kInt16:
3924       switch (input_type) {
3925         case DataType::Type::kUint16:
3926         case DataType::Type::kInt32:
3927           __ Sbfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 16);
3928           break;
3929         case DataType::Type::kInt64:
3930           __ Sbfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 16);
3931           break;
3932 
3933         default:
3934           LOG(FATAL) << "Unexpected type conversion from " << input_type
3935                      << " to " << result_type;
3936       }
3937       break;
3938 
3939     case DataType::Type::kInt32:
3940       switch (input_type) {
3941         case DataType::Type::kInt64:
3942           DCHECK(out.IsRegister());
3943           if (in.IsRegisterPair()) {
3944             __ Mov(OutputRegister(conversion), LowRegisterFrom(in));
3945           } else if (in.IsDoubleStackSlot()) {
3946             GetAssembler()->LoadFromOffset(kLoadWord,
3947                                            OutputRegister(conversion),
3948                                            sp,
3949                                            in.GetStackIndex());
3950           } else {
3951             DCHECK(in.IsConstant());
3952             DCHECK(in.GetConstant()->IsLongConstant());
3953             int64_t value = in.GetConstant()->AsLongConstant()->GetValue();
3954             __ Mov(OutputRegister(conversion), static_cast<int32_t>(value));
3955           }
3956           break;
3957 
3958         case DataType::Type::kFloat32: {
3959           vixl32::SRegister temp = LowSRegisterFrom(locations->GetTemp(0));
3960           __ Vcvt(S32, F32, temp, InputSRegisterAt(conversion, 0));
3961           __ Vmov(OutputRegister(conversion), temp);
3962           break;
3963         }
3964 
3965         case DataType::Type::kFloat64: {
3966           vixl32::SRegister temp_s = LowSRegisterFrom(locations->GetTemp(0));
3967           __ Vcvt(S32, F64, temp_s, DRegisterFrom(in));
3968           __ Vmov(OutputRegister(conversion), temp_s);
3969           break;
3970         }
3971 
3972         default:
3973           LOG(FATAL) << "Unexpected type conversion from " << input_type
3974                      << " to " << result_type;
3975       }
3976       break;
3977 
3978     case DataType::Type::kInt64:
3979       switch (input_type) {
3980         case DataType::Type::kBool:
3981         case DataType::Type::kUint8:
3982         case DataType::Type::kInt8:
3983         case DataType::Type::kUint16:
3984         case DataType::Type::kInt16:
3985         case DataType::Type::kInt32:
3986           DCHECK(out.IsRegisterPair());
3987           DCHECK(in.IsRegister());
3988           __ Mov(LowRegisterFrom(out), InputRegisterAt(conversion, 0));
3989           // Sign extension.
3990           __ Asr(HighRegisterFrom(out), LowRegisterFrom(out), 31);
3991           break;
3992 
3993         case DataType::Type::kFloat32:
3994           codegen_->InvokeRuntime(kQuickF2l, conversion, conversion->GetDexPc());
3995           CheckEntrypointTypes<kQuickF2l, int64_t, float>();
3996           break;
3997 
3998         case DataType::Type::kFloat64:
3999           codegen_->InvokeRuntime(kQuickD2l, conversion, conversion->GetDexPc());
4000           CheckEntrypointTypes<kQuickD2l, int64_t, double>();
4001           break;
4002 
4003         default:
4004           LOG(FATAL) << "Unexpected type conversion from " << input_type
4005                      << " to " << result_type;
4006       }
4007       break;
4008 
4009     case DataType::Type::kFloat32:
4010       switch (input_type) {
4011         case DataType::Type::kBool:
4012         case DataType::Type::kUint8:
4013         case DataType::Type::kInt8:
4014         case DataType::Type::kUint16:
4015         case DataType::Type::kInt16:
4016         case DataType::Type::kInt32:
4017           __ Vmov(OutputSRegister(conversion), InputRegisterAt(conversion, 0));
4018           __ Vcvt(F32, S32, OutputSRegister(conversion), OutputSRegister(conversion));
4019           break;
4020 
4021         case DataType::Type::kInt64:
4022           codegen_->InvokeRuntime(kQuickL2f, conversion, conversion->GetDexPc());
4023           CheckEntrypointTypes<kQuickL2f, float, int64_t>();
4024           break;
4025 
4026         case DataType::Type::kFloat64:
4027           __ Vcvt(F32, F64, OutputSRegister(conversion), DRegisterFrom(in));
4028           break;
4029 
4030         default:
4031           LOG(FATAL) << "Unexpected type conversion from " << input_type
4032                      << " to " << result_type;
4033       }
4034       break;
4035 
4036     case DataType::Type::kFloat64:
4037       switch (input_type) {
4038         case DataType::Type::kBool:
4039         case DataType::Type::kUint8:
4040         case DataType::Type::kInt8:
4041         case DataType::Type::kUint16:
4042         case DataType::Type::kInt16:
4043         case DataType::Type::kInt32:
4044           __ Vmov(LowSRegisterFrom(out), InputRegisterAt(conversion, 0));
4045           __ Vcvt(F64, S32, DRegisterFrom(out), LowSRegisterFrom(out));
4046           break;
4047 
4048         case DataType::Type::kInt64: {
4049           vixl32::Register low = LowRegisterFrom(in);
4050           vixl32::Register high = HighRegisterFrom(in);
4051           vixl32::SRegister out_s = LowSRegisterFrom(out);
4052           vixl32::DRegister out_d = DRegisterFrom(out);
4053           vixl32::SRegister temp_s = LowSRegisterFrom(locations->GetTemp(0));
4054           vixl32::DRegister temp_d = DRegisterFrom(locations->GetTemp(0));
4055           vixl32::DRegister constant_d = DRegisterFrom(locations->GetTemp(1));
4056 
4057           // temp_d = int-to-double(high)
4058           __ Vmov(temp_s, high);
4059           __ Vcvt(F64, S32, temp_d, temp_s);
4060           // constant_d = k2Pow32EncodingForDouble
4061           __ Vmov(constant_d, bit_cast<double, int64_t>(k2Pow32EncodingForDouble));
4062           // out_d = unsigned-to-double(low)
4063           __ Vmov(out_s, low);
4064           __ Vcvt(F64, U32, out_d, out_s);
4065           // out_d += temp_d * constant_d
4066           __ Vmla(F64, out_d, temp_d, constant_d);
4067           break;
4068         }
4069 
4070         case DataType::Type::kFloat32:
4071           __ Vcvt(F64, F32, DRegisterFrom(out), InputSRegisterAt(conversion, 0));
4072           break;
4073 
4074         default:
4075           LOG(FATAL) << "Unexpected type conversion from " << input_type
4076                      << " to " << result_type;
4077       }
4078       break;
4079 
4080     default:
4081       LOG(FATAL) << "Unexpected type conversion from " << input_type
4082                  << " to " << result_type;
4083   }
4084 }
4085 
VisitAdd(HAdd * add)4086 void LocationsBuilderARMVIXL::VisitAdd(HAdd* add) {
4087   LocationSummary* locations =
4088       new (GetGraph()->GetAllocator()) LocationSummary(add, LocationSummary::kNoCall);
4089   switch (add->GetResultType()) {
4090     case DataType::Type::kInt32: {
4091       locations->SetInAt(0, Location::RequiresRegister());
4092       locations->SetInAt(1, Location::RegisterOrConstant(add->InputAt(1)));
4093       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4094       break;
4095     }
4096 
4097     case DataType::Type::kInt64: {
4098       locations->SetInAt(0, Location::RequiresRegister());
4099       locations->SetInAt(1, ArmEncodableConstantOrRegister(add->InputAt(1), ADD));
4100       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4101       break;
4102     }
4103 
4104     case DataType::Type::kFloat32:
4105     case DataType::Type::kFloat64: {
4106       locations->SetInAt(0, Location::RequiresFpuRegister());
4107       locations->SetInAt(1, Location::RequiresFpuRegister());
4108       locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4109       break;
4110     }
4111 
4112     default:
4113       LOG(FATAL) << "Unexpected add type " << add->GetResultType();
4114   }
4115 }
4116 
VisitAdd(HAdd * add)4117 void InstructionCodeGeneratorARMVIXL::VisitAdd(HAdd* add) {
4118   LocationSummary* locations = add->GetLocations();
4119   Location out = locations->Out();
4120   Location first = locations->InAt(0);
4121   Location second = locations->InAt(1);
4122 
4123   switch (add->GetResultType()) {
4124     case DataType::Type::kInt32: {
4125       __ Add(OutputRegister(add), InputRegisterAt(add, 0), InputOperandAt(add, 1));
4126       }
4127       break;
4128 
4129     case DataType::Type::kInt64: {
4130       if (second.IsConstant()) {
4131         uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
4132         GenerateAddLongConst(out, first, value);
4133       } else {
4134         DCHECK(second.IsRegisterPair());
4135         __ Adds(LowRegisterFrom(out), LowRegisterFrom(first), LowRegisterFrom(second));
4136         __ Adc(HighRegisterFrom(out), HighRegisterFrom(first), HighRegisterFrom(second));
4137       }
4138       break;
4139     }
4140 
4141     case DataType::Type::kFloat32:
4142     case DataType::Type::kFloat64:
4143       __ Vadd(OutputVRegister(add), InputVRegisterAt(add, 0), InputVRegisterAt(add, 1));
4144       break;
4145 
4146     default:
4147       LOG(FATAL) << "Unexpected add type " << add->GetResultType();
4148   }
4149 }
4150 
VisitSub(HSub * sub)4151 void LocationsBuilderARMVIXL::VisitSub(HSub* sub) {
4152   LocationSummary* locations =
4153       new (GetGraph()->GetAllocator()) LocationSummary(sub, LocationSummary::kNoCall);
4154   switch (sub->GetResultType()) {
4155     case DataType::Type::kInt32: {
4156       locations->SetInAt(0, Location::RequiresRegister());
4157       locations->SetInAt(1, Location::RegisterOrConstant(sub->InputAt(1)));
4158       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4159       break;
4160     }
4161 
4162     case DataType::Type::kInt64: {
4163       locations->SetInAt(0, Location::RequiresRegister());
4164       locations->SetInAt(1, ArmEncodableConstantOrRegister(sub->InputAt(1), SUB));
4165       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4166       break;
4167     }
4168     case DataType::Type::kFloat32:
4169     case DataType::Type::kFloat64: {
4170       locations->SetInAt(0, Location::RequiresFpuRegister());
4171       locations->SetInAt(1, Location::RequiresFpuRegister());
4172       locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4173       break;
4174     }
4175     default:
4176       LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
4177   }
4178 }
4179 
VisitSub(HSub * sub)4180 void InstructionCodeGeneratorARMVIXL::VisitSub(HSub* sub) {
4181   LocationSummary* locations = sub->GetLocations();
4182   Location out = locations->Out();
4183   Location first = locations->InAt(0);
4184   Location second = locations->InAt(1);
4185   switch (sub->GetResultType()) {
4186     case DataType::Type::kInt32: {
4187       __ Sub(OutputRegister(sub), InputRegisterAt(sub, 0), InputOperandAt(sub, 1));
4188       break;
4189     }
4190 
4191     case DataType::Type::kInt64: {
4192       if (second.IsConstant()) {
4193         uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
4194         GenerateAddLongConst(out, first, -value);
4195       } else {
4196         DCHECK(second.IsRegisterPair());
4197         __ Subs(LowRegisterFrom(out), LowRegisterFrom(first), LowRegisterFrom(second));
4198         __ Sbc(HighRegisterFrom(out), HighRegisterFrom(first), HighRegisterFrom(second));
4199       }
4200       break;
4201     }
4202 
4203     case DataType::Type::kFloat32:
4204     case DataType::Type::kFloat64:
4205       __ Vsub(OutputVRegister(sub), InputVRegisterAt(sub, 0), InputVRegisterAt(sub, 1));
4206       break;
4207 
4208     default:
4209       LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
4210   }
4211 }
4212 
VisitMul(HMul * mul)4213 void LocationsBuilderARMVIXL::VisitMul(HMul* mul) {
4214   LocationSummary* locations =
4215       new (GetGraph()->GetAllocator()) LocationSummary(mul, LocationSummary::kNoCall);
4216   switch (mul->GetResultType()) {
4217     case DataType::Type::kInt32:
4218     case DataType::Type::kInt64:  {
4219       locations->SetInAt(0, Location::RequiresRegister());
4220       locations->SetInAt(1, Location::RequiresRegister());
4221       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4222       break;
4223     }
4224 
4225     case DataType::Type::kFloat32:
4226     case DataType::Type::kFloat64: {
4227       locations->SetInAt(0, Location::RequiresFpuRegister());
4228       locations->SetInAt(1, Location::RequiresFpuRegister());
4229       locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4230       break;
4231     }
4232 
4233     default:
4234       LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
4235   }
4236 }
4237 
VisitMul(HMul * mul)4238 void InstructionCodeGeneratorARMVIXL::VisitMul(HMul* mul) {
4239   LocationSummary* locations = mul->GetLocations();
4240   Location out = locations->Out();
4241   Location first = locations->InAt(0);
4242   Location second = locations->InAt(1);
4243   switch (mul->GetResultType()) {
4244     case DataType::Type::kInt32: {
4245       __ Mul(OutputRegister(mul), InputRegisterAt(mul, 0), InputRegisterAt(mul, 1));
4246       break;
4247     }
4248     case DataType::Type::kInt64: {
4249       vixl32::Register out_hi = HighRegisterFrom(out);
4250       vixl32::Register out_lo = LowRegisterFrom(out);
4251       vixl32::Register in1_hi = HighRegisterFrom(first);
4252       vixl32::Register in1_lo = LowRegisterFrom(first);
4253       vixl32::Register in2_hi = HighRegisterFrom(second);
4254       vixl32::Register in2_lo = LowRegisterFrom(second);
4255 
4256       // Extra checks to protect caused by the existence of R1_R2.
4257       // The algorithm is wrong if out.hi is either in1.lo or in2.lo:
4258       // (e.g. in1=r0_r1, in2=r2_r3 and out=r1_r2);
4259       DCHECK(!out_hi.Is(in1_lo));
4260       DCHECK(!out_hi.Is(in2_lo));
4261 
4262       // input: in1 - 64 bits, in2 - 64 bits
4263       // output: out
4264       // formula: out.hi : out.lo = (in1.lo * in2.hi + in1.hi * in2.lo)* 2^32 + in1.lo * in2.lo
4265       // parts: out.hi = in1.lo * in2.hi + in1.hi * in2.lo + (in1.lo * in2.lo)[63:32]
4266       // parts: out.lo = (in1.lo * in2.lo)[31:0]
4267 
4268       UseScratchRegisterScope temps(GetVIXLAssembler());
4269       vixl32::Register temp = temps.Acquire();
4270       // temp <- in1.lo * in2.hi
4271       __ Mul(temp, in1_lo, in2_hi);
4272       // out.hi <- in1.lo * in2.hi + in1.hi * in2.lo
4273       __ Mla(out_hi, in1_hi, in2_lo, temp);
4274       // out.lo <- (in1.lo * in2.lo)[31:0];
4275       __ Umull(out_lo, temp, in1_lo, in2_lo);
4276       // out.hi <- in2.hi * in1.lo +  in2.lo * in1.hi + (in1.lo * in2.lo)[63:32]
4277       __ Add(out_hi, out_hi, temp);
4278       break;
4279     }
4280 
4281     case DataType::Type::kFloat32:
4282     case DataType::Type::kFloat64:
4283       __ Vmul(OutputVRegister(mul), InputVRegisterAt(mul, 0), InputVRegisterAt(mul, 1));
4284       break;
4285 
4286     default:
4287       LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
4288   }
4289 }
4290 
DivRemOneOrMinusOne(HBinaryOperation * instruction)4291 void InstructionCodeGeneratorARMVIXL::DivRemOneOrMinusOne(HBinaryOperation* instruction) {
4292   DCHECK(instruction->IsDiv() || instruction->IsRem());
4293   DCHECK(instruction->GetResultType() == DataType::Type::kInt32);
4294 
4295   Location second = instruction->GetLocations()->InAt(1);
4296   DCHECK(second.IsConstant());
4297 
4298   vixl32::Register out = OutputRegister(instruction);
4299   vixl32::Register dividend = InputRegisterAt(instruction, 0);
4300   int32_t imm = Int32ConstantFrom(second);
4301   DCHECK(imm == 1 || imm == -1);
4302 
4303   if (instruction->IsRem()) {
4304     __ Mov(out, 0);
4305   } else {
4306     if (imm == 1) {
4307       __ Mov(out, dividend);
4308     } else {
4309       __ Rsb(out, dividend, 0);
4310     }
4311   }
4312 }
4313 
DivRemByPowerOfTwo(HBinaryOperation * instruction)4314 void InstructionCodeGeneratorARMVIXL::DivRemByPowerOfTwo(HBinaryOperation* instruction) {
4315   DCHECK(instruction->IsDiv() || instruction->IsRem());
4316   DCHECK(instruction->GetResultType() == DataType::Type::kInt32);
4317 
4318   LocationSummary* locations = instruction->GetLocations();
4319   Location second = locations->InAt(1);
4320   DCHECK(second.IsConstant());
4321 
4322   vixl32::Register out = OutputRegister(instruction);
4323   vixl32::Register dividend = InputRegisterAt(instruction, 0);
4324   int32_t imm = Int32ConstantFrom(second);
4325   uint32_t abs_imm = static_cast<uint32_t>(AbsOrMin(imm));
4326   int ctz_imm = CTZ(abs_imm);
4327 
4328   auto generate_div_code = [this, imm, ctz_imm](vixl32::Register out, vixl32::Register in) {
4329     __ Asr(out, in, ctz_imm);
4330     if (imm < 0) {
4331       __ Rsb(out, out, 0);
4332     }
4333   };
4334 
4335   if (HasNonNegativeOrMinIntInputAt(instruction, 0)) {
4336     // No need to adjust the result for non-negative dividends or the INT32_MIN dividend.
4337     // NOTE: The generated code for HDiv/HRem correctly works for the INT32_MIN dividend:
4338     //   imm == 2
4339     //     HDiv
4340     //      add out, dividend(0x80000000), dividend(0x80000000), lsr #31 => out = 0x80000001
4341     //      asr out, out(0x80000001), #1 => out = 0xc0000000
4342     //      This is the same as 'asr out, dividend(0x80000000), #1'
4343     //
4344     //   imm > 2
4345     //     HDiv
4346     //      asr out, dividend(0x80000000), #31 => out = -1
4347     //      add out, dividend(0x80000000), out(-1), lsr #(32 - ctz_imm) => out = 0b10..01..1,
4348     //          where the number of the rightmost 1s is ctz_imm.
4349     //      asr out, out(0b10..01..1), #ctz_imm => out = 0b1..10..0, where the number of the
4350     //          leftmost 1s is ctz_imm + 1.
4351     //      This is the same as 'asr out, dividend(0x80000000), #ctz_imm'.
4352     //
4353     //   imm == INT32_MIN
4354     //     HDiv
4355     //      asr out, dividend(0x80000000), #31 => out = -1
4356     //      add out, dividend(0x80000000), out(-1), lsr #1 => out = 0xc0000000
4357     //      asr out, out(0xc0000000), #31 => out = -1
4358     //      rsb out, out(-1), #0 => out = 1
4359     //      This is the same as
4360     //        asr out, dividend(0x80000000), #31
4361     //        rsb out, out, #0
4362     //
4363     //
4364     //   INT_MIN % imm must be 0 for any imm of power 2. 'and' and 'ubfx' work only with bits
4365     //   0..30 of a dividend. For INT32_MIN those bits are zeros. So 'and' and 'ubfx' always
4366     //   produce zero.
4367     if (instruction->IsDiv()) {
4368       generate_div_code(out, dividend);
4369     } else {
4370       if (GetVIXLAssembler()->IsModifiedImmediate(abs_imm - 1)) {
4371         __ And(out, dividend, abs_imm - 1);
4372       } else {
4373         __ Ubfx(out, dividend, 0, ctz_imm);
4374       }
4375       return;
4376     }
4377   } else {
4378     vixl32::Register add_right_input = dividend;
4379     if (ctz_imm > 1) {
4380       __ Asr(out, dividend, 31);
4381       add_right_input = out;
4382     }
4383     __ Add(out, dividend, Operand(add_right_input, vixl32::LSR, 32 - ctz_imm));
4384 
4385     if (instruction->IsDiv()) {
4386       generate_div_code(out, out);
4387     } else {
4388       __ Bfc(out, 0, ctz_imm);
4389       __ Sub(out, dividend, out);
4390     }
4391   }
4392 }
4393 
GenerateDivRemWithAnyConstant(HBinaryOperation * instruction)4394 void InstructionCodeGeneratorARMVIXL::GenerateDivRemWithAnyConstant(HBinaryOperation* instruction) {
4395   DCHECK(instruction->IsDiv() || instruction->IsRem());
4396   DCHECK(instruction->GetResultType() == DataType::Type::kInt32);
4397 
4398   LocationSummary* locations = instruction->GetLocations();
4399   Location second = locations->InAt(1);
4400   DCHECK(second.IsConstant());
4401 
4402   vixl32::Register out = OutputRegister(instruction);
4403   vixl32::Register dividend = InputRegisterAt(instruction, 0);
4404   vixl32::Register temp1 = RegisterFrom(locations->GetTemp(0));
4405   vixl32::Register temp2 = RegisterFrom(locations->GetTemp(1));
4406   int32_t imm = Int32ConstantFrom(second);
4407 
4408   int64_t magic;
4409   int shift;
4410   CalculateMagicAndShiftForDivRem(imm, /* is_long= */ false, &magic, &shift);
4411 
4412   auto generate_unsigned_div_code =[this, magic, shift](vixl32::Register out,
4413                                                         vixl32::Register dividend,
4414                                                         vixl32::Register temp1,
4415                                                         vixl32::Register temp2) {
4416     // TODO(VIXL): Change the static cast to Operand::From() after VIXL is fixed.
4417     __ Mov(temp1, static_cast<int32_t>(magic));
4418     if (magic > 0 && shift == 0) {
4419       __ Smull(temp2, out, dividend, temp1);
4420     } else {
4421       __ Smull(temp2, temp1, dividend, temp1);
4422       if (magic < 0) {
4423         // The negative magic M = static_cast<int>(m) means that the multiplier m is greater
4424         // than INT32_MAX. In such a case shift is never 0.
4425         // Proof:
4426         //   m = (2^p + d - 2^p % d) / d, where p = 32 + shift, d > 2
4427         //
4428         //   If shift == 0, m = (2^32 + d - 2^32 % d) / d =
4429         //   = (2^32 + d - (2^32 - (2^32 / d) * d)) / d =
4430         //   = (d + (2^32 / d) * d) / d = 1 + (2^32 / d), here '/' is the integer division.
4431         //
4432         //   1 + (2^32 / d) is decreasing when d is increasing.
4433         //   The maximum is 1 431 655 766, when d == 3. This value is less than INT32_MAX.
4434         //   the minimum is 3, when d = 2^31 -1.
4435         //   So for all values of d in [3, INT32_MAX] m with p == 32 is in [3, INT32_MAX) and
4436         //   is never less than 0.
4437         __ Add(temp1, temp1, dividend);
4438       }
4439       DCHECK_NE(shift, 0);
4440       __ Lsr(out, temp1, shift);
4441     }
4442   };
4443 
4444   if (imm > 0 && HasNonNegativeInputAt(instruction, 0)) {
4445     // No need to adjust the result for a non-negative dividend and a positive divisor.
4446     if (instruction->IsDiv()) {
4447       generate_unsigned_div_code(out, dividend, temp1, temp2);
4448     } else {
4449       generate_unsigned_div_code(temp1, dividend, temp1, temp2);
4450       __ Mov(temp2, imm);
4451       __ Mls(out, temp1, temp2, dividend);
4452     }
4453   } else {
4454     // TODO(VIXL): Change the static cast to Operand::From() after VIXL is fixed.
4455     __ Mov(temp1, static_cast<int32_t>(magic));
4456     __ Smull(temp2, temp1, dividend, temp1);
4457 
4458     if (imm > 0 && magic < 0) {
4459       __ Add(temp1, temp1, dividend);
4460     } else if (imm < 0 && magic > 0) {
4461       __ Sub(temp1, temp1, dividend);
4462     }
4463 
4464     if (shift != 0) {
4465       __ Asr(temp1, temp1, shift);
4466     }
4467 
4468     if (instruction->IsDiv()) {
4469       __ Sub(out, temp1, Operand(temp1, vixl32::Shift(ASR), 31));
4470     } else {
4471       __ Sub(temp1, temp1, Operand(temp1, vixl32::Shift(ASR), 31));
4472       // TODO: Strength reduction for mls.
4473       __ Mov(temp2, imm);
4474       __ Mls(out, temp1, temp2, dividend);
4475     }
4476   }
4477 }
4478 
GenerateDivRemConstantIntegral(HBinaryOperation * instruction)4479 void InstructionCodeGeneratorARMVIXL::GenerateDivRemConstantIntegral(
4480     HBinaryOperation* instruction) {
4481   DCHECK(instruction->IsDiv() || instruction->IsRem());
4482   DCHECK(instruction->GetResultType() == DataType::Type::kInt32);
4483 
4484   Location second = instruction->GetLocations()->InAt(1);
4485   DCHECK(second.IsConstant());
4486 
4487   int32_t imm = Int32ConstantFrom(second);
4488   if (imm == 0) {
4489     // Do not generate anything. DivZeroCheck would prevent any code to be executed.
4490   } else if (imm == 1 || imm == -1) {
4491     DivRemOneOrMinusOne(instruction);
4492   } else if (IsPowerOfTwo(AbsOrMin(imm))) {
4493     DivRemByPowerOfTwo(instruction);
4494   } else {
4495     DCHECK(imm <= -2 || imm >= 2);
4496     GenerateDivRemWithAnyConstant(instruction);
4497   }
4498 }
4499 
VisitDiv(HDiv * div)4500 void LocationsBuilderARMVIXL::VisitDiv(HDiv* div) {
4501   LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
4502   if (div->GetResultType() == DataType::Type::kInt64) {
4503     // pLdiv runtime call.
4504     call_kind = LocationSummary::kCallOnMainOnly;
4505   } else if (div->GetResultType() == DataType::Type::kInt32 && div->InputAt(1)->IsConstant()) {
4506     // sdiv will be replaced by other instruction sequence.
4507   } else if (div->GetResultType() == DataType::Type::kInt32 &&
4508              !codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4509     // pIdivmod runtime call.
4510     call_kind = LocationSummary::kCallOnMainOnly;
4511   }
4512 
4513   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(div, call_kind);
4514 
4515   switch (div->GetResultType()) {
4516     case DataType::Type::kInt32: {
4517       if (div->InputAt(1)->IsConstant()) {
4518         locations->SetInAt(0, Location::RequiresRegister());
4519         locations->SetInAt(1, Location::ConstantLocation(div->InputAt(1)->AsConstant()));
4520         int32_t value = Int32ConstantFrom(div->InputAt(1));
4521         Location::OutputOverlap out_overlaps = Location::kNoOutputOverlap;
4522         if (value == 1 || value == 0 || value == -1) {
4523           // No temp register required.
4524         } else if (IsPowerOfTwo(AbsOrMin(value)) &&
4525                    value != 2 &&
4526                    value != -2 &&
4527                    !HasNonNegativeOrMinIntInputAt(div, 0)) {
4528           // The "out" register is used as a temporary, so it overlaps with the inputs.
4529           out_overlaps = Location::kOutputOverlap;
4530         } else {
4531           locations->AddRegisterTemps(2);
4532         }
4533         locations->SetOut(Location::RequiresRegister(), out_overlaps);
4534       } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4535         locations->SetInAt(0, Location::RequiresRegister());
4536         locations->SetInAt(1, Location::RequiresRegister());
4537         locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4538       } else {
4539         InvokeRuntimeCallingConventionARMVIXL calling_convention;
4540         locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
4541         locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
4542         // Note: divmod will compute both the quotient and the remainder as the pair R0 and R1, but
4543         //       we only need the former.
4544         locations->SetOut(LocationFrom(r0));
4545       }
4546       break;
4547     }
4548     case DataType::Type::kInt64: {
4549       InvokeRuntimeCallingConventionARMVIXL calling_convention;
4550       locations->SetInAt(0, LocationFrom(
4551           calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
4552       locations->SetInAt(1, LocationFrom(
4553           calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3)));
4554       locations->SetOut(LocationFrom(r0, r1));
4555       break;
4556     }
4557     case DataType::Type::kFloat32:
4558     case DataType::Type::kFloat64: {
4559       locations->SetInAt(0, Location::RequiresFpuRegister());
4560       locations->SetInAt(1, Location::RequiresFpuRegister());
4561       locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4562       break;
4563     }
4564 
4565     default:
4566       LOG(FATAL) << "Unexpected div type " << div->GetResultType();
4567   }
4568 }
4569 
VisitDiv(HDiv * div)4570 void InstructionCodeGeneratorARMVIXL::VisitDiv(HDiv* div) {
4571   Location lhs = div->GetLocations()->InAt(0);
4572   Location rhs = div->GetLocations()->InAt(1);
4573 
4574   switch (div->GetResultType()) {
4575     case DataType::Type::kInt32: {
4576       if (rhs.IsConstant()) {
4577         GenerateDivRemConstantIntegral(div);
4578       } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4579         __ Sdiv(OutputRegister(div), InputRegisterAt(div, 0), InputRegisterAt(div, 1));
4580       } else {
4581         InvokeRuntimeCallingConventionARMVIXL calling_convention;
4582         DCHECK(calling_convention.GetRegisterAt(0).Is(RegisterFrom(lhs)));
4583         DCHECK(calling_convention.GetRegisterAt(1).Is(RegisterFrom(rhs)));
4584         DCHECK(r0.Is(OutputRegister(div)));
4585 
4586         codegen_->InvokeRuntime(kQuickIdivmod, div, div->GetDexPc());
4587         CheckEntrypointTypes<kQuickIdivmod, int32_t, int32_t, int32_t>();
4588       }
4589       break;
4590     }
4591 
4592     case DataType::Type::kInt64: {
4593       InvokeRuntimeCallingConventionARMVIXL calling_convention;
4594       DCHECK(calling_convention.GetRegisterAt(0).Is(LowRegisterFrom(lhs)));
4595       DCHECK(calling_convention.GetRegisterAt(1).Is(HighRegisterFrom(lhs)));
4596       DCHECK(calling_convention.GetRegisterAt(2).Is(LowRegisterFrom(rhs)));
4597       DCHECK(calling_convention.GetRegisterAt(3).Is(HighRegisterFrom(rhs)));
4598       DCHECK(LowRegisterFrom(div->GetLocations()->Out()).Is(r0));
4599       DCHECK(HighRegisterFrom(div->GetLocations()->Out()).Is(r1));
4600 
4601       codegen_->InvokeRuntime(kQuickLdiv, div, div->GetDexPc());
4602       CheckEntrypointTypes<kQuickLdiv, int64_t, int64_t, int64_t>();
4603       break;
4604     }
4605 
4606     case DataType::Type::kFloat32:
4607     case DataType::Type::kFloat64:
4608       __ Vdiv(OutputVRegister(div), InputVRegisterAt(div, 0), InputVRegisterAt(div, 1));
4609       break;
4610 
4611     default:
4612       LOG(FATAL) << "Unexpected div type " << div->GetResultType();
4613   }
4614 }
4615 
VisitRem(HRem * rem)4616 void LocationsBuilderARMVIXL::VisitRem(HRem* rem) {
4617   DataType::Type type = rem->GetResultType();
4618 
4619   // Most remainders are implemented in the runtime.
4620   LocationSummary::CallKind call_kind = LocationSummary::kCallOnMainOnly;
4621   if (rem->GetResultType() == DataType::Type::kInt32 && rem->InputAt(1)->IsConstant()) {
4622     // sdiv will be replaced by other instruction sequence.
4623     call_kind = LocationSummary::kNoCall;
4624   } else if ((rem->GetResultType() == DataType::Type::kInt32)
4625              && codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4626     // Have hardware divide instruction for int, do it with three instructions.
4627     call_kind = LocationSummary::kNoCall;
4628   }
4629 
4630   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(rem, call_kind);
4631 
4632   switch (type) {
4633     case DataType::Type::kInt32: {
4634       if (rem->InputAt(1)->IsConstant()) {
4635         locations->SetInAt(0, Location::RequiresRegister());
4636         locations->SetInAt(1, Location::ConstantLocation(rem->InputAt(1)->AsConstant()));
4637         int32_t value = Int32ConstantFrom(rem->InputAt(1));
4638         Location::OutputOverlap out_overlaps = Location::kNoOutputOverlap;
4639         if (value == 1 || value == 0 || value == -1) {
4640           // No temp register required.
4641         } else if (IsPowerOfTwo(AbsOrMin(value)) && !HasNonNegativeOrMinIntInputAt(rem, 0)) {
4642           // The "out" register is used as a temporary, so it overlaps with the inputs.
4643           out_overlaps = Location::kOutputOverlap;
4644         } else {
4645           locations->AddRegisterTemps(2);
4646         }
4647         locations->SetOut(Location::RequiresRegister(), out_overlaps);
4648       } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4649         locations->SetInAt(0, Location::RequiresRegister());
4650         locations->SetInAt(1, Location::RequiresRegister());
4651         locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4652         locations->AddTemp(Location::RequiresRegister());
4653       } else {
4654         InvokeRuntimeCallingConventionARMVIXL calling_convention;
4655         locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
4656         locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
4657         // Note: divmod will compute both the quotient and the remainder as the pair R0 and R1, but
4658         //       we only need the latter.
4659         locations->SetOut(LocationFrom(r1));
4660       }
4661       break;
4662     }
4663     case DataType::Type::kInt64: {
4664       InvokeRuntimeCallingConventionARMVIXL calling_convention;
4665       locations->SetInAt(0, LocationFrom(
4666           calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
4667       locations->SetInAt(1, LocationFrom(
4668           calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3)));
4669       // The runtime helper puts the output in R2,R3.
4670       locations->SetOut(LocationFrom(r2, r3));
4671       break;
4672     }
4673     case DataType::Type::kFloat32: {
4674       InvokeRuntimeCallingConventionARMVIXL calling_convention;
4675       locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
4676       locations->SetInAt(1, LocationFrom(calling_convention.GetFpuRegisterAt(1)));
4677       locations->SetOut(LocationFrom(s0));
4678       break;
4679     }
4680 
4681     case DataType::Type::kFloat64: {
4682       InvokeRuntimeCallingConventionARMVIXL calling_convention;
4683       locations->SetInAt(0, LocationFrom(
4684           calling_convention.GetFpuRegisterAt(0), calling_convention.GetFpuRegisterAt(1)));
4685       locations->SetInAt(1, LocationFrom(
4686           calling_convention.GetFpuRegisterAt(2), calling_convention.GetFpuRegisterAt(3)));
4687       locations->SetOut(LocationFrom(s0, s1));
4688       break;
4689     }
4690 
4691     default:
4692       LOG(FATAL) << "Unexpected rem type " << type;
4693   }
4694 }
4695 
VisitRem(HRem * rem)4696 void InstructionCodeGeneratorARMVIXL::VisitRem(HRem* rem) {
4697   LocationSummary* locations = rem->GetLocations();
4698   Location second = locations->InAt(1);
4699 
4700   DataType::Type type = rem->GetResultType();
4701   switch (type) {
4702     case DataType::Type::kInt32: {
4703         vixl32::Register reg1 = InputRegisterAt(rem, 0);
4704         vixl32::Register out_reg = OutputRegister(rem);
4705         if (second.IsConstant()) {
4706           GenerateDivRemConstantIntegral(rem);
4707         } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4708         vixl32::Register reg2 = RegisterFrom(second);
4709         vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
4710 
4711         // temp = reg1 / reg2  (integer division)
4712         // dest = reg1 - temp * reg2
4713         __ Sdiv(temp, reg1, reg2);
4714         __ Mls(out_reg, temp, reg2, reg1);
4715       } else {
4716         InvokeRuntimeCallingConventionARMVIXL calling_convention;
4717         DCHECK(reg1.Is(calling_convention.GetRegisterAt(0)));
4718         DCHECK(RegisterFrom(second).Is(calling_convention.GetRegisterAt(1)));
4719         DCHECK(out_reg.Is(r1));
4720 
4721         codegen_->InvokeRuntime(kQuickIdivmod, rem, rem->GetDexPc());
4722         CheckEntrypointTypes<kQuickIdivmod, int32_t, int32_t, int32_t>();
4723       }
4724       break;
4725     }
4726 
4727     case DataType::Type::kInt64: {
4728       codegen_->InvokeRuntime(kQuickLmod, rem, rem->GetDexPc());
4729         CheckEntrypointTypes<kQuickLmod, int64_t, int64_t, int64_t>();
4730       break;
4731     }
4732 
4733     case DataType::Type::kFloat32: {
4734       codegen_->InvokeRuntime(kQuickFmodf, rem, rem->GetDexPc());
4735       CheckEntrypointTypes<kQuickFmodf, float, float, float>();
4736       break;
4737     }
4738 
4739     case DataType::Type::kFloat64: {
4740       codegen_->InvokeRuntime(kQuickFmod, rem, rem->GetDexPc());
4741       CheckEntrypointTypes<kQuickFmod, double, double, double>();
4742       break;
4743     }
4744 
4745     default:
4746       LOG(FATAL) << "Unexpected rem type " << type;
4747   }
4748 }
4749 
CreateMinMaxLocations(ArenaAllocator * allocator,HBinaryOperation * minmax)4750 static void CreateMinMaxLocations(ArenaAllocator* allocator, HBinaryOperation* minmax) {
4751   LocationSummary* locations = new (allocator) LocationSummary(minmax);
4752   switch (minmax->GetResultType()) {
4753     case DataType::Type::kInt32:
4754       locations->SetInAt(0, Location::RequiresRegister());
4755       locations->SetInAt(1, Location::RequiresRegister());
4756       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4757       break;
4758     case DataType::Type::kInt64:
4759       locations->SetInAt(0, Location::RequiresRegister());
4760       locations->SetInAt(1, Location::RequiresRegister());
4761       locations->SetOut(Location::SameAsFirstInput());
4762       break;
4763     case DataType::Type::kFloat32:
4764       locations->SetInAt(0, Location::RequiresFpuRegister());
4765       locations->SetInAt(1, Location::RequiresFpuRegister());
4766       locations->SetOut(Location::SameAsFirstInput());
4767       locations->AddTemp(Location::RequiresRegister());
4768       break;
4769     case DataType::Type::kFloat64:
4770       locations->SetInAt(0, Location::RequiresFpuRegister());
4771       locations->SetInAt(1, Location::RequiresFpuRegister());
4772       locations->SetOut(Location::SameAsFirstInput());
4773       break;
4774     default:
4775       LOG(FATAL) << "Unexpected type for HMinMax " << minmax->GetResultType();
4776   }
4777 }
4778 
GenerateMinMaxInt(LocationSummary * locations,bool is_min)4779 void InstructionCodeGeneratorARMVIXL::GenerateMinMaxInt(LocationSummary* locations, bool is_min) {
4780   Location op1_loc = locations->InAt(0);
4781   Location op2_loc = locations->InAt(1);
4782   Location out_loc = locations->Out();
4783 
4784   vixl32::Register op1 = RegisterFrom(op1_loc);
4785   vixl32::Register op2 = RegisterFrom(op2_loc);
4786   vixl32::Register out = RegisterFrom(out_loc);
4787 
4788   __ Cmp(op1, op2);
4789 
4790   {
4791     ExactAssemblyScope aas(GetVIXLAssembler(),
4792                            3 * kMaxInstructionSizeInBytes,
4793                            CodeBufferCheckScope::kMaximumSize);
4794 
4795     __ ite(is_min ? lt : gt);
4796     __ mov(is_min ? lt : gt, out, op1);
4797     __ mov(is_min ? ge : le, out, op2);
4798   }
4799 }
4800 
GenerateMinMaxLong(LocationSummary * locations,bool is_min)4801 void InstructionCodeGeneratorARMVIXL::GenerateMinMaxLong(LocationSummary* locations, bool is_min) {
4802   Location op1_loc = locations->InAt(0);
4803   Location op2_loc = locations->InAt(1);
4804   Location out_loc = locations->Out();
4805 
4806   // Optimization: don't generate any code if inputs are the same.
4807   if (op1_loc.Equals(op2_loc)) {
4808     DCHECK(out_loc.Equals(op1_loc));  // out_loc is set as SameAsFirstInput() in location builder.
4809     return;
4810   }
4811 
4812   vixl32::Register op1_lo = LowRegisterFrom(op1_loc);
4813   vixl32::Register op1_hi = HighRegisterFrom(op1_loc);
4814   vixl32::Register op2_lo = LowRegisterFrom(op2_loc);
4815   vixl32::Register op2_hi = HighRegisterFrom(op2_loc);
4816   vixl32::Register out_lo = LowRegisterFrom(out_loc);
4817   vixl32::Register out_hi = HighRegisterFrom(out_loc);
4818   UseScratchRegisterScope temps(GetVIXLAssembler());
4819   const vixl32::Register temp = temps.Acquire();
4820 
4821   DCHECK(op1_lo.Is(out_lo));
4822   DCHECK(op1_hi.Is(out_hi));
4823 
4824   // Compare op1 >= op2, or op1 < op2.
4825   __ Cmp(out_lo, op2_lo);
4826   __ Sbcs(temp, out_hi, op2_hi);
4827 
4828   // Now GE/LT condition code is correct for the long comparison.
4829   {
4830     vixl32::ConditionType cond = is_min ? ge : lt;
4831     ExactAssemblyScope it_scope(GetVIXLAssembler(),
4832                                 3 * kMaxInstructionSizeInBytes,
4833                                 CodeBufferCheckScope::kMaximumSize);
4834     __ itt(cond);
4835     __ mov(cond, out_lo, op2_lo);
4836     __ mov(cond, out_hi, op2_hi);
4837   }
4838 }
4839 
GenerateMinMaxFloat(HInstruction * minmax,bool is_min)4840 void InstructionCodeGeneratorARMVIXL::GenerateMinMaxFloat(HInstruction* minmax, bool is_min) {
4841   LocationSummary* locations = minmax->GetLocations();
4842   Location op1_loc = locations->InAt(0);
4843   Location op2_loc = locations->InAt(1);
4844   Location out_loc = locations->Out();
4845 
4846   // Optimization: don't generate any code if inputs are the same.
4847   if (op1_loc.Equals(op2_loc)) {
4848     DCHECK(out_loc.Equals(op1_loc));  // out_loc is set as SameAsFirstInput() in location builder.
4849     return;
4850   }
4851 
4852   vixl32::SRegister op1 = SRegisterFrom(op1_loc);
4853   vixl32::SRegister op2 = SRegisterFrom(op2_loc);
4854   vixl32::SRegister out = SRegisterFrom(out_loc);
4855 
4856   UseScratchRegisterScope temps(GetVIXLAssembler());
4857   const vixl32::Register temp1 = temps.Acquire();
4858   vixl32::Register temp2 = RegisterFrom(locations->GetTemp(0));
4859   vixl32::Label nan, done;
4860   vixl32::Label* final_label = codegen_->GetFinalLabel(minmax, &done);
4861 
4862   DCHECK(op1.Is(out));
4863 
4864   __ Vcmp(op1, op2);
4865   __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
4866   __ B(vs, &nan, /* is_far_target= */ false);  // if un-ordered, go to NaN handling.
4867 
4868   // op1 <> op2
4869   vixl32::ConditionType cond = is_min ? gt : lt;
4870   {
4871     ExactAssemblyScope it_scope(GetVIXLAssembler(),
4872                                 2 * kMaxInstructionSizeInBytes,
4873                                 CodeBufferCheckScope::kMaximumSize);
4874     __ it(cond);
4875     __ vmov(cond, F32, out, op2);
4876   }
4877   // for <>(not equal), we've done min/max calculation.
4878   __ B(ne, final_label, /* is_far_target= */ false);
4879 
4880   // handle op1 == op2, max(+0.0,-0.0), min(+0.0,-0.0).
4881   __ Vmov(temp1, op1);
4882   __ Vmov(temp2, op2);
4883   if (is_min) {
4884     __ Orr(temp1, temp1, temp2);
4885   } else {
4886     __ And(temp1, temp1, temp2);
4887   }
4888   __ Vmov(out, temp1);
4889   __ B(final_label);
4890 
4891   // handle NaN input.
4892   __ Bind(&nan);
4893   __ Movt(temp1, High16Bits(kNanFloat));  // 0x7FC0xxxx is a NaN.
4894   __ Vmov(out, temp1);
4895 
4896   if (done.IsReferenced()) {
4897     __ Bind(&done);
4898   }
4899 }
4900 
GenerateMinMaxDouble(HInstruction * minmax,bool is_min)4901 void InstructionCodeGeneratorARMVIXL::GenerateMinMaxDouble(HInstruction* minmax, bool is_min) {
4902   LocationSummary* locations = minmax->GetLocations();
4903   Location op1_loc = locations->InAt(0);
4904   Location op2_loc = locations->InAt(1);
4905   Location out_loc = locations->Out();
4906 
4907   // Optimization: don't generate any code if inputs are the same.
4908   if (op1_loc.Equals(op2_loc)) {
4909     DCHECK(out_loc.Equals(op1_loc));  // out_loc is set as SameAsFirstInput() in.
4910     return;
4911   }
4912 
4913   vixl32::DRegister op1 = DRegisterFrom(op1_loc);
4914   vixl32::DRegister op2 = DRegisterFrom(op2_loc);
4915   vixl32::DRegister out = DRegisterFrom(out_loc);
4916   vixl32::Label handle_nan_eq, done;
4917   vixl32::Label* final_label = codegen_->GetFinalLabel(minmax, &done);
4918 
4919   DCHECK(op1.Is(out));
4920 
4921   __ Vcmp(op1, op2);
4922   __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
4923   __ B(vs, &handle_nan_eq, /* is_far_target= */ false);  // if un-ordered, go to NaN handling.
4924 
4925   // op1 <> op2
4926   vixl32::ConditionType cond = is_min ? gt : lt;
4927   {
4928     ExactAssemblyScope it_scope(GetVIXLAssembler(),
4929                                 2 * kMaxInstructionSizeInBytes,
4930                                 CodeBufferCheckScope::kMaximumSize);
4931     __ it(cond);
4932     __ vmov(cond, F64, out, op2);
4933   }
4934   // for <>(not equal), we've done min/max calculation.
4935   __ B(ne, final_label, /* is_far_target= */ false);
4936 
4937   // handle op1 == op2, max(+0.0,-0.0).
4938   if (!is_min) {
4939     __ Vand(F64, out, op1, op2);
4940     __ B(final_label);
4941   }
4942 
4943   // handle op1 == op2, min(+0.0,-0.0), NaN input.
4944   __ Bind(&handle_nan_eq);
4945   __ Vorr(F64, out, op1, op2);  // assemble op1/-0.0/NaN.
4946 
4947   if (done.IsReferenced()) {
4948     __ Bind(&done);
4949   }
4950 }
4951 
GenerateMinMax(HBinaryOperation * minmax,bool is_min)4952 void InstructionCodeGeneratorARMVIXL::GenerateMinMax(HBinaryOperation* minmax, bool is_min) {
4953   DataType::Type type = minmax->GetResultType();
4954   switch (type) {
4955     case DataType::Type::kInt32:
4956       GenerateMinMaxInt(minmax->GetLocations(), is_min);
4957       break;
4958     case DataType::Type::kInt64:
4959       GenerateMinMaxLong(minmax->GetLocations(), is_min);
4960       break;
4961     case DataType::Type::kFloat32:
4962       GenerateMinMaxFloat(minmax, is_min);
4963       break;
4964     case DataType::Type::kFloat64:
4965       GenerateMinMaxDouble(minmax, is_min);
4966       break;
4967     default:
4968       LOG(FATAL) << "Unexpected type for HMinMax " << type;
4969   }
4970 }
4971 
VisitMin(HMin * min)4972 void LocationsBuilderARMVIXL::VisitMin(HMin* min) {
4973   CreateMinMaxLocations(GetGraph()->GetAllocator(), min);
4974 }
4975 
VisitMin(HMin * min)4976 void InstructionCodeGeneratorARMVIXL::VisitMin(HMin* min) {
4977   GenerateMinMax(min, /*is_min*/ true);
4978 }
4979 
VisitMax(HMax * max)4980 void LocationsBuilderARMVIXL::VisitMax(HMax* max) {
4981   CreateMinMaxLocations(GetGraph()->GetAllocator(), max);
4982 }
4983 
VisitMax(HMax * max)4984 void InstructionCodeGeneratorARMVIXL::VisitMax(HMax* max) {
4985   GenerateMinMax(max, /*is_min*/ false);
4986 }
4987 
VisitAbs(HAbs * abs)4988 void LocationsBuilderARMVIXL::VisitAbs(HAbs* abs) {
4989   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(abs);
4990   switch (abs->GetResultType()) {
4991     case DataType::Type::kInt32:
4992     case DataType::Type::kInt64:
4993       locations->SetInAt(0, Location::RequiresRegister());
4994       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4995       locations->AddTemp(Location::RequiresRegister());
4996       break;
4997     case DataType::Type::kFloat32:
4998     case DataType::Type::kFloat64:
4999       locations->SetInAt(0, Location::RequiresFpuRegister());
5000       locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
5001       break;
5002     default:
5003       LOG(FATAL) << "Unexpected type for abs operation " << abs->GetResultType();
5004   }
5005 }
5006 
VisitAbs(HAbs * abs)5007 void InstructionCodeGeneratorARMVIXL::VisitAbs(HAbs* abs) {
5008   LocationSummary* locations = abs->GetLocations();
5009   switch (abs->GetResultType()) {
5010     case DataType::Type::kInt32: {
5011       vixl32::Register in_reg = RegisterFrom(locations->InAt(0));
5012       vixl32::Register out_reg = RegisterFrom(locations->Out());
5013       vixl32::Register mask = RegisterFrom(locations->GetTemp(0));
5014       __ Asr(mask, in_reg, 31);
5015       __ Add(out_reg, in_reg, mask);
5016       __ Eor(out_reg, out_reg, mask);
5017       break;
5018     }
5019     case DataType::Type::kInt64: {
5020       Location in = locations->InAt(0);
5021       vixl32::Register in_reg_lo = LowRegisterFrom(in);
5022       vixl32::Register in_reg_hi = HighRegisterFrom(in);
5023       Location output = locations->Out();
5024       vixl32::Register out_reg_lo = LowRegisterFrom(output);
5025       vixl32::Register out_reg_hi = HighRegisterFrom(output);
5026       DCHECK(!out_reg_lo.Is(in_reg_hi)) << "Diagonal overlap unexpected.";
5027       vixl32::Register mask = RegisterFrom(locations->GetTemp(0));
5028       __ Asr(mask, in_reg_hi, 31);
5029       __ Adds(out_reg_lo, in_reg_lo, mask);
5030       __ Adc(out_reg_hi, in_reg_hi, mask);
5031       __ Eor(out_reg_lo, out_reg_lo, mask);
5032       __ Eor(out_reg_hi, out_reg_hi, mask);
5033       break;
5034     }
5035     case DataType::Type::kFloat32:
5036     case DataType::Type::kFloat64:
5037       __ Vabs(OutputVRegister(abs), InputVRegisterAt(abs, 0));
5038       break;
5039     default:
5040       LOG(FATAL) << "Unexpected type for abs operation " << abs->GetResultType();
5041   }
5042 }
5043 
VisitDivZeroCheck(HDivZeroCheck * instruction)5044 void LocationsBuilderARMVIXL::VisitDivZeroCheck(HDivZeroCheck* instruction) {
5045   LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
5046   locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0)));
5047 }
5048 
VisitDivZeroCheck(HDivZeroCheck * instruction)5049 void InstructionCodeGeneratorARMVIXL::VisitDivZeroCheck(HDivZeroCheck* instruction) {
5050   DivZeroCheckSlowPathARMVIXL* slow_path =
5051       new (codegen_->GetScopedAllocator()) DivZeroCheckSlowPathARMVIXL(instruction);
5052   codegen_->AddSlowPath(slow_path);
5053 
5054   LocationSummary* locations = instruction->GetLocations();
5055   Location value = locations->InAt(0);
5056 
5057   switch (instruction->GetType()) {
5058     case DataType::Type::kBool:
5059     case DataType::Type::kUint8:
5060     case DataType::Type::kInt8:
5061     case DataType::Type::kUint16:
5062     case DataType::Type::kInt16:
5063     case DataType::Type::kInt32: {
5064       if (value.IsRegister()) {
5065         __ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
5066       } else {
5067         DCHECK(value.IsConstant()) << value;
5068         if (Int32ConstantFrom(value) == 0) {
5069           __ B(slow_path->GetEntryLabel());
5070         }
5071       }
5072       break;
5073     }
5074     case DataType::Type::kInt64: {
5075       if (value.IsRegisterPair()) {
5076         UseScratchRegisterScope temps(GetVIXLAssembler());
5077         vixl32::Register temp = temps.Acquire();
5078         __ Orrs(temp, LowRegisterFrom(value), HighRegisterFrom(value));
5079         __ B(eq, slow_path->GetEntryLabel());
5080       } else {
5081         DCHECK(value.IsConstant()) << value;
5082         if (Int64ConstantFrom(value) == 0) {
5083           __ B(slow_path->GetEntryLabel());
5084         }
5085       }
5086       break;
5087     }
5088     default:
5089       LOG(FATAL) << "Unexpected type for HDivZeroCheck " << instruction->GetType();
5090   }
5091 }
5092 
HandleIntegerRotate(HRor * ror)5093 void InstructionCodeGeneratorARMVIXL::HandleIntegerRotate(HRor* ror) {
5094   LocationSummary* locations = ror->GetLocations();
5095   vixl32::Register in = InputRegisterAt(ror, 0);
5096   Location rhs = locations->InAt(1);
5097   vixl32::Register out = OutputRegister(ror);
5098 
5099   if (rhs.IsConstant()) {
5100     // Arm32 and Thumb2 assemblers require a rotation on the interval [1,31],
5101     // so map all rotations to a +ve. equivalent in that range.
5102     // (e.g. left *or* right by -2 bits == 30 bits in the same direction.)
5103     uint32_t rot = CodeGenerator::GetInt32ValueOf(rhs.GetConstant()) & 0x1F;
5104     if (rot) {
5105       // Rotate, mapping left rotations to right equivalents if necessary.
5106       // (e.g. left by 2 bits == right by 30.)
5107       __ Ror(out, in, rot);
5108     } else if (!out.Is(in)) {
5109       __ Mov(out, in);
5110     }
5111   } else {
5112     __ Ror(out, in, RegisterFrom(rhs));
5113   }
5114 }
5115 
5116 // Gain some speed by mapping all Long rotates onto equivalent pairs of Integer
5117 // rotates by swapping input regs (effectively rotating by the first 32-bits of
5118 // a larger rotation) or flipping direction (thus treating larger right/left
5119 // rotations as sub-word sized rotations in the other direction) as appropriate.
HandleLongRotate(HRor * ror)5120 void InstructionCodeGeneratorARMVIXL::HandleLongRotate(HRor* ror) {
5121   LocationSummary* locations = ror->GetLocations();
5122   vixl32::Register in_reg_lo = LowRegisterFrom(locations->InAt(0));
5123   vixl32::Register in_reg_hi = HighRegisterFrom(locations->InAt(0));
5124   Location rhs = locations->InAt(1);
5125   vixl32::Register out_reg_lo = LowRegisterFrom(locations->Out());
5126   vixl32::Register out_reg_hi = HighRegisterFrom(locations->Out());
5127 
5128   if (rhs.IsConstant()) {
5129     uint64_t rot = CodeGenerator::GetInt64ValueOf(rhs.GetConstant());
5130     // Map all rotations to +ve. equivalents on the interval [0,63].
5131     rot &= kMaxLongShiftDistance;
5132     // For rotates over a word in size, 'pre-rotate' by 32-bits to keep rotate
5133     // logic below to a simple pair of binary orr.
5134     // (e.g. 34 bits == in_reg swap + 2 bits right.)
5135     if (rot >= kArmBitsPerWord) {
5136       rot -= kArmBitsPerWord;
5137       std::swap(in_reg_hi, in_reg_lo);
5138     }
5139     // Rotate, or mov to out for zero or word size rotations.
5140     if (rot != 0u) {
5141       __ Lsr(out_reg_hi, in_reg_hi, Operand::From(rot));
5142       __ Orr(out_reg_hi, out_reg_hi, Operand(in_reg_lo, ShiftType::LSL, kArmBitsPerWord - rot));
5143       __ Lsr(out_reg_lo, in_reg_lo, Operand::From(rot));
5144       __ Orr(out_reg_lo, out_reg_lo, Operand(in_reg_hi, ShiftType::LSL, kArmBitsPerWord - rot));
5145     } else {
5146       __ Mov(out_reg_lo, in_reg_lo);
5147       __ Mov(out_reg_hi, in_reg_hi);
5148     }
5149   } else {
5150     vixl32::Register shift_right = RegisterFrom(locations->GetTemp(0));
5151     vixl32::Register shift_left = RegisterFrom(locations->GetTemp(1));
5152     vixl32::Label end;
5153     vixl32::Label shift_by_32_plus_shift_right;
5154     vixl32::Label* final_label = codegen_->GetFinalLabel(ror, &end);
5155 
5156     __ And(shift_right, RegisterFrom(rhs), 0x1F);
5157     __ Lsrs(shift_left, RegisterFrom(rhs), 6);
5158     __ Rsb(LeaveFlags, shift_left, shift_right, Operand::From(kArmBitsPerWord));
5159     __ B(cc, &shift_by_32_plus_shift_right, /* is_far_target= */ false);
5160 
5161     // out_reg_hi = (reg_hi << shift_left) | (reg_lo >> shift_right).
5162     // out_reg_lo = (reg_lo << shift_left) | (reg_hi >> shift_right).
5163     __ Lsl(out_reg_hi, in_reg_hi, shift_left);
5164     __ Lsr(out_reg_lo, in_reg_lo, shift_right);
5165     __ Add(out_reg_hi, out_reg_hi, out_reg_lo);
5166     __ Lsl(out_reg_lo, in_reg_lo, shift_left);
5167     __ Lsr(shift_left, in_reg_hi, shift_right);
5168     __ Add(out_reg_lo, out_reg_lo, shift_left);
5169     __ B(final_label);
5170 
5171     __ Bind(&shift_by_32_plus_shift_right);  // Shift by 32+shift_right.
5172     // out_reg_hi = (reg_hi >> shift_right) | (reg_lo << shift_left).
5173     // out_reg_lo = (reg_lo >> shift_right) | (reg_hi << shift_left).
5174     __ Lsr(out_reg_hi, in_reg_hi, shift_right);
5175     __ Lsl(out_reg_lo, in_reg_lo, shift_left);
5176     __ Add(out_reg_hi, out_reg_hi, out_reg_lo);
5177     __ Lsr(out_reg_lo, in_reg_lo, shift_right);
5178     __ Lsl(shift_right, in_reg_hi, shift_left);
5179     __ Add(out_reg_lo, out_reg_lo, shift_right);
5180 
5181     if (end.IsReferenced()) {
5182       __ Bind(&end);
5183     }
5184   }
5185 }
5186 
VisitRor(HRor * ror)5187 void LocationsBuilderARMVIXL::VisitRor(HRor* ror) {
5188   LocationSummary* locations =
5189       new (GetGraph()->GetAllocator()) LocationSummary(ror, LocationSummary::kNoCall);
5190   switch (ror->GetResultType()) {
5191     case DataType::Type::kInt32: {
5192       locations->SetInAt(0, Location::RequiresRegister());
5193       locations->SetInAt(1, Location::RegisterOrConstant(ror->InputAt(1)));
5194       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5195       break;
5196     }
5197     case DataType::Type::kInt64: {
5198       locations->SetInAt(0, Location::RequiresRegister());
5199       if (ror->InputAt(1)->IsConstant()) {
5200         locations->SetInAt(1, Location::ConstantLocation(ror->InputAt(1)->AsConstant()));
5201       } else {
5202         locations->SetInAt(1, Location::RequiresRegister());
5203         locations->AddTemp(Location::RequiresRegister());
5204         locations->AddTemp(Location::RequiresRegister());
5205       }
5206       locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
5207       break;
5208     }
5209     default:
5210       LOG(FATAL) << "Unexpected operation type " << ror->GetResultType();
5211   }
5212 }
5213 
VisitRor(HRor * ror)5214 void InstructionCodeGeneratorARMVIXL::VisitRor(HRor* ror) {
5215   DataType::Type type = ror->GetResultType();
5216   switch (type) {
5217     case DataType::Type::kInt32: {
5218       HandleIntegerRotate(ror);
5219       break;
5220     }
5221     case DataType::Type::kInt64: {
5222       HandleLongRotate(ror);
5223       break;
5224     }
5225     default:
5226       LOG(FATAL) << "Unexpected operation type " << type;
5227       UNREACHABLE();
5228   }
5229 }
5230 
HandleShift(HBinaryOperation * op)5231 void LocationsBuilderARMVIXL::HandleShift(HBinaryOperation* op) {
5232   DCHECK(op->IsShl() || op->IsShr() || op->IsUShr());
5233 
5234   LocationSummary* locations =
5235       new (GetGraph()->GetAllocator()) LocationSummary(op, LocationSummary::kNoCall);
5236 
5237   switch (op->GetResultType()) {
5238     case DataType::Type::kInt32: {
5239       locations->SetInAt(0, Location::RequiresRegister());
5240       if (op->InputAt(1)->IsConstant()) {
5241         locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant()));
5242         locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5243       } else {
5244         locations->SetInAt(1, Location::RequiresRegister());
5245         // Make the output overlap, as it will be used to hold the masked
5246         // second input.
5247         locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
5248       }
5249       break;
5250     }
5251     case DataType::Type::kInt64: {
5252       locations->SetInAt(0, Location::RequiresRegister());
5253       if (op->InputAt(1)->IsConstant()) {
5254         locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant()));
5255         // For simplicity, use kOutputOverlap even though we only require that low registers
5256         // don't clash with high registers which the register allocator currently guarantees.
5257         locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
5258       } else {
5259         locations->SetInAt(1, Location::RequiresRegister());
5260         locations->AddTemp(Location::RequiresRegister());
5261         locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
5262       }
5263       break;
5264     }
5265     default:
5266       LOG(FATAL) << "Unexpected operation type " << op->GetResultType();
5267   }
5268 }
5269 
HandleShift(HBinaryOperation * op)5270 void InstructionCodeGeneratorARMVIXL::HandleShift(HBinaryOperation* op) {
5271   DCHECK(op->IsShl() || op->IsShr() || op->IsUShr());
5272 
5273   LocationSummary* locations = op->GetLocations();
5274   Location out = locations->Out();
5275   Location first = locations->InAt(0);
5276   Location second = locations->InAt(1);
5277 
5278   DataType::Type type = op->GetResultType();
5279   switch (type) {
5280     case DataType::Type::kInt32: {
5281       vixl32::Register out_reg = OutputRegister(op);
5282       vixl32::Register first_reg = InputRegisterAt(op, 0);
5283       if (second.IsRegister()) {
5284         vixl32::Register second_reg = RegisterFrom(second);
5285         // ARM doesn't mask the shift count so we need to do it ourselves.
5286         __ And(out_reg, second_reg, kMaxIntShiftDistance);
5287         if (op->IsShl()) {
5288           __ Lsl(out_reg, first_reg, out_reg);
5289         } else if (op->IsShr()) {
5290           __ Asr(out_reg, first_reg, out_reg);
5291         } else {
5292           __ Lsr(out_reg, first_reg, out_reg);
5293         }
5294       } else {
5295         int32_t cst = Int32ConstantFrom(second);
5296         uint32_t shift_value = cst & kMaxIntShiftDistance;
5297         if (shift_value == 0) {  // ARM does not support shifting with 0 immediate.
5298           __ Mov(out_reg, first_reg);
5299         } else if (op->IsShl()) {
5300           __ Lsl(out_reg, first_reg, shift_value);
5301         } else if (op->IsShr()) {
5302           __ Asr(out_reg, first_reg, shift_value);
5303         } else {
5304           __ Lsr(out_reg, first_reg, shift_value);
5305         }
5306       }
5307       break;
5308     }
5309     case DataType::Type::kInt64: {
5310       vixl32::Register o_h = HighRegisterFrom(out);
5311       vixl32::Register o_l = LowRegisterFrom(out);
5312 
5313       vixl32::Register high = HighRegisterFrom(first);
5314       vixl32::Register low = LowRegisterFrom(first);
5315 
5316       if (second.IsRegister()) {
5317         vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
5318 
5319         vixl32::Register second_reg = RegisterFrom(second);
5320 
5321         if (op->IsShl()) {
5322           __ And(o_l, second_reg, kMaxLongShiftDistance);
5323           // Shift the high part
5324           __ Lsl(o_h, high, o_l);
5325           // Shift the low part and `or` what overflew on the high part
5326           __ Rsb(temp, o_l, Operand::From(kArmBitsPerWord));
5327           __ Lsr(temp, low, temp);
5328           __ Orr(o_h, o_h, temp);
5329           // If the shift is > 32 bits, override the high part
5330           __ Subs(temp, o_l, Operand::From(kArmBitsPerWord));
5331           {
5332             ExactAssemblyScope guard(GetVIXLAssembler(),
5333                                      2 * vixl32::kMaxInstructionSizeInBytes,
5334                                      CodeBufferCheckScope::kMaximumSize);
5335             __ it(pl);
5336             __ lsl(pl, o_h, low, temp);
5337           }
5338           // Shift the low part
5339           __ Lsl(o_l, low, o_l);
5340         } else if (op->IsShr()) {
5341           __ And(o_h, second_reg, kMaxLongShiftDistance);
5342           // Shift the low part
5343           __ Lsr(o_l, low, o_h);
5344           // Shift the high part and `or` what underflew on the low part
5345           __ Rsb(temp, o_h, Operand::From(kArmBitsPerWord));
5346           __ Lsl(temp, high, temp);
5347           __ Orr(o_l, o_l, temp);
5348           // If the shift is > 32 bits, override the low part
5349           __ Subs(temp, o_h, Operand::From(kArmBitsPerWord));
5350           {
5351             ExactAssemblyScope guard(GetVIXLAssembler(),
5352                                      2 * vixl32::kMaxInstructionSizeInBytes,
5353                                      CodeBufferCheckScope::kMaximumSize);
5354             __ it(pl);
5355             __ asr(pl, o_l, high, temp);
5356           }
5357           // Shift the high part
5358           __ Asr(o_h, high, o_h);
5359         } else {
5360           __ And(o_h, second_reg, kMaxLongShiftDistance);
5361           // same as Shr except we use `Lsr`s and not `Asr`s
5362           __ Lsr(o_l, low, o_h);
5363           __ Rsb(temp, o_h, Operand::From(kArmBitsPerWord));
5364           __ Lsl(temp, high, temp);
5365           __ Orr(o_l, o_l, temp);
5366           __ Subs(temp, o_h, Operand::From(kArmBitsPerWord));
5367           {
5368             ExactAssemblyScope guard(GetVIXLAssembler(),
5369                                      2 * vixl32::kMaxInstructionSizeInBytes,
5370                                      CodeBufferCheckScope::kMaximumSize);
5371           __ it(pl);
5372           __ lsr(pl, o_l, high, temp);
5373           }
5374           __ Lsr(o_h, high, o_h);
5375         }
5376       } else {
5377         // Register allocator doesn't create partial overlap.
5378         DCHECK(!o_l.Is(high));
5379         DCHECK(!o_h.Is(low));
5380         int32_t cst = Int32ConstantFrom(second);
5381         uint32_t shift_value = cst & kMaxLongShiftDistance;
5382         if (shift_value > 32) {
5383           if (op->IsShl()) {
5384             __ Lsl(o_h, low, shift_value - 32);
5385             __ Mov(o_l, 0);
5386           } else if (op->IsShr()) {
5387             __ Asr(o_l, high, shift_value - 32);
5388             __ Asr(o_h, high, 31);
5389           } else {
5390             __ Lsr(o_l, high, shift_value - 32);
5391             __ Mov(o_h, 0);
5392           }
5393         } else if (shift_value == 32) {
5394           if (op->IsShl()) {
5395             __ Mov(o_h, low);
5396             __ Mov(o_l, 0);
5397           } else if (op->IsShr()) {
5398             __ Mov(o_l, high);
5399             __ Asr(o_h, high, 31);
5400           } else {
5401             __ Mov(o_l, high);
5402             __ Mov(o_h, 0);
5403           }
5404         } else if (shift_value == 1) {
5405           if (op->IsShl()) {
5406             __ Lsls(o_l, low, 1);
5407             __ Adc(o_h, high, high);
5408           } else if (op->IsShr()) {
5409             __ Asrs(o_h, high, 1);
5410             __ Rrx(o_l, low);
5411           } else {
5412             __ Lsrs(o_h, high, 1);
5413             __ Rrx(o_l, low);
5414           }
5415         } else if (shift_value == 0) {
5416           __ Mov(o_l, low);
5417           __ Mov(o_h, high);
5418         } else {
5419           DCHECK(0 < shift_value && shift_value < 32) << shift_value;
5420           if (op->IsShl()) {
5421             __ Lsl(o_h, high, shift_value);
5422             __ Orr(o_h, o_h, Operand(low, ShiftType::LSR, 32 - shift_value));
5423             __ Lsl(o_l, low, shift_value);
5424           } else if (op->IsShr()) {
5425             __ Lsr(o_l, low, shift_value);
5426             __ Orr(o_l, o_l, Operand(high, ShiftType::LSL, 32 - shift_value));
5427             __ Asr(o_h, high, shift_value);
5428           } else {
5429             __ Lsr(o_l, low, shift_value);
5430             __ Orr(o_l, o_l, Operand(high, ShiftType::LSL, 32 - shift_value));
5431             __ Lsr(o_h, high, shift_value);
5432           }
5433         }
5434       }
5435       break;
5436     }
5437     default:
5438       LOG(FATAL) << "Unexpected operation type " << type;
5439       UNREACHABLE();
5440   }
5441 }
5442 
VisitShl(HShl * shl)5443 void LocationsBuilderARMVIXL::VisitShl(HShl* shl) {
5444   HandleShift(shl);
5445 }
5446 
VisitShl(HShl * shl)5447 void InstructionCodeGeneratorARMVIXL::VisitShl(HShl* shl) {
5448   HandleShift(shl);
5449 }
5450 
VisitShr(HShr * shr)5451 void LocationsBuilderARMVIXL::VisitShr(HShr* shr) {
5452   HandleShift(shr);
5453 }
5454 
VisitShr(HShr * shr)5455 void InstructionCodeGeneratorARMVIXL::VisitShr(HShr* shr) {
5456   HandleShift(shr);
5457 }
5458 
VisitUShr(HUShr * ushr)5459 void LocationsBuilderARMVIXL::VisitUShr(HUShr* ushr) {
5460   HandleShift(ushr);
5461 }
5462 
VisitUShr(HUShr * ushr)5463 void InstructionCodeGeneratorARMVIXL::VisitUShr(HUShr* ushr) {
5464   HandleShift(ushr);
5465 }
5466 
VisitNewInstance(HNewInstance * instruction)5467 void LocationsBuilderARMVIXL::VisitNewInstance(HNewInstance* instruction) {
5468   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
5469       instruction, LocationSummary::kCallOnMainOnly);
5470   InvokeRuntimeCallingConventionARMVIXL calling_convention;
5471   locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
5472   locations->SetOut(LocationFrom(r0));
5473 }
5474 
VisitNewInstance(HNewInstance * instruction)5475 void InstructionCodeGeneratorARMVIXL::VisitNewInstance(HNewInstance* instruction) {
5476   codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
5477   CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
5478   codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 12);
5479 }
5480 
VisitNewArray(HNewArray * instruction)5481 void LocationsBuilderARMVIXL::VisitNewArray(HNewArray* instruction) {
5482   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
5483       instruction, LocationSummary::kCallOnMainOnly);
5484   InvokeRuntimeCallingConventionARMVIXL calling_convention;
5485   locations->SetOut(LocationFrom(r0));
5486   locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
5487   locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
5488 }
5489 
VisitNewArray(HNewArray * instruction)5490 void InstructionCodeGeneratorARMVIXL::VisitNewArray(HNewArray* instruction) {
5491   // Note: if heap poisoning is enabled, the entry point takes care of poisoning the reference.
5492   QuickEntrypointEnum entrypoint = CodeGenerator::GetArrayAllocationEntrypoint(instruction);
5493   codegen_->InvokeRuntime(entrypoint, instruction, instruction->GetDexPc());
5494   CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>();
5495   DCHECK(!codegen_->IsLeafMethod());
5496   codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 13);
5497 }
5498 
VisitParameterValue(HParameterValue * instruction)5499 void LocationsBuilderARMVIXL::VisitParameterValue(HParameterValue* instruction) {
5500   LocationSummary* locations =
5501       new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
5502   Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
5503   if (location.IsStackSlot()) {
5504     location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
5505   } else if (location.IsDoubleStackSlot()) {
5506     location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
5507   }
5508   locations->SetOut(location);
5509 }
5510 
VisitParameterValue(HParameterValue * instruction ATTRIBUTE_UNUSED)5511 void InstructionCodeGeneratorARMVIXL::VisitParameterValue(
5512     HParameterValue* instruction ATTRIBUTE_UNUSED) {
5513   // Nothing to do, the parameter is already at its location.
5514 }
5515 
VisitCurrentMethod(HCurrentMethod * instruction)5516 void LocationsBuilderARMVIXL::VisitCurrentMethod(HCurrentMethod* instruction) {
5517   LocationSummary* locations =
5518       new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
5519   locations->SetOut(LocationFrom(kMethodRegister));
5520 }
5521 
VisitCurrentMethod(HCurrentMethod * instruction ATTRIBUTE_UNUSED)5522 void InstructionCodeGeneratorARMVIXL::VisitCurrentMethod(
5523     HCurrentMethod* instruction ATTRIBUTE_UNUSED) {
5524   // Nothing to do, the method is already at its location.
5525 }
5526 
VisitNot(HNot * not_)5527 void LocationsBuilderARMVIXL::VisitNot(HNot* not_) {
5528   LocationSummary* locations =
5529       new (GetGraph()->GetAllocator()) LocationSummary(not_, LocationSummary::kNoCall);
5530   locations->SetInAt(0, Location::RequiresRegister());
5531   locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5532 }
5533 
VisitNot(HNot * not_)5534 void InstructionCodeGeneratorARMVIXL::VisitNot(HNot* not_) {
5535   LocationSummary* locations = not_->GetLocations();
5536   Location out = locations->Out();
5537   Location in = locations->InAt(0);
5538   switch (not_->GetResultType()) {
5539     case DataType::Type::kInt32:
5540       __ Mvn(OutputRegister(not_), InputRegisterAt(not_, 0));
5541       break;
5542 
5543     case DataType::Type::kInt64:
5544       __ Mvn(LowRegisterFrom(out), LowRegisterFrom(in));
5545       __ Mvn(HighRegisterFrom(out), HighRegisterFrom(in));
5546       break;
5547 
5548     default:
5549       LOG(FATAL) << "Unimplemented type for not operation " << not_->GetResultType();
5550   }
5551 }
5552 
VisitBooleanNot(HBooleanNot * bool_not)5553 void LocationsBuilderARMVIXL::VisitBooleanNot(HBooleanNot* bool_not) {
5554   LocationSummary* locations =
5555       new (GetGraph()->GetAllocator()) LocationSummary(bool_not, LocationSummary::kNoCall);
5556   locations->SetInAt(0, Location::RequiresRegister());
5557   locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5558 }
5559 
VisitBooleanNot(HBooleanNot * bool_not)5560 void InstructionCodeGeneratorARMVIXL::VisitBooleanNot(HBooleanNot* bool_not) {
5561   __ Eor(OutputRegister(bool_not), InputRegister(bool_not), 1);
5562 }
5563 
VisitCompare(HCompare * compare)5564 void LocationsBuilderARMVIXL::VisitCompare(HCompare* compare) {
5565   LocationSummary* locations =
5566       new (GetGraph()->GetAllocator()) LocationSummary(compare, LocationSummary::kNoCall);
5567   switch (compare->InputAt(0)->GetType()) {
5568     case DataType::Type::kBool:
5569     case DataType::Type::kUint8:
5570     case DataType::Type::kInt8:
5571     case DataType::Type::kUint16:
5572     case DataType::Type::kInt16:
5573     case DataType::Type::kInt32:
5574     case DataType::Type::kInt64: {
5575       locations->SetInAt(0, Location::RequiresRegister());
5576       locations->SetInAt(1, Location::RequiresRegister());
5577       // Output overlaps because it is written before doing the low comparison.
5578       locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
5579       break;
5580     }
5581     case DataType::Type::kFloat32:
5582     case DataType::Type::kFloat64: {
5583       locations->SetInAt(0, Location::RequiresFpuRegister());
5584       locations->SetInAt(1, ArithmeticZeroOrFpuRegister(compare->InputAt(1)));
5585       locations->SetOut(Location::RequiresRegister());
5586       break;
5587     }
5588     default:
5589       LOG(FATAL) << "Unexpected type for compare operation " << compare->InputAt(0)->GetType();
5590   }
5591 }
5592 
VisitCompare(HCompare * compare)5593 void InstructionCodeGeneratorARMVIXL::VisitCompare(HCompare* compare) {
5594   LocationSummary* locations = compare->GetLocations();
5595   vixl32::Register out = OutputRegister(compare);
5596   Location left = locations->InAt(0);
5597   Location right = locations->InAt(1);
5598 
5599   vixl32::Label less, greater, done;
5600   vixl32::Label* final_label = codegen_->GetFinalLabel(compare, &done);
5601   DataType::Type type = compare->InputAt(0)->GetType();
5602   vixl32::Condition less_cond = vixl32::Condition::None();
5603   switch (type) {
5604     case DataType::Type::kBool:
5605     case DataType::Type::kUint8:
5606     case DataType::Type::kInt8:
5607     case DataType::Type::kUint16:
5608     case DataType::Type::kInt16:
5609     case DataType::Type::kInt32: {
5610       // Emit move to `out` before the `Cmp`, as `Mov` might affect the status flags.
5611       __ Mov(out, 0);
5612       __ Cmp(RegisterFrom(left), RegisterFrom(right));  // Signed compare.
5613       less_cond = lt;
5614       break;
5615     }
5616     case DataType::Type::kInt64: {
5617       __ Cmp(HighRegisterFrom(left), HighRegisterFrom(right));  // Signed compare.
5618       __ B(lt, &less, /* is_far_target= */ false);
5619       __ B(gt, &greater, /* is_far_target= */ false);
5620       // Emit move to `out` before the last `Cmp`, as `Mov` might affect the status flags.
5621       __ Mov(out, 0);
5622       __ Cmp(LowRegisterFrom(left), LowRegisterFrom(right));  // Unsigned compare.
5623       less_cond = lo;
5624       break;
5625     }
5626     case DataType::Type::kFloat32:
5627     case DataType::Type::kFloat64: {
5628       __ Mov(out, 0);
5629       GenerateVcmp(compare, codegen_);
5630       // To branch on the FP compare result we transfer FPSCR to APSR (encoded as PC in VMRS).
5631       __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
5632       less_cond = ARMFPCondition(kCondLT, compare->IsGtBias());
5633       break;
5634     }
5635     default:
5636       LOG(FATAL) << "Unexpected compare type " << type;
5637       UNREACHABLE();
5638   }
5639 
5640   __ B(eq, final_label, /* is_far_target= */ false);
5641   __ B(less_cond, &less, /* is_far_target= */ false);
5642 
5643   __ Bind(&greater);
5644   __ Mov(out, 1);
5645   __ B(final_label);
5646 
5647   __ Bind(&less);
5648   __ Mov(out, -1);
5649 
5650   if (done.IsReferenced()) {
5651     __ Bind(&done);
5652   }
5653 }
5654 
VisitPhi(HPhi * instruction)5655 void LocationsBuilderARMVIXL::VisitPhi(HPhi* instruction) {
5656   LocationSummary* locations =
5657       new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
5658   for (size_t i = 0, e = locations->GetInputCount(); i < e; ++i) {
5659     locations->SetInAt(i, Location::Any());
5660   }
5661   locations->SetOut(Location::Any());
5662 }
5663 
VisitPhi(HPhi * instruction ATTRIBUTE_UNUSED)5664 void InstructionCodeGeneratorARMVIXL::VisitPhi(HPhi* instruction ATTRIBUTE_UNUSED) {
5665   LOG(FATAL) << "Unreachable";
5666 }
5667 
GenerateMemoryBarrier(MemBarrierKind kind)5668 void CodeGeneratorARMVIXL::GenerateMemoryBarrier(MemBarrierKind kind) {
5669   // TODO (ported from quick): revisit ARM barrier kinds.
5670   DmbOptions flavor = DmbOptions::ISH;  // Quiet C++ warnings.
5671   switch (kind) {
5672     case MemBarrierKind::kAnyStore:
5673     case MemBarrierKind::kLoadAny:
5674     case MemBarrierKind::kAnyAny: {
5675       flavor = DmbOptions::ISH;
5676       break;
5677     }
5678     case MemBarrierKind::kStoreStore: {
5679       flavor = DmbOptions::ISHST;
5680       break;
5681     }
5682     default:
5683       LOG(FATAL) << "Unexpected memory barrier " << kind;
5684   }
5685   __ Dmb(flavor);
5686 }
5687 
GenerateWideAtomicLoad(vixl32::Register addr,uint32_t offset,vixl32::Register out_lo,vixl32::Register out_hi)5688 void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicLoad(vixl32::Register addr,
5689                                                              uint32_t offset,
5690                                                              vixl32::Register out_lo,
5691                                                              vixl32::Register out_hi) {
5692   UseScratchRegisterScope temps(GetVIXLAssembler());
5693   if (offset != 0) {
5694     vixl32::Register temp = temps.Acquire();
5695     __ Add(temp, addr, offset);
5696     addr = temp;
5697   }
5698   __ Ldrexd(out_lo, out_hi, MemOperand(addr));
5699 }
5700 
GenerateWideAtomicStore(vixl32::Register addr,uint32_t offset,vixl32::Register value_lo,vixl32::Register value_hi,vixl32::Register temp1,vixl32::Register temp2,HInstruction * instruction)5701 void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicStore(vixl32::Register addr,
5702                                                               uint32_t offset,
5703                                                               vixl32::Register value_lo,
5704                                                               vixl32::Register value_hi,
5705                                                               vixl32::Register temp1,
5706                                                               vixl32::Register temp2,
5707                                                               HInstruction* instruction) {
5708   UseScratchRegisterScope temps(GetVIXLAssembler());
5709   vixl32::Label fail;
5710   if (offset != 0) {
5711     vixl32::Register temp = temps.Acquire();
5712     __ Add(temp, addr, offset);
5713     addr = temp;
5714   }
5715   __ Bind(&fail);
5716   {
5717     // Ensure the pc position is recorded immediately after the `ldrexd` instruction.
5718     ExactAssemblyScope aas(GetVIXLAssembler(),
5719                            vixl32::kMaxInstructionSizeInBytes,
5720                            CodeBufferCheckScope::kMaximumSize);
5721     // We need a load followed by store. (The address used in a STREX instruction must
5722     // be the same as the address in the most recently executed LDREX instruction.)
5723     __ ldrexd(temp1, temp2, MemOperand(addr));
5724     codegen_->MaybeRecordImplicitNullCheck(instruction);
5725   }
5726   __ Strexd(temp1, value_lo, value_hi, MemOperand(addr));
5727   __ CompareAndBranchIfNonZero(temp1, &fail);
5728 }
5729 
HandleFieldSet(HInstruction * instruction,const FieldInfo & field_info)5730 void LocationsBuilderARMVIXL::HandleFieldSet(
5731     HInstruction* instruction, const FieldInfo& field_info) {
5732   DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
5733 
5734   LocationSummary* locations =
5735       new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
5736   locations->SetInAt(0, Location::RequiresRegister());
5737 
5738   DataType::Type field_type = field_info.GetFieldType();
5739   if (DataType::IsFloatingPointType(field_type)) {
5740     locations->SetInAt(1, Location::RequiresFpuRegister());
5741   } else {
5742     locations->SetInAt(1, Location::RequiresRegister());
5743   }
5744 
5745   bool is_wide = field_type == DataType::Type::kInt64 || field_type == DataType::Type::kFloat64;
5746   bool generate_volatile = field_info.IsVolatile()
5747       && is_wide
5748       && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
5749   bool needs_write_barrier =
5750       CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1));
5751   // Temporary registers for the write barrier.
5752   // TODO: consider renaming StoreNeedsWriteBarrier to StoreNeedsGCMark.
5753   if (needs_write_barrier) {
5754     locations->AddTemp(Location::RequiresRegister());  // Possibly used for reference poisoning too.
5755     locations->AddTemp(Location::RequiresRegister());
5756   } else if (generate_volatile) {
5757     // ARM encoding have some additional constraints for ldrexd/strexd:
5758     // - registers need to be consecutive
5759     // - the first register should be even but not R14.
5760     // We don't test for ARM yet, and the assertion makes sure that we
5761     // revisit this if we ever enable ARM encoding.
5762     DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet());
5763 
5764     locations->AddTemp(Location::RequiresRegister());
5765     locations->AddTemp(Location::RequiresRegister());
5766     if (field_type == DataType::Type::kFloat64) {
5767       // For doubles we need two more registers to copy the value.
5768       locations->AddTemp(LocationFrom(r2));
5769       locations->AddTemp(LocationFrom(r3));
5770     }
5771   }
5772 }
5773 
HandleFieldSet(HInstruction * instruction,const FieldInfo & field_info,bool value_can_be_null)5774 void InstructionCodeGeneratorARMVIXL::HandleFieldSet(HInstruction* instruction,
5775                                                      const FieldInfo& field_info,
5776                                                      bool value_can_be_null) {
5777   DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
5778 
5779   LocationSummary* locations = instruction->GetLocations();
5780   vixl32::Register base = InputRegisterAt(instruction, 0);
5781   Location value = locations->InAt(1);
5782   std::optional<vixl::aarch32::Label> pred_is_null;
5783 
5784   bool is_predicated =
5785       instruction->IsInstanceFieldSet() && instruction->AsInstanceFieldSet()->GetIsPredicatedSet();
5786   bool is_volatile = field_info.IsVolatile();
5787   bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
5788   DataType::Type field_type = field_info.GetFieldType();
5789   uint32_t offset = field_info.GetFieldOffset().Uint32Value();
5790   bool needs_write_barrier =
5791       CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1));
5792 
5793   if (is_predicated) {
5794     pred_is_null.emplace();
5795     __ CompareAndBranchIfZero(base, &*pred_is_null, /* is_far_target= */ false);
5796   }
5797 
5798   if (is_volatile) {
5799     codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
5800   }
5801 
5802   switch (field_type) {
5803     case DataType::Type::kBool:
5804     case DataType::Type::kUint8:
5805     case DataType::Type::kInt8:
5806     case DataType::Type::kUint16:
5807     case DataType::Type::kInt16:
5808     case DataType::Type::kInt32: {
5809       // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
5810       EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
5811       StoreOperandType operand_type = GetStoreOperandType(field_type);
5812       GetAssembler()->StoreToOffset(operand_type, RegisterFrom(value), base, offset);
5813       codegen_->MaybeRecordImplicitNullCheck(instruction);
5814       break;
5815     }
5816 
5817     case DataType::Type::kReference: {
5818       vixl32::Register value_reg = RegisterFrom(value);
5819       if (kPoisonHeapReferences && needs_write_barrier) {
5820         // Note that in the case where `value` is a null reference,
5821         // we do not enter this block, as a null reference does not
5822         // need poisoning.
5823         DCHECK_EQ(field_type, DataType::Type::kReference);
5824         value_reg = RegisterFrom(locations->GetTemp(0));
5825         __ Mov(value_reg, RegisterFrom(value));
5826         GetAssembler()->PoisonHeapReference(value_reg);
5827       }
5828       // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
5829       EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
5830       GetAssembler()->StoreToOffset(kStoreWord, value_reg, base, offset);
5831       codegen_->MaybeRecordImplicitNullCheck(instruction);
5832       break;
5833     }
5834 
5835     case DataType::Type::kInt64: {
5836       if (is_volatile && !atomic_ldrd_strd) {
5837         GenerateWideAtomicStore(base,
5838                                 offset,
5839                                 LowRegisterFrom(value),
5840                                 HighRegisterFrom(value),
5841                                 RegisterFrom(locations->GetTemp(0)),
5842                                 RegisterFrom(locations->GetTemp(1)),
5843                                 instruction);
5844       } else {
5845         // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
5846         EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
5847         GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), base, offset);
5848         codegen_->MaybeRecordImplicitNullCheck(instruction);
5849       }
5850       break;
5851     }
5852 
5853     case DataType::Type::kFloat32: {
5854       // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
5855       EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
5856       GetAssembler()->StoreSToOffset(SRegisterFrom(value), base, offset);
5857       codegen_->MaybeRecordImplicitNullCheck(instruction);
5858       break;
5859     }
5860 
5861     case DataType::Type::kFloat64: {
5862       vixl32::DRegister value_reg = DRegisterFrom(value);
5863       if (is_volatile && !atomic_ldrd_strd) {
5864         vixl32::Register value_reg_lo = RegisterFrom(locations->GetTemp(0));
5865         vixl32::Register value_reg_hi = RegisterFrom(locations->GetTemp(1));
5866 
5867         __ Vmov(value_reg_lo, value_reg_hi, value_reg);
5868 
5869         GenerateWideAtomicStore(base,
5870                                 offset,
5871                                 value_reg_lo,
5872                                 value_reg_hi,
5873                                 RegisterFrom(locations->GetTemp(2)),
5874                                 RegisterFrom(locations->GetTemp(3)),
5875                                 instruction);
5876       } else {
5877         // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
5878         EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
5879         GetAssembler()->StoreDToOffset(value_reg, base, offset);
5880         codegen_->MaybeRecordImplicitNullCheck(instruction);
5881       }
5882       break;
5883     }
5884 
5885     case DataType::Type::kUint32:
5886     case DataType::Type::kUint64:
5887     case DataType::Type::kVoid:
5888       LOG(FATAL) << "Unreachable type " << field_type;
5889       UNREACHABLE();
5890   }
5891 
5892   if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
5893     vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
5894     vixl32::Register card = RegisterFrom(locations->GetTemp(1));
5895     codegen_->MarkGCCard(temp, card, base, RegisterFrom(value), value_can_be_null);
5896   }
5897 
5898   if (is_volatile) {
5899     codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
5900   }
5901 
5902   if (is_predicated) {
5903     __ Bind(&*pred_is_null);
5904   }
5905 }
5906 
HandleFieldGet(HInstruction * instruction,const FieldInfo & field_info)5907 void LocationsBuilderARMVIXL::HandleFieldGet(HInstruction* instruction,
5908                                              const FieldInfo& field_info) {
5909   DCHECK(instruction->IsInstanceFieldGet() ||
5910          instruction->IsStaticFieldGet() ||
5911          instruction->IsPredicatedInstanceFieldGet());
5912 
5913   bool object_field_get_with_read_barrier =
5914       kEmitCompilerReadBarrier && (field_info.GetFieldType() == DataType::Type::kReference);
5915   bool is_predicated = instruction->IsPredicatedInstanceFieldGet();
5916   LocationSummary* locations =
5917       new (GetGraph()->GetAllocator()) LocationSummary(instruction,
5918                                                        object_field_get_with_read_barrier
5919                                                            ? LocationSummary::kCallOnSlowPath
5920                                                            : LocationSummary::kNoCall);
5921   if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
5922     locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
5923   }
5924   // Input for object receiver.
5925   locations->SetInAt(is_predicated ? 1 : 0, Location::RequiresRegister());
5926 
5927   bool volatile_for_double = field_info.IsVolatile()
5928       && (field_info.GetFieldType() == DataType::Type::kFloat64)
5929       && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
5930   // The output overlaps in case of volatile long: we don't want the
5931   // code generated by GenerateWideAtomicLoad to overwrite the
5932   // object's location.  Likewise, in the case of an object field get
5933   // with read barriers enabled, we do not want the load to overwrite
5934   // the object's location, as we need it to emit the read barrier.
5935   bool overlap =
5936       (field_info.IsVolatile() && (field_info.GetFieldType() == DataType::Type::kInt64)) ||
5937       object_field_get_with_read_barrier;
5938 
5939   if (DataType::IsFloatingPointType(instruction->GetType())) {
5940     if (is_predicated) {
5941       locations->SetInAt(0, Location::RequiresFpuRegister());
5942       locations->SetOut(Location::SameAsFirstInput());
5943     } else {
5944       locations->SetOut(Location::RequiresFpuRegister());
5945     }
5946   } else {
5947     if (is_predicated) {
5948       locations->SetInAt(0, Location::RequiresRegister());
5949       locations->SetOut(Location::SameAsFirstInput());
5950     } else {
5951       locations->SetOut(Location::RequiresRegister(),
5952                         (overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap));
5953     }
5954   }
5955   if (volatile_for_double) {
5956     // ARM encoding have some additional constraints for ldrexd/strexd:
5957     // - registers need to be consecutive
5958     // - the first register should be even but not R14.
5959     // We don't test for ARM yet, and the assertion makes sure that we
5960     // revisit this if we ever enable ARM encoding.
5961     DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet());
5962     locations->AddTemp(Location::RequiresRegister());
5963     locations->AddTemp(Location::RequiresRegister());
5964   } else if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
5965     // We need a temporary register for the read barrier load in
5966     // CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier()
5967     // only if the offset is too big.
5968     if (field_info.GetFieldOffset().Uint32Value() >= kReferenceLoadMinFarOffset) {
5969       locations->AddTemp(Location::RequiresRegister());
5970     }
5971   }
5972 }
5973 
ArithmeticZeroOrFpuRegister(HInstruction * input)5974 Location LocationsBuilderARMVIXL::ArithmeticZeroOrFpuRegister(HInstruction* input) {
5975   DCHECK(DataType::IsFloatingPointType(input->GetType())) << input->GetType();
5976   if ((input->IsFloatConstant() && (input->AsFloatConstant()->IsArithmeticZero())) ||
5977       (input->IsDoubleConstant() && (input->AsDoubleConstant()->IsArithmeticZero()))) {
5978     return Location::ConstantLocation(input->AsConstant());
5979   } else {
5980     return Location::RequiresFpuRegister();
5981   }
5982 }
5983 
ArmEncodableConstantOrRegister(HInstruction * constant,Opcode opcode)5984 Location LocationsBuilderARMVIXL::ArmEncodableConstantOrRegister(HInstruction* constant,
5985                                                                  Opcode opcode) {
5986   DCHECK(!DataType::IsFloatingPointType(constant->GetType()));
5987   if (constant->IsConstant() &&
5988       CanEncodeConstantAsImmediate(constant->AsConstant(), opcode)) {
5989     return Location::ConstantLocation(constant->AsConstant());
5990   }
5991   return Location::RequiresRegister();
5992 }
5993 
CanEncode32BitConstantAsImmediate(CodeGeneratorARMVIXL * codegen,uint32_t value,Opcode opcode,vixl32::FlagsUpdate flags_update=vixl32::FlagsUpdate::DontCare)5994 static bool CanEncode32BitConstantAsImmediate(
5995     CodeGeneratorARMVIXL* codegen,
5996     uint32_t value,
5997     Opcode opcode,
5998     vixl32::FlagsUpdate flags_update = vixl32::FlagsUpdate::DontCare) {
5999   ArmVIXLAssembler* assembler = codegen->GetAssembler();
6000   if (assembler->ShifterOperandCanHold(opcode, value, flags_update)) {
6001     return true;
6002   }
6003   Opcode neg_opcode = kNoOperand;
6004   uint32_t neg_value = 0;
6005   switch (opcode) {
6006     case AND: neg_opcode = BIC; neg_value = ~value; break;
6007     case ORR: neg_opcode = ORN; neg_value = ~value; break;
6008     case ADD: neg_opcode = SUB; neg_value = -value; break;
6009     case ADC: neg_opcode = SBC; neg_value = ~value; break;
6010     case SUB: neg_opcode = ADD; neg_value = -value; break;
6011     case SBC: neg_opcode = ADC; neg_value = ~value; break;
6012     case MOV: neg_opcode = MVN; neg_value = ~value; break;
6013     default:
6014       return false;
6015   }
6016 
6017   if (assembler->ShifterOperandCanHold(neg_opcode, neg_value, flags_update)) {
6018     return true;
6019   }
6020 
6021   return opcode == AND && IsPowerOfTwo(value + 1);
6022 }
6023 
CanEncodeConstantAsImmediate(HConstant * input_cst,Opcode opcode)6024 bool LocationsBuilderARMVIXL::CanEncodeConstantAsImmediate(HConstant* input_cst, Opcode opcode) {
6025   uint64_t value = static_cast<uint64_t>(Int64FromConstant(input_cst));
6026   if (DataType::Is64BitType(input_cst->GetType())) {
6027     Opcode high_opcode = opcode;
6028     vixl32::FlagsUpdate low_flags_update = vixl32::FlagsUpdate::DontCare;
6029     switch (opcode) {
6030       case SUB:
6031         // Flip the operation to an ADD.
6032         value = -value;
6033         opcode = ADD;
6034         FALLTHROUGH_INTENDED;
6035       case ADD:
6036         if (Low32Bits(value) == 0u) {
6037           return CanEncode32BitConstantAsImmediate(codegen_, High32Bits(value), opcode);
6038         }
6039         high_opcode = ADC;
6040         low_flags_update = vixl32::FlagsUpdate::SetFlags;
6041         break;
6042       default:
6043         break;
6044     }
6045     return CanEncode32BitConstantAsImmediate(codegen_, High32Bits(value), high_opcode) &&
6046            CanEncode32BitConstantAsImmediate(codegen_, Low32Bits(value), opcode, low_flags_update);
6047   } else {
6048     return CanEncode32BitConstantAsImmediate(codegen_, Low32Bits(value), opcode);
6049   }
6050 }
6051 
HandleFieldGet(HInstruction * instruction,const FieldInfo & field_info)6052 void InstructionCodeGeneratorARMVIXL::HandleFieldGet(HInstruction* instruction,
6053                                                      const FieldInfo& field_info) {
6054   DCHECK(instruction->IsInstanceFieldGet() ||
6055          instruction->IsStaticFieldGet() ||
6056          instruction->IsPredicatedInstanceFieldGet());
6057 
6058   LocationSummary* locations = instruction->GetLocations();
6059   uint32_t receiver_input = instruction->IsPredicatedInstanceFieldGet() ? 1 : 0;
6060   vixl32::Register base = InputRegisterAt(instruction, receiver_input);
6061   Location out = locations->Out();
6062   bool is_volatile = field_info.IsVolatile();
6063   bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
6064   DCHECK_EQ(DataType::Size(field_info.GetFieldType()), DataType::Size(instruction->GetType()));
6065   DataType::Type load_type = instruction->GetType();
6066   uint32_t offset = field_info.GetFieldOffset().Uint32Value();
6067 
6068   switch (load_type) {
6069     case DataType::Type::kBool:
6070     case DataType::Type::kUint8:
6071     case DataType::Type::kInt8:
6072     case DataType::Type::kUint16:
6073     case DataType::Type::kInt16:
6074     case DataType::Type::kInt32: {
6075       // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
6076       EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
6077       LoadOperandType operand_type = GetLoadOperandType(load_type);
6078       GetAssembler()->LoadFromOffset(operand_type, RegisterFrom(out), base, offset);
6079       codegen_->MaybeRecordImplicitNullCheck(instruction);
6080       break;
6081     }
6082 
6083     case DataType::Type::kReference: {
6084       // /* HeapReference<Object> */ out = *(base + offset)
6085       if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
6086         Location maybe_temp = (locations->GetTempCount() != 0) ? locations->GetTemp(0) : Location();
6087         // Note that a potential implicit null check is handled in this
6088         // CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier call.
6089         codegen_->GenerateFieldLoadWithBakerReadBarrier(
6090             instruction, out, base, offset, maybe_temp, /* needs_null_check= */ true);
6091         if (is_volatile) {
6092           codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
6093         }
6094       } else {
6095         {
6096           // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
6097           EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
6098           GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(out), base, offset);
6099           codegen_->MaybeRecordImplicitNullCheck(instruction);
6100         }
6101         if (is_volatile) {
6102           codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
6103         }
6104         // If read barriers are enabled, emit read barriers other than
6105         // Baker's using a slow path (and also unpoison the loaded
6106         // reference, if heap poisoning is enabled).
6107         codegen_->MaybeGenerateReadBarrierSlow(
6108             instruction, out, out, locations->InAt(receiver_input), offset);
6109       }
6110       break;
6111     }
6112 
6113     case DataType::Type::kInt64: {
6114       // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
6115       EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
6116       if (is_volatile && !atomic_ldrd_strd) {
6117         GenerateWideAtomicLoad(base, offset, LowRegisterFrom(out), HighRegisterFrom(out));
6118       } else {
6119         GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out), base, offset);
6120       }
6121       codegen_->MaybeRecordImplicitNullCheck(instruction);
6122       break;
6123     }
6124 
6125     case DataType::Type::kFloat32: {
6126       // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
6127       EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
6128       GetAssembler()->LoadSFromOffset(SRegisterFrom(out), base, offset);
6129       codegen_->MaybeRecordImplicitNullCheck(instruction);
6130       break;
6131     }
6132 
6133     case DataType::Type::kFloat64: {
6134       // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
6135       EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
6136       vixl32::DRegister out_dreg = DRegisterFrom(out);
6137       if (is_volatile && !atomic_ldrd_strd) {
6138         vixl32::Register lo = RegisterFrom(locations->GetTemp(0));
6139         vixl32::Register hi = RegisterFrom(locations->GetTemp(1));
6140         GenerateWideAtomicLoad(base, offset, lo, hi);
6141         codegen_->MaybeRecordImplicitNullCheck(instruction);
6142         __ Vmov(out_dreg, lo, hi);
6143       } else {
6144         GetAssembler()->LoadDFromOffset(out_dreg, base, offset);
6145         codegen_->MaybeRecordImplicitNullCheck(instruction);
6146       }
6147       break;
6148     }
6149 
6150     case DataType::Type::kUint32:
6151     case DataType::Type::kUint64:
6152     case DataType::Type::kVoid:
6153       LOG(FATAL) << "Unreachable type " << load_type;
6154       UNREACHABLE();
6155   }
6156 
6157   if (is_volatile) {
6158     if (load_type == DataType::Type::kReference) {
6159       // Memory barriers, in the case of references, are also handled
6160       // in the previous switch statement.
6161     } else {
6162       codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
6163     }
6164   }
6165 }
6166 
VisitInstanceFieldSet(HInstanceFieldSet * instruction)6167 void LocationsBuilderARMVIXL::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
6168   HandleFieldSet(instruction, instruction->GetFieldInfo());
6169 }
6170 
VisitInstanceFieldSet(HInstanceFieldSet * instruction)6171 void InstructionCodeGeneratorARMVIXL::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
6172   HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
6173 }
6174 
VisitInstanceFieldGet(HInstanceFieldGet * instruction)6175 void LocationsBuilderARMVIXL::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
6176   HandleFieldGet(instruction, instruction->GetFieldInfo());
6177 }
6178 
VisitPredicatedInstanceFieldGet(HPredicatedInstanceFieldGet * instruction)6179 void LocationsBuilderARMVIXL::VisitPredicatedInstanceFieldGet(
6180     HPredicatedInstanceFieldGet* instruction) {
6181   HandleFieldGet(instruction, instruction->GetFieldInfo());
6182 }
6183 
VisitPredicatedInstanceFieldGet(HPredicatedInstanceFieldGet * instruction)6184 void InstructionCodeGeneratorARMVIXL::VisitPredicatedInstanceFieldGet(
6185     HPredicatedInstanceFieldGet* instruction) {
6186   vixl::aarch32::Label finish;
6187   __ CompareAndBranchIfZero(InputRegisterAt(instruction, 1), &finish, false);
6188   HandleFieldGet(instruction, instruction->GetFieldInfo());
6189   __ Bind(&finish);
6190 }
6191 
VisitInstanceFieldGet(HInstanceFieldGet * instruction)6192 void InstructionCodeGeneratorARMVIXL::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
6193   HandleFieldGet(instruction, instruction->GetFieldInfo());
6194 }
6195 
VisitStaticFieldGet(HStaticFieldGet * instruction)6196 void LocationsBuilderARMVIXL::VisitStaticFieldGet(HStaticFieldGet* instruction) {
6197   HandleFieldGet(instruction, instruction->GetFieldInfo());
6198 }
6199 
VisitStaticFieldGet(HStaticFieldGet * instruction)6200 void InstructionCodeGeneratorARMVIXL::VisitStaticFieldGet(HStaticFieldGet* instruction) {
6201   HandleFieldGet(instruction, instruction->GetFieldInfo());
6202 }
6203 
VisitStaticFieldSet(HStaticFieldSet * instruction)6204 void LocationsBuilderARMVIXL::VisitStaticFieldSet(HStaticFieldSet* instruction) {
6205   HandleFieldSet(instruction, instruction->GetFieldInfo());
6206 }
6207 
VisitStaticFieldSet(HStaticFieldSet * instruction)6208 void InstructionCodeGeneratorARMVIXL::VisitStaticFieldSet(HStaticFieldSet* instruction) {
6209   HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
6210 }
6211 
VisitStringBuilderAppend(HStringBuilderAppend * instruction)6212 void LocationsBuilderARMVIXL::VisitStringBuilderAppend(HStringBuilderAppend* instruction) {
6213   codegen_->CreateStringBuilderAppendLocations(instruction, LocationFrom(r0));
6214 }
6215 
VisitStringBuilderAppend(HStringBuilderAppend * instruction)6216 void InstructionCodeGeneratorARMVIXL::VisitStringBuilderAppend(HStringBuilderAppend* instruction) {
6217   __ Mov(r0, instruction->GetFormat()->GetValue());
6218   codegen_->InvokeRuntime(kQuickStringBuilderAppend, instruction, instruction->GetDexPc());
6219 }
6220 
VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet * instruction)6221 void LocationsBuilderARMVIXL::VisitUnresolvedInstanceFieldGet(
6222     HUnresolvedInstanceFieldGet* instruction) {
6223   FieldAccessCallingConventionARMVIXL calling_convention;
6224   codegen_->CreateUnresolvedFieldLocationSummary(
6225       instruction, instruction->GetFieldType(), calling_convention);
6226 }
6227 
VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet * instruction)6228 void InstructionCodeGeneratorARMVIXL::VisitUnresolvedInstanceFieldGet(
6229     HUnresolvedInstanceFieldGet* instruction) {
6230   FieldAccessCallingConventionARMVIXL calling_convention;
6231   codegen_->GenerateUnresolvedFieldAccess(instruction,
6232                                           instruction->GetFieldType(),
6233                                           instruction->GetFieldIndex(),
6234                                           instruction->GetDexPc(),
6235                                           calling_convention);
6236 }
6237 
VisitUnresolvedInstanceFieldSet(HUnresolvedInstanceFieldSet * instruction)6238 void LocationsBuilderARMVIXL::VisitUnresolvedInstanceFieldSet(
6239     HUnresolvedInstanceFieldSet* instruction) {
6240   FieldAccessCallingConventionARMVIXL calling_convention;
6241   codegen_->CreateUnresolvedFieldLocationSummary(
6242       instruction, instruction->GetFieldType(), calling_convention);
6243 }
6244 
VisitUnresolvedInstanceFieldSet(HUnresolvedInstanceFieldSet * instruction)6245 void InstructionCodeGeneratorARMVIXL::VisitUnresolvedInstanceFieldSet(
6246     HUnresolvedInstanceFieldSet* instruction) {
6247   FieldAccessCallingConventionARMVIXL calling_convention;
6248   codegen_->GenerateUnresolvedFieldAccess(instruction,
6249                                           instruction->GetFieldType(),
6250                                           instruction->GetFieldIndex(),
6251                                           instruction->GetDexPc(),
6252                                           calling_convention);
6253 }
6254 
VisitUnresolvedStaticFieldGet(HUnresolvedStaticFieldGet * instruction)6255 void LocationsBuilderARMVIXL::VisitUnresolvedStaticFieldGet(
6256     HUnresolvedStaticFieldGet* instruction) {
6257   FieldAccessCallingConventionARMVIXL calling_convention;
6258   codegen_->CreateUnresolvedFieldLocationSummary(
6259       instruction, instruction->GetFieldType(), calling_convention);
6260 }
6261 
VisitUnresolvedStaticFieldGet(HUnresolvedStaticFieldGet * instruction)6262 void InstructionCodeGeneratorARMVIXL::VisitUnresolvedStaticFieldGet(
6263     HUnresolvedStaticFieldGet* instruction) {
6264   FieldAccessCallingConventionARMVIXL calling_convention;
6265   codegen_->GenerateUnresolvedFieldAccess(instruction,
6266                                           instruction->GetFieldType(),
6267                                           instruction->GetFieldIndex(),
6268                                           instruction->GetDexPc(),
6269                                           calling_convention);
6270 }
6271 
VisitUnresolvedStaticFieldSet(HUnresolvedStaticFieldSet * instruction)6272 void LocationsBuilderARMVIXL::VisitUnresolvedStaticFieldSet(
6273     HUnresolvedStaticFieldSet* instruction) {
6274   FieldAccessCallingConventionARMVIXL calling_convention;
6275   codegen_->CreateUnresolvedFieldLocationSummary(
6276       instruction, instruction->GetFieldType(), calling_convention);
6277 }
6278 
VisitUnresolvedStaticFieldSet(HUnresolvedStaticFieldSet * instruction)6279 void InstructionCodeGeneratorARMVIXL::VisitUnresolvedStaticFieldSet(
6280     HUnresolvedStaticFieldSet* instruction) {
6281   FieldAccessCallingConventionARMVIXL calling_convention;
6282   codegen_->GenerateUnresolvedFieldAccess(instruction,
6283                                           instruction->GetFieldType(),
6284                                           instruction->GetFieldIndex(),
6285                                           instruction->GetDexPc(),
6286                                           calling_convention);
6287 }
6288 
VisitNullCheck(HNullCheck * instruction)6289 void LocationsBuilderARMVIXL::VisitNullCheck(HNullCheck* instruction) {
6290   LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
6291   locations->SetInAt(0, Location::RequiresRegister());
6292 }
6293 
GenerateImplicitNullCheck(HNullCheck * instruction)6294 void CodeGeneratorARMVIXL::GenerateImplicitNullCheck(HNullCheck* instruction) {
6295   if (CanMoveNullCheckToUser(instruction)) {
6296     return;
6297   }
6298 
6299   UseScratchRegisterScope temps(GetVIXLAssembler());
6300   // Ensure the pc position is recorded immediately after the `ldr` instruction.
6301   ExactAssemblyScope aas(GetVIXLAssembler(),
6302                          vixl32::kMaxInstructionSizeInBytes,
6303                          CodeBufferCheckScope::kMaximumSize);
6304   __ ldr(temps.Acquire(), MemOperand(InputRegisterAt(instruction, 0)));
6305   RecordPcInfo(instruction, instruction->GetDexPc());
6306 }
6307 
GenerateExplicitNullCheck(HNullCheck * instruction)6308 void CodeGeneratorARMVIXL::GenerateExplicitNullCheck(HNullCheck* instruction) {
6309   NullCheckSlowPathARMVIXL* slow_path =
6310       new (GetScopedAllocator()) NullCheckSlowPathARMVIXL(instruction);
6311   AddSlowPath(slow_path);
6312   __ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
6313 }
6314 
VisitNullCheck(HNullCheck * instruction)6315 void InstructionCodeGeneratorARMVIXL::VisitNullCheck(HNullCheck* instruction) {
6316   codegen_->GenerateNullCheck(instruction);
6317 }
6318 
LoadFromShiftedRegOffset(DataType::Type type,Location out_loc,vixl32::Register base,vixl32::Register reg_index,vixl32::Condition cond)6319 void CodeGeneratorARMVIXL::LoadFromShiftedRegOffset(DataType::Type type,
6320                                                     Location out_loc,
6321                                                     vixl32::Register base,
6322                                                     vixl32::Register reg_index,
6323                                                     vixl32::Condition cond) {
6324   uint32_t shift_count = DataType::SizeShift(type);
6325   MemOperand mem_address(base, reg_index, vixl32::LSL, shift_count);
6326 
6327   switch (type) {
6328     case DataType::Type::kBool:
6329     case DataType::Type::kUint8:
6330       __ Ldrb(cond, RegisterFrom(out_loc), mem_address);
6331       break;
6332     case DataType::Type::kInt8:
6333       __ Ldrsb(cond, RegisterFrom(out_loc), mem_address);
6334       break;
6335     case DataType::Type::kUint16:
6336       __ Ldrh(cond, RegisterFrom(out_loc), mem_address);
6337       break;
6338     case DataType::Type::kInt16:
6339       __ Ldrsh(cond, RegisterFrom(out_loc), mem_address);
6340       break;
6341     case DataType::Type::kReference:
6342     case DataType::Type::kInt32:
6343       __ Ldr(cond, RegisterFrom(out_loc), mem_address);
6344       break;
6345     // T32 doesn't support LoadFromShiftedRegOffset mem address mode for these types.
6346     case DataType::Type::kInt64:
6347     case DataType::Type::kFloat32:
6348     case DataType::Type::kFloat64:
6349     default:
6350       LOG(FATAL) << "Unreachable type " << type;
6351       UNREACHABLE();
6352   }
6353 }
6354 
StoreToShiftedRegOffset(DataType::Type type,Location loc,vixl32::Register base,vixl32::Register reg_index,vixl32::Condition cond)6355 void CodeGeneratorARMVIXL::StoreToShiftedRegOffset(DataType::Type type,
6356                                                    Location loc,
6357                                                    vixl32::Register base,
6358                                                    vixl32::Register reg_index,
6359                                                    vixl32::Condition cond) {
6360   uint32_t shift_count = DataType::SizeShift(type);
6361   MemOperand mem_address(base, reg_index, vixl32::LSL, shift_count);
6362 
6363   switch (type) {
6364     case DataType::Type::kBool:
6365     case DataType::Type::kUint8:
6366     case DataType::Type::kInt8:
6367       __ Strb(cond, RegisterFrom(loc), mem_address);
6368       break;
6369     case DataType::Type::kUint16:
6370     case DataType::Type::kInt16:
6371       __ Strh(cond, RegisterFrom(loc), mem_address);
6372       break;
6373     case DataType::Type::kReference:
6374     case DataType::Type::kInt32:
6375       __ Str(cond, RegisterFrom(loc), mem_address);
6376       break;
6377     // T32 doesn't support StoreToShiftedRegOffset mem address mode for these types.
6378     case DataType::Type::kInt64:
6379     case DataType::Type::kFloat32:
6380     case DataType::Type::kFloat64:
6381     default:
6382       LOG(FATAL) << "Unreachable type " << type;
6383       UNREACHABLE();
6384   }
6385 }
6386 
VisitArrayGet(HArrayGet * instruction)6387 void LocationsBuilderARMVIXL::VisitArrayGet(HArrayGet* instruction) {
6388   bool object_array_get_with_read_barrier =
6389       kEmitCompilerReadBarrier && (instruction->GetType() == DataType::Type::kReference);
6390   LocationSummary* locations =
6391       new (GetGraph()->GetAllocator()) LocationSummary(instruction,
6392                                                        object_array_get_with_read_barrier
6393                                                            ? LocationSummary::kCallOnSlowPath
6394                                                            : LocationSummary::kNoCall);
6395   if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
6396     locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
6397   }
6398   locations->SetInAt(0, Location::RequiresRegister());
6399   locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
6400   if (DataType::IsFloatingPointType(instruction->GetType())) {
6401     locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
6402   } else {
6403     // The output overlaps in the case of an object array get with
6404     // read barriers enabled: we do not want the move to overwrite the
6405     // array's location, as we need it to emit the read barrier.
6406     locations->SetOut(
6407         Location::RequiresRegister(),
6408         object_array_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap);
6409   }
6410   if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
6411     if (instruction->GetIndex()->IsConstant()) {
6412       // Array loads with constant index are treated as field loads.
6413       // We need a temporary register for the read barrier load in
6414       // CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier()
6415       // only if the offset is too big.
6416       uint32_t offset = CodeGenerator::GetArrayDataOffset(instruction);
6417       uint32_t index = instruction->GetIndex()->AsIntConstant()->GetValue();
6418       offset += index << DataType::SizeShift(DataType::Type::kReference);
6419       if (offset >= kReferenceLoadMinFarOffset) {
6420         locations->AddTemp(Location::RequiresRegister());
6421       }
6422     } else {
6423       // We need a non-scratch temporary for the array data pointer in
6424       // CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier().
6425       locations->AddTemp(Location::RequiresRegister());
6426     }
6427   } else if (mirror::kUseStringCompression && instruction->IsStringCharAt()) {
6428     // Also need a temporary for String compression feature.
6429     locations->AddTemp(Location::RequiresRegister());
6430   }
6431 }
6432 
VisitArrayGet(HArrayGet * instruction)6433 void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) {
6434   LocationSummary* locations = instruction->GetLocations();
6435   Location obj_loc = locations->InAt(0);
6436   vixl32::Register obj = InputRegisterAt(instruction, 0);
6437   Location index = locations->InAt(1);
6438   Location out_loc = locations->Out();
6439   uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
6440   DataType::Type type = instruction->GetType();
6441   const bool maybe_compressed_char_at = mirror::kUseStringCompression &&
6442                                         instruction->IsStringCharAt();
6443   HInstruction* array_instr = instruction->GetArray();
6444   bool has_intermediate_address = array_instr->IsIntermediateAddress();
6445 
6446   switch (type) {
6447     case DataType::Type::kBool:
6448     case DataType::Type::kUint8:
6449     case DataType::Type::kInt8:
6450     case DataType::Type::kUint16:
6451     case DataType::Type::kInt16:
6452     case DataType::Type::kInt32: {
6453       vixl32::Register length;
6454       if (maybe_compressed_char_at) {
6455         length = RegisterFrom(locations->GetTemp(0));
6456         uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
6457         // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
6458         EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
6459         GetAssembler()->LoadFromOffset(kLoadWord, length, obj, count_offset);
6460         codegen_->MaybeRecordImplicitNullCheck(instruction);
6461       }
6462       if (index.IsConstant()) {
6463         int32_t const_index = Int32ConstantFrom(index);
6464         if (maybe_compressed_char_at) {
6465           vixl32::Label uncompressed_load, done;
6466           vixl32::Label* final_label = codegen_->GetFinalLabel(instruction, &done);
6467           __ Lsrs(length, length, 1u);  // LSRS has a 16-bit encoding, TST (immediate) does not.
6468           static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
6469                         "Expecting 0=compressed, 1=uncompressed");
6470           __ B(cs, &uncompressed_load, /* is_far_target= */ false);
6471           GetAssembler()->LoadFromOffset(kLoadUnsignedByte,
6472                                          RegisterFrom(out_loc),
6473                                          obj,
6474                                          data_offset + const_index);
6475           __ B(final_label);
6476           __ Bind(&uncompressed_load);
6477           GetAssembler()->LoadFromOffset(GetLoadOperandType(DataType::Type::kUint16),
6478                                          RegisterFrom(out_loc),
6479                                          obj,
6480                                          data_offset + (const_index << 1));
6481           if (done.IsReferenced()) {
6482             __ Bind(&done);
6483           }
6484         } else {
6485           uint32_t full_offset = data_offset + (const_index << DataType::SizeShift(type));
6486 
6487           // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
6488           EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
6489           LoadOperandType load_type = GetLoadOperandType(type);
6490           GetAssembler()->LoadFromOffset(load_type, RegisterFrom(out_loc), obj, full_offset);
6491           codegen_->MaybeRecordImplicitNullCheck(instruction);
6492         }
6493       } else {
6494         UseScratchRegisterScope temps(GetVIXLAssembler());
6495         vixl32::Register temp = temps.Acquire();
6496 
6497         if (has_intermediate_address) {
6498           // We do not need to compute the intermediate address from the array: the
6499           // input instruction has done it already. See the comment in
6500           // `TryExtractArrayAccessAddress()`.
6501           if (kIsDebugBuild) {
6502             HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
6503             DCHECK_EQ(Uint64ConstantFrom(tmp->GetOffset()), data_offset);
6504           }
6505           temp = obj;
6506         } else {
6507           __ Add(temp, obj, data_offset);
6508         }
6509         if (maybe_compressed_char_at) {
6510           vixl32::Label uncompressed_load, done;
6511           vixl32::Label* final_label = codegen_->GetFinalLabel(instruction, &done);
6512           __ Lsrs(length, length, 1u);  // LSRS has a 16-bit encoding, TST (immediate) does not.
6513           static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
6514                         "Expecting 0=compressed, 1=uncompressed");
6515           __ B(cs, &uncompressed_load, /* is_far_target= */ false);
6516           __ Ldrb(RegisterFrom(out_loc), MemOperand(temp, RegisterFrom(index), vixl32::LSL, 0));
6517           __ B(final_label);
6518           __ Bind(&uncompressed_load);
6519           __ Ldrh(RegisterFrom(out_loc), MemOperand(temp, RegisterFrom(index), vixl32::LSL, 1));
6520           if (done.IsReferenced()) {
6521             __ Bind(&done);
6522           }
6523         } else {
6524           // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
6525           EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
6526           codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index));
6527           codegen_->MaybeRecordImplicitNullCheck(instruction);
6528         }
6529       }
6530       break;
6531     }
6532 
6533     case DataType::Type::kReference: {
6534       // The read barrier instrumentation of object ArrayGet
6535       // instructions does not support the HIntermediateAddress
6536       // instruction.
6537       DCHECK(!(has_intermediate_address && kEmitCompilerReadBarrier));
6538 
6539       static_assert(
6540           sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
6541           "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
6542       // /* HeapReference<Object> */ out =
6543       //     *(obj + data_offset + index * sizeof(HeapReference<Object>))
6544       if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
6545         // Note that a potential implicit null check is handled in this
6546         // CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier call.
6547         DCHECK(!instruction->CanDoImplicitNullCheckOn(instruction->InputAt(0)));
6548         if (index.IsConstant()) {
6549           // Array load with a constant index can be treated as a field load.
6550           Location maybe_temp =
6551               (locations->GetTempCount() != 0) ? locations->GetTemp(0) : Location();
6552           data_offset += Int32ConstantFrom(index) << DataType::SizeShift(type);
6553           codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction,
6554                                                           out_loc,
6555                                                           obj,
6556                                                           data_offset,
6557                                                           maybe_temp,
6558                                                           /* needs_null_check= */ false);
6559         } else {
6560           Location temp = locations->GetTemp(0);
6561           codegen_->GenerateArrayLoadWithBakerReadBarrier(
6562               out_loc, obj, data_offset, index, temp, /* needs_null_check= */ false);
6563         }
6564       } else {
6565         vixl32::Register out = OutputRegister(instruction);
6566         if (index.IsConstant()) {
6567           size_t offset = (Int32ConstantFrom(index) << TIMES_4) + data_offset;
6568           {
6569             // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
6570             EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
6571             GetAssembler()->LoadFromOffset(kLoadWord, out, obj, offset);
6572             codegen_->MaybeRecordImplicitNullCheck(instruction);
6573           }
6574           // If read barriers are enabled, emit read barriers other than
6575           // Baker's using a slow path (and also unpoison the loaded
6576           // reference, if heap poisoning is enabled).
6577           codegen_->MaybeGenerateReadBarrierSlow(instruction, out_loc, out_loc, obj_loc, offset);
6578         } else {
6579           UseScratchRegisterScope temps(GetVIXLAssembler());
6580           vixl32::Register temp = temps.Acquire();
6581 
6582           if (has_intermediate_address) {
6583             // We do not need to compute the intermediate address from the array: the
6584             // input instruction has done it already. See the comment in
6585             // `TryExtractArrayAccessAddress()`.
6586             if (kIsDebugBuild) {
6587               HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
6588               DCHECK_EQ(Uint64ConstantFrom(tmp->GetOffset()), data_offset);
6589             }
6590             temp = obj;
6591           } else {
6592             __ Add(temp, obj, data_offset);
6593           }
6594           {
6595             // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
6596             EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
6597             codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index));
6598             temps.Close();
6599             codegen_->MaybeRecordImplicitNullCheck(instruction);
6600           }
6601           // If read barriers are enabled, emit read barriers other than
6602           // Baker's using a slow path (and also unpoison the loaded
6603           // reference, if heap poisoning is enabled).
6604           codegen_->MaybeGenerateReadBarrierSlow(
6605               instruction, out_loc, out_loc, obj_loc, data_offset, index);
6606         }
6607       }
6608       break;
6609     }
6610 
6611     case DataType::Type::kInt64: {
6612       // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
6613       // As two macro instructions can be emitted the max size is doubled.
6614       EmissionCheckScope guard(GetVIXLAssembler(), 2 * kMaxMacroInstructionSizeInBytes);
6615       if (index.IsConstant()) {
6616         size_t offset =
6617             (Int32ConstantFrom(index) << TIMES_8) + data_offset;
6618         GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out_loc), obj, offset);
6619       } else {
6620         UseScratchRegisterScope temps(GetVIXLAssembler());
6621         vixl32::Register temp = temps.Acquire();
6622         __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
6623         GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out_loc), temp, data_offset);
6624       }
6625       codegen_->MaybeRecordImplicitNullCheck(instruction);
6626       break;
6627     }
6628 
6629     case DataType::Type::kFloat32: {
6630       // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
6631       // As two macro instructions can be emitted the max size is doubled.
6632       EmissionCheckScope guard(GetVIXLAssembler(), 2 * kMaxMacroInstructionSizeInBytes);
6633       vixl32::SRegister out = SRegisterFrom(out_loc);
6634       if (index.IsConstant()) {
6635         size_t offset = (Int32ConstantFrom(index) << TIMES_4) + data_offset;
6636         GetAssembler()->LoadSFromOffset(out, obj, offset);
6637       } else {
6638         UseScratchRegisterScope temps(GetVIXLAssembler());
6639         vixl32::Register temp = temps.Acquire();
6640         __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_4));
6641         GetAssembler()->LoadSFromOffset(out, temp, data_offset);
6642       }
6643       codegen_->MaybeRecordImplicitNullCheck(instruction);
6644       break;
6645     }
6646 
6647     case DataType::Type::kFloat64: {
6648       // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
6649       // As two macro instructions can be emitted the max size is doubled.
6650       EmissionCheckScope guard(GetVIXLAssembler(), 2 * kMaxMacroInstructionSizeInBytes);
6651       if (index.IsConstant()) {
6652         size_t offset = (Int32ConstantFrom(index) << TIMES_8) + data_offset;
6653         GetAssembler()->LoadDFromOffset(DRegisterFrom(out_loc), obj, offset);
6654       } else {
6655         UseScratchRegisterScope temps(GetVIXLAssembler());
6656         vixl32::Register temp = temps.Acquire();
6657         __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
6658         GetAssembler()->LoadDFromOffset(DRegisterFrom(out_loc), temp, data_offset);
6659       }
6660       codegen_->MaybeRecordImplicitNullCheck(instruction);
6661       break;
6662     }
6663 
6664     case DataType::Type::kUint32:
6665     case DataType::Type::kUint64:
6666     case DataType::Type::kVoid:
6667       LOG(FATAL) << "Unreachable type " << type;
6668       UNREACHABLE();
6669   }
6670 }
6671 
VisitArraySet(HArraySet * instruction)6672 void LocationsBuilderARMVIXL::VisitArraySet(HArraySet* instruction) {
6673   DataType::Type value_type = instruction->GetComponentType();
6674 
6675   bool needs_write_barrier =
6676       CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
6677   bool needs_type_check = instruction->NeedsTypeCheck();
6678 
6679   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
6680       instruction,
6681       needs_type_check ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall);
6682 
6683   locations->SetInAt(0, Location::RequiresRegister());
6684   locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
6685   if (DataType::IsFloatingPointType(value_type)) {
6686     locations->SetInAt(2, Location::RequiresFpuRegister());
6687   } else {
6688     locations->SetInAt(2, Location::RequiresRegister());
6689   }
6690   if (needs_write_barrier) {
6691     // Temporary registers for the write barrier.
6692     locations->AddTemp(Location::RequiresRegister());  // Possibly used for ref. poisoning too.
6693     locations->AddTemp(Location::RequiresRegister());
6694   }
6695 }
6696 
VisitArraySet(HArraySet * instruction)6697 void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) {
6698   LocationSummary* locations = instruction->GetLocations();
6699   vixl32::Register array = InputRegisterAt(instruction, 0);
6700   Location index = locations->InAt(1);
6701   DataType::Type value_type = instruction->GetComponentType();
6702   bool needs_type_check = instruction->NeedsTypeCheck();
6703   bool needs_write_barrier =
6704       CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
6705   uint32_t data_offset =
6706       mirror::Array::DataOffset(DataType::Size(value_type)).Uint32Value();
6707   Location value_loc = locations->InAt(2);
6708   HInstruction* array_instr = instruction->GetArray();
6709   bool has_intermediate_address = array_instr->IsIntermediateAddress();
6710 
6711   switch (value_type) {
6712     case DataType::Type::kBool:
6713     case DataType::Type::kUint8:
6714     case DataType::Type::kInt8:
6715     case DataType::Type::kUint16:
6716     case DataType::Type::kInt16:
6717     case DataType::Type::kInt32: {
6718       if (index.IsConstant()) {
6719         int32_t const_index = Int32ConstantFrom(index);
6720         uint32_t full_offset =
6721             data_offset + (const_index << DataType::SizeShift(value_type));
6722         StoreOperandType store_type = GetStoreOperandType(value_type);
6723         // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
6724         EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
6725         GetAssembler()->StoreToOffset(store_type, RegisterFrom(value_loc), array, full_offset);
6726         codegen_->MaybeRecordImplicitNullCheck(instruction);
6727       } else {
6728         UseScratchRegisterScope temps(GetVIXLAssembler());
6729         vixl32::Register temp = temps.Acquire();
6730 
6731         if (has_intermediate_address) {
6732           // We do not need to compute the intermediate address from the array: the
6733           // input instruction has done it already. See the comment in
6734           // `TryExtractArrayAccessAddress()`.
6735           if (kIsDebugBuild) {
6736             HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
6737             DCHECK_EQ(Uint64ConstantFrom(tmp->GetOffset()), data_offset);
6738           }
6739           temp = array;
6740         } else {
6741           __ Add(temp, array, data_offset);
6742         }
6743         // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
6744         EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
6745         codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
6746         codegen_->MaybeRecordImplicitNullCheck(instruction);
6747       }
6748       break;
6749     }
6750 
6751     case DataType::Type::kReference: {
6752       vixl32::Register value = RegisterFrom(value_loc);
6753       // TryExtractArrayAccessAddress optimization is never applied for non-primitive ArraySet.
6754       // See the comment in instruction_simplifier_shared.cc.
6755       DCHECK(!has_intermediate_address);
6756 
6757       if (instruction->InputAt(2)->IsNullConstant()) {
6758         // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
6759         // As two macro instructions can be emitted the max size is doubled.
6760         EmissionCheckScope guard(GetVIXLAssembler(), 2 * kMaxMacroInstructionSizeInBytes);
6761         // Just setting null.
6762         if (index.IsConstant()) {
6763           size_t offset = (Int32ConstantFrom(index) << TIMES_4) + data_offset;
6764           GetAssembler()->StoreToOffset(kStoreWord, value, array, offset);
6765         } else {
6766           DCHECK(index.IsRegister()) << index;
6767           UseScratchRegisterScope temps(GetVIXLAssembler());
6768           vixl32::Register temp = temps.Acquire();
6769           __ Add(temp, array, data_offset);
6770           codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
6771         }
6772         codegen_->MaybeRecordImplicitNullCheck(instruction);
6773         DCHECK(!needs_write_barrier);
6774         DCHECK(!needs_type_check);
6775         break;
6776       }
6777 
6778       DCHECK(needs_write_barrier);
6779       Location temp1_loc = locations->GetTemp(0);
6780       vixl32::Register temp1 = RegisterFrom(temp1_loc);
6781       Location temp2_loc = locations->GetTemp(1);
6782       vixl32::Register temp2 = RegisterFrom(temp2_loc);
6783 
6784       bool can_value_be_null = instruction->GetValueCanBeNull();
6785       vixl32::Label do_store;
6786       if (can_value_be_null) {
6787         __ CompareAndBranchIfZero(value, &do_store, /* is_far_target= */ false);
6788       }
6789 
6790       SlowPathCodeARMVIXL* slow_path = nullptr;
6791       if (needs_type_check) {
6792         slow_path = new (codegen_->GetScopedAllocator()) ArraySetSlowPathARMVIXL(instruction);
6793         codegen_->AddSlowPath(slow_path);
6794 
6795         const uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
6796         const uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
6797         const uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
6798 
6799         // Note that when read barriers are enabled, the type checks
6800         // are performed without read barriers.  This is fine, even in
6801         // the case where a class object is in the from-space after
6802         // the flip, as a comparison involving such a type would not
6803         // produce a false positive; it may of course produce a false
6804         // negative, in which case we would take the ArraySet slow
6805         // path.
6806 
6807         {
6808           // Ensure we record the pc position immediately after the `ldr` instruction.
6809           ExactAssemblyScope aas(GetVIXLAssembler(),
6810                                  vixl32::kMaxInstructionSizeInBytes,
6811                                  CodeBufferCheckScope::kMaximumSize);
6812           // /* HeapReference<Class> */ temp1 = array->klass_
6813           __ ldr(temp1, MemOperand(array, class_offset));
6814           codegen_->MaybeRecordImplicitNullCheck(instruction);
6815         }
6816         GetAssembler()->MaybeUnpoisonHeapReference(temp1);
6817 
6818         // /* HeapReference<Class> */ temp1 = temp1->component_type_
6819         GetAssembler()->LoadFromOffset(kLoadWord, temp1, temp1, component_offset);
6820         // /* HeapReference<Class> */ temp2 = value->klass_
6821         GetAssembler()->LoadFromOffset(kLoadWord, temp2, value, class_offset);
6822         // If heap poisoning is enabled, no need to unpoison `temp1`
6823         // nor `temp2`, as we are comparing two poisoned references.
6824         __ Cmp(temp1, temp2);
6825 
6826         if (instruction->StaticTypeOfArrayIsObjectArray()) {
6827           vixl32::Label do_put;
6828           __ B(eq, &do_put, /* is_far_target= */ false);
6829           // If heap poisoning is enabled, the `temp1` reference has
6830           // not been unpoisoned yet; unpoison it now.
6831           GetAssembler()->MaybeUnpoisonHeapReference(temp1);
6832 
6833           // /* HeapReference<Class> */ temp1 = temp1->super_class_
6834           GetAssembler()->LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
6835           // If heap poisoning is enabled, no need to unpoison
6836           // `temp1`, as we are comparing against null below.
6837           __ CompareAndBranchIfNonZero(temp1, slow_path->GetEntryLabel());
6838           __ Bind(&do_put);
6839         } else {
6840           __ B(ne, slow_path->GetEntryLabel());
6841         }
6842       }
6843 
6844       codegen_->MarkGCCard(temp1, temp2, array, value, /* value_can_be_null= */ false);
6845 
6846       if (can_value_be_null) {
6847         DCHECK(do_store.IsReferenced());
6848         __ Bind(&do_store);
6849       }
6850 
6851       vixl32::Register source = value;
6852       if (kPoisonHeapReferences) {
6853         // Note that in the case where `value` is a null reference,
6854         // we do not enter this block, as a null reference does not
6855         // need poisoning.
6856         DCHECK_EQ(value_type, DataType::Type::kReference);
6857         __ Mov(temp1, value);
6858         GetAssembler()->PoisonHeapReference(temp1);
6859         source = temp1;
6860       }
6861 
6862       {
6863         // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
6864         // As two macro instructions can be emitted the max size is doubled.
6865         EmissionCheckScope guard(GetVIXLAssembler(), 2 * kMaxMacroInstructionSizeInBytes);
6866         if (index.IsConstant()) {
6867           size_t offset = (Int32ConstantFrom(index) << TIMES_4) + data_offset;
6868           GetAssembler()->StoreToOffset(kStoreWord, source, array, offset);
6869         } else {
6870           DCHECK(index.IsRegister()) << index;
6871 
6872           UseScratchRegisterScope temps(GetVIXLAssembler());
6873           vixl32::Register temp = temps.Acquire();
6874           __ Add(temp, array, data_offset);
6875           codegen_->StoreToShiftedRegOffset(value_type,
6876                                             LocationFrom(source),
6877                                             temp,
6878                                             RegisterFrom(index));
6879         }
6880 
6881         if (can_value_be_null || !needs_type_check) {
6882           codegen_->MaybeRecordImplicitNullCheck(instruction);
6883         }
6884       }
6885 
6886       if (slow_path != nullptr) {
6887         __ Bind(slow_path->GetExitLabel());
6888       }
6889 
6890       break;
6891     }
6892 
6893     case DataType::Type::kInt64: {
6894       // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
6895       // As two macro instructions can be emitted the max size is doubled.
6896       EmissionCheckScope guard(GetVIXLAssembler(), 2 * kMaxMacroInstructionSizeInBytes);
6897       Location value = locations->InAt(2);
6898       if (index.IsConstant()) {
6899         size_t offset =
6900             (Int32ConstantFrom(index) << TIMES_8) + data_offset;
6901         GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), array, offset);
6902       } else {
6903         UseScratchRegisterScope temps(GetVIXLAssembler());
6904         vixl32::Register temp = temps.Acquire();
6905         __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
6906         GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), temp, data_offset);
6907       }
6908       codegen_->MaybeRecordImplicitNullCheck(instruction);
6909       break;
6910     }
6911 
6912     case DataType::Type::kFloat32: {
6913       // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
6914       // As two macro instructions can be emitted the max size is doubled.
6915       EmissionCheckScope guard(GetVIXLAssembler(), 2 * kMaxMacroInstructionSizeInBytes);
6916       Location value = locations->InAt(2);
6917       DCHECK(value.IsFpuRegister());
6918       if (index.IsConstant()) {
6919         size_t offset = (Int32ConstantFrom(index) << TIMES_4) + data_offset;
6920         GetAssembler()->StoreSToOffset(SRegisterFrom(value), array, offset);
6921       } else {
6922         UseScratchRegisterScope temps(GetVIXLAssembler());
6923         vixl32::Register temp = temps.Acquire();
6924         __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_4));
6925         GetAssembler()->StoreSToOffset(SRegisterFrom(value), temp, data_offset);
6926       }
6927       codegen_->MaybeRecordImplicitNullCheck(instruction);
6928       break;
6929     }
6930 
6931     case DataType::Type::kFloat64: {
6932       // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
6933       // As two macro instructions can be emitted the max size is doubled.
6934       EmissionCheckScope guard(GetVIXLAssembler(), 2 * kMaxMacroInstructionSizeInBytes);
6935       Location value = locations->InAt(2);
6936       DCHECK(value.IsFpuRegisterPair());
6937       if (index.IsConstant()) {
6938         size_t offset = (Int32ConstantFrom(index) << TIMES_8) + data_offset;
6939         GetAssembler()->StoreDToOffset(DRegisterFrom(value), array, offset);
6940       } else {
6941         UseScratchRegisterScope temps(GetVIXLAssembler());
6942         vixl32::Register temp = temps.Acquire();
6943         __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
6944         GetAssembler()->StoreDToOffset(DRegisterFrom(value), temp, data_offset);
6945       }
6946       codegen_->MaybeRecordImplicitNullCheck(instruction);
6947       break;
6948     }
6949 
6950     case DataType::Type::kUint32:
6951     case DataType::Type::kUint64:
6952     case DataType::Type::kVoid:
6953       LOG(FATAL) << "Unreachable type " << value_type;
6954       UNREACHABLE();
6955   }
6956 }
6957 
VisitArrayLength(HArrayLength * instruction)6958 void LocationsBuilderARMVIXL::VisitArrayLength(HArrayLength* instruction) {
6959   LocationSummary* locations =
6960       new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
6961   locations->SetInAt(0, Location::RequiresRegister());
6962   locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
6963 }
6964 
VisitArrayLength(HArrayLength * instruction)6965 void InstructionCodeGeneratorARMVIXL::VisitArrayLength(HArrayLength* instruction) {
6966   uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction);
6967   vixl32::Register obj = InputRegisterAt(instruction, 0);
6968   vixl32::Register out = OutputRegister(instruction);
6969   {
6970     ExactAssemblyScope aas(GetVIXLAssembler(),
6971                            vixl32::kMaxInstructionSizeInBytes,
6972                            CodeBufferCheckScope::kMaximumSize);
6973     __ ldr(out, MemOperand(obj, offset));
6974     codegen_->MaybeRecordImplicitNullCheck(instruction);
6975   }
6976   // Mask out compression flag from String's array length.
6977   if (mirror::kUseStringCompression && instruction->IsStringLength()) {
6978     __ Lsr(out, out, 1u);
6979   }
6980 }
6981 
VisitIntermediateAddress(HIntermediateAddress * instruction)6982 void LocationsBuilderARMVIXL::VisitIntermediateAddress(HIntermediateAddress* instruction) {
6983   LocationSummary* locations =
6984       new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
6985 
6986   locations->SetInAt(0, Location::RequiresRegister());
6987   locations->SetInAt(1, Location::RegisterOrConstant(instruction->GetOffset()));
6988   locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
6989 }
6990 
VisitIntermediateAddress(HIntermediateAddress * instruction)6991 void InstructionCodeGeneratorARMVIXL::VisitIntermediateAddress(HIntermediateAddress* instruction) {
6992   vixl32::Register out = OutputRegister(instruction);
6993   vixl32::Register first = InputRegisterAt(instruction, 0);
6994   Location second = instruction->GetLocations()->InAt(1);
6995 
6996   if (second.IsRegister()) {
6997     __ Add(out, first, RegisterFrom(second));
6998   } else {
6999     __ Add(out, first, Int32ConstantFrom(second));
7000   }
7001 }
7002 
VisitIntermediateAddressIndex(HIntermediateAddressIndex * instruction)7003 void LocationsBuilderARMVIXL::VisitIntermediateAddressIndex(
7004     HIntermediateAddressIndex* instruction) {
7005   LOG(FATAL) << "Unreachable " << instruction->GetId();
7006 }
7007 
VisitIntermediateAddressIndex(HIntermediateAddressIndex * instruction)7008 void InstructionCodeGeneratorARMVIXL::VisitIntermediateAddressIndex(
7009     HIntermediateAddressIndex* instruction) {
7010   LOG(FATAL) << "Unreachable " << instruction->GetId();
7011 }
7012 
VisitBoundsCheck(HBoundsCheck * instruction)7013 void LocationsBuilderARMVIXL::VisitBoundsCheck(HBoundsCheck* instruction) {
7014   RegisterSet caller_saves = RegisterSet::Empty();
7015   InvokeRuntimeCallingConventionARMVIXL calling_convention;
7016   caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0)));
7017   caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(1)));
7018   LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction, caller_saves);
7019 
7020   HInstruction* index = instruction->InputAt(0);
7021   HInstruction* length = instruction->InputAt(1);
7022   // If both index and length are constants we can statically check the bounds. But if at least one
7023   // of them is not encodable ArmEncodableConstantOrRegister will create
7024   // Location::RequiresRegister() which is not desired to happen. Instead we create constant
7025   // locations.
7026   bool both_const = index->IsConstant() && length->IsConstant();
7027   locations->SetInAt(0, both_const
7028       ? Location::ConstantLocation(index->AsConstant())
7029       : ArmEncodableConstantOrRegister(index, CMP));
7030   locations->SetInAt(1, both_const
7031       ? Location::ConstantLocation(length->AsConstant())
7032       : ArmEncodableConstantOrRegister(length, CMP));
7033 }
7034 
VisitBoundsCheck(HBoundsCheck * instruction)7035 void InstructionCodeGeneratorARMVIXL::VisitBoundsCheck(HBoundsCheck* instruction) {
7036   LocationSummary* locations = instruction->GetLocations();
7037   Location index_loc = locations->InAt(0);
7038   Location length_loc = locations->InAt(1);
7039 
7040   if (length_loc.IsConstant()) {
7041     int32_t length = Int32ConstantFrom(length_loc);
7042     if (index_loc.IsConstant()) {
7043       // BCE will remove the bounds check if we are guaranteed to pass.
7044       int32_t index = Int32ConstantFrom(index_loc);
7045       if (index < 0 || index >= length) {
7046         SlowPathCodeARMVIXL* slow_path =
7047             new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathARMVIXL(instruction);
7048         codegen_->AddSlowPath(slow_path);
7049         __ B(slow_path->GetEntryLabel());
7050       } else {
7051         // Some optimization after BCE may have generated this, and we should not
7052         // generate a bounds check if it is a valid range.
7053       }
7054       return;
7055     }
7056 
7057     SlowPathCodeARMVIXL* slow_path =
7058         new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathARMVIXL(instruction);
7059     __ Cmp(RegisterFrom(index_loc), length);
7060     codegen_->AddSlowPath(slow_path);
7061     __ B(hs, slow_path->GetEntryLabel());
7062   } else {
7063     SlowPathCodeARMVIXL* slow_path =
7064         new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathARMVIXL(instruction);
7065     __ Cmp(RegisterFrom(length_loc), InputOperandAt(instruction, 0));
7066     codegen_->AddSlowPath(slow_path);
7067     __ B(ls, slow_path->GetEntryLabel());
7068   }
7069 }
7070 
MarkGCCard(vixl32::Register temp,vixl32::Register card,vixl32::Register object,vixl32::Register value,bool value_can_be_null)7071 void CodeGeneratorARMVIXL::MarkGCCard(vixl32::Register temp,
7072                                       vixl32::Register card,
7073                                       vixl32::Register object,
7074                                       vixl32::Register value,
7075                                       bool value_can_be_null) {
7076   vixl32::Label is_null;
7077   if (value_can_be_null) {
7078     __ CompareAndBranchIfZero(value, &is_null, /* is_far_target=*/ false);
7079   }
7080   // Load the address of the card table into `card`.
7081   GetAssembler()->LoadFromOffset(
7082       kLoadWord, card, tr, Thread::CardTableOffset<kArmPointerSize>().Int32Value());
7083   // Calculate the offset (in the card table) of the card corresponding to
7084   // `object`.
7085   __ Lsr(temp, object, Operand::From(gc::accounting::CardTable::kCardShift));
7086   // Write the `art::gc::accounting::CardTable::kCardDirty` value into the
7087   // `object`'s card.
7088   //
7089   // Register `card` contains the address of the card table. Note that the card
7090   // table's base is biased during its creation so that it always starts at an
7091   // address whose least-significant byte is equal to `kCardDirty` (see
7092   // art::gc::accounting::CardTable::Create). Therefore the STRB instruction
7093   // below writes the `kCardDirty` (byte) value into the `object`'s card
7094   // (located at `card + object >> kCardShift`).
7095   //
7096   // This dual use of the value in register `card` (1. to calculate the location
7097   // of the card to mark; and 2. to load the `kCardDirty` value) saves a load
7098   // (no need to explicitly load `kCardDirty` as an immediate value).
7099   __ Strb(card, MemOperand(card, temp));
7100   if (value_can_be_null) {
7101     __ Bind(&is_null);
7102   }
7103 }
7104 
VisitParallelMove(HParallelMove * instruction ATTRIBUTE_UNUSED)7105 void LocationsBuilderARMVIXL::VisitParallelMove(HParallelMove* instruction ATTRIBUTE_UNUSED) {
7106   LOG(FATAL) << "Unreachable";
7107 }
7108 
VisitParallelMove(HParallelMove * instruction)7109 void InstructionCodeGeneratorARMVIXL::VisitParallelMove(HParallelMove* instruction) {
7110   if (instruction->GetNext()->IsSuspendCheck() &&
7111       instruction->GetBlock()->GetLoopInformation() != nullptr) {
7112     HSuspendCheck* suspend_check = instruction->GetNext()->AsSuspendCheck();
7113     // The back edge will generate the suspend check.
7114     codegen_->ClearSpillSlotsFromLoopPhisInStackMap(suspend_check, instruction);
7115   }
7116 
7117   codegen_->GetMoveResolver()->EmitNativeCode(instruction);
7118 }
7119 
VisitSuspendCheck(HSuspendCheck * instruction)7120 void LocationsBuilderARMVIXL::VisitSuspendCheck(HSuspendCheck* instruction) {
7121   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
7122       instruction, LocationSummary::kCallOnSlowPath);
7123   locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
7124 }
7125 
VisitSuspendCheck(HSuspendCheck * instruction)7126 void InstructionCodeGeneratorARMVIXL::VisitSuspendCheck(HSuspendCheck* instruction) {
7127   HBasicBlock* block = instruction->GetBlock();
7128   if (block->GetLoopInformation() != nullptr) {
7129     DCHECK(block->GetLoopInformation()->GetSuspendCheck() == instruction);
7130     // The back edge will generate the suspend check.
7131     return;
7132   }
7133   if (block->IsEntryBlock() && instruction->GetNext()->IsGoto()) {
7134     // The goto will generate the suspend check.
7135     return;
7136   }
7137   GenerateSuspendCheck(instruction, nullptr);
7138   codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 14);
7139 }
7140 
GenerateSuspendCheck(HSuspendCheck * instruction,HBasicBlock * successor)7141 void InstructionCodeGeneratorARMVIXL::GenerateSuspendCheck(HSuspendCheck* instruction,
7142                                                            HBasicBlock* successor) {
7143   SuspendCheckSlowPathARMVIXL* slow_path =
7144       down_cast<SuspendCheckSlowPathARMVIXL*>(instruction->GetSlowPath());
7145   if (slow_path == nullptr) {
7146     slow_path =
7147         new (codegen_->GetScopedAllocator()) SuspendCheckSlowPathARMVIXL(instruction, successor);
7148     instruction->SetSlowPath(slow_path);
7149     codegen_->AddSlowPath(slow_path);
7150     if (successor != nullptr) {
7151       DCHECK(successor->IsLoopHeader());
7152     }
7153   } else {
7154     DCHECK_EQ(slow_path->GetSuccessor(), successor);
7155   }
7156 
7157   UseScratchRegisterScope temps(GetVIXLAssembler());
7158   vixl32::Register temp = temps.Acquire();
7159   GetAssembler()->LoadFromOffset(
7160       kLoadWord, temp, tr, Thread::ThreadFlagsOffset<kArmPointerSize>().Int32Value());
7161   __ Tst(temp, Thread::SuspendOrCheckpointRequestFlags());
7162   if (successor == nullptr) {
7163     __ B(ne, slow_path->GetEntryLabel());
7164     __ Bind(slow_path->GetReturnLabel());
7165   } else {
7166     __ B(eq, codegen_->GetLabelOf(successor));
7167     __ B(slow_path->GetEntryLabel());
7168   }
7169 }
7170 
GetAssembler() const7171 ArmVIXLAssembler* ParallelMoveResolverARMVIXL::GetAssembler() const {
7172   return codegen_->GetAssembler();
7173 }
7174 
EmitMove(size_t index)7175 void ParallelMoveResolverARMVIXL::EmitMove(size_t index) {
7176   UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
7177   MoveOperands* move = moves_[index];
7178   Location source = move->GetSource();
7179   Location destination = move->GetDestination();
7180 
7181   if (source.IsRegister()) {
7182     if (destination.IsRegister()) {
7183       __ Mov(RegisterFrom(destination), RegisterFrom(source));
7184     } else if (destination.IsFpuRegister()) {
7185       __ Vmov(SRegisterFrom(destination), RegisterFrom(source));
7186     } else {
7187       DCHECK(destination.IsStackSlot());
7188       GetAssembler()->StoreToOffset(kStoreWord,
7189                                     RegisterFrom(source),
7190                                     sp,
7191                                     destination.GetStackIndex());
7192     }
7193   } else if (source.IsStackSlot()) {
7194     if (destination.IsRegister()) {
7195       GetAssembler()->LoadFromOffset(kLoadWord,
7196                                      RegisterFrom(destination),
7197                                      sp,
7198                                      source.GetStackIndex());
7199     } else if (destination.IsFpuRegister()) {
7200       GetAssembler()->LoadSFromOffset(SRegisterFrom(destination), sp, source.GetStackIndex());
7201     } else {
7202       DCHECK(destination.IsStackSlot());
7203       vixl32::Register temp = temps.Acquire();
7204       GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, source.GetStackIndex());
7205       GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
7206     }
7207   } else if (source.IsFpuRegister()) {
7208     if (destination.IsRegister()) {
7209       __ Vmov(RegisterFrom(destination), SRegisterFrom(source));
7210     } else if (destination.IsFpuRegister()) {
7211       __ Vmov(SRegisterFrom(destination), SRegisterFrom(source));
7212     } else {
7213       DCHECK(destination.IsStackSlot());
7214       GetAssembler()->StoreSToOffset(SRegisterFrom(source), sp, destination.GetStackIndex());
7215     }
7216   } else if (source.IsDoubleStackSlot()) {
7217     if (destination.IsDoubleStackSlot()) {
7218       vixl32::DRegister temp = temps.AcquireD();
7219       GetAssembler()->LoadDFromOffset(temp, sp, source.GetStackIndex());
7220       GetAssembler()->StoreDToOffset(temp, sp, destination.GetStackIndex());
7221     } else if (destination.IsRegisterPair()) {
7222       DCHECK(ExpectedPairLayout(destination));
7223       GetAssembler()->LoadFromOffset(
7224           kLoadWordPair, LowRegisterFrom(destination), sp, source.GetStackIndex());
7225     } else {
7226       DCHECK(destination.IsFpuRegisterPair()) << destination;
7227       GetAssembler()->LoadDFromOffset(DRegisterFrom(destination), sp, source.GetStackIndex());
7228     }
7229   } else if (source.IsRegisterPair()) {
7230     if (destination.IsRegisterPair()) {
7231       __ Mov(LowRegisterFrom(destination), LowRegisterFrom(source));
7232       __ Mov(HighRegisterFrom(destination), HighRegisterFrom(source));
7233     } else if (destination.IsFpuRegisterPair()) {
7234       __ Vmov(DRegisterFrom(destination), LowRegisterFrom(source), HighRegisterFrom(source));
7235     } else {
7236       DCHECK(destination.IsDoubleStackSlot()) << destination;
7237       DCHECK(ExpectedPairLayout(source));
7238       GetAssembler()->StoreToOffset(kStoreWordPair,
7239                                     LowRegisterFrom(source),
7240                                     sp,
7241                                     destination.GetStackIndex());
7242     }
7243   } else if (source.IsFpuRegisterPair()) {
7244     if (destination.IsRegisterPair()) {
7245       __ Vmov(LowRegisterFrom(destination), HighRegisterFrom(destination), DRegisterFrom(source));
7246     } else if (destination.IsFpuRegisterPair()) {
7247       __ Vmov(DRegisterFrom(destination), DRegisterFrom(source));
7248     } else {
7249       DCHECK(destination.IsDoubleStackSlot()) << destination;
7250       GetAssembler()->StoreDToOffset(DRegisterFrom(source), sp, destination.GetStackIndex());
7251     }
7252   } else {
7253     DCHECK(source.IsConstant()) << source;
7254     HConstant* constant = source.GetConstant();
7255     if (constant->IsIntConstant() || constant->IsNullConstant()) {
7256       int32_t value = CodeGenerator::GetInt32ValueOf(constant);
7257       if (destination.IsRegister()) {
7258         __ Mov(RegisterFrom(destination), value);
7259       } else {
7260         DCHECK(destination.IsStackSlot());
7261         vixl32::Register temp = temps.Acquire();
7262         __ Mov(temp, value);
7263         GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
7264       }
7265     } else if (constant->IsLongConstant()) {
7266       int64_t value = Int64ConstantFrom(source);
7267       if (destination.IsRegisterPair()) {
7268         __ Mov(LowRegisterFrom(destination), Low32Bits(value));
7269         __ Mov(HighRegisterFrom(destination), High32Bits(value));
7270       } else {
7271         DCHECK(destination.IsDoubleStackSlot()) << destination;
7272         vixl32::Register temp = temps.Acquire();
7273         __ Mov(temp, Low32Bits(value));
7274         GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
7275         __ Mov(temp, High32Bits(value));
7276         GetAssembler()->StoreToOffset(kStoreWord,
7277                                       temp,
7278                                       sp,
7279                                       destination.GetHighStackIndex(kArmWordSize));
7280       }
7281     } else if (constant->IsDoubleConstant()) {
7282       double value = constant->AsDoubleConstant()->GetValue();
7283       if (destination.IsFpuRegisterPair()) {
7284         __ Vmov(DRegisterFrom(destination), value);
7285       } else {
7286         DCHECK(destination.IsDoubleStackSlot()) << destination;
7287         uint64_t int_value = bit_cast<uint64_t, double>(value);
7288         vixl32::Register temp = temps.Acquire();
7289         __ Mov(temp, Low32Bits(int_value));
7290         GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
7291         __ Mov(temp, High32Bits(int_value));
7292         GetAssembler()->StoreToOffset(kStoreWord,
7293                                       temp,
7294                                       sp,
7295                                       destination.GetHighStackIndex(kArmWordSize));
7296       }
7297     } else {
7298       DCHECK(constant->IsFloatConstant()) << constant->DebugName();
7299       float value = constant->AsFloatConstant()->GetValue();
7300       if (destination.IsFpuRegister()) {
7301         __ Vmov(SRegisterFrom(destination), value);
7302       } else {
7303         DCHECK(destination.IsStackSlot());
7304         vixl32::Register temp = temps.Acquire();
7305         __ Mov(temp, bit_cast<int32_t, float>(value));
7306         GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
7307       }
7308     }
7309   }
7310 }
7311 
Exchange(vixl32::Register reg,int mem)7312 void ParallelMoveResolverARMVIXL::Exchange(vixl32::Register reg, int mem) {
7313   UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
7314   vixl32::Register temp = temps.Acquire();
7315   __ Mov(temp, reg);
7316   GetAssembler()->LoadFromOffset(kLoadWord, reg, sp, mem);
7317   GetAssembler()->StoreToOffset(kStoreWord, temp, sp, mem);
7318 }
7319 
Exchange(int mem1,int mem2)7320 void ParallelMoveResolverARMVIXL::Exchange(int mem1, int mem2) {
7321   // TODO(VIXL32): Double check the performance of this implementation.
7322   UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
7323   vixl32::Register temp1 = temps.Acquire();
7324   ScratchRegisterScope ensure_scratch(
7325       this, temp1.GetCode(), r0.GetCode(), codegen_->GetNumberOfCoreRegisters());
7326   vixl32::Register temp2(ensure_scratch.GetRegister());
7327 
7328   int stack_offset = ensure_scratch.IsSpilled() ? kArmWordSize : 0;
7329   GetAssembler()->LoadFromOffset(kLoadWord, temp1, sp, mem1 + stack_offset);
7330   GetAssembler()->LoadFromOffset(kLoadWord, temp2, sp, mem2 + stack_offset);
7331   GetAssembler()->StoreToOffset(kStoreWord, temp1, sp, mem2 + stack_offset);
7332   GetAssembler()->StoreToOffset(kStoreWord, temp2, sp, mem1 + stack_offset);
7333 }
7334 
EmitSwap(size_t index)7335 void ParallelMoveResolverARMVIXL::EmitSwap(size_t index) {
7336   MoveOperands* move = moves_[index];
7337   Location source = move->GetSource();
7338   Location destination = move->GetDestination();
7339   UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
7340 
7341   if (source.IsRegister() && destination.IsRegister()) {
7342     vixl32::Register temp = temps.Acquire();
7343     DCHECK(!RegisterFrom(source).Is(temp));
7344     DCHECK(!RegisterFrom(destination).Is(temp));
7345     __ Mov(temp, RegisterFrom(destination));
7346     __ Mov(RegisterFrom(destination), RegisterFrom(source));
7347     __ Mov(RegisterFrom(source), temp);
7348   } else if (source.IsRegister() && destination.IsStackSlot()) {
7349     Exchange(RegisterFrom(source), destination.GetStackIndex());
7350   } else if (source.IsStackSlot() && destination.IsRegister()) {
7351     Exchange(RegisterFrom(destination), source.GetStackIndex());
7352   } else if (source.IsStackSlot() && destination.IsStackSlot()) {
7353     Exchange(source.GetStackIndex(), destination.GetStackIndex());
7354   } else if (source.IsFpuRegister() && destination.IsFpuRegister()) {
7355     vixl32::Register temp = temps.Acquire();
7356     __ Vmov(temp, SRegisterFrom(source));
7357     __ Vmov(SRegisterFrom(source), SRegisterFrom(destination));
7358     __ Vmov(SRegisterFrom(destination), temp);
7359   } else if (source.IsRegisterPair() && destination.IsRegisterPair()) {
7360     vixl32::DRegister temp = temps.AcquireD();
7361     __ Vmov(temp, LowRegisterFrom(source), HighRegisterFrom(source));
7362     __ Mov(LowRegisterFrom(source), LowRegisterFrom(destination));
7363     __ Mov(HighRegisterFrom(source), HighRegisterFrom(destination));
7364     __ Vmov(LowRegisterFrom(destination), HighRegisterFrom(destination), temp);
7365   } else if (source.IsRegisterPair() || destination.IsRegisterPair()) {
7366     vixl32::Register low_reg = LowRegisterFrom(source.IsRegisterPair() ? source : destination);
7367     int mem = source.IsRegisterPair() ? destination.GetStackIndex() : source.GetStackIndex();
7368     DCHECK(ExpectedPairLayout(source.IsRegisterPair() ? source : destination));
7369     vixl32::DRegister temp = temps.AcquireD();
7370     __ Vmov(temp, low_reg, vixl32::Register(low_reg.GetCode() + 1));
7371     GetAssembler()->LoadFromOffset(kLoadWordPair, low_reg, sp, mem);
7372     GetAssembler()->StoreDToOffset(temp, sp, mem);
7373   } else if (source.IsFpuRegisterPair() && destination.IsFpuRegisterPair()) {
7374     vixl32::DRegister first = DRegisterFrom(source);
7375     vixl32::DRegister second = DRegisterFrom(destination);
7376     vixl32::DRegister temp = temps.AcquireD();
7377     __ Vmov(temp, first);
7378     __ Vmov(first, second);
7379     __ Vmov(second, temp);
7380   } else if (source.IsFpuRegisterPair() || destination.IsFpuRegisterPair()) {
7381     vixl32::DRegister reg = source.IsFpuRegisterPair()
7382         ? DRegisterFrom(source)
7383         : DRegisterFrom(destination);
7384     int mem = source.IsFpuRegisterPair()
7385         ? destination.GetStackIndex()
7386         : source.GetStackIndex();
7387     vixl32::DRegister temp = temps.AcquireD();
7388     __ Vmov(temp, reg);
7389     GetAssembler()->LoadDFromOffset(reg, sp, mem);
7390     GetAssembler()->StoreDToOffset(temp, sp, mem);
7391   } else if (source.IsFpuRegister() || destination.IsFpuRegister()) {
7392     vixl32::SRegister reg = source.IsFpuRegister()
7393         ? SRegisterFrom(source)
7394         : SRegisterFrom(destination);
7395     int mem = source.IsFpuRegister()
7396         ? destination.GetStackIndex()
7397         : source.GetStackIndex();
7398     vixl32::Register temp = temps.Acquire();
7399     __ Vmov(temp, reg);
7400     GetAssembler()->LoadSFromOffset(reg, sp, mem);
7401     GetAssembler()->StoreToOffset(kStoreWord, temp, sp, mem);
7402   } else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) {
7403     vixl32::DRegister temp1 = temps.AcquireD();
7404     vixl32::DRegister temp2 = temps.AcquireD();
7405     __ Vldr(temp1, MemOperand(sp, source.GetStackIndex()));
7406     __ Vldr(temp2, MemOperand(sp, destination.GetStackIndex()));
7407     __ Vstr(temp1, MemOperand(sp, destination.GetStackIndex()));
7408     __ Vstr(temp2, MemOperand(sp, source.GetStackIndex()));
7409   } else {
7410     LOG(FATAL) << "Unimplemented" << source << " <-> " << destination;
7411   }
7412 }
7413 
SpillScratch(int reg)7414 void ParallelMoveResolverARMVIXL::SpillScratch(int reg) {
7415   __ Push(vixl32::Register(reg));
7416 }
7417 
RestoreScratch(int reg)7418 void ParallelMoveResolverARMVIXL::RestoreScratch(int reg) {
7419   __ Pop(vixl32::Register(reg));
7420 }
7421 
GetSupportedLoadClassKind(HLoadClass::LoadKind desired_class_load_kind)7422 HLoadClass::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadClassKind(
7423     HLoadClass::LoadKind desired_class_load_kind) {
7424   switch (desired_class_load_kind) {
7425     case HLoadClass::LoadKind::kInvalid:
7426       LOG(FATAL) << "UNREACHABLE";
7427       UNREACHABLE();
7428     case HLoadClass::LoadKind::kReferrersClass:
7429       break;
7430     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
7431     case HLoadClass::LoadKind::kBootImageRelRo:
7432     case HLoadClass::LoadKind::kBssEntry:
7433     case HLoadClass::LoadKind::kBssEntryPublic:
7434     case HLoadClass::LoadKind::kBssEntryPackage:
7435       DCHECK(!GetCompilerOptions().IsJitCompiler());
7436       break;
7437     case HLoadClass::LoadKind::kJitBootImageAddress:
7438     case HLoadClass::LoadKind::kJitTableAddress:
7439       DCHECK(GetCompilerOptions().IsJitCompiler());
7440       break;
7441     case HLoadClass::LoadKind::kRuntimeCall:
7442       break;
7443   }
7444   return desired_class_load_kind;
7445 }
7446 
VisitLoadClass(HLoadClass * cls)7447 void LocationsBuilderARMVIXL::VisitLoadClass(HLoadClass* cls) {
7448   HLoadClass::LoadKind load_kind = cls->GetLoadKind();
7449   if (load_kind == HLoadClass::LoadKind::kRuntimeCall) {
7450     InvokeRuntimeCallingConventionARMVIXL calling_convention;
7451     CodeGenerator::CreateLoadClassRuntimeCallLocationSummary(
7452         cls,
7453         LocationFrom(calling_convention.GetRegisterAt(0)),
7454         LocationFrom(r0));
7455     DCHECK(calling_convention.GetRegisterAt(0).Is(r0));
7456     return;
7457   }
7458   DCHECK_EQ(cls->NeedsAccessCheck(),
7459             load_kind == HLoadClass::LoadKind::kBssEntryPublic ||
7460                 load_kind == HLoadClass::LoadKind::kBssEntryPackage);
7461 
7462   const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
7463   LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier)
7464       ? LocationSummary::kCallOnSlowPath
7465       : LocationSummary::kNoCall;
7466   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(cls, call_kind);
7467   if (kUseBakerReadBarrier && requires_read_barrier && !cls->NeedsEnvironment()) {
7468     locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
7469   }
7470 
7471   if (load_kind == HLoadClass::LoadKind::kReferrersClass) {
7472     locations->SetInAt(0, Location::RequiresRegister());
7473   }
7474   locations->SetOut(Location::RequiresRegister());
7475   if (load_kind == HLoadClass::LoadKind::kBssEntry) {
7476     if (!kUseReadBarrier || kUseBakerReadBarrier) {
7477       // Rely on the type resolution or initialization and marking to save everything we need.
7478       locations->SetCustomSlowPathCallerSaves(OneRegInReferenceOutSaveEverythingCallerSaves());
7479     } else {
7480       // For non-Baker read barrier we have a temp-clobbering call.
7481     }
7482   }
7483 }
7484 
7485 // NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
7486 // move.
VisitLoadClass(HLoadClass * cls)7487 void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFETY_ANALYSIS {
7488   HLoadClass::LoadKind load_kind = cls->GetLoadKind();
7489   if (load_kind == HLoadClass::LoadKind::kRuntimeCall) {
7490     codegen_->GenerateLoadClassRuntimeCall(cls);
7491     codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 15);
7492     return;
7493   }
7494   DCHECK_EQ(cls->NeedsAccessCheck(),
7495             load_kind == HLoadClass::LoadKind::kBssEntryPublic ||
7496                 load_kind == HLoadClass::LoadKind::kBssEntryPackage);
7497 
7498   LocationSummary* locations = cls->GetLocations();
7499   Location out_loc = locations->Out();
7500   vixl32::Register out = OutputRegister(cls);
7501 
7502   const ReadBarrierOption read_barrier_option = cls->IsInBootImage()
7503       ? kWithoutReadBarrier
7504       : kCompilerReadBarrierOption;
7505   bool generate_null_check = false;
7506   switch (load_kind) {
7507     case HLoadClass::LoadKind::kReferrersClass: {
7508       DCHECK(!cls->CanCallRuntime());
7509       DCHECK(!cls->MustGenerateClinitCheck());
7510       // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
7511       vixl32::Register current_method = InputRegisterAt(cls, 0);
7512       codegen_->GenerateGcRootFieldLoad(cls,
7513                                         out_loc,
7514                                         current_method,
7515                                         ArtMethod::DeclaringClassOffset().Int32Value(),
7516                                         read_barrier_option);
7517       break;
7518     }
7519     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: {
7520       DCHECK(codegen_->GetCompilerOptions().IsBootImage() ||
7521              codegen_->GetCompilerOptions().IsBootImageExtension());
7522       DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
7523       CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
7524           codegen_->NewBootImageTypePatch(cls->GetDexFile(), cls->GetTypeIndex());
7525       codegen_->EmitMovwMovtPlaceholder(labels, out);
7526       break;
7527     }
7528     case HLoadClass::LoadKind::kBootImageRelRo: {
7529       DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
7530       uint32_t boot_image_offset = CodeGenerator::GetBootImageOffset(cls);
7531       codegen_->LoadBootImageRelRoEntry(out, boot_image_offset);
7532       break;
7533     }
7534     case HLoadClass::LoadKind::kBssEntry:
7535     case HLoadClass::LoadKind::kBssEntryPublic:
7536     case HLoadClass::LoadKind::kBssEntryPackage: {
7537       CodeGeneratorARMVIXL::PcRelativePatchInfo* labels = codegen_->NewTypeBssEntryPatch(cls);
7538       codegen_->EmitMovwMovtPlaceholder(labels, out);
7539       // All aligned loads are implicitly atomic consume operations on ARM.
7540       codegen_->GenerateGcRootFieldLoad(cls, out_loc, out, /*offset=*/ 0, read_barrier_option);
7541       generate_null_check = true;
7542       break;
7543     }
7544     case HLoadClass::LoadKind::kJitBootImageAddress: {
7545       DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
7546       uint32_t address = reinterpret_cast32<uint32_t>(cls->GetClass().Get());
7547       DCHECK_NE(address, 0u);
7548       __ Ldr(out, codegen_->DeduplicateBootImageAddressLiteral(address));
7549       break;
7550     }
7551     case HLoadClass::LoadKind::kJitTableAddress: {
7552       __ Ldr(out, codegen_->DeduplicateJitClassLiteral(cls->GetDexFile(),
7553                                                        cls->GetTypeIndex(),
7554                                                        cls->GetClass()));
7555       // /* GcRoot<mirror::Class> */ out = *out
7556       codegen_->GenerateGcRootFieldLoad(cls, out_loc, out, /*offset=*/ 0, read_barrier_option);
7557       break;
7558     }
7559     case HLoadClass::LoadKind::kRuntimeCall:
7560     case HLoadClass::LoadKind::kInvalid:
7561       LOG(FATAL) << "UNREACHABLE";
7562       UNREACHABLE();
7563   }
7564 
7565   if (generate_null_check || cls->MustGenerateClinitCheck()) {
7566     DCHECK(cls->CanCallRuntime());
7567     LoadClassSlowPathARMVIXL* slow_path =
7568         new (codegen_->GetScopedAllocator()) LoadClassSlowPathARMVIXL(cls, cls);
7569     codegen_->AddSlowPath(slow_path);
7570     if (generate_null_check) {
7571       __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
7572     }
7573     if (cls->MustGenerateClinitCheck()) {
7574       GenerateClassInitializationCheck(slow_path, out);
7575     } else {
7576       __ Bind(slow_path->GetExitLabel());
7577     }
7578     codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 16);
7579   }
7580 }
7581 
VisitLoadMethodHandle(HLoadMethodHandle * load)7582 void LocationsBuilderARMVIXL::VisitLoadMethodHandle(HLoadMethodHandle* load) {
7583   InvokeRuntimeCallingConventionARMVIXL calling_convention;
7584   Location location = LocationFrom(calling_convention.GetRegisterAt(0));
7585   CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(load, location, location);
7586 }
7587 
VisitLoadMethodHandle(HLoadMethodHandle * load)7588 void InstructionCodeGeneratorARMVIXL::VisitLoadMethodHandle(HLoadMethodHandle* load) {
7589   codegen_->GenerateLoadMethodHandleRuntimeCall(load);
7590 }
7591 
VisitLoadMethodType(HLoadMethodType * load)7592 void LocationsBuilderARMVIXL::VisitLoadMethodType(HLoadMethodType* load) {
7593   InvokeRuntimeCallingConventionARMVIXL calling_convention;
7594   Location location = LocationFrom(calling_convention.GetRegisterAt(0));
7595   CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, location, location);
7596 }
7597 
VisitLoadMethodType(HLoadMethodType * load)7598 void InstructionCodeGeneratorARMVIXL::VisitLoadMethodType(HLoadMethodType* load) {
7599   codegen_->GenerateLoadMethodTypeRuntimeCall(load);
7600 }
7601 
VisitClinitCheck(HClinitCheck * check)7602 void LocationsBuilderARMVIXL::VisitClinitCheck(HClinitCheck* check) {
7603   LocationSummary* locations =
7604       new (GetGraph()->GetAllocator()) LocationSummary(check, LocationSummary::kCallOnSlowPath);
7605   locations->SetInAt(0, Location::RequiresRegister());
7606   if (check->HasUses()) {
7607     locations->SetOut(Location::SameAsFirstInput());
7608   }
7609   // Rely on the type initialization to save everything we need.
7610   locations->SetCustomSlowPathCallerSaves(OneRegInReferenceOutSaveEverythingCallerSaves());
7611 }
7612 
VisitClinitCheck(HClinitCheck * check)7613 void InstructionCodeGeneratorARMVIXL::VisitClinitCheck(HClinitCheck* check) {
7614   // We assume the class is not null.
7615   LoadClassSlowPathARMVIXL* slow_path =
7616       new (codegen_->GetScopedAllocator()) LoadClassSlowPathARMVIXL(check->GetLoadClass(), check);
7617   codegen_->AddSlowPath(slow_path);
7618   GenerateClassInitializationCheck(slow_path, InputRegisterAt(check, 0));
7619 }
7620 
GenerateClassInitializationCheck(LoadClassSlowPathARMVIXL * slow_path,vixl32::Register class_reg)7621 void InstructionCodeGeneratorARMVIXL::GenerateClassInitializationCheck(
7622     LoadClassSlowPathARMVIXL* slow_path, vixl32::Register class_reg) {
7623   UseScratchRegisterScope temps(GetVIXLAssembler());
7624   vixl32::Register temp = temps.Acquire();
7625   constexpr size_t status_lsb_position = SubtypeCheckBits::BitStructSizeOf();
7626   constexpr uint32_t shifted_visibly_initialized_value =
7627       enum_cast<uint32_t>(ClassStatus::kVisiblyInitialized) << status_lsb_position;
7628 
7629   const size_t status_offset = mirror::Class::StatusOffset().SizeValue();
7630   GetAssembler()->LoadFromOffset(kLoadWord, temp, class_reg, status_offset);
7631   __ Cmp(temp, shifted_visibly_initialized_value);
7632   __ B(lo, slow_path->GetEntryLabel());
7633   __ Bind(slow_path->GetExitLabel());
7634 }
7635 
GenerateBitstringTypeCheckCompare(HTypeCheckInstruction * check,vixl32::Register temp,vixl32::FlagsUpdate flags_update)7636 void InstructionCodeGeneratorARMVIXL::GenerateBitstringTypeCheckCompare(
7637     HTypeCheckInstruction* check,
7638     vixl32::Register temp,
7639     vixl32::FlagsUpdate flags_update) {
7640   uint32_t path_to_root = check->GetBitstringPathToRoot();
7641   uint32_t mask = check->GetBitstringMask();
7642   DCHECK(IsPowerOfTwo(mask + 1));
7643   size_t mask_bits = WhichPowerOf2(mask + 1);
7644 
7645   // Note that HInstanceOf shall check for zero value in `temp` but HCheckCast needs
7646   // the Z flag for BNE. This is indicated by the `flags_update` parameter.
7647   if (mask_bits == 16u) {
7648     // Load only the bitstring part of the status word.
7649     __ Ldrh(temp, MemOperand(temp, mirror::Class::StatusOffset().Int32Value()));
7650     // Check if the bitstring bits are equal to `path_to_root`.
7651     if (flags_update == SetFlags) {
7652       __ Cmp(temp, path_to_root);
7653     } else {
7654       __ Sub(temp, temp, path_to_root);
7655     }
7656   } else {
7657     // /* uint32_t */ temp = temp->status_
7658     __ Ldr(temp, MemOperand(temp, mirror::Class::StatusOffset().Int32Value()));
7659     if (GetAssembler()->ShifterOperandCanHold(SUB, path_to_root)) {
7660       // Compare the bitstring bits using SUB.
7661       __ Sub(temp, temp, path_to_root);
7662       // Shift out bits that do not contribute to the comparison.
7663       __ Lsl(flags_update, temp, temp, dchecked_integral_cast<uint32_t>(32u - mask_bits));
7664     } else if (IsUint<16>(path_to_root)) {
7665       if (temp.IsLow()) {
7666         // Note: Optimized for size but contains one more dependent instruction than necessary.
7667         //       MOVW+SUB(register) would be 8 bytes unless we find a low-reg temporary but the
7668         //       macro assembler would use the high reg IP for the constant by default.
7669         // Compare the bitstring bits using SUB.
7670         __ Sub(temp, temp, path_to_root & 0x00ffu);  // 16-bit SUB (immediate) T2
7671         __ Sub(temp, temp, path_to_root & 0xff00u);  // 32-bit SUB (immediate) T3
7672         // Shift out bits that do not contribute to the comparison.
7673         __ Lsl(flags_update, temp, temp, dchecked_integral_cast<uint32_t>(32u - mask_bits));
7674       } else {
7675         // Extract the bitstring bits.
7676         __ Ubfx(temp, temp, 0, mask_bits);
7677         // Check if the bitstring bits are equal to `path_to_root`.
7678         if (flags_update == SetFlags) {
7679           __ Cmp(temp, path_to_root);
7680         } else {
7681           __ Sub(temp, temp, path_to_root);
7682         }
7683       }
7684     } else {
7685       // Shift out bits that do not contribute to the comparison.
7686       __ Lsl(temp, temp, dchecked_integral_cast<uint32_t>(32u - mask_bits));
7687       // Check if the shifted bitstring bits are equal to `path_to_root << (32u - mask_bits)`.
7688       if (flags_update == SetFlags) {
7689         __ Cmp(temp, path_to_root << (32u - mask_bits));
7690       } else {
7691         __ Sub(temp, temp, path_to_root << (32u - mask_bits));
7692       }
7693     }
7694   }
7695 }
7696 
GetSupportedLoadStringKind(HLoadString::LoadKind desired_string_load_kind)7697 HLoadString::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadStringKind(
7698     HLoadString::LoadKind desired_string_load_kind) {
7699   switch (desired_string_load_kind) {
7700     case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
7701     case HLoadString::LoadKind::kBootImageRelRo:
7702     case HLoadString::LoadKind::kBssEntry:
7703       DCHECK(!GetCompilerOptions().IsJitCompiler());
7704       break;
7705     case HLoadString::LoadKind::kJitBootImageAddress:
7706     case HLoadString::LoadKind::kJitTableAddress:
7707       DCHECK(GetCompilerOptions().IsJitCompiler());
7708       break;
7709     case HLoadString::LoadKind::kRuntimeCall:
7710       break;
7711   }
7712   return desired_string_load_kind;
7713 }
7714 
VisitLoadString(HLoadString * load)7715 void LocationsBuilderARMVIXL::VisitLoadString(HLoadString* load) {
7716   LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load);
7717   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(load, call_kind);
7718   HLoadString::LoadKind load_kind = load->GetLoadKind();
7719   if (load_kind == HLoadString::LoadKind::kRuntimeCall) {
7720     locations->SetOut(LocationFrom(r0));
7721   } else {
7722     locations->SetOut(Location::RequiresRegister());
7723     if (load_kind == HLoadString::LoadKind::kBssEntry) {
7724       if (!kUseReadBarrier || kUseBakerReadBarrier) {
7725         // Rely on the pResolveString and marking to save everything we need, including temps.
7726         locations->SetCustomSlowPathCallerSaves(OneRegInReferenceOutSaveEverythingCallerSaves());
7727       } else {
7728         // For non-Baker read barrier we have a temp-clobbering call.
7729       }
7730     }
7731   }
7732 }
7733 
7734 // NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
7735 // move.
VisitLoadString(HLoadString * load)7736 void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) NO_THREAD_SAFETY_ANALYSIS {
7737   LocationSummary* locations = load->GetLocations();
7738   Location out_loc = locations->Out();
7739   vixl32::Register out = OutputRegister(load);
7740   HLoadString::LoadKind load_kind = load->GetLoadKind();
7741 
7742   switch (load_kind) {
7743     case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
7744       DCHECK(codegen_->GetCompilerOptions().IsBootImage() ||
7745              codegen_->GetCompilerOptions().IsBootImageExtension());
7746       CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
7747           codegen_->NewBootImageStringPatch(load->GetDexFile(), load->GetStringIndex());
7748       codegen_->EmitMovwMovtPlaceholder(labels, out);
7749       return;
7750     }
7751     case HLoadString::LoadKind::kBootImageRelRo: {
7752       DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
7753       uint32_t boot_image_offset = CodeGenerator::GetBootImageOffset(load);
7754       codegen_->LoadBootImageRelRoEntry(out, boot_image_offset);
7755       return;
7756     }
7757     case HLoadString::LoadKind::kBssEntry: {
7758       CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
7759           codegen_->NewStringBssEntryPatch(load->GetDexFile(), load->GetStringIndex());
7760       codegen_->EmitMovwMovtPlaceholder(labels, out);
7761       // All aligned loads are implicitly atomic consume operations on ARM.
7762       codegen_->GenerateGcRootFieldLoad(
7763           load, out_loc, out, /*offset=*/ 0, kCompilerReadBarrierOption);
7764       LoadStringSlowPathARMVIXL* slow_path =
7765           new (codegen_->GetScopedAllocator()) LoadStringSlowPathARMVIXL(load);
7766       codegen_->AddSlowPath(slow_path);
7767       __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
7768       __ Bind(slow_path->GetExitLabel());
7769       codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 17);
7770       return;
7771     }
7772     case HLoadString::LoadKind::kJitBootImageAddress: {
7773       uint32_t address = reinterpret_cast32<uint32_t>(load->GetString().Get());
7774       DCHECK_NE(address, 0u);
7775       __ Ldr(out, codegen_->DeduplicateBootImageAddressLiteral(address));
7776       return;
7777     }
7778     case HLoadString::LoadKind::kJitTableAddress: {
7779       __ Ldr(out, codegen_->DeduplicateJitStringLiteral(load->GetDexFile(),
7780                                                         load->GetStringIndex(),
7781                                                         load->GetString()));
7782       // /* GcRoot<mirror::String> */ out = *out
7783       codegen_->GenerateGcRootFieldLoad(
7784           load, out_loc, out, /*offset=*/ 0, kCompilerReadBarrierOption);
7785       return;
7786     }
7787     default:
7788       break;
7789   }
7790 
7791   // TODO: Re-add the compiler code to do string dex cache lookup again.
7792   DCHECK_EQ(load->GetLoadKind(), HLoadString::LoadKind::kRuntimeCall);
7793   InvokeRuntimeCallingConventionARMVIXL calling_convention;
7794   __ Mov(calling_convention.GetRegisterAt(0), load->GetStringIndex().index_);
7795   codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc());
7796   CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
7797   codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 18);
7798 }
7799 
GetExceptionTlsOffset()7800 static int32_t GetExceptionTlsOffset() {
7801   return Thread::ExceptionOffset<kArmPointerSize>().Int32Value();
7802 }
7803 
VisitLoadException(HLoadException * load)7804 void LocationsBuilderARMVIXL::VisitLoadException(HLoadException* load) {
7805   LocationSummary* locations =
7806       new (GetGraph()->GetAllocator()) LocationSummary(load, LocationSummary::kNoCall);
7807   locations->SetOut(Location::RequiresRegister());
7808 }
7809 
VisitLoadException(HLoadException * load)7810 void InstructionCodeGeneratorARMVIXL::VisitLoadException(HLoadException* load) {
7811   vixl32::Register out = OutputRegister(load);
7812   GetAssembler()->LoadFromOffset(kLoadWord, out, tr, GetExceptionTlsOffset());
7813 }
7814 
7815 
VisitClearException(HClearException * clear)7816 void LocationsBuilderARMVIXL::VisitClearException(HClearException* clear) {
7817   new (GetGraph()->GetAllocator()) LocationSummary(clear, LocationSummary::kNoCall);
7818 }
7819 
VisitClearException(HClearException * clear ATTRIBUTE_UNUSED)7820 void InstructionCodeGeneratorARMVIXL::VisitClearException(HClearException* clear ATTRIBUTE_UNUSED) {
7821   UseScratchRegisterScope temps(GetVIXLAssembler());
7822   vixl32::Register temp = temps.Acquire();
7823   __ Mov(temp, 0);
7824   GetAssembler()->StoreToOffset(kStoreWord, temp, tr, GetExceptionTlsOffset());
7825 }
7826 
VisitThrow(HThrow * instruction)7827 void LocationsBuilderARMVIXL::VisitThrow(HThrow* instruction) {
7828   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
7829       instruction, LocationSummary::kCallOnMainOnly);
7830   InvokeRuntimeCallingConventionARMVIXL calling_convention;
7831   locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
7832 }
7833 
VisitThrow(HThrow * instruction)7834 void InstructionCodeGeneratorARMVIXL::VisitThrow(HThrow* instruction) {
7835   codegen_->InvokeRuntime(kQuickDeliverException, instruction, instruction->GetDexPc());
7836   CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
7837 }
7838 
7839 // Temp is used for read barrier.
NumberOfInstanceOfTemps(TypeCheckKind type_check_kind)7840 static size_t NumberOfInstanceOfTemps(TypeCheckKind type_check_kind) {
7841   if (kEmitCompilerReadBarrier &&
7842        (kUseBakerReadBarrier ||
7843           type_check_kind == TypeCheckKind::kAbstractClassCheck ||
7844           type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
7845           type_check_kind == TypeCheckKind::kArrayObjectCheck)) {
7846     return 1;
7847   }
7848   return 0;
7849 }
7850 
7851 // Interface case has 3 temps, one for holding the number of interfaces, one for the current
7852 // interface pointer, one for loading the current interface.
7853 // The other checks have one temp for loading the object's class.
NumberOfCheckCastTemps(TypeCheckKind type_check_kind)7854 static size_t NumberOfCheckCastTemps(TypeCheckKind type_check_kind) {
7855   if (type_check_kind == TypeCheckKind::kInterfaceCheck) {
7856     return 3;
7857   }
7858   return 1 + NumberOfInstanceOfTemps(type_check_kind);
7859 }
7860 
VisitInstanceOf(HInstanceOf * instruction)7861 void LocationsBuilderARMVIXL::VisitInstanceOf(HInstanceOf* instruction) {
7862   LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
7863   TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
7864   bool baker_read_barrier_slow_path = false;
7865   switch (type_check_kind) {
7866     case TypeCheckKind::kExactCheck:
7867     case TypeCheckKind::kAbstractClassCheck:
7868     case TypeCheckKind::kClassHierarchyCheck:
7869     case TypeCheckKind::kArrayObjectCheck: {
7870       bool needs_read_barrier = CodeGenerator::InstanceOfNeedsReadBarrier(instruction);
7871       call_kind = needs_read_barrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall;
7872       baker_read_barrier_slow_path = kUseBakerReadBarrier && needs_read_barrier;
7873       break;
7874     }
7875     case TypeCheckKind::kArrayCheck:
7876     case TypeCheckKind::kUnresolvedCheck:
7877     case TypeCheckKind::kInterfaceCheck:
7878       call_kind = LocationSummary::kCallOnSlowPath;
7879       break;
7880     case TypeCheckKind::kBitstringCheck:
7881       break;
7882   }
7883 
7884   LocationSummary* locations =
7885       new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
7886   if (baker_read_barrier_slow_path) {
7887     locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
7888   }
7889   locations->SetInAt(0, Location::RequiresRegister());
7890   if (type_check_kind == TypeCheckKind::kBitstringCheck) {
7891     locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
7892     locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
7893     locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
7894   } else {
7895     locations->SetInAt(1, Location::RequiresRegister());
7896   }
7897   // The "out" register is used as a temporary, so it overlaps with the inputs.
7898   // Note that TypeCheckSlowPathARM uses this register too.
7899   locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
7900   locations->AddRegisterTemps(NumberOfInstanceOfTemps(type_check_kind));
7901 }
7902 
VisitInstanceOf(HInstanceOf * instruction)7903 void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) {
7904   TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
7905   LocationSummary* locations = instruction->GetLocations();
7906   Location obj_loc = locations->InAt(0);
7907   vixl32::Register obj = InputRegisterAt(instruction, 0);
7908   vixl32::Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck)
7909       ? vixl32::Register()
7910       : InputRegisterAt(instruction, 1);
7911   Location out_loc = locations->Out();
7912   vixl32::Register out = OutputRegister(instruction);
7913   const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind);
7914   DCHECK_LE(num_temps, 1u);
7915   Location maybe_temp_loc = (num_temps >= 1) ? locations->GetTemp(0) : Location::NoLocation();
7916   uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
7917   uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
7918   uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
7919   uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
7920   vixl32::Label done;
7921   vixl32::Label* const final_label = codegen_->GetFinalLabel(instruction, &done);
7922   SlowPathCodeARMVIXL* slow_path = nullptr;
7923 
7924   // Return 0 if `obj` is null.
7925   // avoid null check if we know obj is not null.
7926   if (instruction->MustDoNullCheck()) {
7927     DCHECK(!out.Is(obj));
7928     __ Mov(out, 0);
7929     __ CompareAndBranchIfZero(obj, final_label, /* is_far_target= */ false);
7930   }
7931 
7932   switch (type_check_kind) {
7933     case TypeCheckKind::kExactCheck: {
7934       ReadBarrierOption read_barrier_option =
7935           CodeGenerator::ReadBarrierOptionForInstanceOf(instruction);
7936       // /* HeapReference<Class> */ out = obj->klass_
7937       GenerateReferenceLoadTwoRegisters(instruction,
7938                                         out_loc,
7939                                         obj_loc,
7940                                         class_offset,
7941                                         maybe_temp_loc,
7942                                         read_barrier_option);
7943       // Classes must be equal for the instanceof to succeed.
7944       __ Cmp(out, cls);
7945       // We speculatively set the result to false without changing the condition
7946       // flags, which allows us to avoid some branching later.
7947       __ Mov(LeaveFlags, out, 0);
7948 
7949       // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
7950       // we check that the output is in a low register, so that a 16-bit MOV
7951       // encoding can be used.
7952       if (out.IsLow()) {
7953         // We use the scope because of the IT block that follows.
7954         ExactAssemblyScope guard(GetVIXLAssembler(),
7955                                  2 * vixl32::k16BitT32InstructionSizeInBytes,
7956                                  CodeBufferCheckScope::kExactSize);
7957 
7958         __ it(eq);
7959         __ mov(eq, out, 1);
7960       } else {
7961         __ B(ne, final_label, /* is_far_target= */ false);
7962         __ Mov(out, 1);
7963       }
7964 
7965       break;
7966     }
7967 
7968     case TypeCheckKind::kAbstractClassCheck: {
7969       ReadBarrierOption read_barrier_option =
7970           CodeGenerator::ReadBarrierOptionForInstanceOf(instruction);
7971       // /* HeapReference<Class> */ out = obj->klass_
7972       GenerateReferenceLoadTwoRegisters(instruction,
7973                                         out_loc,
7974                                         obj_loc,
7975                                         class_offset,
7976                                         maybe_temp_loc,
7977                                         read_barrier_option);
7978       // If the class is abstract, we eagerly fetch the super class of the
7979       // object to avoid doing a comparison we know will fail.
7980       vixl32::Label loop;
7981       __ Bind(&loop);
7982       // /* HeapReference<Class> */ out = out->super_class_
7983       GenerateReferenceLoadOneRegister(instruction,
7984                                        out_loc,
7985                                        super_offset,
7986                                        maybe_temp_loc,
7987                                        read_barrier_option);
7988       // If `out` is null, we use it for the result, and jump to the final label.
7989       __ CompareAndBranchIfZero(out, final_label, /* is_far_target= */ false);
7990       __ Cmp(out, cls);
7991       __ B(ne, &loop, /* is_far_target= */ false);
7992       __ Mov(out, 1);
7993       break;
7994     }
7995 
7996     case TypeCheckKind::kClassHierarchyCheck: {
7997       ReadBarrierOption read_barrier_option =
7998           CodeGenerator::ReadBarrierOptionForInstanceOf(instruction);
7999       // /* HeapReference<Class> */ out = obj->klass_
8000       GenerateReferenceLoadTwoRegisters(instruction,
8001                                         out_loc,
8002                                         obj_loc,
8003                                         class_offset,
8004                                         maybe_temp_loc,
8005                                         read_barrier_option);
8006       // Walk over the class hierarchy to find a match.
8007       vixl32::Label loop, success;
8008       __ Bind(&loop);
8009       __ Cmp(out, cls);
8010       __ B(eq, &success, /* is_far_target= */ false);
8011       // /* HeapReference<Class> */ out = out->super_class_
8012       GenerateReferenceLoadOneRegister(instruction,
8013                                        out_loc,
8014                                        super_offset,
8015                                        maybe_temp_loc,
8016                                        read_barrier_option);
8017       // This is essentially a null check, but it sets the condition flags to the
8018       // proper value for the code that follows the loop, i.e. not `eq`.
8019       __ Cmp(out, 1);
8020       __ B(hs, &loop, /* is_far_target= */ false);
8021 
8022       // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
8023       // we check that the output is in a low register, so that a 16-bit MOV
8024       // encoding can be used.
8025       if (out.IsLow()) {
8026         // If `out` is null, we use it for the result, and the condition flags
8027         // have already been set to `ne`, so the IT block that comes afterwards
8028         // (and which handles the successful case) turns into a NOP (instead of
8029         // overwriting `out`).
8030         __ Bind(&success);
8031 
8032         // We use the scope because of the IT block that follows.
8033         ExactAssemblyScope guard(GetVIXLAssembler(),
8034                                  2 * vixl32::k16BitT32InstructionSizeInBytes,
8035                                  CodeBufferCheckScope::kExactSize);
8036 
8037         // There is only one branch to the `success` label (which is bound to this
8038         // IT block), and it has the same condition, `eq`, so in that case the MOV
8039         // is executed.
8040         __ it(eq);
8041         __ mov(eq, out, 1);
8042       } else {
8043         // If `out` is null, we use it for the result, and jump to the final label.
8044         __ B(final_label);
8045         __ Bind(&success);
8046         __ Mov(out, 1);
8047       }
8048 
8049       break;
8050     }
8051 
8052     case TypeCheckKind::kArrayObjectCheck: {
8053       ReadBarrierOption read_barrier_option =
8054           CodeGenerator::ReadBarrierOptionForInstanceOf(instruction);
8055       // /* HeapReference<Class> */ out = obj->klass_
8056       GenerateReferenceLoadTwoRegisters(instruction,
8057                                         out_loc,
8058                                         obj_loc,
8059                                         class_offset,
8060                                         maybe_temp_loc,
8061                                         read_barrier_option);
8062       // Do an exact check.
8063       vixl32::Label exact_check;
8064       __ Cmp(out, cls);
8065       __ B(eq, &exact_check, /* is_far_target= */ false);
8066       // Otherwise, we need to check that the object's class is a non-primitive array.
8067       // /* HeapReference<Class> */ out = out->component_type_
8068       GenerateReferenceLoadOneRegister(instruction,
8069                                        out_loc,
8070                                        component_offset,
8071                                        maybe_temp_loc,
8072                                        read_barrier_option);
8073       // If `out` is null, we use it for the result, and jump to the final label.
8074       __ CompareAndBranchIfZero(out, final_label, /* is_far_target= */ false);
8075       GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset);
8076       static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
8077       __ Cmp(out, 0);
8078       // We speculatively set the result to false without changing the condition
8079       // flags, which allows us to avoid some branching later.
8080       __ Mov(LeaveFlags, out, 0);
8081 
8082       // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
8083       // we check that the output is in a low register, so that a 16-bit MOV
8084       // encoding can be used.
8085       if (out.IsLow()) {
8086         __ Bind(&exact_check);
8087 
8088         // We use the scope because of the IT block that follows.
8089         ExactAssemblyScope guard(GetVIXLAssembler(),
8090                                  2 * vixl32::k16BitT32InstructionSizeInBytes,
8091                                  CodeBufferCheckScope::kExactSize);
8092 
8093         __ it(eq);
8094         __ mov(eq, out, 1);
8095       } else {
8096         __ B(ne, final_label, /* is_far_target= */ false);
8097         __ Bind(&exact_check);
8098         __ Mov(out, 1);
8099       }
8100 
8101       break;
8102     }
8103 
8104     case TypeCheckKind::kArrayCheck: {
8105       // No read barrier since the slow path will retry upon failure.
8106       // /* HeapReference<Class> */ out = obj->klass_
8107       GenerateReferenceLoadTwoRegisters(instruction,
8108                                         out_loc,
8109                                         obj_loc,
8110                                         class_offset,
8111                                         maybe_temp_loc,
8112                                         kWithoutReadBarrier);
8113       __ Cmp(out, cls);
8114       DCHECK(locations->OnlyCallsOnSlowPath());
8115       slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathARMVIXL(
8116           instruction, /* is_fatal= */ false);
8117       codegen_->AddSlowPath(slow_path);
8118       __ B(ne, slow_path->GetEntryLabel());
8119       __ Mov(out, 1);
8120       break;
8121     }
8122 
8123     case TypeCheckKind::kUnresolvedCheck:
8124     case TypeCheckKind::kInterfaceCheck: {
8125       // Note that we indeed only call on slow path, but we always go
8126       // into the slow path for the unresolved and interface check
8127       // cases.
8128       //
8129       // We cannot directly call the InstanceofNonTrivial runtime
8130       // entry point without resorting to a type checking slow path
8131       // here (i.e. by calling InvokeRuntime directly), as it would
8132       // require to assign fixed registers for the inputs of this
8133       // HInstanceOf instruction (following the runtime calling
8134       // convention), which might be cluttered by the potential first
8135       // read barrier emission at the beginning of this method.
8136       //
8137       // TODO: Introduce a new runtime entry point taking the object
8138       // to test (instead of its class) as argument, and let it deal
8139       // with the read barrier issues. This will let us refactor this
8140       // case of the `switch` code as it was previously (with a direct
8141       // call to the runtime not using a type checking slow path).
8142       // This should also be beneficial for the other cases above.
8143       DCHECK(locations->OnlyCallsOnSlowPath());
8144       slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathARMVIXL(
8145           instruction, /* is_fatal= */ false);
8146       codegen_->AddSlowPath(slow_path);
8147       __ B(slow_path->GetEntryLabel());
8148       break;
8149     }
8150 
8151     case TypeCheckKind::kBitstringCheck: {
8152       // /* HeapReference<Class> */ temp = obj->klass_
8153       GenerateReferenceLoadTwoRegisters(instruction,
8154                                         out_loc,
8155                                         obj_loc,
8156                                         class_offset,
8157                                         maybe_temp_loc,
8158                                         kWithoutReadBarrier);
8159 
8160       GenerateBitstringTypeCheckCompare(instruction, out, DontCare);
8161       // If `out` is a low reg and we would have another low reg temp, we could
8162       // optimize this as RSBS+ADC, see GenerateConditionWithZero().
8163       //
8164       // Also, in some cases when `out` is a low reg and we're loading a constant to IP
8165       // it would make sense to use CMP+MOV+IT+MOV instead of SUB+CLZ+LSR as the code size
8166       // would be the same and we would have fewer direct data dependencies.
8167       codegen_->GenerateConditionWithZero(kCondEQ, out, out);  // CLZ+LSR
8168       break;
8169     }
8170   }
8171 
8172   if (done.IsReferenced()) {
8173     __ Bind(&done);
8174   }
8175 
8176   if (slow_path != nullptr) {
8177     __ Bind(slow_path->GetExitLabel());
8178   }
8179 }
8180 
VisitCheckCast(HCheckCast * instruction)8181 void LocationsBuilderARMVIXL::VisitCheckCast(HCheckCast* instruction) {
8182   TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
8183   LocationSummary::CallKind call_kind = CodeGenerator::GetCheckCastCallKind(instruction);
8184   LocationSummary* locations =
8185       new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
8186   locations->SetInAt(0, Location::RequiresRegister());
8187   if (type_check_kind == TypeCheckKind::kBitstringCheck) {
8188     locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
8189     locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
8190     locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
8191   } else {
8192     locations->SetInAt(1, Location::RequiresRegister());
8193   }
8194   locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind));
8195 }
8196 
VisitCheckCast(HCheckCast * instruction)8197 void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) {
8198   TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
8199   LocationSummary* locations = instruction->GetLocations();
8200   Location obj_loc = locations->InAt(0);
8201   vixl32::Register obj = InputRegisterAt(instruction, 0);
8202   vixl32::Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck)
8203       ? vixl32::Register()
8204       : InputRegisterAt(instruction, 1);
8205   Location temp_loc = locations->GetTemp(0);
8206   vixl32::Register temp = RegisterFrom(temp_loc);
8207   const size_t num_temps = NumberOfCheckCastTemps(type_check_kind);
8208   DCHECK_LE(num_temps, 3u);
8209   Location maybe_temp2_loc = (num_temps >= 2) ? locations->GetTemp(1) : Location::NoLocation();
8210   Location maybe_temp3_loc = (num_temps >= 3) ? locations->GetTemp(2) : Location::NoLocation();
8211   const uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
8212   const uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
8213   const uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
8214   const uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
8215   const uint32_t iftable_offset = mirror::Class::IfTableOffset().Uint32Value();
8216   const uint32_t array_length_offset = mirror::Array::LengthOffset().Uint32Value();
8217   const uint32_t object_array_data_offset =
8218       mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value();
8219 
8220   bool is_type_check_slow_path_fatal = CodeGenerator::IsTypeCheckSlowPathFatal(instruction);
8221   SlowPathCodeARMVIXL* type_check_slow_path =
8222       new (codegen_->GetScopedAllocator()) TypeCheckSlowPathARMVIXL(
8223           instruction, is_type_check_slow_path_fatal);
8224   codegen_->AddSlowPath(type_check_slow_path);
8225 
8226   vixl32::Label done;
8227   vixl32::Label* final_label = codegen_->GetFinalLabel(instruction, &done);
8228   // Avoid null check if we know obj is not null.
8229   if (instruction->MustDoNullCheck()) {
8230     __ CompareAndBranchIfZero(obj, final_label, /* is_far_target= */ false);
8231   }
8232 
8233   switch (type_check_kind) {
8234     case TypeCheckKind::kExactCheck:
8235     case TypeCheckKind::kArrayCheck: {
8236       // /* HeapReference<Class> */ temp = obj->klass_
8237       GenerateReferenceLoadTwoRegisters(instruction,
8238                                         temp_loc,
8239                                         obj_loc,
8240                                         class_offset,
8241                                         maybe_temp2_loc,
8242                                         kWithoutReadBarrier);
8243 
8244       __ Cmp(temp, cls);
8245       // Jump to slow path for throwing the exception or doing a
8246       // more involved array check.
8247       __ B(ne, type_check_slow_path->GetEntryLabel());
8248       break;
8249     }
8250 
8251     case TypeCheckKind::kAbstractClassCheck: {
8252       // /* HeapReference<Class> */ temp = obj->klass_
8253       GenerateReferenceLoadTwoRegisters(instruction,
8254                                         temp_loc,
8255                                         obj_loc,
8256                                         class_offset,
8257                                         maybe_temp2_loc,
8258                                         kWithoutReadBarrier);
8259 
8260       // If the class is abstract, we eagerly fetch the super class of the
8261       // object to avoid doing a comparison we know will fail.
8262       vixl32::Label loop;
8263       __ Bind(&loop);
8264       // /* HeapReference<Class> */ temp = temp->super_class_
8265       GenerateReferenceLoadOneRegister(instruction,
8266                                        temp_loc,
8267                                        super_offset,
8268                                        maybe_temp2_loc,
8269                                        kWithoutReadBarrier);
8270 
8271       // If the class reference currently in `temp` is null, jump to the slow path to throw the
8272       // exception.
8273       __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
8274 
8275       // Otherwise, compare the classes.
8276       __ Cmp(temp, cls);
8277       __ B(ne, &loop, /* is_far_target= */ false);
8278       break;
8279     }
8280 
8281     case TypeCheckKind::kClassHierarchyCheck: {
8282       // /* HeapReference<Class> */ temp = obj->klass_
8283       GenerateReferenceLoadTwoRegisters(instruction,
8284                                         temp_loc,
8285                                         obj_loc,
8286                                         class_offset,
8287                                         maybe_temp2_loc,
8288                                         kWithoutReadBarrier);
8289 
8290       // Walk over the class hierarchy to find a match.
8291       vixl32::Label loop;
8292       __ Bind(&loop);
8293       __ Cmp(temp, cls);
8294       __ B(eq, final_label, /* is_far_target= */ false);
8295 
8296       // /* HeapReference<Class> */ temp = temp->super_class_
8297       GenerateReferenceLoadOneRegister(instruction,
8298                                        temp_loc,
8299                                        super_offset,
8300                                        maybe_temp2_loc,
8301                                        kWithoutReadBarrier);
8302 
8303       // If the class reference currently in `temp` is null, jump to the slow path to throw the
8304       // exception.
8305       __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
8306       // Otherwise, jump to the beginning of the loop.
8307       __ B(&loop);
8308       break;
8309     }
8310 
8311     case TypeCheckKind::kArrayObjectCheck:  {
8312       // /* HeapReference<Class> */ temp = obj->klass_
8313       GenerateReferenceLoadTwoRegisters(instruction,
8314                                         temp_loc,
8315                                         obj_loc,
8316                                         class_offset,
8317                                         maybe_temp2_loc,
8318                                         kWithoutReadBarrier);
8319 
8320       // Do an exact check.
8321       __ Cmp(temp, cls);
8322       __ B(eq, final_label, /* is_far_target= */ false);
8323 
8324       // Otherwise, we need to check that the object's class is a non-primitive array.
8325       // /* HeapReference<Class> */ temp = temp->component_type_
8326       GenerateReferenceLoadOneRegister(instruction,
8327                                        temp_loc,
8328                                        component_offset,
8329                                        maybe_temp2_loc,
8330                                        kWithoutReadBarrier);
8331       // If the component type is null, jump to the slow path to throw the exception.
8332       __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
8333       // Otherwise,the object is indeed an array, jump to label `check_non_primitive_component_type`
8334       // to further check that this component type is not a primitive type.
8335       GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, temp, temp, primitive_offset);
8336       static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
8337       __ CompareAndBranchIfNonZero(temp, type_check_slow_path->GetEntryLabel());
8338       break;
8339     }
8340 
8341     case TypeCheckKind::kUnresolvedCheck:
8342       // We always go into the type check slow path for the unresolved check case.
8343       // We cannot directly call the CheckCast runtime entry point
8344       // without resorting to a type checking slow path here (i.e. by
8345       // calling InvokeRuntime directly), as it would require to
8346       // assign fixed registers for the inputs of this HInstanceOf
8347       // instruction (following the runtime calling convention), which
8348       // might be cluttered by the potential first read barrier
8349       // emission at the beginning of this method.
8350 
8351       __ B(type_check_slow_path->GetEntryLabel());
8352       break;
8353 
8354     case TypeCheckKind::kInterfaceCheck: {
8355       // Avoid read barriers to improve performance of the fast path. We can not get false
8356       // positives by doing this.
8357       // /* HeapReference<Class> */ temp = obj->klass_
8358       GenerateReferenceLoadTwoRegisters(instruction,
8359                                         temp_loc,
8360                                         obj_loc,
8361                                         class_offset,
8362                                         maybe_temp2_loc,
8363                                         kWithoutReadBarrier);
8364 
8365       // /* HeapReference<Class> */ temp = temp->iftable_
8366       GenerateReferenceLoadTwoRegisters(instruction,
8367                                         temp_loc,
8368                                         temp_loc,
8369                                         iftable_offset,
8370                                         maybe_temp2_loc,
8371                                         kWithoutReadBarrier);
8372       // Iftable is never null.
8373       __ Ldr(RegisterFrom(maybe_temp2_loc), MemOperand(temp, array_length_offset));
8374       // Loop through the iftable and check if any class matches.
8375       vixl32::Label start_loop;
8376       __ Bind(&start_loop);
8377       __ CompareAndBranchIfZero(RegisterFrom(maybe_temp2_loc),
8378                                 type_check_slow_path->GetEntryLabel());
8379       __ Ldr(RegisterFrom(maybe_temp3_loc), MemOperand(temp, object_array_data_offset));
8380       GetAssembler()->MaybeUnpoisonHeapReference(RegisterFrom(maybe_temp3_loc));
8381       // Go to next interface.
8382       __ Add(temp, temp, Operand::From(2 * kHeapReferenceSize));
8383       __ Sub(RegisterFrom(maybe_temp2_loc), RegisterFrom(maybe_temp2_loc), 2);
8384       // Compare the classes and continue the loop if they do not match.
8385       __ Cmp(cls, RegisterFrom(maybe_temp3_loc));
8386       __ B(ne, &start_loop, /* is_far_target= */ false);
8387       break;
8388     }
8389 
8390     case TypeCheckKind::kBitstringCheck: {
8391       // /* HeapReference<Class> */ temp = obj->klass_
8392       GenerateReferenceLoadTwoRegisters(instruction,
8393                                         temp_loc,
8394                                         obj_loc,
8395                                         class_offset,
8396                                         maybe_temp2_loc,
8397                                         kWithoutReadBarrier);
8398 
8399       GenerateBitstringTypeCheckCompare(instruction, temp, SetFlags);
8400       __ B(ne, type_check_slow_path->GetEntryLabel());
8401       break;
8402     }
8403   }
8404   if (done.IsReferenced()) {
8405     __ Bind(&done);
8406   }
8407 
8408   __ Bind(type_check_slow_path->GetExitLabel());
8409 }
8410 
VisitMonitorOperation(HMonitorOperation * instruction)8411 void LocationsBuilderARMVIXL::VisitMonitorOperation(HMonitorOperation* instruction) {
8412   LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
8413       instruction, LocationSummary::kCallOnMainOnly);
8414   InvokeRuntimeCallingConventionARMVIXL calling_convention;
8415   locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
8416 }
8417 
VisitMonitorOperation(HMonitorOperation * instruction)8418 void InstructionCodeGeneratorARMVIXL::VisitMonitorOperation(HMonitorOperation* instruction) {
8419   codegen_->InvokeRuntime(instruction->IsEnter() ? kQuickLockObject : kQuickUnlockObject,
8420                           instruction,
8421                           instruction->GetDexPc());
8422   if (instruction->IsEnter()) {
8423     CheckEntrypointTypes<kQuickLockObject, void, mirror::Object*>();
8424   } else {
8425     CheckEntrypointTypes<kQuickUnlockObject, void, mirror::Object*>();
8426   }
8427   codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 19);
8428 }
8429 
VisitAnd(HAnd * instruction)8430 void LocationsBuilderARMVIXL::VisitAnd(HAnd* instruction) {
8431   HandleBitwiseOperation(instruction, AND);
8432 }
8433 
VisitOr(HOr * instruction)8434 void LocationsBuilderARMVIXL::VisitOr(HOr* instruction) {
8435   HandleBitwiseOperation(instruction, ORR);
8436 }
8437 
VisitXor(HXor * instruction)8438 void LocationsBuilderARMVIXL::VisitXor(HXor* instruction) {
8439   HandleBitwiseOperation(instruction, EOR);
8440 }
8441 
HandleBitwiseOperation(HBinaryOperation * instruction,Opcode opcode)8442 void LocationsBuilderARMVIXL::HandleBitwiseOperation(HBinaryOperation* instruction, Opcode opcode) {
8443   LocationSummary* locations =
8444       new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
8445   DCHECK(instruction->GetResultType() == DataType::Type::kInt32
8446          || instruction->GetResultType() == DataType::Type::kInt64);
8447   // Note: GVN reorders commutative operations to have the constant on the right hand side.
8448   locations->SetInAt(0, Location::RequiresRegister());
8449   locations->SetInAt(1, ArmEncodableConstantOrRegister(instruction->InputAt(1), opcode));
8450   locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
8451 }
8452 
VisitAnd(HAnd * instruction)8453 void InstructionCodeGeneratorARMVIXL::VisitAnd(HAnd* instruction) {
8454   HandleBitwiseOperation(instruction);
8455 }
8456 
VisitOr(HOr * instruction)8457 void InstructionCodeGeneratorARMVIXL::VisitOr(HOr* instruction) {
8458   HandleBitwiseOperation(instruction);
8459 }
8460 
VisitXor(HXor * instruction)8461 void InstructionCodeGeneratorARMVIXL::VisitXor(HXor* instruction) {
8462   HandleBitwiseOperation(instruction);
8463 }
8464 
VisitBitwiseNegatedRight(HBitwiseNegatedRight * instruction)8465 void LocationsBuilderARMVIXL::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) {
8466   LocationSummary* locations =
8467       new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
8468   DCHECK(instruction->GetResultType() == DataType::Type::kInt32
8469          || instruction->GetResultType() == DataType::Type::kInt64);
8470 
8471   locations->SetInAt(0, Location::RequiresRegister());
8472   locations->SetInAt(1, Location::RequiresRegister());
8473   locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
8474 }
8475 
VisitBitwiseNegatedRight(HBitwiseNegatedRight * instruction)8476 void InstructionCodeGeneratorARMVIXL::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) {
8477   LocationSummary* locations = instruction->GetLocations();
8478   Location first = locations->InAt(0);
8479   Location second = locations->InAt(1);
8480   Location out = locations->Out();
8481 
8482   if (instruction->GetResultType() == DataType::Type::kInt32) {
8483     vixl32::Register first_reg = RegisterFrom(first);
8484     vixl32::Register second_reg = RegisterFrom(second);
8485     vixl32::Register out_reg = RegisterFrom(out);
8486 
8487     switch (instruction->GetOpKind()) {
8488       case HInstruction::kAnd:
8489         __ Bic(out_reg, first_reg, second_reg);
8490         break;
8491       case HInstruction::kOr:
8492         __ Orn(out_reg, first_reg, second_reg);
8493         break;
8494       // There is no EON on arm.
8495       case HInstruction::kXor:
8496       default:
8497         LOG(FATAL) << "Unexpected instruction " << instruction->DebugName();
8498         UNREACHABLE();
8499     }
8500     return;
8501 
8502   } else {
8503     DCHECK_EQ(instruction->GetResultType(), DataType::Type::kInt64);
8504     vixl32::Register first_low = LowRegisterFrom(first);
8505     vixl32::Register first_high = HighRegisterFrom(first);
8506     vixl32::Register second_low = LowRegisterFrom(second);
8507     vixl32::Register second_high = HighRegisterFrom(second);
8508     vixl32::Register out_low = LowRegisterFrom(out);
8509     vixl32::Register out_high = HighRegisterFrom(out);
8510 
8511     switch (instruction->GetOpKind()) {
8512       case HInstruction::kAnd:
8513         __ Bic(out_low, first_low, second_low);
8514         __ Bic(out_high, first_high, second_high);
8515         break;
8516       case HInstruction::kOr:
8517         __ Orn(out_low, first_low, second_low);
8518         __ Orn(out_high, first_high, second_high);
8519         break;
8520       // There is no EON on arm.
8521       case HInstruction::kXor:
8522       default:
8523         LOG(FATAL) << "Unexpected instruction " << instruction->DebugName();
8524         UNREACHABLE();
8525     }
8526   }
8527 }
8528 
VisitDataProcWithShifterOp(HDataProcWithShifterOp * instruction)8529 void LocationsBuilderARMVIXL::VisitDataProcWithShifterOp(
8530     HDataProcWithShifterOp* instruction) {
8531   DCHECK(instruction->GetType() == DataType::Type::kInt32 ||
8532          instruction->GetType() == DataType::Type::kInt64);
8533   LocationSummary* locations =
8534       new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
8535   const bool overlap = instruction->GetType() == DataType::Type::kInt64 &&
8536                        HDataProcWithShifterOp::IsExtensionOp(instruction->GetOpKind());
8537 
8538   locations->SetInAt(0, Location::RequiresRegister());
8539   locations->SetInAt(1, Location::RequiresRegister());
8540   locations->SetOut(Location::RequiresRegister(),
8541                     overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap);
8542 }
8543 
VisitDataProcWithShifterOp(HDataProcWithShifterOp * instruction)8544 void InstructionCodeGeneratorARMVIXL::VisitDataProcWithShifterOp(
8545     HDataProcWithShifterOp* instruction) {
8546   const LocationSummary* const locations = instruction->GetLocations();
8547   const HInstruction::InstructionKind kind = instruction->GetInstrKind();
8548   const HDataProcWithShifterOp::OpKind op_kind = instruction->GetOpKind();
8549 
8550   if (instruction->GetType() == DataType::Type::kInt32) {
8551     const vixl32::Register first = InputRegisterAt(instruction, 0);
8552     const vixl32::Register output = OutputRegister(instruction);
8553     const vixl32::Register second = instruction->InputAt(1)->GetType() == DataType::Type::kInt64
8554         ? LowRegisterFrom(locations->InAt(1))
8555         : InputRegisterAt(instruction, 1);
8556 
8557     if (HDataProcWithShifterOp::IsExtensionOp(op_kind)) {
8558       DCHECK_EQ(kind, HInstruction::kAdd);
8559 
8560       switch (op_kind) {
8561         case HDataProcWithShifterOp::kUXTB:
8562           __ Uxtab(output, first, second);
8563           break;
8564         case HDataProcWithShifterOp::kUXTH:
8565           __ Uxtah(output, first, second);
8566           break;
8567         case HDataProcWithShifterOp::kSXTB:
8568           __ Sxtab(output, first, second);
8569           break;
8570         case HDataProcWithShifterOp::kSXTH:
8571           __ Sxtah(output, first, second);
8572           break;
8573         default:
8574           LOG(FATAL) << "Unexpected operation kind: " << op_kind;
8575           UNREACHABLE();
8576       }
8577     } else {
8578       GenerateDataProcInstruction(kind,
8579                                   output,
8580                                   first,
8581                                   Operand(second,
8582                                           ShiftFromOpKind(op_kind),
8583                                           instruction->GetShiftAmount()),
8584                                   codegen_);
8585     }
8586   } else {
8587     DCHECK_EQ(instruction->GetType(), DataType::Type::kInt64);
8588 
8589     if (HDataProcWithShifterOp::IsExtensionOp(op_kind)) {
8590       const vixl32::Register second = InputRegisterAt(instruction, 1);
8591 
8592       DCHECK(!LowRegisterFrom(locations->Out()).Is(second));
8593       GenerateDataProc(kind,
8594                        locations->Out(),
8595                        locations->InAt(0),
8596                        second,
8597                        Operand(second, ShiftType::ASR, 31),
8598                        codegen_);
8599     } else {
8600       GenerateLongDataProc(instruction, codegen_);
8601     }
8602   }
8603 }
8604 
8605 // TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
GenerateAndConst(vixl32::Register out,vixl32::Register first,uint32_t value)8606 void InstructionCodeGeneratorARMVIXL::GenerateAndConst(vixl32::Register out,
8607                                                        vixl32::Register first,
8608                                                        uint32_t value) {
8609   // Optimize special cases for individual halfs of `and-long` (`and` is simplified earlier).
8610   if (value == 0xffffffffu) {
8611     if (!out.Is(first)) {
8612       __ Mov(out, first);
8613     }
8614     return;
8615   }
8616   if (value == 0u) {
8617     __ Mov(out, 0);
8618     return;
8619   }
8620   if (GetAssembler()->ShifterOperandCanHold(AND, value)) {
8621     __ And(out, first, value);
8622   } else if (GetAssembler()->ShifterOperandCanHold(BIC, ~value)) {
8623     __ Bic(out, first, ~value);
8624   } else {
8625     DCHECK(IsPowerOfTwo(value + 1));
8626     __ Ubfx(out, first, 0, WhichPowerOf2(value + 1));
8627   }
8628 }
8629 
8630 // TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
GenerateOrrConst(vixl32::Register out,vixl32::Register first,uint32_t value)8631 void InstructionCodeGeneratorARMVIXL::GenerateOrrConst(vixl32::Register out,
8632                                                        vixl32::Register first,
8633                                                        uint32_t value) {
8634   // Optimize special cases for individual halfs of `or-long` (`or` is simplified earlier).
8635   if (value == 0u) {
8636     if (!out.Is(first)) {
8637       __ Mov(out, first);
8638     }
8639     return;
8640   }
8641   if (value == 0xffffffffu) {
8642     __ Mvn(out, 0);
8643     return;
8644   }
8645   if (GetAssembler()->ShifterOperandCanHold(ORR, value)) {
8646     __ Orr(out, first, value);
8647   } else {
8648     DCHECK(GetAssembler()->ShifterOperandCanHold(ORN, ~value));
8649     __ Orn(out, first, ~value);
8650   }
8651 }
8652 
8653 // TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
GenerateEorConst(vixl32::Register out,vixl32::Register first,uint32_t value)8654 void InstructionCodeGeneratorARMVIXL::GenerateEorConst(vixl32::Register out,
8655                                                        vixl32::Register first,
8656                                                        uint32_t value) {
8657   // Optimize special case for individual halfs of `xor-long` (`xor` is simplified earlier).
8658   if (value == 0u) {
8659     if (!out.Is(first)) {
8660       __ Mov(out, first);
8661     }
8662     return;
8663   }
8664   __ Eor(out, first, value);
8665 }
8666 
GenerateAddLongConst(Location out,Location first,uint64_t value)8667 void InstructionCodeGeneratorARMVIXL::GenerateAddLongConst(Location out,
8668                                                            Location first,
8669                                                            uint64_t value) {
8670   vixl32::Register out_low = LowRegisterFrom(out);
8671   vixl32::Register out_high = HighRegisterFrom(out);
8672   vixl32::Register first_low = LowRegisterFrom(first);
8673   vixl32::Register first_high = HighRegisterFrom(first);
8674   uint32_t value_low = Low32Bits(value);
8675   uint32_t value_high = High32Bits(value);
8676   if (value_low == 0u) {
8677     if (!out_low.Is(first_low)) {
8678       __ Mov(out_low, first_low);
8679     }
8680     __ Add(out_high, first_high, value_high);
8681     return;
8682   }
8683   __ Adds(out_low, first_low, value_low);
8684   if (GetAssembler()->ShifterOperandCanHold(ADC, value_high)) {
8685     __ Adc(out_high, first_high, value_high);
8686   } else {
8687     DCHECK(GetAssembler()->ShifterOperandCanHold(SBC, ~value_high));
8688     __ Sbc(out_high, first_high, ~value_high);
8689   }
8690 }
8691 
HandleBitwiseOperation(HBinaryOperation * instruction)8692 void InstructionCodeGeneratorARMVIXL::HandleBitwiseOperation(HBinaryOperation* instruction) {
8693   LocationSummary* locations = instruction->GetLocations();
8694   Location first = locations->InAt(0);
8695   Location second = locations->InAt(1);
8696   Location out = locations->Out();
8697 
8698   if (second.IsConstant()) {
8699     uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
8700     uint32_t value_low = Low32Bits(value);
8701     if (instruction->GetResultType() == DataType::Type::kInt32) {
8702       vixl32::Register first_reg = InputRegisterAt(instruction, 0);
8703       vixl32::Register out_reg = OutputRegister(instruction);
8704       if (instruction->IsAnd()) {
8705         GenerateAndConst(out_reg, first_reg, value_low);
8706       } else if (instruction->IsOr()) {
8707         GenerateOrrConst(out_reg, first_reg, value_low);
8708       } else {
8709         DCHECK(instruction->IsXor());
8710         GenerateEorConst(out_reg, first_reg, value_low);
8711       }
8712     } else {
8713       DCHECK_EQ(instruction->GetResultType(), DataType::Type::kInt64);
8714       uint32_t value_high = High32Bits(value);
8715       vixl32::Register first_low = LowRegisterFrom(first);
8716       vixl32::Register first_high = HighRegisterFrom(first);
8717       vixl32::Register out_low = LowRegisterFrom(out);
8718       vixl32::Register out_high = HighRegisterFrom(out);
8719       if (instruction->IsAnd()) {
8720         GenerateAndConst(out_low, first_low, value_low);
8721         GenerateAndConst(out_high, first_high, value_high);
8722       } else if (instruction->IsOr()) {
8723         GenerateOrrConst(out_low, first_low, value_low);
8724         GenerateOrrConst(out_high, first_high, value_high);
8725       } else {
8726         DCHECK(instruction->IsXor());
8727         GenerateEorConst(out_low, first_low, value_low);
8728         GenerateEorConst(out_high, first_high, value_high);
8729       }
8730     }
8731     return;
8732   }
8733 
8734   if (instruction->GetResultType() == DataType::Type::kInt32) {
8735     vixl32::Register first_reg = InputRegisterAt(instruction, 0);
8736     vixl32::Register second_reg = InputRegisterAt(instruction, 1);
8737     vixl32::Register out_reg = OutputRegister(instruction);
8738     if (instruction->IsAnd()) {
8739       __ And(out_reg, first_reg, second_reg);
8740     } else if (instruction->IsOr()) {
8741       __ Orr(out_reg, first_reg, second_reg);
8742     } else {
8743       DCHECK(instruction->IsXor());
8744       __ Eor(out_reg, first_reg, second_reg);
8745     }
8746   } else {
8747     DCHECK_EQ(instruction->GetResultType(), DataType::Type::kInt64);
8748     vixl32::Register first_low = LowRegisterFrom(first);
8749     vixl32::Register first_high = HighRegisterFrom(first);
8750     vixl32::Register second_low = LowRegisterFrom(second);
8751     vixl32::Register second_high = HighRegisterFrom(second);
8752     vixl32::Register out_low = LowRegisterFrom(out);
8753     vixl32::Register out_high = HighRegisterFrom(out);
8754     if (instruction->IsAnd()) {
8755       __ And(out_low, first_low, second_low);
8756       __ And(out_high, first_high, second_high);
8757     } else if (instruction->IsOr()) {
8758       __ Orr(out_low, first_low, second_low);
8759       __ Orr(out_high, first_high, second_high);
8760     } else {
8761       DCHECK(instruction->IsXor());
8762       __ Eor(out_low, first_low, second_low);
8763       __ Eor(out_high, first_high, second_high);
8764     }
8765   }
8766 }
8767 
GenerateReferenceLoadOneRegister(HInstruction * instruction,Location out,uint32_t offset,Location maybe_temp,ReadBarrierOption read_barrier_option)8768 void InstructionCodeGeneratorARMVIXL::GenerateReferenceLoadOneRegister(
8769     HInstruction* instruction,
8770     Location out,
8771     uint32_t offset,
8772     Location maybe_temp,
8773     ReadBarrierOption read_barrier_option) {
8774   vixl32::Register out_reg = RegisterFrom(out);
8775   if (read_barrier_option == kWithReadBarrier) {
8776     CHECK(kEmitCompilerReadBarrier);
8777     DCHECK(maybe_temp.IsRegister()) << maybe_temp;
8778     if (kUseBakerReadBarrier) {
8779       // Load with fast path based Baker's read barrier.
8780       // /* HeapReference<Object> */ out = *(out + offset)
8781       codegen_->GenerateFieldLoadWithBakerReadBarrier(
8782           instruction, out, out_reg, offset, maybe_temp, /* needs_null_check= */ false);
8783     } else {
8784       // Load with slow path based read barrier.
8785       // Save the value of `out` into `maybe_temp` before overwriting it
8786       // in the following move operation, as we will need it for the
8787       // read barrier below.
8788       __ Mov(RegisterFrom(maybe_temp), out_reg);
8789       // /* HeapReference<Object> */ out = *(out + offset)
8790       GetAssembler()->LoadFromOffset(kLoadWord, out_reg, out_reg, offset);
8791       codegen_->GenerateReadBarrierSlow(instruction, out, out, maybe_temp, offset);
8792     }
8793   } else {
8794     // Plain load with no read barrier.
8795     // /* HeapReference<Object> */ out = *(out + offset)
8796     GetAssembler()->LoadFromOffset(kLoadWord, out_reg, out_reg, offset);
8797     GetAssembler()->MaybeUnpoisonHeapReference(out_reg);
8798   }
8799 }
8800 
GenerateReferenceLoadTwoRegisters(HInstruction * instruction,Location out,Location obj,uint32_t offset,Location maybe_temp,ReadBarrierOption read_barrier_option)8801 void InstructionCodeGeneratorARMVIXL::GenerateReferenceLoadTwoRegisters(
8802     HInstruction* instruction,
8803     Location out,
8804     Location obj,
8805     uint32_t offset,
8806     Location maybe_temp,
8807     ReadBarrierOption read_barrier_option) {
8808   vixl32::Register out_reg = RegisterFrom(out);
8809   vixl32::Register obj_reg = RegisterFrom(obj);
8810   if (read_barrier_option == kWithReadBarrier) {
8811     CHECK(kEmitCompilerReadBarrier);
8812     if (kUseBakerReadBarrier) {
8813       DCHECK(maybe_temp.IsRegister()) << maybe_temp;
8814       // Load with fast path based Baker's read barrier.
8815       // /* HeapReference<Object> */ out = *(obj + offset)
8816       codegen_->GenerateFieldLoadWithBakerReadBarrier(
8817           instruction, out, obj_reg, offset, maybe_temp, /* needs_null_check= */ false);
8818     } else {
8819       // Load with slow path based read barrier.
8820       // /* HeapReference<Object> */ out = *(obj + offset)
8821       GetAssembler()->LoadFromOffset(kLoadWord, out_reg, obj_reg, offset);
8822       codegen_->GenerateReadBarrierSlow(instruction, out, out, obj, offset);
8823     }
8824   } else {
8825     // Plain load with no read barrier.
8826     // /* HeapReference<Object> */ out = *(obj + offset)
8827     GetAssembler()->LoadFromOffset(kLoadWord, out_reg, obj_reg, offset);
8828     GetAssembler()->MaybeUnpoisonHeapReference(out_reg);
8829   }
8830 }
8831 
GenerateGcRootFieldLoad(HInstruction * instruction,Location root,vixl32::Register obj,uint32_t offset,ReadBarrierOption read_barrier_option)8832 void CodeGeneratorARMVIXL::GenerateGcRootFieldLoad(
8833     HInstruction* instruction,
8834     Location root,
8835     vixl32::Register obj,
8836     uint32_t offset,
8837     ReadBarrierOption read_barrier_option) {
8838   vixl32::Register root_reg = RegisterFrom(root);
8839   if (read_barrier_option == kWithReadBarrier) {
8840     DCHECK(kEmitCompilerReadBarrier);
8841     if (kUseBakerReadBarrier) {
8842       // Fast path implementation of art::ReadBarrier::BarrierForRoot when
8843       // Baker's read barrier are used.
8844 
8845       // Query `art::Thread::Current()->GetIsGcMarking()` (stored in
8846       // the Marking Register) to decide whether we need to enter
8847       // the slow path to mark the GC root.
8848       //
8849       // We use shared thunks for the slow path; shared within the method
8850       // for JIT, across methods for AOT. That thunk checks the reference
8851       // and jumps to the entrypoint if needed.
8852       //
8853       //     lr = &return_address;
8854       //     GcRoot<mirror::Object> root = *(obj+offset);  // Original reference load.
8855       //     if (mr) {  // Thread::Current()->GetIsGcMarking()
8856       //       goto gc_root_thunk<root_reg>(lr)
8857       //     }
8858       //   return_address:
8859 
8860       UseScratchRegisterScope temps(GetVIXLAssembler());
8861       temps.Exclude(ip);
8862       bool narrow = CanEmitNarrowLdr(root_reg, obj, offset);
8863       uint32_t custom_data = EncodeBakerReadBarrierGcRootData(root_reg.GetCode(), narrow);
8864 
8865       size_t narrow_instructions = /* CMP */ (mr.IsLow() ? 1u : 0u) + /* LDR */ (narrow ? 1u : 0u);
8866       size_t wide_instructions = /* ADR+CMP+LDR+BNE */ 4u - narrow_instructions;
8867       size_t exact_size = wide_instructions * vixl32::k32BitT32InstructionSizeInBytes +
8868                           narrow_instructions * vixl32::k16BitT32InstructionSizeInBytes;
8869       ExactAssemblyScope guard(GetVIXLAssembler(), exact_size);
8870       vixl32::Label return_address;
8871       EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
8872       __ cmp(mr, Operand(0));
8873       // Currently the offset is always within range. If that changes,
8874       // we shall have to split the load the same way as for fields.
8875       DCHECK_LT(offset, kReferenceLoadMinFarOffset);
8876       ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
8877       __ ldr(EncodingSize(narrow ? Narrow : Wide), root_reg, MemOperand(obj, offset));
8878       EmitBakerReadBarrierBne(custom_data);
8879       __ bind(&return_address);
8880       DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
8881                 narrow ? BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_OFFSET
8882                        : BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_OFFSET);
8883     } else {
8884       // GC root loaded through a slow path for read barriers other
8885       // than Baker's.
8886       // /* GcRoot<mirror::Object>* */ root = obj + offset
8887       __ Add(root_reg, obj, offset);
8888       // /* mirror::Object* */ root = root->Read()
8889       GenerateReadBarrierForRootSlow(instruction, root, root);
8890     }
8891   } else {
8892     // Plain GC root load with no read barrier.
8893     // /* GcRoot<mirror::Object> */ root = *(obj + offset)
8894     GetAssembler()->LoadFromOffset(kLoadWord, root_reg, obj, offset);
8895     // Note that GC roots are not affected by heap poisoning, thus we
8896     // do not have to unpoison `root_reg` here.
8897   }
8898   MaybeGenerateMarkingRegisterCheck(/* code= */ 20);
8899 }
8900 
GenerateIntrinsicCasMoveWithBakerReadBarrier(vixl::aarch32::Register marked_old_value,vixl::aarch32::Register old_value)8901 void CodeGeneratorARMVIXL::GenerateIntrinsicCasMoveWithBakerReadBarrier(
8902     vixl::aarch32::Register marked_old_value,
8903     vixl::aarch32::Register old_value) {
8904   DCHECK(kEmitCompilerReadBarrier);
8905   DCHECK(kUseBakerReadBarrier);
8906 
8907   // Similar to the Baker RB path in GenerateGcRootFieldLoad(), with a MOV instead of LDR.
8908   // For low registers, we can reuse the GC root narrow entrypoint, for high registers
8909   // we use a specialized entrypoint because the register bits are 8-11 instead of 12-15.
8910   bool narrow_mov = marked_old_value.IsLow();
8911   uint32_t custom_data = narrow_mov
8912       ? EncodeBakerReadBarrierGcRootData(marked_old_value.GetCode(), /*narrow=*/ true)
8913       : EncodeBakerReadBarrierIntrinsicCasData(marked_old_value.GetCode());
8914 
8915   size_t narrow_instructions = /* CMP */ (mr.IsLow() ? 1u : 0u) + /* MOV */ (narrow_mov ? 1u : 0u);
8916   size_t wide_instructions = /* ADR+CMP+MOV+BNE */ 4u - narrow_instructions;
8917   size_t exact_size = wide_instructions * vixl32::k32BitT32InstructionSizeInBytes +
8918                       narrow_instructions * vixl32::k16BitT32InstructionSizeInBytes;
8919   ExactAssemblyScope guard(GetVIXLAssembler(), exact_size);
8920   vixl32::Label return_address;
8921   EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
8922   __ cmp(mr, Operand(0));
8923   ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
8924   __ mov(EncodingSize(narrow_mov ? Narrow : Wide), marked_old_value, old_value);
8925   EmitBakerReadBarrierBne(custom_data);
8926   __ bind(&return_address);
8927   DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
8928             narrow_mov
8929                 ? BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_OFFSET
8930                 : BAKER_MARK_INTROSPECTION_INTRINSIC_CAS_MOV_OFFSET);
8931 }
8932 
GenerateFieldLoadWithBakerReadBarrier(HInstruction * instruction,Location ref,vixl32::Register obj,const vixl32::MemOperand & src,bool needs_null_check)8933 void CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
8934                                                                  Location ref,
8935                                                                  vixl32::Register obj,
8936                                                                  const vixl32::MemOperand& src,
8937                                                                  bool needs_null_check) {
8938   DCHECK(kEmitCompilerReadBarrier);
8939   DCHECK(kUseBakerReadBarrier);
8940 
8941   // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
8942   // Marking Register) to decide whether we need to enter the slow
8943   // path to mark the reference. Then, in the slow path, check the
8944   // gray bit in the lock word of the reference's holder (`obj`) to
8945   // decide whether to mark `ref` or not.
8946   //
8947   // We use shared thunks for the slow path; shared within the method
8948   // for JIT, across methods for AOT. That thunk checks the holder
8949   // and jumps to the entrypoint if needed. If the holder is not gray,
8950   // it creates a fake dependency and returns to the LDR instruction.
8951   //
8952   //     lr = &gray_return_address;
8953   //     if (mr) {  // Thread::Current()->GetIsGcMarking()
8954   //       goto field_thunk<holder_reg, base_reg>(lr)
8955   //     }
8956   //   not_gray_return_address:
8957   //     // Original reference load. If the offset is too large to fit
8958   //     // into LDR, we use an adjusted base register here.
8959   //     HeapReference<mirror::Object> reference = *(obj+offset);
8960   //   gray_return_address:
8961 
8962   DCHECK(src.GetAddrMode() == vixl32::Offset);
8963   DCHECK_ALIGNED(src.GetOffsetImmediate(), sizeof(mirror::HeapReference<mirror::Object>));
8964   vixl32::Register ref_reg = RegisterFrom(ref, DataType::Type::kReference);
8965   bool narrow = CanEmitNarrowLdr(ref_reg, src.GetBaseRegister(), src.GetOffsetImmediate());
8966 
8967   UseScratchRegisterScope temps(GetVIXLAssembler());
8968   temps.Exclude(ip);
8969   uint32_t custom_data =
8970       EncodeBakerReadBarrierFieldData(src.GetBaseRegister().GetCode(), obj.GetCode(), narrow);
8971 
8972   {
8973     size_t narrow_instructions =
8974         /* CMP */ (mr.IsLow() ? 1u : 0u) +
8975         /* LDR+unpoison? */ (narrow ? (kPoisonHeapReferences ? 2u : 1u) : 0u);
8976     size_t wide_instructions =
8977         /* ADR+CMP+LDR+BNE+unpoison? */ (kPoisonHeapReferences ? 5u : 4u) - narrow_instructions;
8978     size_t exact_size = wide_instructions * vixl32::k32BitT32InstructionSizeInBytes +
8979                         narrow_instructions * vixl32::k16BitT32InstructionSizeInBytes;
8980     ExactAssemblyScope guard(GetVIXLAssembler(), exact_size);
8981     vixl32::Label return_address;
8982     EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
8983     __ cmp(mr, Operand(0));
8984     EmitBakerReadBarrierBne(custom_data);
8985     ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
8986     __ ldr(EncodingSize(narrow ? Narrow : Wide), ref_reg, src);
8987     if (needs_null_check) {
8988       MaybeRecordImplicitNullCheck(instruction);
8989     }
8990     // Note: We need a specific width for the unpoisoning NEG.
8991     if (kPoisonHeapReferences) {
8992       if (narrow) {
8993         // The only 16-bit encoding is T1 which sets flags outside IT block (i.e. RSBS, not RSB).
8994         __ rsbs(EncodingSize(Narrow), ref_reg, ref_reg, Operand(0));
8995       } else {
8996         __ rsb(EncodingSize(Wide), ref_reg, ref_reg, Operand(0));
8997       }
8998     }
8999     __ bind(&return_address);
9000     DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
9001               narrow ? BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET
9002                      : BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET);
9003   }
9004   MaybeGenerateMarkingRegisterCheck(/* code= */ 21, /* temp_loc= */ LocationFrom(ip));
9005 }
9006 
GenerateFieldLoadWithBakerReadBarrier(HInstruction * instruction,Location ref,vixl32::Register obj,uint32_t offset,Location maybe_temp,bool needs_null_check)9007 void CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
9008                                                                  Location ref,
9009                                                                  vixl32::Register obj,
9010                                                                  uint32_t offset,
9011                                                                  Location maybe_temp,
9012                                                                  bool needs_null_check) {
9013   DCHECK_ALIGNED(offset, sizeof(mirror::HeapReference<mirror::Object>));
9014   vixl32::Register base = obj;
9015   if (offset >= kReferenceLoadMinFarOffset) {
9016     base = RegisterFrom(maybe_temp);
9017     static_assert(IsPowerOfTwo(kReferenceLoadMinFarOffset), "Expecting a power of 2.");
9018     __ Add(base, obj, Operand(offset & ~(kReferenceLoadMinFarOffset - 1u)));
9019     offset &= (kReferenceLoadMinFarOffset - 1u);
9020   }
9021   GenerateFieldLoadWithBakerReadBarrier(
9022       instruction, ref, obj, MemOperand(base, offset), needs_null_check);
9023 }
9024 
GenerateArrayLoadWithBakerReadBarrier(Location ref,vixl32::Register obj,uint32_t data_offset,Location index,Location temp,bool needs_null_check)9025 void CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier(Location ref,
9026                                                                  vixl32::Register obj,
9027                                                                  uint32_t data_offset,
9028                                                                  Location index,
9029                                                                  Location temp,
9030                                                                  bool needs_null_check) {
9031   DCHECK(kEmitCompilerReadBarrier);
9032   DCHECK(kUseBakerReadBarrier);
9033 
9034   static_assert(
9035       sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
9036       "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
9037   ScaleFactor scale_factor = TIMES_4;
9038 
9039   // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
9040   // Marking Register) to decide whether we need to enter the slow
9041   // path to mark the reference. Then, in the slow path, check the
9042   // gray bit in the lock word of the reference's holder (`obj`) to
9043   // decide whether to mark `ref` or not.
9044   //
9045   // We use shared thunks for the slow path; shared within the method
9046   // for JIT, across methods for AOT. That thunk checks the holder
9047   // and jumps to the entrypoint if needed. If the holder is not gray,
9048   // it creates a fake dependency and returns to the LDR instruction.
9049   //
9050   //     lr = &gray_return_address;
9051   //     if (mr) {  // Thread::Current()->GetIsGcMarking()
9052   //       goto array_thunk<base_reg>(lr)
9053   //     }
9054   //   not_gray_return_address:
9055   //     // Original reference load. If the offset is too large to fit
9056   //     // into LDR, we use an adjusted base register here.
9057   //     HeapReference<mirror::Object> reference = data[index];
9058   //   gray_return_address:
9059 
9060   DCHECK(index.IsValid());
9061   vixl32::Register index_reg = RegisterFrom(index, DataType::Type::kInt32);
9062   vixl32::Register ref_reg = RegisterFrom(ref, DataType::Type::kReference);
9063   vixl32::Register data_reg = RegisterFrom(temp, DataType::Type::kInt32);  // Raw pointer.
9064 
9065   UseScratchRegisterScope temps(GetVIXLAssembler());
9066   temps.Exclude(ip);
9067   uint32_t custom_data = EncodeBakerReadBarrierArrayData(data_reg.GetCode());
9068 
9069   __ Add(data_reg, obj, Operand(data_offset));
9070   {
9071     size_t narrow_instructions = /* CMP */ (mr.IsLow() ? 1u : 0u);
9072     size_t wide_instructions =
9073         /* ADR+CMP+BNE+LDR+unpoison? */ (kPoisonHeapReferences ? 5u : 4u) - narrow_instructions;
9074     size_t exact_size = wide_instructions * vixl32::k32BitT32InstructionSizeInBytes +
9075                         narrow_instructions * vixl32::k16BitT32InstructionSizeInBytes;
9076     ExactAssemblyScope guard(GetVIXLAssembler(), exact_size);
9077     vixl32::Label return_address;
9078     EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
9079     __ cmp(mr, Operand(0));
9080     EmitBakerReadBarrierBne(custom_data);
9081     ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
9082     __ ldr(ref_reg, MemOperand(data_reg, index_reg, vixl32::LSL, scale_factor));
9083     DCHECK(!needs_null_check);  // The thunk cannot handle the null check.
9084     // Note: We need a Wide NEG for the unpoisoning.
9085     if (kPoisonHeapReferences) {
9086       __ rsb(EncodingSize(Wide), ref_reg, ref_reg, Operand(0));
9087     }
9088     __ bind(&return_address);
9089     DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
9090               BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET);
9091   }
9092   MaybeGenerateMarkingRegisterCheck(/* code= */ 22, /* temp_loc= */ LocationFrom(ip));
9093 }
9094 
MaybeGenerateMarkingRegisterCheck(int code,Location temp_loc)9095 void CodeGeneratorARMVIXL::MaybeGenerateMarkingRegisterCheck(int code, Location temp_loc) {
9096   // The following condition is a compile-time one, so it does not have a run-time cost.
9097   if (kEmitCompilerReadBarrier && kUseBakerReadBarrier && kIsDebugBuild) {
9098     // The following condition is a run-time one; it is executed after the
9099     // previous compile-time test, to avoid penalizing non-debug builds.
9100     if (GetCompilerOptions().EmitRunTimeChecksInDebugMode()) {
9101       UseScratchRegisterScope temps(GetVIXLAssembler());
9102       vixl32::Register temp = temp_loc.IsValid() ? RegisterFrom(temp_loc) : temps.Acquire();
9103       GetAssembler()->GenerateMarkingRegisterCheck(temp,
9104                                                    kMarkingRegisterCheckBreakCodeBaseCode + code);
9105     }
9106   }
9107 }
9108 
AddReadBarrierSlowPath(HInstruction * instruction,Location out,Location ref,Location obj,uint32_t offset,Location index)9109 SlowPathCodeARMVIXL* CodeGeneratorARMVIXL::AddReadBarrierSlowPath(HInstruction* instruction,
9110                                                                   Location out,
9111                                                                   Location ref,
9112                                                                   Location obj,
9113                                                                   uint32_t offset,
9114                                                                   Location index) {
9115   SlowPathCodeARMVIXL* slow_path = new (GetScopedAllocator())
9116       ReadBarrierForHeapReferenceSlowPathARMVIXL(instruction, out, ref, obj, offset, index);
9117   AddSlowPath(slow_path);
9118   return slow_path;
9119 }
9120 
GenerateReadBarrierSlow(HInstruction * instruction,Location out,Location ref,Location obj,uint32_t offset,Location index)9121 void CodeGeneratorARMVIXL::GenerateReadBarrierSlow(HInstruction* instruction,
9122                                                    Location out,
9123                                                    Location ref,
9124                                                    Location obj,
9125                                                    uint32_t offset,
9126                                                    Location index) {
9127   DCHECK(kEmitCompilerReadBarrier);
9128 
9129   // Insert a slow path based read barrier *after* the reference load.
9130   //
9131   // If heap poisoning is enabled, the unpoisoning of the loaded
9132   // reference will be carried out by the runtime within the slow
9133   // path.
9134   //
9135   // Note that `ref` currently does not get unpoisoned (when heap
9136   // poisoning is enabled), which is alright as the `ref` argument is
9137   // not used by the artReadBarrierSlow entry point.
9138   //
9139   // TODO: Unpoison `ref` when it is used by artReadBarrierSlow.
9140   SlowPathCodeARMVIXL* slow_path =
9141       AddReadBarrierSlowPath(instruction, out, ref, obj, offset, index);
9142 
9143   __ B(slow_path->GetEntryLabel());
9144   __ Bind(slow_path->GetExitLabel());
9145 }
9146 
MaybeGenerateReadBarrierSlow(HInstruction * instruction,Location out,Location ref,Location obj,uint32_t offset,Location index)9147 void CodeGeneratorARMVIXL::MaybeGenerateReadBarrierSlow(HInstruction* instruction,
9148                                                         Location out,
9149                                                         Location ref,
9150                                                         Location obj,
9151                                                         uint32_t offset,
9152                                                         Location index) {
9153   if (kEmitCompilerReadBarrier) {
9154     // Baker's read barriers shall be handled by the fast path
9155     // (CodeGeneratorARMVIXL::GenerateReferenceLoadWithBakerReadBarrier).
9156     DCHECK(!kUseBakerReadBarrier);
9157     // If heap poisoning is enabled, unpoisoning will be taken care of
9158     // by the runtime within the slow path.
9159     GenerateReadBarrierSlow(instruction, out, ref, obj, offset, index);
9160   } else if (kPoisonHeapReferences) {
9161     GetAssembler()->UnpoisonHeapReference(RegisterFrom(out));
9162   }
9163 }
9164 
GenerateReadBarrierForRootSlow(HInstruction * instruction,Location out,Location root)9165 void CodeGeneratorARMVIXL::GenerateReadBarrierForRootSlow(HInstruction* instruction,
9166                                                           Location out,
9167                                                           Location root) {
9168   DCHECK(kEmitCompilerReadBarrier);
9169 
9170   // Insert a slow path based read barrier *after* the GC root load.
9171   //
9172   // Note that GC roots are not affected by heap poisoning, so we do
9173   // not need to do anything special for this here.
9174   SlowPathCodeARMVIXL* slow_path =
9175       new (GetScopedAllocator()) ReadBarrierForRootSlowPathARMVIXL(instruction, out, root);
9176   AddSlowPath(slow_path);
9177 
9178   __ B(slow_path->GetEntryLabel());
9179   __ Bind(slow_path->GetExitLabel());
9180 }
9181 
9182 // Check if the desired_dispatch_info is supported. If it is, return it,
9183 // otherwise return a fall-back info that should be used instead.
GetSupportedInvokeStaticOrDirectDispatch(const HInvokeStaticOrDirect::DispatchInfo & desired_dispatch_info,ArtMethod * method)9184 HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARMVIXL::GetSupportedInvokeStaticOrDirectDispatch(
9185     const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
9186     ArtMethod* method) {
9187   if (method->IsIntrinsic() &&
9188       desired_dispatch_info.code_ptr_location == CodePtrLocation::kCallCriticalNative) {
9189     // As a work-around for soft-float native ABI interfering with type checks, we are
9190     // inserting fake calls to Float.floatToRawIntBits() or Double.doubleToRawLongBits()
9191     // when a float or double argument is passed in core registers but we cannot do that
9192     // for actual intrinsic implementations that expect them in FP registers. Therefore
9193     // we do not use `kCallCriticalNative` for intrinsics with FP arguments; if they are
9194     // properly intrinsified, the dispatch type does not matter anyway.
9195     ScopedObjectAccess soa(Thread::Current());
9196     uint32_t shorty_len;
9197     const char* shorty = method->GetShorty(&shorty_len);
9198     for (uint32_t i = 1; i != shorty_len; ++i) {
9199       if (shorty[i] == 'D' || shorty[i] == 'F') {
9200         HInvokeStaticOrDirect::DispatchInfo dispatch_info = desired_dispatch_info;
9201         dispatch_info.code_ptr_location = CodePtrLocation::kCallArtMethod;
9202         return dispatch_info;
9203       }
9204     }
9205   }
9206   return desired_dispatch_info;
9207 }
9208 
9209 
LoadMethod(MethodLoadKind load_kind,Location temp,HInvoke * invoke)9210 void CodeGeneratorARMVIXL::LoadMethod(MethodLoadKind load_kind, Location temp, HInvoke* invoke) {
9211   switch (load_kind) {
9212     case MethodLoadKind::kBootImageLinkTimePcRelative: {
9213       DCHECK(GetCompilerOptions().IsBootImage() || GetCompilerOptions().IsBootImageExtension());
9214       PcRelativePatchInfo* labels = NewBootImageMethodPatch(invoke->GetResolvedMethodReference());
9215       vixl32::Register temp_reg = RegisterFrom(temp);
9216       EmitMovwMovtPlaceholder(labels, temp_reg);
9217       break;
9218     }
9219     case MethodLoadKind::kBootImageRelRo: {
9220       uint32_t boot_image_offset = GetBootImageOffset(invoke);
9221       LoadBootImageRelRoEntry(RegisterFrom(temp), boot_image_offset);
9222       break;
9223     }
9224     case MethodLoadKind::kBssEntry: {
9225       PcRelativePatchInfo* labels = NewMethodBssEntryPatch(invoke->GetMethodReference());
9226       vixl32::Register temp_reg = RegisterFrom(temp);
9227       EmitMovwMovtPlaceholder(labels, temp_reg);
9228       // All aligned loads are implicitly atomic consume operations on ARM.
9229       GetAssembler()->LoadFromOffset(kLoadWord, temp_reg, temp_reg, /* offset*/ 0);
9230       break;
9231     }
9232     case MethodLoadKind::kJitDirectAddress: {
9233       __ Mov(RegisterFrom(temp), Operand::From(invoke->GetResolvedMethod()));
9234       break;
9235     }
9236     case MethodLoadKind::kRuntimeCall: {
9237       // Test situation, don't do anything.
9238       break;
9239     }
9240     default: {
9241       LOG(FATAL) << "Load kind should have already been handled " << load_kind;
9242       UNREACHABLE();
9243     }
9244   }
9245 }
9246 
GenerateStaticOrDirectCall(HInvokeStaticOrDirect * invoke,Location temp,SlowPathCode * slow_path)9247 void CodeGeneratorARMVIXL::GenerateStaticOrDirectCall(
9248     HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path) {
9249   Location callee_method = temp;  // For all kinds except kRecursive, callee will be in temp.
9250   switch (invoke->GetMethodLoadKind()) {
9251     case MethodLoadKind::kStringInit: {
9252       uint32_t offset =
9253           GetThreadOffset<kArmPointerSize>(invoke->GetStringInitEntryPoint()).Int32Value();
9254       // temp = thread->string_init_entrypoint
9255       GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(temp), tr, offset);
9256       break;
9257     }
9258     case MethodLoadKind::kRecursive: {
9259       callee_method = invoke->GetLocations()->InAt(invoke->GetCurrentMethodIndex());
9260       break;
9261     }
9262     case MethodLoadKind::kRuntimeCall: {
9263       GenerateInvokeStaticOrDirectRuntimeCall(invoke, temp, slow_path);
9264       return;  // No code pointer retrieval; the runtime performs the call directly.
9265     }
9266     case MethodLoadKind::kBootImageLinkTimePcRelative:
9267       // Note: Unlike arm64, x86 and x86-64, we do not avoid the materialization of method
9268       // pointer for kCallCriticalNative because it would not save us an instruction from
9269       // the current sequence MOVW+MOVT+ADD(pc)+LDR+BL. The ADD(pc) separates the patched
9270       // offset instructions MOVW+MOVT from the entrypoint load, so they cannot be fused.
9271       FALLTHROUGH_INTENDED;
9272     default: {
9273       LoadMethod(invoke->GetMethodLoadKind(), temp, invoke);
9274       break;
9275     }
9276   }
9277 
9278   auto call_code_pointer_member = [&](MemberOffset offset) {
9279     // LR = callee_method->member;
9280     GetAssembler()->LoadFromOffset(kLoadWord, lr, RegisterFrom(callee_method), offset.Int32Value());
9281     {
9282       // Use a scope to help guarantee that `RecordPcInfo()` records the correct pc.
9283       // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
9284       ExactAssemblyScope aas(GetVIXLAssembler(),
9285                              vixl32::k16BitT32InstructionSizeInBytes,
9286                              CodeBufferCheckScope::kExactSize);
9287       // LR()
9288       __ blx(lr);
9289       RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
9290     }
9291   };
9292   switch (invoke->GetCodePtrLocation()) {
9293     case CodePtrLocation::kCallSelf:
9294       {
9295         DCHECK(!GetGraph()->HasShouldDeoptimizeFlag());
9296         // Use a scope to help guarantee that `RecordPcInfo()` records the correct pc.
9297         ExactAssemblyScope aas(GetVIXLAssembler(),
9298                                vixl32::k32BitT32InstructionSizeInBytes,
9299                                CodeBufferCheckScope::kMaximumSize);
9300         __ bl(GetFrameEntryLabel());
9301         RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
9302       }
9303       break;
9304     case CodePtrLocation::kCallCriticalNative: {
9305       size_t out_frame_size =
9306           PrepareCriticalNativeCall<CriticalNativeCallingConventionVisitorARMVIXL,
9307                                     kAapcsStackAlignment,
9308                                     GetCriticalNativeDirectCallFrameSize>(invoke);
9309       call_code_pointer_member(ArtMethod::EntryPointFromJniOffset(kArmPointerSize));
9310       // Move the result when needed due to native and managed ABI mismatch.
9311       switch (invoke->GetType()) {
9312         case DataType::Type::kFloat32:
9313           __ Vmov(s0, r0);
9314           break;
9315         case DataType::Type::kFloat64:
9316           __ Vmov(d0, r0, r1);
9317           break;
9318         case DataType::Type::kBool:
9319         case DataType::Type::kInt8:
9320         case DataType::Type::kUint16:
9321         case DataType::Type::kInt16:
9322         case DataType::Type::kInt32:
9323         case DataType::Type::kInt64:
9324         case DataType::Type::kVoid:
9325           break;
9326         default:
9327           DCHECK(false) << invoke->GetType();
9328           break;
9329       }
9330       if (out_frame_size != 0u) {
9331         DecreaseFrame(out_frame_size);
9332       }
9333       break;
9334     }
9335     case CodePtrLocation::kCallArtMethod:
9336       call_code_pointer_member(ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize));
9337       break;
9338   }
9339 
9340   DCHECK(!IsLeafMethod());
9341 }
9342 
GenerateVirtualCall(HInvokeVirtual * invoke,Location temp_location,SlowPathCode * slow_path)9343 void CodeGeneratorARMVIXL::GenerateVirtualCall(
9344     HInvokeVirtual* invoke, Location temp_location, SlowPathCode* slow_path) {
9345   vixl32::Register temp = RegisterFrom(temp_location);
9346   uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
9347       invoke->GetVTableIndex(), kArmPointerSize).Uint32Value();
9348 
9349   // Use the calling convention instead of the location of the receiver, as
9350   // intrinsics may have put the receiver in a different register. In the intrinsics
9351   // slow path, the arguments have been moved to the right place, so here we are
9352   // guaranteed that the receiver is the first register of the calling convention.
9353   InvokeDexCallingConventionARMVIXL calling_convention;
9354   vixl32::Register receiver = calling_convention.GetRegisterAt(0);
9355   uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
9356   {
9357     // Make sure the pc is recorded immediately after the `ldr` instruction.
9358     ExactAssemblyScope aas(GetVIXLAssembler(),
9359                            vixl32::kMaxInstructionSizeInBytes,
9360                            CodeBufferCheckScope::kMaximumSize);
9361     // /* HeapReference<Class> */ temp = receiver->klass_
9362     __ ldr(temp, MemOperand(receiver, class_offset));
9363     MaybeRecordImplicitNullCheck(invoke);
9364   }
9365   // Instead of simply (possibly) unpoisoning `temp` here, we should
9366   // emit a read barrier for the previous class reference load.
9367   // However this is not required in practice, as this is an
9368   // intermediate/temporary reference and because the current
9369   // concurrent copying collector keeps the from-space memory
9370   // intact/accessible until the end of the marking phase (the
9371   // concurrent copying collector may not in the future).
9372   GetAssembler()->MaybeUnpoisonHeapReference(temp);
9373 
9374   // If we're compiling baseline, update the inline cache.
9375   MaybeGenerateInlineCacheCheck(invoke, temp);
9376 
9377   // temp = temp->GetMethodAt(method_offset);
9378   uint32_t entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(
9379       kArmPointerSize).Int32Value();
9380   GetAssembler()->LoadFromOffset(kLoadWord, temp, temp, method_offset);
9381   // LR = temp->GetEntryPoint();
9382   GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, entry_point);
9383   {
9384     // Use a scope to help guarantee that `RecordPcInfo()` records the correct pc.
9385     // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
9386     ExactAssemblyScope aas(GetVIXLAssembler(),
9387                            vixl32::k16BitT32InstructionSizeInBytes,
9388                            CodeBufferCheckScope::kExactSize);
9389     // LR();
9390     __ blx(lr);
9391     RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
9392   }
9393 }
9394 
NewBootImageIntrinsicPatch(uint32_t intrinsic_data)9395 CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewBootImageIntrinsicPatch(
9396     uint32_t intrinsic_data) {
9397   return NewPcRelativePatch(/* dex_file= */ nullptr, intrinsic_data, &boot_image_other_patches_);
9398 }
9399 
NewBootImageRelRoPatch(uint32_t boot_image_offset)9400 CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewBootImageRelRoPatch(
9401     uint32_t boot_image_offset) {
9402   return NewPcRelativePatch(/* dex_file= */ nullptr,
9403                             boot_image_offset,
9404                             &boot_image_other_patches_);
9405 }
9406 
NewBootImageMethodPatch(MethodReference target_method)9407 CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewBootImageMethodPatch(
9408     MethodReference target_method) {
9409   return NewPcRelativePatch(
9410       target_method.dex_file, target_method.index, &boot_image_method_patches_);
9411 }
9412 
NewMethodBssEntryPatch(MethodReference target_method)9413 CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewMethodBssEntryPatch(
9414     MethodReference target_method) {
9415   return NewPcRelativePatch(
9416       target_method.dex_file, target_method.index, &method_bss_entry_patches_);
9417 }
9418 
NewBootImageTypePatch(const DexFile & dex_file,dex::TypeIndex type_index)9419 CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewBootImageTypePatch(
9420     const DexFile& dex_file, dex::TypeIndex type_index) {
9421   return NewPcRelativePatch(&dex_file, type_index.index_, &boot_image_type_patches_);
9422 }
9423 
NewTypeBssEntryPatch(HLoadClass * load_class)9424 CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewTypeBssEntryPatch(
9425     HLoadClass* load_class) {
9426   const DexFile& dex_file = load_class->GetDexFile();
9427   dex::TypeIndex type_index = load_class->GetTypeIndex();
9428   ArenaDeque<PcRelativePatchInfo>* patches = nullptr;
9429   switch (load_class->GetLoadKind()) {
9430     case HLoadClass::LoadKind::kBssEntry:
9431       patches = &type_bss_entry_patches_;
9432       break;
9433     case HLoadClass::LoadKind::kBssEntryPublic:
9434       patches = &public_type_bss_entry_patches_;
9435       break;
9436     case HLoadClass::LoadKind::kBssEntryPackage:
9437       patches = &package_type_bss_entry_patches_;
9438       break;
9439     default:
9440       LOG(FATAL) << "Unexpected load kind: " << load_class->GetLoadKind();
9441       UNREACHABLE();
9442   }
9443   return NewPcRelativePatch(&dex_file, type_index.index_, patches);
9444 }
9445 
NewBootImageStringPatch(const DexFile & dex_file,dex::StringIndex string_index)9446 CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewBootImageStringPatch(
9447     const DexFile& dex_file, dex::StringIndex string_index) {
9448   return NewPcRelativePatch(&dex_file, string_index.index_, &boot_image_string_patches_);
9449 }
9450 
NewStringBssEntryPatch(const DexFile & dex_file,dex::StringIndex string_index)9451 CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewStringBssEntryPatch(
9452     const DexFile& dex_file, dex::StringIndex string_index) {
9453   return NewPcRelativePatch(&dex_file, string_index.index_, &string_bss_entry_patches_);
9454 }
9455 
NewPcRelativePatch(const DexFile * dex_file,uint32_t offset_or_index,ArenaDeque<PcRelativePatchInfo> * patches)9456 CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewPcRelativePatch(
9457     const DexFile* dex_file, uint32_t offset_or_index, ArenaDeque<PcRelativePatchInfo>* patches) {
9458   patches->emplace_back(dex_file, offset_or_index);
9459   return &patches->back();
9460 }
9461 
EmitEntrypointThunkCall(ThreadOffset32 entrypoint_offset)9462 void CodeGeneratorARMVIXL::EmitEntrypointThunkCall(ThreadOffset32 entrypoint_offset) {
9463   DCHECK(!__ AllowMacroInstructions());  // In ExactAssemblyScope.
9464   DCHECK(!GetCompilerOptions().IsJitCompiler());
9465   call_entrypoint_patches_.emplace_back(/*dex_file*/ nullptr, entrypoint_offset.Uint32Value());
9466   vixl::aarch32::Label* bl_label = &call_entrypoint_patches_.back().label;
9467   __ bind(bl_label);
9468   vixl32::Label placeholder_label;
9469   __ bl(&placeholder_label);  // Placeholder, patched at link-time.
9470   __ bind(&placeholder_label);
9471 }
9472 
EmitBakerReadBarrierBne(uint32_t custom_data)9473 void CodeGeneratorARMVIXL::EmitBakerReadBarrierBne(uint32_t custom_data) {
9474   DCHECK(!__ AllowMacroInstructions());  // In ExactAssemblyScope.
9475   if (GetCompilerOptions().IsJitCompiler()) {
9476     auto it = jit_baker_read_barrier_slow_paths_.FindOrAdd(custom_data);
9477     vixl::aarch32::Label* slow_path_entry = &it->second.label;
9478     __ b(ne, EncodingSize(Wide), slow_path_entry);
9479   } else {
9480     baker_read_barrier_patches_.emplace_back(custom_data);
9481     vixl::aarch32::Label* patch_label = &baker_read_barrier_patches_.back().label;
9482     __ bind(patch_label);
9483     vixl32::Label placeholder_label;
9484     __ b(ne, EncodingSize(Wide), &placeholder_label);  // Placeholder, patched at link-time.
9485     __ bind(&placeholder_label);
9486   }
9487 }
9488 
DeduplicateBootImageAddressLiteral(uint32_t address)9489 VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateBootImageAddressLiteral(uint32_t address) {
9490   return DeduplicateUint32Literal(address, &uint32_literals_);
9491 }
9492 
DeduplicateJitStringLiteral(const DexFile & dex_file,dex::StringIndex string_index,Handle<mirror::String> handle)9493 VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateJitStringLiteral(
9494     const DexFile& dex_file,
9495     dex::StringIndex string_index,
9496     Handle<mirror::String> handle) {
9497   ReserveJitStringRoot(StringReference(&dex_file, string_index), handle);
9498   return jit_string_patches_.GetOrCreate(
9499       StringReference(&dex_file, string_index),
9500       [this]() {
9501         return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* value= */ 0u);
9502       });
9503 }
9504 
DeduplicateJitClassLiteral(const DexFile & dex_file,dex::TypeIndex type_index,Handle<mirror::Class> handle)9505 VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateJitClassLiteral(const DexFile& dex_file,
9506                                                       dex::TypeIndex type_index,
9507                                                       Handle<mirror::Class> handle) {
9508   ReserveJitClassRoot(TypeReference(&dex_file, type_index), handle);
9509   return jit_class_patches_.GetOrCreate(
9510       TypeReference(&dex_file, type_index),
9511       [this]() {
9512         return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* value= */ 0u);
9513       });
9514 }
9515 
LoadBootImageRelRoEntry(vixl32::Register reg,uint32_t boot_image_offset)9516 void CodeGeneratorARMVIXL::LoadBootImageRelRoEntry(vixl32::Register reg,
9517                                                    uint32_t boot_image_offset) {
9518   CodeGeneratorARMVIXL::PcRelativePatchInfo* labels = NewBootImageRelRoPatch(boot_image_offset);
9519   EmitMovwMovtPlaceholder(labels, reg);
9520   __ Ldr(reg, MemOperand(reg, /*offset=*/ 0));
9521 }
9522 
LoadBootImageAddress(vixl32::Register reg,uint32_t boot_image_reference)9523 void CodeGeneratorARMVIXL::LoadBootImageAddress(vixl32::Register reg,
9524                                                 uint32_t boot_image_reference) {
9525   if (GetCompilerOptions().IsBootImage()) {
9526     CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
9527         NewBootImageIntrinsicPatch(boot_image_reference);
9528     EmitMovwMovtPlaceholder(labels, reg);
9529   } else if (GetCompilerOptions().GetCompilePic()) {
9530     LoadBootImageRelRoEntry(reg, boot_image_reference);
9531   } else {
9532     DCHECK(GetCompilerOptions().IsJitCompiler());
9533     gc::Heap* heap = Runtime::Current()->GetHeap();
9534     DCHECK(!heap->GetBootImageSpaces().empty());
9535     uintptr_t address =
9536         reinterpret_cast<uintptr_t>(heap->GetBootImageSpaces()[0]->Begin() + boot_image_reference);
9537     __ Ldr(reg, DeduplicateBootImageAddressLiteral(dchecked_integral_cast<uint32_t>(address)));
9538   }
9539 }
9540 
LoadTypeForBootImageIntrinsic(vixl::aarch32::Register reg,TypeReference target_type)9541 void CodeGeneratorARMVIXL::LoadTypeForBootImageIntrinsic(vixl::aarch32::Register reg,
9542                                                          TypeReference target_type) {
9543   // Load the class the same way as for HLoadClass::LoadKind::kBootImageLinkTimePcRelative.
9544   DCHECK(GetCompilerOptions().IsBootImage() || GetCompilerOptions().IsBootImageExtension());
9545   PcRelativePatchInfo* labels =
9546       NewBootImageTypePatch(*target_type.dex_file, target_type.TypeIndex());
9547   EmitMovwMovtPlaceholder(labels, reg);
9548 }
9549 
LoadIntrinsicDeclaringClass(vixl32::Register reg,HInvoke * invoke)9550 void CodeGeneratorARMVIXL::LoadIntrinsicDeclaringClass(vixl32::Register reg, HInvoke* invoke) {
9551   DCHECK_NE(invoke->GetIntrinsic(), Intrinsics::kNone);
9552   if (GetCompilerOptions().IsBootImage()) {
9553     MethodReference target_method = invoke->GetResolvedMethodReference();
9554     dex::TypeIndex type_idx = target_method.dex_file->GetMethodId(target_method.index).class_idx_;
9555     LoadTypeForBootImageIntrinsic(reg, TypeReference(target_method.dex_file, type_idx));
9556   } else {
9557     uint32_t boot_image_offset = GetBootImageOffsetOfIntrinsicDeclaringClass(invoke);
9558     LoadBootImageAddress(reg, boot_image_offset);
9559   }
9560 }
9561 
LoadClassRootForIntrinsic(vixl::aarch32::Register reg,ClassRoot class_root)9562 void CodeGeneratorARMVIXL::LoadClassRootForIntrinsic(vixl::aarch32::Register reg,
9563                                                      ClassRoot class_root) {
9564   if (GetCompilerOptions().IsBootImage()) {
9565     ScopedObjectAccess soa(Thread::Current());
9566     ObjPtr<mirror::Class> klass = GetClassRoot(class_root);
9567     TypeReference target_type(&klass->GetDexFile(), klass->GetDexTypeIndex());
9568     LoadTypeForBootImageIntrinsic(reg, target_type);
9569   } else {
9570     uint32_t boot_image_offset = GetBootImageOffset(class_root);
9571     LoadBootImageAddress(reg, boot_image_offset);
9572   }
9573 }
9574 
9575 template <linker::LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)>
EmitPcRelativeLinkerPatches(const ArenaDeque<PcRelativePatchInfo> & infos,ArenaVector<linker::LinkerPatch> * linker_patches)9576 inline void CodeGeneratorARMVIXL::EmitPcRelativeLinkerPatches(
9577     const ArenaDeque<PcRelativePatchInfo>& infos,
9578     ArenaVector<linker::LinkerPatch>* linker_patches) {
9579   for (const PcRelativePatchInfo& info : infos) {
9580     const DexFile* dex_file = info.target_dex_file;
9581     size_t offset_or_index = info.offset_or_index;
9582     DCHECK(info.add_pc_label.IsBound());
9583     uint32_t add_pc_offset = dchecked_integral_cast<uint32_t>(info.add_pc_label.GetLocation());
9584     // Add MOVW patch.
9585     DCHECK(info.movw_label.IsBound());
9586     uint32_t movw_offset = dchecked_integral_cast<uint32_t>(info.movw_label.GetLocation());
9587     linker_patches->push_back(Factory(movw_offset, dex_file, add_pc_offset, offset_or_index));
9588     // Add MOVT patch.
9589     DCHECK(info.movt_label.IsBound());
9590     uint32_t movt_offset = dchecked_integral_cast<uint32_t>(info.movt_label.GetLocation());
9591     linker_patches->push_back(Factory(movt_offset, dex_file, add_pc_offset, offset_or_index));
9592   }
9593 }
9594 
9595 template <linker::LinkerPatch (*Factory)(size_t, uint32_t, uint32_t)>
NoDexFileAdapter(size_t literal_offset,const DexFile * target_dex_file,uint32_t pc_insn_offset,uint32_t boot_image_offset)9596 linker::LinkerPatch NoDexFileAdapter(size_t literal_offset,
9597                                      const DexFile* target_dex_file,
9598                                      uint32_t pc_insn_offset,
9599                                      uint32_t boot_image_offset) {
9600   DCHECK(target_dex_file == nullptr);  // Unused for these patches, should be null.
9601   return Factory(literal_offset, pc_insn_offset, boot_image_offset);
9602 }
9603 
EmitLinkerPatches(ArenaVector<linker::LinkerPatch> * linker_patches)9604 void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* linker_patches) {
9605   DCHECK(linker_patches->empty());
9606   size_t size =
9607       /* MOVW+MOVT for each entry */ 2u * boot_image_method_patches_.size() +
9608       /* MOVW+MOVT for each entry */ 2u * method_bss_entry_patches_.size() +
9609       /* MOVW+MOVT for each entry */ 2u * boot_image_type_patches_.size() +
9610       /* MOVW+MOVT for each entry */ 2u * type_bss_entry_patches_.size() +
9611       /* MOVW+MOVT for each entry */ 2u * public_type_bss_entry_patches_.size() +
9612       /* MOVW+MOVT for each entry */ 2u * package_type_bss_entry_patches_.size() +
9613       /* MOVW+MOVT for each entry */ 2u * boot_image_string_patches_.size() +
9614       /* MOVW+MOVT for each entry */ 2u * string_bss_entry_patches_.size() +
9615       /* MOVW+MOVT for each entry */ 2u * boot_image_other_patches_.size() +
9616       call_entrypoint_patches_.size() +
9617       baker_read_barrier_patches_.size();
9618   linker_patches->reserve(size);
9619   if (GetCompilerOptions().IsBootImage() || GetCompilerOptions().IsBootImageExtension()) {
9620     EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeMethodPatch>(
9621         boot_image_method_patches_, linker_patches);
9622     EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeTypePatch>(
9623         boot_image_type_patches_, linker_patches);
9624     EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeStringPatch>(
9625         boot_image_string_patches_, linker_patches);
9626   } else {
9627     DCHECK(boot_image_method_patches_.empty());
9628     DCHECK(boot_image_type_patches_.empty());
9629     DCHECK(boot_image_string_patches_.empty());
9630   }
9631   if (GetCompilerOptions().IsBootImage()) {
9632     EmitPcRelativeLinkerPatches<NoDexFileAdapter<linker::LinkerPatch::IntrinsicReferencePatch>>(
9633         boot_image_other_patches_, linker_patches);
9634   } else {
9635     EmitPcRelativeLinkerPatches<NoDexFileAdapter<linker::LinkerPatch::DataBimgRelRoPatch>>(
9636         boot_image_other_patches_, linker_patches);
9637   }
9638   EmitPcRelativeLinkerPatches<linker::LinkerPatch::MethodBssEntryPatch>(
9639       method_bss_entry_patches_, linker_patches);
9640   EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeBssEntryPatch>(
9641       type_bss_entry_patches_, linker_patches);
9642   EmitPcRelativeLinkerPatches<linker::LinkerPatch::PublicTypeBssEntryPatch>(
9643       public_type_bss_entry_patches_, linker_patches);
9644   EmitPcRelativeLinkerPatches<linker::LinkerPatch::PackageTypeBssEntryPatch>(
9645       package_type_bss_entry_patches_, linker_patches);
9646   EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringBssEntryPatch>(
9647       string_bss_entry_patches_, linker_patches);
9648   for (const PatchInfo<vixl32::Label>& info : call_entrypoint_patches_) {
9649     DCHECK(info.target_dex_file == nullptr);
9650     linker_patches->push_back(linker::LinkerPatch::CallEntrypointPatch(
9651         info.label.GetLocation(), info.offset_or_index));
9652   }
9653   for (const BakerReadBarrierPatchInfo& info : baker_read_barrier_patches_) {
9654     linker_patches->push_back(linker::LinkerPatch::BakerReadBarrierBranchPatch(
9655         info.label.GetLocation(), info.custom_data));
9656   }
9657   DCHECK_EQ(size, linker_patches->size());
9658 }
9659 
NeedsThunkCode(const linker::LinkerPatch & patch) const9660 bool CodeGeneratorARMVIXL::NeedsThunkCode(const linker::LinkerPatch& patch) const {
9661   return patch.GetType() == linker::LinkerPatch::Type::kCallEntrypoint ||
9662          patch.GetType() == linker::LinkerPatch::Type::kBakerReadBarrierBranch ||
9663          patch.GetType() == linker::LinkerPatch::Type::kCallRelative;
9664 }
9665 
EmitThunkCode(const linker::LinkerPatch & patch,ArenaVector<uint8_t> * code,std::string * debug_name)9666 void CodeGeneratorARMVIXL::EmitThunkCode(const linker::LinkerPatch& patch,
9667                                          /*out*/ ArenaVector<uint8_t>* code,
9668                                          /*out*/ std::string* debug_name) {
9669   arm::ArmVIXLAssembler assembler(GetGraph()->GetAllocator());
9670   switch (patch.GetType()) {
9671     case linker::LinkerPatch::Type::kCallRelative: {
9672       // The thunk just uses the entry point in the ArtMethod. This works even for calls
9673       // to the generic JNI and interpreter trampolines.
9674       MemberOffset offset = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize);
9675       assembler.LoadFromOffset(arm::kLoadWord, vixl32::pc, vixl32::r0, offset.Int32Value());
9676       assembler.GetVIXLAssembler()->Bkpt(0);
9677       if (debug_name != nullptr && GetCompilerOptions().GenerateAnyDebugInfo()) {
9678         *debug_name = "MethodCallThunk";
9679       }
9680       break;
9681     }
9682     case linker::LinkerPatch::Type::kCallEntrypoint: {
9683       assembler.LoadFromOffset(arm::kLoadWord, vixl32::pc, tr, patch.EntrypointOffset());
9684       assembler.GetVIXLAssembler()->Bkpt(0);
9685       if (debug_name != nullptr && GetCompilerOptions().GenerateAnyDebugInfo()) {
9686         *debug_name = "EntrypointCallThunk_" + std::to_string(patch.EntrypointOffset());
9687       }
9688       break;
9689     }
9690     case linker::LinkerPatch::Type::kBakerReadBarrierBranch: {
9691       DCHECK_EQ(patch.GetBakerCustomValue2(), 0u);
9692       CompileBakerReadBarrierThunk(assembler, patch.GetBakerCustomValue1(), debug_name);
9693       break;
9694     }
9695     default:
9696       LOG(FATAL) << "Unexpected patch type " << patch.GetType();
9697       UNREACHABLE();
9698   }
9699 
9700   // Ensure we emit the literal pool if any.
9701   assembler.FinalizeCode();
9702   code->resize(assembler.CodeSize());
9703   MemoryRegion code_region(code->data(), code->size());
9704   assembler.FinalizeInstructions(code_region);
9705 }
9706 
DeduplicateUint32Literal(uint32_t value,Uint32ToLiteralMap * map)9707 VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateUint32Literal(
9708     uint32_t value,
9709     Uint32ToLiteralMap* map) {
9710   return map->GetOrCreate(
9711       value,
9712       [this, value]() {
9713         return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* value= */ value);
9714       });
9715 }
9716 
VisitMultiplyAccumulate(HMultiplyAccumulate * instr)9717 void LocationsBuilderARMVIXL::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
9718   LocationSummary* locations =
9719       new (GetGraph()->GetAllocator()) LocationSummary(instr, LocationSummary::kNoCall);
9720   locations->SetInAt(HMultiplyAccumulate::kInputAccumulatorIndex,
9721                      Location::RequiresRegister());
9722   locations->SetInAt(HMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister());
9723   locations->SetInAt(HMultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister());
9724   locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
9725 }
9726 
VisitMultiplyAccumulate(HMultiplyAccumulate * instr)9727 void InstructionCodeGeneratorARMVIXL::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
9728   vixl32::Register res = OutputRegister(instr);
9729   vixl32::Register accumulator =
9730       InputRegisterAt(instr, HMultiplyAccumulate::kInputAccumulatorIndex);
9731   vixl32::Register mul_left =
9732       InputRegisterAt(instr, HMultiplyAccumulate::kInputMulLeftIndex);
9733   vixl32::Register mul_right =
9734       InputRegisterAt(instr, HMultiplyAccumulate::kInputMulRightIndex);
9735 
9736   if (instr->GetOpKind() == HInstruction::kAdd) {
9737     __ Mla(res, mul_left, mul_right, accumulator);
9738   } else {
9739     __ Mls(res, mul_left, mul_right, accumulator);
9740   }
9741 }
9742 
VisitBoundType(HBoundType * instruction ATTRIBUTE_UNUSED)9743 void LocationsBuilderARMVIXL::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
9744   // Nothing to do, this should be removed during prepare for register allocator.
9745   LOG(FATAL) << "Unreachable";
9746 }
9747 
VisitBoundType(HBoundType * instruction ATTRIBUTE_UNUSED)9748 void InstructionCodeGeneratorARMVIXL::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
9749   // Nothing to do, this should be removed during prepare for register allocator.
9750   LOG(FATAL) << "Unreachable";
9751 }
9752 
9753 // Simple implementation of packed switch - generate cascaded compare/jumps.
VisitPackedSwitch(HPackedSwitch * switch_instr)9754 void LocationsBuilderARMVIXL::VisitPackedSwitch(HPackedSwitch* switch_instr) {
9755   LocationSummary* locations =
9756       new (GetGraph()->GetAllocator()) LocationSummary(switch_instr, LocationSummary::kNoCall);
9757   locations->SetInAt(0, Location::RequiresRegister());
9758   if (switch_instr->GetNumEntries() > kPackedSwitchCompareJumpThreshold &&
9759       codegen_->GetAssembler()->GetVIXLAssembler()->IsUsingT32()) {
9760     locations->AddTemp(Location::RequiresRegister());  // We need a temp for the table base.
9761     if (switch_instr->GetStartValue() != 0) {
9762       locations->AddTemp(Location::RequiresRegister());  // We need a temp for the bias.
9763     }
9764   }
9765 }
9766 
9767 // TODO(VIXL): Investigate and reach the parity with old arm codegen.
VisitPackedSwitch(HPackedSwitch * switch_instr)9768 void InstructionCodeGeneratorARMVIXL::VisitPackedSwitch(HPackedSwitch* switch_instr) {
9769   int32_t lower_bound = switch_instr->GetStartValue();
9770   uint32_t num_entries = switch_instr->GetNumEntries();
9771   LocationSummary* locations = switch_instr->GetLocations();
9772   vixl32::Register value_reg = InputRegisterAt(switch_instr, 0);
9773   HBasicBlock* default_block = switch_instr->GetDefaultBlock();
9774 
9775   if (num_entries <= kPackedSwitchCompareJumpThreshold ||
9776       !codegen_->GetAssembler()->GetVIXLAssembler()->IsUsingT32()) {
9777     // Create a series of compare/jumps.
9778     UseScratchRegisterScope temps(GetVIXLAssembler());
9779     vixl32::Register temp_reg = temps.Acquire();
9780     // Note: It is fine for the below AddConstantSetFlags() using IP register to temporarily store
9781     // the immediate, because IP is used as the destination register. For the other
9782     // AddConstantSetFlags() and GenerateCompareWithImmediate(), the immediate values are constant,
9783     // and they can be encoded in the instruction without making use of IP register.
9784     __ Adds(temp_reg, value_reg, -lower_bound);
9785 
9786     const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors();
9787     // Jump to successors[0] if value == lower_bound.
9788     __ B(eq, codegen_->GetLabelOf(successors[0]));
9789     int32_t last_index = 0;
9790     for (; num_entries - last_index > 2; last_index += 2) {
9791       __ Adds(temp_reg, temp_reg, -2);
9792       // Jump to successors[last_index + 1] if value < case_value[last_index + 2].
9793       __ B(lo, codegen_->GetLabelOf(successors[last_index + 1]));
9794       // Jump to successors[last_index + 2] if value == case_value[last_index + 2].
9795       __ B(eq, codegen_->GetLabelOf(successors[last_index + 2]));
9796     }
9797     if (num_entries - last_index == 2) {
9798       // The last missing case_value.
9799       __ Cmp(temp_reg, 1);
9800       __ B(eq, codegen_->GetLabelOf(successors[last_index + 1]));
9801     }
9802 
9803     // And the default for any other value.
9804     if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) {
9805       __ B(codegen_->GetLabelOf(default_block));
9806     }
9807   } else {
9808     // Create a table lookup.
9809     vixl32::Register table_base = RegisterFrom(locations->GetTemp(0));
9810 
9811     JumpTableARMVIXL* jump_table = codegen_->CreateJumpTable(switch_instr);
9812 
9813     // Remove the bias.
9814     vixl32::Register key_reg;
9815     if (lower_bound != 0) {
9816       key_reg = RegisterFrom(locations->GetTemp(1));
9817       __ Sub(key_reg, value_reg, lower_bound);
9818     } else {
9819       key_reg = value_reg;
9820     }
9821 
9822     // Check whether the value is in the table, jump to default block if not.
9823     __ Cmp(key_reg, num_entries - 1);
9824     __ B(hi, codegen_->GetLabelOf(default_block));
9825 
9826     UseScratchRegisterScope temps(GetVIXLAssembler());
9827     vixl32::Register jump_offset = temps.Acquire();
9828 
9829     // Load jump offset from the table.
9830     {
9831       const size_t jump_size = switch_instr->GetNumEntries() * sizeof(int32_t);
9832       ExactAssemblyScope aas(GetVIXLAssembler(),
9833                              (vixl32::kMaxInstructionSizeInBytes * 4) + jump_size,
9834                              CodeBufferCheckScope::kMaximumSize);
9835       __ adr(table_base, jump_table->GetTableStartLabel());
9836       __ ldr(jump_offset, MemOperand(table_base, key_reg, vixl32::LSL, 2));
9837 
9838       // Jump to target block by branching to table_base(pc related) + offset.
9839       vixl32::Register target_address = table_base;
9840       __ add(target_address, table_base, jump_offset);
9841       __ bx(target_address);
9842 
9843       jump_table->EmitTable(codegen_);
9844     }
9845   }
9846 }
9847 
9848 // Copy the result of a call into the given target.
MoveFromReturnRegister(Location trg,DataType::Type type)9849 void CodeGeneratorARMVIXL::MoveFromReturnRegister(Location trg, DataType::Type type) {
9850   if (!trg.IsValid()) {
9851     DCHECK_EQ(type, DataType::Type::kVoid);
9852     return;
9853   }
9854 
9855   DCHECK_NE(type, DataType::Type::kVoid);
9856 
9857   Location return_loc = InvokeDexCallingConventionVisitorARMVIXL().GetReturnLocation(type);
9858   if (return_loc.Equals(trg)) {
9859     return;
9860   }
9861 
9862   // Let the parallel move resolver take care of all of this.
9863   HParallelMove parallel_move(GetGraph()->GetAllocator());
9864   parallel_move.AddMove(return_loc, trg, type, nullptr);
9865   GetMoveResolver()->EmitNativeCode(&parallel_move);
9866 }
9867 
VisitClassTableGet(HClassTableGet * instruction)9868 void LocationsBuilderARMVIXL::VisitClassTableGet(HClassTableGet* instruction) {
9869   LocationSummary* locations =
9870       new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
9871   locations->SetInAt(0, Location::RequiresRegister());
9872   locations->SetOut(Location::RequiresRegister());
9873 }
9874 
VisitClassTableGet(HClassTableGet * instruction)9875 void InstructionCodeGeneratorARMVIXL::VisitClassTableGet(HClassTableGet* instruction) {
9876   if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) {
9877     uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
9878         instruction->GetIndex(), kArmPointerSize).SizeValue();
9879     GetAssembler()->LoadFromOffset(kLoadWord,
9880                                    OutputRegister(instruction),
9881                                    InputRegisterAt(instruction, 0),
9882                                    method_offset);
9883   } else {
9884     uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement(
9885         instruction->GetIndex(), kArmPointerSize));
9886     GetAssembler()->LoadFromOffset(kLoadWord,
9887                                    OutputRegister(instruction),
9888                                    InputRegisterAt(instruction, 0),
9889                                    mirror::Class::ImtPtrOffset(kArmPointerSize).Uint32Value());
9890     GetAssembler()->LoadFromOffset(kLoadWord,
9891                                    OutputRegister(instruction),
9892                                    OutputRegister(instruction),
9893                                    method_offset);
9894   }
9895 }
9896 
PatchJitRootUse(uint8_t * code,const uint8_t * roots_data,VIXLUInt32Literal * literal,uint64_t index_in_table)9897 static void PatchJitRootUse(uint8_t* code,
9898                             const uint8_t* roots_data,
9899                             VIXLUInt32Literal* literal,
9900                             uint64_t index_in_table) {
9901   DCHECK(literal->IsBound());
9902   uint32_t literal_offset = literal->GetLocation();
9903   uintptr_t address =
9904       reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>);
9905   uint8_t* data = code + literal_offset;
9906   reinterpret_cast<uint32_t*>(data)[0] = dchecked_integral_cast<uint32_t>(address);
9907 }
9908 
EmitJitRootPatches(uint8_t * code,const uint8_t * roots_data)9909 void CodeGeneratorARMVIXL::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) {
9910   for (const auto& entry : jit_string_patches_) {
9911     const StringReference& string_reference = entry.first;
9912     VIXLUInt32Literal* table_entry_literal = entry.second;
9913     uint64_t index_in_table = GetJitStringRootIndex(string_reference);
9914     PatchJitRootUse(code, roots_data, table_entry_literal, index_in_table);
9915   }
9916   for (const auto& entry : jit_class_patches_) {
9917     const TypeReference& type_reference = entry.first;
9918     VIXLUInt32Literal* table_entry_literal = entry.second;
9919     uint64_t index_in_table = GetJitClassRootIndex(type_reference);
9920     PatchJitRootUse(code, roots_data, table_entry_literal, index_in_table);
9921   }
9922 }
9923 
EmitMovwMovtPlaceholder(CodeGeneratorARMVIXL::PcRelativePatchInfo * labels,vixl32::Register out)9924 void CodeGeneratorARMVIXL::EmitMovwMovtPlaceholder(
9925     CodeGeneratorARMVIXL::PcRelativePatchInfo* labels,
9926     vixl32::Register out) {
9927   ExactAssemblyScope aas(GetVIXLAssembler(),
9928                          3 * vixl32::kMaxInstructionSizeInBytes,
9929                          CodeBufferCheckScope::kMaximumSize);
9930   // TODO(VIXL): Think about using mov instead of movw.
9931   __ bind(&labels->movw_label);
9932   __ movw(out, /* operand= */ 0u);
9933   __ bind(&labels->movt_label);
9934   __ movt(out, /* operand= */ 0u);
9935   __ bind(&labels->add_pc_label);
9936   __ add(out, out, pc);
9937 }
9938 
9939 #undef __
9940 #undef QUICK_ENTRY_POINT
9941 #undef TODO_VIXL32
9942 
9943 #define __ assembler.GetVIXLAssembler()->
9944 
EmitGrayCheckAndFastPath(ArmVIXLAssembler & assembler,vixl32::Register base_reg,vixl32::MemOperand & lock_word,vixl32::Label * slow_path,int32_t raw_ldr_offset,vixl32::Label * throw_npe=nullptr)9945 static void EmitGrayCheckAndFastPath(ArmVIXLAssembler& assembler,
9946                                      vixl32::Register base_reg,
9947                                      vixl32::MemOperand& lock_word,
9948                                      vixl32::Label* slow_path,
9949                                      int32_t raw_ldr_offset,
9950                                      vixl32::Label* throw_npe = nullptr) {
9951   // Load the lock word containing the rb_state.
9952   __ Ldr(ip, lock_word);
9953   // Given the numeric representation, it's enough to check the low bit of the rb_state.
9954   static_assert(ReadBarrier::NonGrayState() == 0, "Expecting non-gray to have value 0");
9955   static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
9956   __ Tst(ip, Operand(LockWord::kReadBarrierStateMaskShifted));
9957   __ B(ne, slow_path, /* is_far_target= */ false);
9958   // To throw NPE, we return to the fast path; the artificial dependence below does not matter.
9959   if (throw_npe != nullptr) {
9960     __ Bind(throw_npe);
9961   }
9962   __ Add(lr, lr, raw_ldr_offset);
9963   // Introduce a dependency on the lock_word including rb_state,
9964   // to prevent load-load reordering, and without using
9965   // a memory barrier (which would be more expensive).
9966   __ Add(base_reg, base_reg, Operand(ip, LSR, 32));
9967   __ Bx(lr);          // And return back to the function.
9968   // Note: The fake dependency is unnecessary for the slow path.
9969 }
9970 
9971 // Load the read barrier introspection entrypoint in register `entrypoint`
LoadReadBarrierMarkIntrospectionEntrypoint(ArmVIXLAssembler & assembler)9972 static vixl32::Register LoadReadBarrierMarkIntrospectionEntrypoint(ArmVIXLAssembler& assembler) {
9973   // The register where the read barrier introspection entrypoint is loaded
9974   // is the marking register. We clobber it here and the entrypoint restores it to 1.
9975   vixl32::Register entrypoint = mr;
9976   // entrypoint = Thread::Current()->pReadBarrierMarkReg12, i.e. pReadBarrierMarkIntrospection.
9977   DCHECK_EQ(ip.GetCode(), 12u);
9978   const int32_t entry_point_offset =
9979       Thread::ReadBarrierMarkEntryPointsOffset<kArmPointerSize>(ip.GetCode());
9980   __ Ldr(entrypoint, MemOperand(tr, entry_point_offset));
9981   return entrypoint;
9982 }
9983 
CompileBakerReadBarrierThunk(ArmVIXLAssembler & assembler,uint32_t encoded_data,std::string * debug_name)9984 void CodeGeneratorARMVIXL::CompileBakerReadBarrierThunk(ArmVIXLAssembler& assembler,
9985                                                         uint32_t encoded_data,
9986                                                         /*out*/ std::string* debug_name) {
9987   BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data);
9988   switch (kind) {
9989     case BakerReadBarrierKind::kField: {
9990       vixl32::Register base_reg(BakerReadBarrierFirstRegField::Decode(encoded_data));
9991       CheckValidReg(base_reg.GetCode());
9992       vixl32::Register holder_reg(BakerReadBarrierSecondRegField::Decode(encoded_data));
9993       CheckValidReg(holder_reg.GetCode());
9994       BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data);
9995       UseScratchRegisterScope temps(assembler.GetVIXLAssembler());
9996       temps.Exclude(ip);
9997       // In the case of a field load, if `base_reg` differs from
9998       // `holder_reg`, the offset was too large and we must have emitted (during the construction
9999       // of the HIR graph, see `art::HInstructionBuilder::BuildInstanceFieldAccess`) and preserved
10000       // (see `art::PrepareForRegisterAllocation::VisitNullCheck`) an explicit null check before
10001       // the load. Otherwise, for implicit null checks, we need to null-check the holder as we do
10002       // not necessarily do that check before going to the thunk.
10003       vixl32::Label throw_npe_label;
10004       vixl32::Label* throw_npe = nullptr;
10005       if (GetCompilerOptions().GetImplicitNullChecks() && holder_reg.Is(base_reg)) {
10006         throw_npe = &throw_npe_label;
10007         __ CompareAndBranchIfZero(holder_reg, throw_npe, /* is_far_target= */ false);
10008       }
10009       // Check if the holder is gray and, if not, add fake dependency to the base register
10010       // and return to the LDR instruction to load the reference. Otherwise, use introspection
10011       // to load the reference and call the entrypoint that performs further checks on the
10012       // reference and marks it if needed.
10013       vixl32::Label slow_path;
10014       MemOperand lock_word(holder_reg, mirror::Object::MonitorOffset().Int32Value());
10015       const int32_t raw_ldr_offset = (width == BakerReadBarrierWidth::kWide)
10016           ? BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET
10017           : BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET;
10018       EmitGrayCheckAndFastPath(
10019           assembler, base_reg, lock_word, &slow_path, raw_ldr_offset, throw_npe);
10020       __ Bind(&slow_path);
10021       const int32_t ldr_offset = /* Thumb state adjustment (LR contains Thumb state). */ -1 +
10022                                  raw_ldr_offset;
10023       vixl32::Register ep_reg = LoadReadBarrierMarkIntrospectionEntrypoint(assembler);
10024       if (width == BakerReadBarrierWidth::kWide) {
10025         MemOperand ldr_half_address(lr, ldr_offset + 2);
10026         __ Ldrh(ip, ldr_half_address);        // Load the LDR immediate half-word with "Rt | imm12".
10027         __ Ubfx(ip, ip, 0, 12);               // Extract the offset imm12.
10028         __ Ldr(ip, MemOperand(base_reg, ip));   // Load the reference.
10029       } else {
10030         MemOperand ldr_address(lr, ldr_offset);
10031         __ Ldrh(ip, ldr_address);             // Load the LDR immediate, encoding T1.
10032         __ Add(ep_reg,                        // Adjust the entrypoint address to the entrypoint
10033                ep_reg,                        // for narrow LDR.
10034                Operand(BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_ENTRYPOINT_OFFSET));
10035         __ Ubfx(ip, ip, 6, 5);                // Extract the imm5, i.e. offset / 4.
10036         __ Ldr(ip, MemOperand(base_reg, ip, LSL, 2));   // Load the reference.
10037       }
10038       // Do not unpoison. With heap poisoning enabled, the entrypoint expects a poisoned reference.
10039       __ Bx(ep_reg);                          // Jump to the entrypoint.
10040       break;
10041     }
10042     case BakerReadBarrierKind::kArray: {
10043       vixl32::Register base_reg(BakerReadBarrierFirstRegField::Decode(encoded_data));
10044       CheckValidReg(base_reg.GetCode());
10045       DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg,
10046                 BakerReadBarrierSecondRegField::Decode(encoded_data));
10047       DCHECK(BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide);
10048       UseScratchRegisterScope temps(assembler.GetVIXLAssembler());
10049       temps.Exclude(ip);
10050       vixl32::Label slow_path;
10051       int32_t data_offset =
10052           mirror::Array::DataOffset(Primitive::ComponentSize(Primitive::kPrimNot)).Int32Value();
10053       MemOperand lock_word(base_reg, mirror::Object::MonitorOffset().Int32Value() - data_offset);
10054       DCHECK_LT(lock_word.GetOffsetImmediate(), 0);
10055       const int32_t raw_ldr_offset = BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET;
10056       EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path, raw_ldr_offset);
10057       __ Bind(&slow_path);
10058       const int32_t ldr_offset = /* Thumb state adjustment (LR contains Thumb state). */ -1 +
10059                                  raw_ldr_offset;
10060       MemOperand ldr_address(lr, ldr_offset + 2);
10061       __ Ldrb(ip, ldr_address);               // Load the LDR (register) byte with "00 | imm2 | Rm",
10062                                               // i.e. Rm+32 because the scale in imm2 is 2.
10063       vixl32::Register ep_reg = LoadReadBarrierMarkIntrospectionEntrypoint(assembler);
10064       __ Bfi(ep_reg, ip, 3, 6);               // Insert ip to the entrypoint address to create
10065                                               // a switch case target based on the index register.
10066       __ Mov(ip, base_reg);                   // Move the base register to ip0.
10067       __ Bx(ep_reg);                          // Jump to the entrypoint's array switch case.
10068       break;
10069     }
10070     case BakerReadBarrierKind::kGcRoot:
10071     case BakerReadBarrierKind::kIntrinsicCas: {
10072       // Check if the reference needs to be marked and if so (i.e. not null, not marked yet
10073       // and it does not have a forwarding address), call the correct introspection entrypoint;
10074       // otherwise return the reference (or the extracted forwarding address).
10075       // There is no gray bit check for GC roots.
10076       vixl32::Register root_reg(BakerReadBarrierFirstRegField::Decode(encoded_data));
10077       CheckValidReg(root_reg.GetCode());
10078       DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg,
10079                 BakerReadBarrierSecondRegField::Decode(encoded_data));
10080       BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data);
10081       UseScratchRegisterScope temps(assembler.GetVIXLAssembler());
10082       temps.Exclude(ip);
10083       vixl32::Label return_label, not_marked, forwarding_address;
10084       __ CompareAndBranchIfZero(root_reg, &return_label, /* is_far_target= */ false);
10085       MemOperand lock_word(root_reg, mirror::Object::MonitorOffset().Int32Value());
10086       __ Ldr(ip, lock_word);
10087       __ Tst(ip, LockWord::kMarkBitStateMaskShifted);
10088       __ B(eq, &not_marked);
10089       __ Bind(&return_label);
10090       __ Bx(lr);
10091       __ Bind(&not_marked);
10092       static_assert(LockWord::kStateShift == 30 && LockWord::kStateForwardingAddress == 3,
10093                     "To use 'CMP ip, #modified-immediate; BHS', we need the lock word state in "
10094                     " the highest bits and the 'forwarding address' state to have all bits set");
10095       __ Cmp(ip, Operand(0xc0000000));
10096       __ B(hs, &forwarding_address);
10097       vixl32::Register ep_reg = LoadReadBarrierMarkIntrospectionEntrypoint(assembler);
10098       // Adjust the art_quick_read_barrier_mark_introspection address
10099       // in kBakerCcEntrypointRegister to one of
10100       //     art_quick_read_barrier_mark_introspection_{gc_roots_{wide,narrow},intrinsic_cas}.
10101       if (kind == BakerReadBarrierKind::kIntrinsicCas) {
10102         DCHECK(width == BakerReadBarrierWidth::kWide);
10103         DCHECK(!root_reg.IsLow());
10104       }
10105       int32_t entrypoint_offset =
10106           (kind == BakerReadBarrierKind::kGcRoot)
10107               ? (width == BakerReadBarrierWidth::kWide)
10108                   ? BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_ENTRYPOINT_OFFSET
10109                   : BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_ENTRYPOINT_OFFSET
10110               : BAKER_MARK_INTROSPECTION_INTRINSIC_CAS_ENTRYPOINT_OFFSET;
10111       __ Add(ep_reg, ep_reg, Operand(entrypoint_offset));
10112       __ Mov(ip, root_reg);
10113       __ Bx(ep_reg);
10114       __ Bind(&forwarding_address);
10115       __ Lsl(root_reg, ip, LockWord::kForwardingAddressShift);
10116       __ Bx(lr);
10117       break;
10118     }
10119     default:
10120       LOG(FATAL) << "Unexpected kind: " << static_cast<uint32_t>(kind);
10121       UNREACHABLE();
10122   }
10123 
10124   // For JIT, the slow path is considered part of the compiled method,
10125   // so JIT should pass null as `debug_name`.
10126   DCHECK_IMPLIES(GetCompilerOptions().IsJitCompiler(), debug_name == nullptr);
10127   if (debug_name != nullptr && GetCompilerOptions().GenerateAnyDebugInfo()) {
10128     std::ostringstream oss;
10129     oss << "BakerReadBarrierThunk";
10130     switch (kind) {
10131       case BakerReadBarrierKind::kField:
10132         oss << "Field";
10133         if (BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide) {
10134           oss << "Wide";
10135         }
10136         oss << "_r" << BakerReadBarrierFirstRegField::Decode(encoded_data)
10137             << "_r" << BakerReadBarrierSecondRegField::Decode(encoded_data);
10138         break;
10139       case BakerReadBarrierKind::kArray:
10140         oss << "Array_r" << BakerReadBarrierFirstRegField::Decode(encoded_data);
10141         DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg,
10142                   BakerReadBarrierSecondRegField::Decode(encoded_data));
10143         DCHECK(BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide);
10144         break;
10145       case BakerReadBarrierKind::kGcRoot:
10146         oss << "GcRoot";
10147         if (BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide) {
10148           oss << "Wide";
10149         }
10150         oss << "_r" << BakerReadBarrierFirstRegField::Decode(encoded_data);
10151         DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg,
10152                   BakerReadBarrierSecondRegField::Decode(encoded_data));
10153         break;
10154       case BakerReadBarrierKind::kIntrinsicCas:
10155         oss << "IntrinsicCas_r" << BakerReadBarrierFirstRegField::Decode(encoded_data);
10156         DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg,
10157                   BakerReadBarrierSecondRegField::Decode(encoded_data));
10158         DCHECK(BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide);
10159         break;
10160     }
10161     *debug_name = oss.str();
10162   }
10163 }
10164 
10165 #undef __
10166 
10167 }  // namespace arm
10168 }  // namespace art
10169