blob: 46a68c1ac00ece10ecdc47ba1b897930ceb01d88 [file] [log] [blame]
Scott Wakelingfe885462016-09-22 10:24:38 +01001/*
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/instruction_set_features_arm.h"
20#include "art_method.h"
21#include "code_generator_utils.h"
22#include "common_arm.h"
23#include "compiled_method.h"
24#include "entrypoints/quick/quick_entrypoints.h"
25#include "gc/accounting/card_table.h"
Anton Kirilov5ec62182016-10-13 20:16:02 +010026#include "intrinsics_arm_vixl.h"
Scott Wakelingfe885462016-09-22 10:24:38 +010027#include "mirror/array-inl.h"
28#include "mirror/class-inl.h"
29#include "thread.h"
30#include "utils/arm/assembler_arm_vixl.h"
31#include "utils/arm/managed_register_arm.h"
32#include "utils/assembler.h"
33#include "utils/stack_checks.h"
34
35namespace art {
36namespace arm {
37
38namespace vixl32 = vixl::aarch32;
39using namespace vixl32; // NOLINT(build/namespaces)
40
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +010041using helpers::DRegisterFrom;
Scott Wakelingfe885462016-09-22 10:24:38 +010042using helpers::DWARFReg;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010043using helpers::HighDRegisterFrom;
44using helpers::HighRegisterFrom;
Scott Wakelingfe885462016-09-22 10:24:38 +010045using helpers::InputOperandAt;
Scott Wakelingc34dba72016-10-03 10:14:44 +010046using helpers::InputRegister;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010047using helpers::InputRegisterAt;
Scott Wakelingfe885462016-09-22 10:24:38 +010048using helpers::InputSRegisterAt;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010049using helpers::InputVRegisterAt;
50using helpers::LocationFrom;
51using helpers::LowRegisterFrom;
52using helpers::LowSRegisterFrom;
53using helpers::OutputRegister;
54using helpers::OutputSRegister;
55using helpers::OutputVRegister;
56using helpers::RegisterFrom;
57using helpers::SRegisterFrom;
Scott Wakelingfe885462016-09-22 10:24:38 +010058
59using RegisterList = vixl32::RegisterList;
60
61static bool ExpectedPairLayout(Location location) {
62 // We expected this for both core and fpu register pairs.
63 return ((location.low() & 1) == 0) && (location.low() + 1 == location.high());
64}
65
Anton Kirilove28d9ae2016-10-25 18:17:23 +010066static constexpr int kCurrentMethodStackOffset = 0;
Scott Wakelingfe885462016-09-22 10:24:38 +010067static constexpr size_t kArmInstrMaxSizeInBytes = 4u;
68
69#ifdef __
70#error "ARM Codegen VIXL macro-assembler macro already defined."
71#endif
72
Scott Wakelingfe885462016-09-22 10:24:38 +010073// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
74#define __ down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler()-> // NOLINT
75#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArmPointerSize, x).Int32Value()
76
77// Marker that code is yet to be, and must, be implemented.
78#define TODO_VIXL32(level) LOG(level) << __PRETTY_FUNCTION__ << " unimplemented "
79
Scott Wakelinga7812ae2016-10-17 10:03:36 +010080// SaveLiveRegisters and RestoreLiveRegisters from SlowPathCodeARM operate on sets of S registers,
81// for each live D registers they treat two corresponding S registers as live ones.
82//
83// Two following functions (SaveContiguousSRegisterList, RestoreContiguousSRegisterList) build
84// from a list of contiguous S registers a list of contiguous D registers (processing first/last
85// S registers corner cases) and save/restore this new list treating them as D registers.
86// - decreasing code size
87// - avoiding hazards on Cortex-A57, when a pair of S registers for an actual live D register is
88// restored and then used in regular non SlowPath code as D register.
89//
90// For the following example (v means the S register is live):
91// D names: | D0 | D1 | D2 | D4 | ...
92// S names: | S0 | S1 | S2 | S3 | S4 | S5 | S6 | S7 | ...
93// Live? | | v | v | v | v | v | v | | ...
94//
95// S1 and S6 will be saved/restored independently; D registers list (D1, D2) will be processed
96// as D registers.
97//
98// TODO(VIXL): All this code should be unnecessary once the VIXL AArch32 backend provides helpers
99// for lists of floating-point registers.
100static size_t SaveContiguousSRegisterList(size_t first,
101 size_t last,
102 CodeGenerator* codegen,
103 size_t stack_offset) {
104 static_assert(kSRegSizeInBytes == kArmWordSize, "Broken assumption on reg/word sizes.");
105 static_assert(kDRegSizeInBytes == 2 * kArmWordSize, "Broken assumption on reg/word sizes.");
106 DCHECK_LE(first, last);
107 if ((first == last) && (first == 0)) {
108 __ Vstr(vixl32::SRegister(first), MemOperand(sp, stack_offset));
109 return stack_offset + kSRegSizeInBytes;
110 }
111 if (first % 2 == 1) {
112 __ Vstr(vixl32::SRegister(first++), MemOperand(sp, stack_offset));
113 stack_offset += kSRegSizeInBytes;
114 }
115
116 bool save_last = false;
117 if (last % 2 == 0) {
118 save_last = true;
119 --last;
120 }
121
122 if (first < last) {
123 vixl32::DRegister d_reg = vixl32::DRegister(first / 2);
124 DCHECK_EQ((last - first + 1) % 2, 0u);
125 size_t number_of_d_regs = (last - first + 1) / 2;
126
127 if (number_of_d_regs == 1) {
128 __ Vstr(d_reg, MemOperand(sp, stack_offset));
129 } else if (number_of_d_regs > 1) {
130 UseScratchRegisterScope temps(down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler());
131 vixl32::Register base = sp;
132 if (stack_offset != 0) {
133 base = temps.Acquire();
134 __ Add(base, sp, stack_offset);
135 }
136 __ Vstm(F64, base, NO_WRITE_BACK, DRegisterList(d_reg, number_of_d_regs));
137 }
138 stack_offset += number_of_d_regs * kDRegSizeInBytes;
139 }
140
141 if (save_last) {
142 __ Vstr(vixl32::SRegister(last + 1), MemOperand(sp, stack_offset));
143 stack_offset += kSRegSizeInBytes;
144 }
145
146 return stack_offset;
147}
148
149static size_t RestoreContiguousSRegisterList(size_t first,
150 size_t last,
151 CodeGenerator* codegen,
152 size_t stack_offset) {
153 static_assert(kSRegSizeInBytes == kArmWordSize, "Broken assumption on reg/word sizes.");
154 static_assert(kDRegSizeInBytes == 2 * kArmWordSize, "Broken assumption on reg/word sizes.");
155 DCHECK_LE(first, last);
156 if ((first == last) && (first == 0)) {
157 __ Vldr(vixl32::SRegister(first), MemOperand(sp, stack_offset));
158 return stack_offset + kSRegSizeInBytes;
159 }
160 if (first % 2 == 1) {
161 __ Vldr(vixl32::SRegister(first++), MemOperand(sp, stack_offset));
162 stack_offset += kSRegSizeInBytes;
163 }
164
165 bool restore_last = false;
166 if (last % 2 == 0) {
167 restore_last = true;
168 --last;
169 }
170
171 if (first < last) {
172 vixl32::DRegister d_reg = vixl32::DRegister(first / 2);
173 DCHECK_EQ((last - first + 1) % 2, 0u);
174 size_t number_of_d_regs = (last - first + 1) / 2;
175 if (number_of_d_regs == 1) {
176 __ Vldr(d_reg, MemOperand(sp, stack_offset));
177 } else if (number_of_d_regs > 1) {
178 UseScratchRegisterScope temps(down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler());
179 vixl32::Register base = sp;
180 if (stack_offset != 0) {
181 base = temps.Acquire();
182 __ Add(base, sp, stack_offset);
183 }
184 __ Vldm(F64, base, NO_WRITE_BACK, DRegisterList(d_reg, number_of_d_regs));
185 }
186 stack_offset += number_of_d_regs * kDRegSizeInBytes;
187 }
188
189 if (restore_last) {
190 __ Vldr(vixl32::SRegister(last + 1), MemOperand(sp, stack_offset));
191 stack_offset += kSRegSizeInBytes;
192 }
193
194 return stack_offset;
195}
196
197void SlowPathCodeARMVIXL::SaveLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
198 size_t stack_offset = codegen->GetFirstRegisterSlotInSlowPath();
199 size_t orig_offset = stack_offset;
200
201 const uint32_t core_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ true);
202 for (uint32_t i : LowToHighBits(core_spills)) {
203 // If the register holds an object, update the stack mask.
204 if (locations->RegisterContainsObject(i)) {
205 locations->SetStackBit(stack_offset / kVRegSize);
206 }
207 DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
208 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
209 saved_core_stack_offsets_[i] = stack_offset;
210 stack_offset += kArmWordSize;
211 }
212
213 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
214 arm_codegen->GetAssembler()->StoreRegisterList(core_spills, orig_offset);
215
216 uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ false);
217 orig_offset = stack_offset;
218 for (uint32_t i : LowToHighBits(fp_spills)) {
219 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
220 saved_fpu_stack_offsets_[i] = stack_offset;
221 stack_offset += kArmWordSize;
222 }
223
224 stack_offset = orig_offset;
225 while (fp_spills != 0u) {
226 uint32_t begin = CTZ(fp_spills);
227 uint32_t tmp = fp_spills + (1u << begin);
228 fp_spills &= tmp; // Clear the contiguous range of 1s.
229 uint32_t end = (tmp == 0u) ? 32u : CTZ(tmp); // CTZ(0) is undefined.
230 stack_offset = SaveContiguousSRegisterList(begin, end - 1, codegen, stack_offset);
231 }
232 DCHECK_LE(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
233}
234
235void SlowPathCodeARMVIXL::RestoreLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
236 size_t stack_offset = codegen->GetFirstRegisterSlotInSlowPath();
237 size_t orig_offset = stack_offset;
238
239 const uint32_t core_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ true);
240 for (uint32_t i : LowToHighBits(core_spills)) {
241 DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
242 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
243 stack_offset += kArmWordSize;
244 }
245
246 // TODO(VIXL): Check the coherency of stack_offset after this with a test.
247 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
248 arm_codegen->GetAssembler()->LoadRegisterList(core_spills, orig_offset);
249
250 uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ false);
251 while (fp_spills != 0u) {
252 uint32_t begin = CTZ(fp_spills);
253 uint32_t tmp = fp_spills + (1u << begin);
254 fp_spills &= tmp; // Clear the contiguous range of 1s.
255 uint32_t end = (tmp == 0u) ? 32u : CTZ(tmp); // CTZ(0) is undefined.
256 stack_offset = RestoreContiguousSRegisterList(begin, end - 1, codegen, stack_offset);
257 }
258 DCHECK_LE(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
259}
260
261class NullCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
262 public:
263 explicit NullCheckSlowPathARMVIXL(HNullCheck* instruction) : SlowPathCodeARMVIXL(instruction) {}
264
265 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
266 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
267 __ Bind(GetEntryLabel());
268 if (instruction_->CanThrowIntoCatchBlock()) {
269 // Live registers will be restored in the catch block if caught.
270 SaveLiveRegisters(codegen, instruction_->GetLocations());
271 }
272 arm_codegen->InvokeRuntime(kQuickThrowNullPointer,
273 instruction_,
274 instruction_->GetDexPc(),
275 this);
276 CheckEntrypointTypes<kQuickThrowNullPointer, void, void>();
277 }
278
279 bool IsFatal() const OVERRIDE { return true; }
280
281 const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathARMVIXL"; }
282
283 private:
284 DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARMVIXL);
285};
286
Scott Wakelingfe885462016-09-22 10:24:38 +0100287class DivZeroCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
288 public:
289 explicit DivZeroCheckSlowPathARMVIXL(HDivZeroCheck* instruction)
290 : SlowPathCodeARMVIXL(instruction) {}
291
292 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100293 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
Scott Wakelingfe885462016-09-22 10:24:38 +0100294 __ Bind(GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100295 arm_codegen->InvokeRuntime(kQuickThrowDivZero, instruction_, instruction_->GetDexPc(), this);
Scott Wakelingfe885462016-09-22 10:24:38 +0100296 CheckEntrypointTypes<kQuickThrowDivZero, void, void>();
297 }
298
299 bool IsFatal() const OVERRIDE { return true; }
300
301 const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathARMVIXL"; }
302
303 private:
304 DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathARMVIXL);
305};
306
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100307class SuspendCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
308 public:
309 SuspendCheckSlowPathARMVIXL(HSuspendCheck* instruction, HBasicBlock* successor)
310 : SlowPathCodeARMVIXL(instruction), successor_(successor) {}
311
312 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
313 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
314 __ Bind(GetEntryLabel());
315 arm_codegen->InvokeRuntime(kQuickTestSuspend, instruction_, instruction_->GetDexPc(), this);
316 CheckEntrypointTypes<kQuickTestSuspend, void, void>();
317 if (successor_ == nullptr) {
318 __ B(GetReturnLabel());
319 } else {
320 __ B(arm_codegen->GetLabelOf(successor_));
321 }
322 }
323
324 vixl32::Label* GetReturnLabel() {
325 DCHECK(successor_ == nullptr);
326 return &return_label_;
327 }
328
329 HBasicBlock* GetSuccessor() const {
330 return successor_;
331 }
332
333 const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathARMVIXL"; }
334
335 private:
336 // If not null, the block to branch to after the suspend check.
337 HBasicBlock* const successor_;
338
339 // If `successor_` is null, the label to branch to after the suspend check.
340 vixl32::Label return_label_;
341
342 DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathARMVIXL);
343};
344
Scott Wakelingc34dba72016-10-03 10:14:44 +0100345class BoundsCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
346 public:
347 explicit BoundsCheckSlowPathARMVIXL(HBoundsCheck* instruction)
348 : SlowPathCodeARMVIXL(instruction) {}
349
350 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
351 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
352 LocationSummary* locations = instruction_->GetLocations();
353
354 __ Bind(GetEntryLabel());
355 if (instruction_->CanThrowIntoCatchBlock()) {
356 // Live registers will be restored in the catch block if caught.
357 SaveLiveRegisters(codegen, instruction_->GetLocations());
358 }
359 // We're moving two locations to locations that could overlap, so we need a parallel
360 // move resolver.
361 InvokeRuntimeCallingConventionARMVIXL calling_convention;
362 codegen->EmitParallelMoves(
363 locations->InAt(0),
364 LocationFrom(calling_convention.GetRegisterAt(0)),
365 Primitive::kPrimInt,
366 locations->InAt(1),
367 LocationFrom(calling_convention.GetRegisterAt(1)),
368 Primitive::kPrimInt);
369 QuickEntrypointEnum entrypoint = instruction_->AsBoundsCheck()->IsStringCharAt()
370 ? kQuickThrowStringBounds
371 : kQuickThrowArrayBounds;
372 arm_codegen->InvokeRuntime(entrypoint, instruction_, instruction_->GetDexPc(), this);
373 CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>();
374 CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>();
375 }
376
377 bool IsFatal() const OVERRIDE { return true; }
378
379 const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathARMVIXL"; }
380
381 private:
382 DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARMVIXL);
383};
384
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100385class LoadClassSlowPathARMVIXL : public SlowPathCodeARMVIXL {
386 public:
387 LoadClassSlowPathARMVIXL(HLoadClass* cls, HInstruction* at, uint32_t dex_pc, bool do_clinit)
388 : SlowPathCodeARMVIXL(at), cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) {
389 DCHECK(at->IsLoadClass() || at->IsClinitCheck());
390 }
391
392 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
393 LocationSummary* locations = at_->GetLocations();
394
395 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
396 __ Bind(GetEntryLabel());
397 SaveLiveRegisters(codegen, locations);
398
399 InvokeRuntimeCallingConventionARMVIXL calling_convention;
400 __ Mov(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex());
401 QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage
402 : kQuickInitializeType;
403 arm_codegen->InvokeRuntime(entrypoint, at_, dex_pc_, this);
404 if (do_clinit_) {
405 CheckEntrypointTypes<kQuickInitializeStaticStorage, void*, uint32_t>();
406 } else {
407 CheckEntrypointTypes<kQuickInitializeType, void*, uint32_t>();
408 }
409
410 // Move the class to the desired location.
411 Location out = locations->Out();
412 if (out.IsValid()) {
413 DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
414 arm_codegen->Move32(locations->Out(), LocationFrom(r0));
415 }
416 RestoreLiveRegisters(codegen, locations);
417 __ B(GetExitLabel());
418 }
419
420 const char* GetDescription() const OVERRIDE { return "LoadClassSlowPathARMVIXL"; }
421
422 private:
423 // The class this slow path will load.
424 HLoadClass* const cls_;
425
426 // The instruction where this slow path is happening.
427 // (Might be the load class or an initialization check).
428 HInstruction* const at_;
429
430 // The dex PC of `at_`.
431 const uint32_t dex_pc_;
432
433 // Whether to initialize the class.
434 const bool do_clinit_;
435
436 DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathARMVIXL);
437};
438
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100439class TypeCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
440 public:
441 TypeCheckSlowPathARMVIXL(HInstruction* instruction, bool is_fatal)
442 : SlowPathCodeARMVIXL(instruction), is_fatal_(is_fatal) {}
443
444 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
445 LocationSummary* locations = instruction_->GetLocations();
446 Location object_class = instruction_->IsCheckCast() ? locations->GetTemp(0)
447 : locations->Out();
448 DCHECK(instruction_->IsCheckCast()
449 || !locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
450
451 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
452 __ Bind(GetEntryLabel());
453
454 if (!is_fatal_) {
455 TODO_VIXL32(FATAL);
456 }
457
458 // We're moving two locations to locations that could overlap, so we need a parallel
459 // move resolver.
460 InvokeRuntimeCallingConventionARMVIXL calling_convention;
461 codegen->EmitParallelMoves(
462 locations->InAt(1),
463 LocationFrom(calling_convention.GetRegisterAt(0)),
464 Primitive::kPrimNot,
465 object_class,
466 LocationFrom(calling_convention.GetRegisterAt(1)),
467 Primitive::kPrimNot);
468
469 if (instruction_->IsInstanceOf()) {
470 TODO_VIXL32(FATAL);
471 } else {
472 DCHECK(instruction_->IsCheckCast());
473 arm_codegen->InvokeRuntime(kQuickCheckCast, instruction_, instruction_->GetDexPc(), this);
474 CheckEntrypointTypes<kQuickCheckCast, void, const mirror::Class*, const mirror::Class*>();
475 }
476
477 if (!is_fatal_) {
478 TODO_VIXL32(FATAL);
479 }
480 }
481
482 const char* GetDescription() const OVERRIDE { return "TypeCheckSlowPathARMVIXL"; }
483
484 bool IsFatal() const OVERRIDE { return is_fatal_; }
485
486 private:
487 const bool is_fatal_;
488
489 DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathARMVIXL);
490};
491
Scott Wakelingc34dba72016-10-03 10:14:44 +0100492class DeoptimizationSlowPathARMVIXL : public SlowPathCodeARMVIXL {
493 public:
494 explicit DeoptimizationSlowPathARMVIXL(HDeoptimize* instruction)
495 : SlowPathCodeARMVIXL(instruction) {}
496
497 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
498 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
499 __ Bind(GetEntryLabel());
500 arm_codegen->InvokeRuntime(kQuickDeoptimize, instruction_, instruction_->GetDexPc(), this);
501 CheckEntrypointTypes<kQuickDeoptimize, void, void>();
502 }
503
504 const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathARMVIXL"; }
505
506 private:
507 DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathARMVIXL);
508};
509
510class ArraySetSlowPathARMVIXL : public SlowPathCodeARMVIXL {
511 public:
512 explicit ArraySetSlowPathARMVIXL(HInstruction* instruction) : SlowPathCodeARMVIXL(instruction) {}
513
514 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
515 LocationSummary* locations = instruction_->GetLocations();
516 __ Bind(GetEntryLabel());
517 SaveLiveRegisters(codegen, locations);
518
519 InvokeRuntimeCallingConventionARMVIXL calling_convention;
520 HParallelMove parallel_move(codegen->GetGraph()->GetArena());
521 parallel_move.AddMove(
522 locations->InAt(0),
523 LocationFrom(calling_convention.GetRegisterAt(0)),
524 Primitive::kPrimNot,
525 nullptr);
526 parallel_move.AddMove(
527 locations->InAt(1),
528 LocationFrom(calling_convention.GetRegisterAt(1)),
529 Primitive::kPrimInt,
530 nullptr);
531 parallel_move.AddMove(
532 locations->InAt(2),
533 LocationFrom(calling_convention.GetRegisterAt(2)),
534 Primitive::kPrimNot,
535 nullptr);
536 codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
537
538 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
539 arm_codegen->InvokeRuntime(kQuickAputObject, instruction_, instruction_->GetDexPc(), this);
540 CheckEntrypointTypes<kQuickAputObject, void, mirror::Array*, int32_t, mirror::Object*>();
541 RestoreLiveRegisters(codegen, locations);
542 __ B(GetExitLabel());
543 }
544
545 const char* GetDescription() const OVERRIDE { return "ArraySetSlowPathARMVIXL"; }
546
547 private:
548 DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathARMVIXL);
549};
550
551
Scott Wakelingfe885462016-09-22 10:24:38 +0100552inline vixl32::Condition ARMCondition(IfCondition cond) {
553 switch (cond) {
554 case kCondEQ: return eq;
555 case kCondNE: return ne;
556 case kCondLT: return lt;
557 case kCondLE: return le;
558 case kCondGT: return gt;
559 case kCondGE: return ge;
560 case kCondB: return lo;
561 case kCondBE: return ls;
562 case kCondA: return hi;
563 case kCondAE: return hs;
564 }
565 LOG(FATAL) << "Unreachable";
566 UNREACHABLE();
567}
568
569// Maps signed condition to unsigned condition.
570inline vixl32::Condition ARMUnsignedCondition(IfCondition cond) {
571 switch (cond) {
572 case kCondEQ: return eq;
573 case kCondNE: return ne;
574 // Signed to unsigned.
575 case kCondLT: return lo;
576 case kCondLE: return ls;
577 case kCondGT: return hi;
578 case kCondGE: return hs;
579 // Unsigned remain unchanged.
580 case kCondB: return lo;
581 case kCondBE: return ls;
582 case kCondA: return hi;
583 case kCondAE: return hs;
584 }
585 LOG(FATAL) << "Unreachable";
586 UNREACHABLE();
587}
588
589inline vixl32::Condition ARMFPCondition(IfCondition cond, bool gt_bias) {
590 // The ARM condition codes can express all the necessary branches, see the
591 // "Meaning (floating-point)" column in the table A8-1 of the ARMv7 reference manual.
592 // There is no dex instruction or HIR that would need the missing conditions
593 // "equal or unordered" or "not equal".
594 switch (cond) {
595 case kCondEQ: return eq;
596 case kCondNE: return ne /* unordered */;
597 case kCondLT: return gt_bias ? cc : lt /* unordered */;
598 case kCondLE: return gt_bias ? ls : le /* unordered */;
599 case kCondGT: return gt_bias ? hi /* unordered */ : gt;
600 case kCondGE: return gt_bias ? cs /* unordered */ : ge;
601 default:
602 LOG(FATAL) << "UNREACHABLE";
603 UNREACHABLE();
604 }
605}
606
Scott Wakelingfe885462016-09-22 10:24:38 +0100607void CodeGeneratorARMVIXL::DumpCoreRegister(std::ostream& stream, int reg) const {
608 stream << vixl32::Register(reg);
609}
610
611void CodeGeneratorARMVIXL::DumpFloatingPointRegister(std::ostream& stream, int reg) const {
612 stream << vixl32::SRegister(reg);
613}
614
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100615static uint32_t ComputeSRegisterListMask(const SRegisterList& regs) {
Scott Wakelingfe885462016-09-22 10:24:38 +0100616 uint32_t mask = 0;
617 for (uint32_t i = regs.GetFirstSRegister().GetCode();
618 i <= regs.GetLastSRegister().GetCode();
619 ++i) {
620 mask |= (1 << i);
621 }
622 return mask;
623}
624
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100625size_t CodeGeneratorARMVIXL::RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
626 GetAssembler()->LoadSFromOffset(vixl32::SRegister(reg_id), sp, stack_index);
627 return kArmWordSize;
628}
629
Scott Wakelingfe885462016-09-22 10:24:38 +0100630#undef __
631
632CodeGeneratorARMVIXL::CodeGeneratorARMVIXL(HGraph* graph,
633 const ArmInstructionSetFeatures& isa_features,
634 const CompilerOptions& compiler_options,
635 OptimizingCompilerStats* stats)
636 : CodeGenerator(graph,
637 kNumberOfCoreRegisters,
638 kNumberOfSRegisters,
639 kNumberOfRegisterPairs,
640 kCoreCalleeSaves.GetList(),
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100641 ComputeSRegisterListMask(kFpuCalleeSaves),
Scott Wakelingfe885462016-09-22 10:24:38 +0100642 compiler_options,
643 stats),
644 block_labels_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
645 location_builder_(graph, this),
646 instruction_visitor_(graph, this),
647 move_resolver_(graph->GetArena(), this),
648 assembler_(graph->GetArena()),
649 isa_features_(isa_features) {
650 // Always save the LR register to mimic Quick.
651 AddAllocatedRegister(Location::RegisterLocation(LR));
Alexandre Rames9c19bd62016-10-24 11:50:32 +0100652 // Give d14 and d15 as scratch registers to VIXL.
653 // They are removed from the register allocator in `SetupBlockedRegisters()`.
654 // TODO(VIXL): We need two scratch D registers for `EmitSwap` when swapping two double stack
655 // slots. If that is sufficiently rare, and we have pressure on FP registers, we could instead
656 // spill in `EmitSwap`. But if we actually are guaranteed to have 32 D registers, we could give
657 // d30 and d31 to VIXL to avoid removing registers from the allocator. If that is the case, we may
658 // also want to investigate giving those 14 other D registers to the allocator.
659 GetVIXLAssembler()->GetScratchVRegisterList()->Combine(d14);
660 GetVIXLAssembler()->GetScratchVRegisterList()->Combine(d15);
Scott Wakelingfe885462016-09-22 10:24:38 +0100661}
662
663#define __ reinterpret_cast<ArmVIXLAssembler*>(GetAssembler())->GetVIXLAssembler()->
664
665void CodeGeneratorARMVIXL::Finalize(CodeAllocator* allocator) {
666 GetAssembler()->FinalizeCode();
667 CodeGenerator::Finalize(allocator);
668}
669
670void CodeGeneratorARMVIXL::SetupBlockedRegisters() const {
Scott Wakelingfe885462016-09-22 10:24:38 +0100671 // Stack register, LR and PC are always reserved.
672 blocked_core_registers_[SP] = true;
673 blocked_core_registers_[LR] = true;
674 blocked_core_registers_[PC] = true;
675
676 // Reserve thread register.
677 blocked_core_registers_[TR] = true;
678
679 // Reserve temp register.
680 blocked_core_registers_[IP] = true;
681
Alexandre Rames9c19bd62016-10-24 11:50:32 +0100682 // Registers s28-s31 (d14-d15) are left to VIXL for scratch registers.
683 // (They are given to the `MacroAssembler` in `CodeGeneratorARMVIXL::CodeGeneratorARMVIXL`.)
684 blocked_fpu_registers_[28] = true;
685 blocked_fpu_registers_[29] = true;
686 blocked_fpu_registers_[30] = true;
687 blocked_fpu_registers_[31] = true;
688
Scott Wakelingfe885462016-09-22 10:24:38 +0100689 if (GetGraph()->IsDebuggable()) {
690 // Stubs do not save callee-save floating point registers. If the graph
691 // is debuggable, we need to deal with these registers differently. For
692 // now, just block them.
693 for (uint32_t i = kFpuCalleeSaves.GetFirstSRegister().GetCode();
694 i <= kFpuCalleeSaves.GetLastSRegister().GetCode();
695 ++i) {
696 blocked_fpu_registers_[i] = true;
697 }
698 }
Scott Wakelingfe885462016-09-22 10:24:38 +0100699}
700
Scott Wakelingfe885462016-09-22 10:24:38 +0100701InstructionCodeGeneratorARMVIXL::InstructionCodeGeneratorARMVIXL(HGraph* graph,
702 CodeGeneratorARMVIXL* codegen)
703 : InstructionCodeGenerator(graph, codegen),
704 assembler_(codegen->GetAssembler()),
705 codegen_(codegen) {}
706
707void CodeGeneratorARMVIXL::ComputeSpillMask() {
708 core_spill_mask_ = allocated_registers_.GetCoreRegisters() & core_callee_save_mask_;
709 DCHECK_NE(core_spill_mask_, 0u) << "At least the return address register must be saved";
710 // There is no easy instruction to restore just the PC on thumb2. We spill and
711 // restore another arbitrary register.
712 core_spill_mask_ |= (1 << kCoreAlwaysSpillRegister.GetCode());
713 fpu_spill_mask_ = allocated_registers_.GetFloatingPointRegisters() & fpu_callee_save_mask_;
714 // We use vpush and vpop for saving and restoring floating point registers, which take
715 // a SRegister and the number of registers to save/restore after that SRegister. We
716 // therefore update the `fpu_spill_mask_` to also contain those registers not allocated,
717 // but in the range.
718 if (fpu_spill_mask_ != 0) {
719 uint32_t least_significant_bit = LeastSignificantBit(fpu_spill_mask_);
720 uint32_t most_significant_bit = MostSignificantBit(fpu_spill_mask_);
721 for (uint32_t i = least_significant_bit + 1 ; i < most_significant_bit; ++i) {
722 fpu_spill_mask_ |= (1 << i);
723 }
724 }
725}
726
727void CodeGeneratorARMVIXL::GenerateFrameEntry() {
728 bool skip_overflow_check =
729 IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kArm);
730 DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks());
731 __ Bind(&frame_entry_label_);
732
733 if (HasEmptyFrame()) {
734 return;
735 }
736
Scott Wakelingfe885462016-09-22 10:24:38 +0100737 if (!skip_overflow_check) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100738 UseScratchRegisterScope temps(GetVIXLAssembler());
739 vixl32::Register temp = temps.Acquire();
Scott Wakelingfe885462016-09-22 10:24:38 +0100740 __ Sub(temp, sp, static_cast<int32_t>(GetStackOverflowReservedBytes(kArm)));
741 // The load must immediately precede RecordPcInfo.
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100742 AssemblerAccurateScope aas(GetVIXLAssembler(),
743 kArmInstrMaxSizeInBytes,
744 CodeBufferCheckScope::kMaximumSize);
745 __ ldr(temp, MemOperand(temp));
746 RecordPcInfo(nullptr, 0);
Scott Wakelingfe885462016-09-22 10:24:38 +0100747 }
748
749 __ Push(RegisterList(core_spill_mask_));
750 GetAssembler()->cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(core_spill_mask_));
751 GetAssembler()->cfi().RelOffsetForMany(DWARFReg(kMethodRegister),
752 0,
753 core_spill_mask_,
754 kArmWordSize);
755 if (fpu_spill_mask_ != 0) {
756 uint32_t first = LeastSignificantBit(fpu_spill_mask_);
757
758 // Check that list is contiguous.
759 DCHECK_EQ(fpu_spill_mask_ >> CTZ(fpu_spill_mask_), ~0u >> (32 - POPCOUNT(fpu_spill_mask_)));
760
761 __ Vpush(SRegisterList(vixl32::SRegister(first), POPCOUNT(fpu_spill_mask_)));
762 GetAssembler()->cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(fpu_spill_mask_));
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100763 GetAssembler()->cfi().RelOffsetForMany(DWARFReg(s0), 0, fpu_spill_mask_, kArmWordSize);
Scott Wakelingfe885462016-09-22 10:24:38 +0100764 }
765 int adjust = GetFrameSize() - FrameEntrySpillSize();
766 __ Sub(sp, sp, adjust);
767 GetAssembler()->cfi().AdjustCFAOffset(adjust);
768 GetAssembler()->StoreToOffset(kStoreWord, kMethodRegister, sp, 0);
769}
770
771void CodeGeneratorARMVIXL::GenerateFrameExit() {
772 if (HasEmptyFrame()) {
773 __ Bx(lr);
774 return;
775 }
776 GetAssembler()->cfi().RememberState();
777 int adjust = GetFrameSize() - FrameEntrySpillSize();
778 __ Add(sp, sp, adjust);
779 GetAssembler()->cfi().AdjustCFAOffset(-adjust);
780 if (fpu_spill_mask_ != 0) {
781 uint32_t first = LeastSignificantBit(fpu_spill_mask_);
782
783 // Check that list is contiguous.
784 DCHECK_EQ(fpu_spill_mask_ >> CTZ(fpu_spill_mask_), ~0u >> (32 - POPCOUNT(fpu_spill_mask_)));
785
786 __ Vpop(SRegisterList(vixl32::SRegister(first), POPCOUNT(fpu_spill_mask_)));
787 GetAssembler()->cfi().AdjustCFAOffset(
788 -static_cast<int>(kArmWordSize) * POPCOUNT(fpu_spill_mask_));
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100789 GetAssembler()->cfi().RestoreMany(DWARFReg(vixl32::SRegister(0)), fpu_spill_mask_);
Scott Wakelingfe885462016-09-22 10:24:38 +0100790 }
791 // Pop LR into PC to return.
792 DCHECK_NE(core_spill_mask_ & (1 << kLrCode), 0U);
793 uint32_t pop_mask = (core_spill_mask_ & (~(1 << kLrCode))) | 1 << kPcCode;
794 __ Pop(RegisterList(pop_mask));
795 GetAssembler()->cfi().RestoreState();
796 GetAssembler()->cfi().DefCFAOffset(GetFrameSize());
797}
798
799void CodeGeneratorARMVIXL::Bind(HBasicBlock* block) {
800 __ Bind(GetLabelOf(block));
801}
802
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100803void CodeGeneratorARMVIXL::Move32(Location destination, Location source) {
804 if (source.Equals(destination)) {
805 return;
806 }
807 if (destination.IsRegister()) {
808 if (source.IsRegister()) {
809 __ Mov(RegisterFrom(destination), RegisterFrom(source));
810 } else if (source.IsFpuRegister()) {
811 __ Vmov(RegisterFrom(destination), SRegisterFrom(source));
812 } else {
813 GetAssembler()->LoadFromOffset(kLoadWord,
814 RegisterFrom(destination),
815 sp,
816 source.GetStackIndex());
817 }
818 } else if (destination.IsFpuRegister()) {
819 if (source.IsRegister()) {
820 __ Vmov(SRegisterFrom(destination), RegisterFrom(source));
821 } else if (source.IsFpuRegister()) {
822 __ Vmov(SRegisterFrom(destination), SRegisterFrom(source));
823 } else {
824 GetAssembler()->LoadSFromOffset(SRegisterFrom(destination), sp, source.GetStackIndex());
825 }
826 } else {
827 DCHECK(destination.IsStackSlot()) << destination;
828 if (source.IsRegister()) {
829 GetAssembler()->StoreToOffset(kStoreWord,
830 RegisterFrom(source),
831 sp,
832 destination.GetStackIndex());
833 } else if (source.IsFpuRegister()) {
834 GetAssembler()->StoreSToOffset(SRegisterFrom(source), sp, destination.GetStackIndex());
835 } else {
836 DCHECK(source.IsStackSlot()) << source;
837 UseScratchRegisterScope temps(GetVIXLAssembler());
838 vixl32::Register temp = temps.Acquire();
839 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, source.GetStackIndex());
840 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
841 }
842 }
843}
844
845void CodeGeneratorARMVIXL::MoveConstant(Location destination ATTRIBUTE_UNUSED,
846 int32_t value ATTRIBUTE_UNUSED) {
Scott Wakelingfe885462016-09-22 10:24:38 +0100847 TODO_VIXL32(FATAL);
848}
849
850void CodeGeneratorARMVIXL::MoveLocation(Location dst, Location src, Primitive::Type dst_type) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100851 // TODO(VIXL): Maybe refactor to have the 'move' implementation here and use it in
852 // `ParallelMoveResolverARMVIXL::EmitMove`, as is done in the `arm64` backend.
853 HParallelMove move(GetGraph()->GetArena());
854 move.AddMove(src, dst, dst_type, nullptr);
855 GetMoveResolver()->EmitNativeCode(&move);
Scott Wakelingfe885462016-09-22 10:24:38 +0100856}
857
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100858void CodeGeneratorARMVIXL::AddLocationAsTemp(Location location ATTRIBUTE_UNUSED,
859 LocationSummary* locations ATTRIBUTE_UNUSED) {
Scott Wakelingfe885462016-09-22 10:24:38 +0100860 TODO_VIXL32(FATAL);
861}
862
863void CodeGeneratorARMVIXL::InvokeRuntime(QuickEntrypointEnum entrypoint,
864 HInstruction* instruction,
865 uint32_t dex_pc,
866 SlowPathCode* slow_path) {
867 ValidateInvokeRuntime(entrypoint, instruction, slow_path);
868 GenerateInvokeRuntime(GetThreadOffset<kArmPointerSize>(entrypoint).Int32Value());
869 if (EntrypointRequiresStackMap(entrypoint)) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100870 // TODO(VIXL): If necessary, use a scope to ensure we record the pc info immediately after the
871 // previous instruction.
Scott Wakelingfe885462016-09-22 10:24:38 +0100872 RecordPcInfo(instruction, dex_pc, slow_path);
873 }
874}
875
876void CodeGeneratorARMVIXL::InvokeRuntimeWithoutRecordingPcInfo(int32_t entry_point_offset,
877 HInstruction* instruction,
878 SlowPathCode* slow_path) {
879 ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction, slow_path);
880 GenerateInvokeRuntime(entry_point_offset);
881}
882
883void CodeGeneratorARMVIXL::GenerateInvokeRuntime(int32_t entry_point_offset) {
884 GetAssembler()->LoadFromOffset(kLoadWord, lr, tr, entry_point_offset);
885 __ Blx(lr);
886}
887
Scott Wakelingfe885462016-09-22 10:24:38 +0100888void InstructionCodeGeneratorARMVIXL::HandleGoto(HInstruction* got, HBasicBlock* successor) {
889 DCHECK(!successor->IsExitBlock());
890 HBasicBlock* block = got->GetBlock();
891 HInstruction* previous = got->GetPrevious();
892 HLoopInformation* info = block->GetLoopInformation();
893
894 if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) {
895 codegen_->ClearSpillSlotsFromLoopPhisInStackMap(info->GetSuspendCheck());
896 GenerateSuspendCheck(info->GetSuspendCheck(), successor);
897 return;
898 }
899 if (block->IsEntryBlock() && (previous != nullptr) && previous->IsSuspendCheck()) {
900 GenerateSuspendCheck(previous->AsSuspendCheck(), nullptr);
901 }
902 if (!codegen_->GoesToNextBlock(block, successor)) {
903 __ B(codegen_->GetLabelOf(successor));
904 }
905}
906
907void LocationsBuilderARMVIXL::VisitGoto(HGoto* got) {
908 got->SetLocations(nullptr);
909}
910
911void InstructionCodeGeneratorARMVIXL::VisitGoto(HGoto* got) {
912 HandleGoto(got, got->GetSuccessor());
913}
914
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100915void LocationsBuilderARMVIXL::VisitTryBoundary(HTryBoundary* try_boundary) {
916 try_boundary->SetLocations(nullptr);
917}
918
919void InstructionCodeGeneratorARMVIXL::VisitTryBoundary(HTryBoundary* try_boundary) {
920 HBasicBlock* successor = try_boundary->GetNormalFlowSuccessor();
921 if (!successor->IsExitBlock()) {
922 HandleGoto(try_boundary, successor);
923 }
924}
925
Scott Wakelingfe885462016-09-22 10:24:38 +0100926void LocationsBuilderARMVIXL::VisitExit(HExit* exit) {
927 exit->SetLocations(nullptr);
928}
929
930void InstructionCodeGeneratorARMVIXL::VisitExit(HExit* exit ATTRIBUTE_UNUSED) {
931}
932
933void InstructionCodeGeneratorARMVIXL::GenerateVcmp(HInstruction* instruction) {
934 Primitive::Type type = instruction->InputAt(0)->GetType();
935 Location lhs_loc = instruction->GetLocations()->InAt(0);
936 Location rhs_loc = instruction->GetLocations()->InAt(1);
937 if (rhs_loc.IsConstant()) {
938 // 0.0 is the only immediate that can be encoded directly in
939 // a VCMP instruction.
940 //
941 // Both the JLS (section 15.20.1) and the JVMS (section 6.5)
942 // specify that in a floating-point comparison, positive zero
943 // and negative zero are considered equal, so we can use the
944 // literal 0.0 for both cases here.
945 //
946 // Note however that some methods (Float.equal, Float.compare,
947 // Float.compareTo, Double.equal, Double.compare,
948 // Double.compareTo, Math.max, Math.min, StrictMath.max,
949 // StrictMath.min) consider 0.0 to be (strictly) greater than
950 // -0.0. So if we ever translate calls to these methods into a
951 // HCompare instruction, we must handle the -0.0 case with
952 // care here.
953 DCHECK(rhs_loc.GetConstant()->IsArithmeticZero());
954 if (type == Primitive::kPrimFloat) {
955 __ Vcmp(F32, InputSRegisterAt(instruction, 0), 0.0);
956 } else {
957 DCHECK_EQ(type, Primitive::kPrimDouble);
Scott Wakelingc34dba72016-10-03 10:14:44 +0100958 __ Vcmp(F64, DRegisterFrom(lhs_loc), 0.0);
Scott Wakelingfe885462016-09-22 10:24:38 +0100959 }
960 } else {
961 if (type == Primitive::kPrimFloat) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100962 __ Vcmp(InputSRegisterAt(instruction, 0), InputSRegisterAt(instruction, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +0100963 } else {
964 DCHECK_EQ(type, Primitive::kPrimDouble);
Scott Wakelingc34dba72016-10-03 10:14:44 +0100965 __ Vcmp(DRegisterFrom(lhs_loc), DRegisterFrom(rhs_loc));
Scott Wakelingfe885462016-09-22 10:24:38 +0100966 }
967 }
968}
969
970void InstructionCodeGeneratorARMVIXL::GenerateFPJumps(HCondition* cond,
971 vixl32::Label* true_label,
972 vixl32::Label* false_label ATTRIBUTE_UNUSED) {
973 // To branch on the result of the FP compare we transfer FPSCR to APSR (encoded as PC in VMRS).
974 __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
975 __ B(ARMFPCondition(cond->GetCondition(), cond->IsGtBias()), true_label);
976}
977
978void InstructionCodeGeneratorARMVIXL::GenerateLongComparesAndJumps(HCondition* cond,
979 vixl32::Label* true_label,
980 vixl32::Label* false_label) {
981 LocationSummary* locations = cond->GetLocations();
982 Location left = locations->InAt(0);
983 Location right = locations->InAt(1);
984 IfCondition if_cond = cond->GetCondition();
985
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100986 vixl32::Register left_high = HighRegisterFrom(left);
987 vixl32::Register left_low = LowRegisterFrom(left);
Scott Wakelingfe885462016-09-22 10:24:38 +0100988 IfCondition true_high_cond = if_cond;
989 IfCondition false_high_cond = cond->GetOppositeCondition();
990 vixl32::Condition final_condition = ARMUnsignedCondition(if_cond); // unsigned on lower part
991
992 // Set the conditions for the test, remembering that == needs to be
993 // decided using the low words.
994 // TODO: consider avoiding jumps with temporary and CMP low+SBC high
995 switch (if_cond) {
996 case kCondEQ:
997 case kCondNE:
998 // Nothing to do.
999 break;
1000 case kCondLT:
1001 false_high_cond = kCondGT;
1002 break;
1003 case kCondLE:
1004 true_high_cond = kCondLT;
1005 break;
1006 case kCondGT:
1007 false_high_cond = kCondLT;
1008 break;
1009 case kCondGE:
1010 true_high_cond = kCondGT;
1011 break;
1012 case kCondB:
1013 false_high_cond = kCondA;
1014 break;
1015 case kCondBE:
1016 true_high_cond = kCondB;
1017 break;
1018 case kCondA:
1019 false_high_cond = kCondB;
1020 break;
1021 case kCondAE:
1022 true_high_cond = kCondA;
1023 break;
1024 }
1025 if (right.IsConstant()) {
1026 int64_t value = right.GetConstant()->AsLongConstant()->GetValue();
1027 int32_t val_low = Low32Bits(value);
1028 int32_t val_high = High32Bits(value);
1029
1030 __ Cmp(left_high, val_high);
1031 if (if_cond == kCondNE) {
1032 __ B(ARMCondition(true_high_cond), true_label);
1033 } else if (if_cond == kCondEQ) {
1034 __ B(ARMCondition(false_high_cond), false_label);
1035 } else {
1036 __ B(ARMCondition(true_high_cond), true_label);
1037 __ B(ARMCondition(false_high_cond), false_label);
1038 }
1039 // Must be equal high, so compare the lows.
1040 __ Cmp(left_low, val_low);
1041 } else {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001042 vixl32::Register right_high = HighRegisterFrom(right);
1043 vixl32::Register right_low = LowRegisterFrom(right);
Scott Wakelingfe885462016-09-22 10:24:38 +01001044
1045 __ Cmp(left_high, right_high);
1046 if (if_cond == kCondNE) {
1047 __ B(ARMCondition(true_high_cond), true_label);
1048 } else if (if_cond == kCondEQ) {
1049 __ B(ARMCondition(false_high_cond), false_label);
1050 } else {
1051 __ B(ARMCondition(true_high_cond), true_label);
1052 __ B(ARMCondition(false_high_cond), false_label);
1053 }
1054 // Must be equal high, so compare the lows.
1055 __ Cmp(left_low, right_low);
1056 }
1057 // The last comparison might be unsigned.
1058 // TODO: optimize cases where this is always true/false
1059 __ B(final_condition, true_label);
1060}
1061
1062void InstructionCodeGeneratorARMVIXL::GenerateCompareTestAndBranch(HCondition* condition,
1063 vixl32::Label* true_target_in,
1064 vixl32::Label* false_target_in) {
1065 // Generated branching requires both targets to be explicit. If either of the
1066 // targets is nullptr (fallthrough) use and bind `fallthrough` instead.
1067 vixl32::Label fallthrough;
1068 vixl32::Label* true_target = (true_target_in == nullptr) ? &fallthrough : true_target_in;
1069 vixl32::Label* false_target = (false_target_in == nullptr) ? &fallthrough : false_target_in;
1070
1071 Primitive::Type type = condition->InputAt(0)->GetType();
1072 switch (type) {
1073 case Primitive::kPrimLong:
1074 GenerateLongComparesAndJumps(condition, true_target, false_target);
1075 break;
1076 case Primitive::kPrimFloat:
1077 case Primitive::kPrimDouble:
1078 GenerateVcmp(condition);
1079 GenerateFPJumps(condition, true_target, false_target);
1080 break;
1081 default:
1082 LOG(FATAL) << "Unexpected compare type " << type;
1083 }
1084
1085 if (false_target != &fallthrough) {
1086 __ B(false_target);
1087 }
1088
1089 if (true_target_in == nullptr || false_target_in == nullptr) {
1090 __ Bind(&fallthrough);
1091 }
1092}
1093
1094void InstructionCodeGeneratorARMVIXL::GenerateTestAndBranch(HInstruction* instruction,
1095 size_t condition_input_index,
1096 vixl32::Label* true_target,
1097 vixl32::Label* false_target) {
1098 HInstruction* cond = instruction->InputAt(condition_input_index);
1099
1100 if (true_target == nullptr && false_target == nullptr) {
1101 // Nothing to do. The code always falls through.
1102 return;
1103 } else if (cond->IsIntConstant()) {
1104 // Constant condition, statically compared against "true" (integer value 1).
1105 if (cond->AsIntConstant()->IsTrue()) {
1106 if (true_target != nullptr) {
1107 __ B(true_target);
1108 }
1109 } else {
1110 DCHECK(cond->AsIntConstant()->IsFalse()) << cond->AsIntConstant()->GetValue();
1111 if (false_target != nullptr) {
1112 __ B(false_target);
1113 }
1114 }
1115 return;
1116 }
1117
1118 // The following code generates these patterns:
1119 // (1) true_target == nullptr && false_target != nullptr
1120 // - opposite condition true => branch to false_target
1121 // (2) true_target != nullptr && false_target == nullptr
1122 // - condition true => branch to true_target
1123 // (3) true_target != nullptr && false_target != nullptr
1124 // - condition true => branch to true_target
1125 // - branch to false_target
1126 if (IsBooleanValueOrMaterializedCondition(cond)) {
1127 // Condition has been materialized, compare the output to 0.
1128 if (kIsDebugBuild) {
1129 Location cond_val = instruction->GetLocations()->InAt(condition_input_index);
1130 DCHECK(cond_val.IsRegister());
1131 }
1132 if (true_target == nullptr) {
1133 __ Cbz(InputRegisterAt(instruction, condition_input_index), false_target);
1134 } else {
1135 __ Cbnz(InputRegisterAt(instruction, condition_input_index), true_target);
1136 }
1137 } else {
1138 // Condition has not been materialized. Use its inputs as the comparison and
1139 // its condition as the branch condition.
1140 HCondition* condition = cond->AsCondition();
1141
1142 // If this is a long or FP comparison that has been folded into
1143 // the HCondition, generate the comparison directly.
1144 Primitive::Type type = condition->InputAt(0)->GetType();
1145 if (type == Primitive::kPrimLong || Primitive::IsFloatingPointType(type)) {
1146 GenerateCompareTestAndBranch(condition, true_target, false_target);
1147 return;
1148 }
1149
1150 LocationSummary* locations = cond->GetLocations();
1151 DCHECK(locations->InAt(0).IsRegister());
1152 vixl32::Register left = InputRegisterAt(cond, 0);
1153 Location right = locations->InAt(1);
1154 if (right.IsRegister()) {
1155 __ Cmp(left, InputRegisterAt(cond, 1));
1156 } else {
1157 DCHECK(right.IsConstant());
1158 __ Cmp(left, CodeGenerator::GetInt32ValueOf(right.GetConstant()));
1159 }
1160 if (true_target == nullptr) {
1161 __ B(ARMCondition(condition->GetOppositeCondition()), false_target);
1162 } else {
1163 __ B(ARMCondition(condition->GetCondition()), true_target);
1164 }
1165 }
1166
1167 // If neither branch falls through (case 3), the conditional branch to `true_target`
1168 // was already emitted (case 2) and we need to emit a jump to `false_target`.
1169 if (true_target != nullptr && false_target != nullptr) {
1170 __ B(false_target);
1171 }
1172}
1173
1174void LocationsBuilderARMVIXL::VisitIf(HIf* if_instr) {
1175 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr);
1176 if (IsBooleanValueOrMaterializedCondition(if_instr->InputAt(0))) {
1177 locations->SetInAt(0, Location::RequiresRegister());
1178 }
1179}
1180
1181void InstructionCodeGeneratorARMVIXL::VisitIf(HIf* if_instr) {
1182 HBasicBlock* true_successor = if_instr->IfTrueSuccessor();
1183 HBasicBlock* false_successor = if_instr->IfFalseSuccessor();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001184 vixl32::Label* true_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), true_successor) ?
1185 nullptr : codegen_->GetLabelOf(true_successor);
1186 vixl32::Label* false_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), false_successor) ?
1187 nullptr : codegen_->GetLabelOf(false_successor);
Scott Wakelingfe885462016-09-22 10:24:38 +01001188 GenerateTestAndBranch(if_instr, /* condition_input_index */ 0, true_target, false_target);
1189}
1190
Scott Wakelingc34dba72016-10-03 10:14:44 +01001191void LocationsBuilderARMVIXL::VisitDeoptimize(HDeoptimize* deoptimize) {
1192 LocationSummary* locations = new (GetGraph()->GetArena())
1193 LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath);
1194 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
1195 if (IsBooleanValueOrMaterializedCondition(deoptimize->InputAt(0))) {
1196 locations->SetInAt(0, Location::RequiresRegister());
1197 }
1198}
1199
1200void InstructionCodeGeneratorARMVIXL::VisitDeoptimize(HDeoptimize* deoptimize) {
1201 SlowPathCodeARMVIXL* slow_path =
1202 deopt_slow_paths_.NewSlowPath<DeoptimizationSlowPathARMVIXL>(deoptimize);
1203 GenerateTestAndBranch(deoptimize,
1204 /* condition_input_index */ 0,
1205 slow_path->GetEntryLabel(),
1206 /* false_target */ nullptr);
1207}
1208
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001209void LocationsBuilderARMVIXL::VisitSelect(HSelect* select) {
1210 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select);
1211 if (Primitive::IsFloatingPointType(select->GetType())) {
1212 locations->SetInAt(0, Location::RequiresFpuRegister());
1213 locations->SetInAt(1, Location::RequiresFpuRegister());
1214 } else {
1215 locations->SetInAt(0, Location::RequiresRegister());
1216 locations->SetInAt(1, Location::RequiresRegister());
1217 }
1218 if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) {
1219 locations->SetInAt(2, Location::RequiresRegister());
1220 }
1221 locations->SetOut(Location::SameAsFirstInput());
1222}
1223
1224void InstructionCodeGeneratorARMVIXL::VisitSelect(HSelect* select) {
1225 LocationSummary* locations = select->GetLocations();
1226 vixl32::Label false_target;
1227 GenerateTestAndBranch(select,
1228 /* condition_input_index */ 2,
1229 /* true_target */ nullptr,
1230 &false_target);
1231 codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType());
1232 __ Bind(&false_target);
1233}
1234
Scott Wakelingfe885462016-09-22 10:24:38 +01001235void CodeGeneratorARMVIXL::GenerateNop() {
1236 __ Nop();
1237}
1238
1239void LocationsBuilderARMVIXL::HandleCondition(HCondition* cond) {
1240 LocationSummary* locations =
1241 new (GetGraph()->GetArena()) LocationSummary(cond, LocationSummary::kNoCall);
1242 // Handle the long/FP comparisons made in instruction simplification.
1243 switch (cond->InputAt(0)->GetType()) {
1244 case Primitive::kPrimLong:
1245 locations->SetInAt(0, Location::RequiresRegister());
1246 locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
1247 if (!cond->IsEmittedAtUseSite()) {
1248 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
1249 }
1250 break;
1251
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001252 // TODO(VIXL): https://android-review.googlesource.com/#/c/252265/
Scott Wakelingfe885462016-09-22 10:24:38 +01001253 case Primitive::kPrimFloat:
1254 case Primitive::kPrimDouble:
1255 locations->SetInAt(0, Location::RequiresFpuRegister());
1256 locations->SetInAt(1, Location::RequiresFpuRegister());
1257 if (!cond->IsEmittedAtUseSite()) {
1258 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
1259 }
1260 break;
1261
1262 default:
1263 locations->SetInAt(0, Location::RequiresRegister());
1264 locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
1265 if (!cond->IsEmittedAtUseSite()) {
1266 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
1267 }
1268 }
1269}
1270
1271void InstructionCodeGeneratorARMVIXL::HandleCondition(HCondition* cond) {
1272 if (cond->IsEmittedAtUseSite()) {
1273 return;
1274 }
1275
Scott Wakelingfe885462016-09-22 10:24:38 +01001276 vixl32::Register out = OutputRegister(cond);
1277 vixl32::Label true_label, false_label;
1278
1279 switch (cond->InputAt(0)->GetType()) {
1280 default: {
1281 // Integer case.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001282 __ Cmp(InputRegisterAt(cond, 0), InputOperandAt(cond, 1));
1283 AssemblerAccurateScope aas(GetVIXLAssembler(),
1284 kArmInstrMaxSizeInBytes * 3u,
1285 CodeBufferCheckScope::kMaximumSize);
1286 __ ite(ARMCondition(cond->GetCondition()));
1287 __ mov(ARMCondition(cond->GetCondition()), OutputRegister(cond), 1);
1288 __ mov(ARMCondition(cond->GetOppositeCondition()), OutputRegister(cond), 0);
Scott Wakelingfe885462016-09-22 10:24:38 +01001289 return;
1290 }
1291 case Primitive::kPrimLong:
1292 GenerateLongComparesAndJumps(cond, &true_label, &false_label);
1293 break;
1294 case Primitive::kPrimFloat:
1295 case Primitive::kPrimDouble:
1296 GenerateVcmp(cond);
1297 GenerateFPJumps(cond, &true_label, &false_label);
1298 break;
1299 }
1300
1301 // Convert the jumps into the result.
1302 vixl32::Label done_label;
1303
1304 // False case: result = 0.
1305 __ Bind(&false_label);
1306 __ Mov(out, 0);
1307 __ B(&done_label);
1308
1309 // True case: result = 1.
1310 __ Bind(&true_label);
1311 __ Mov(out, 1);
1312 __ Bind(&done_label);
1313}
1314
1315void LocationsBuilderARMVIXL::VisitEqual(HEqual* comp) {
1316 HandleCondition(comp);
1317}
1318
1319void InstructionCodeGeneratorARMVIXL::VisitEqual(HEqual* comp) {
1320 HandleCondition(comp);
1321}
1322
1323void LocationsBuilderARMVIXL::VisitNotEqual(HNotEqual* comp) {
1324 HandleCondition(comp);
1325}
1326
1327void InstructionCodeGeneratorARMVIXL::VisitNotEqual(HNotEqual* comp) {
1328 HandleCondition(comp);
1329}
1330
1331void LocationsBuilderARMVIXL::VisitLessThan(HLessThan* comp) {
1332 HandleCondition(comp);
1333}
1334
1335void InstructionCodeGeneratorARMVIXL::VisitLessThan(HLessThan* comp) {
1336 HandleCondition(comp);
1337}
1338
1339void LocationsBuilderARMVIXL::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
1340 HandleCondition(comp);
1341}
1342
1343void InstructionCodeGeneratorARMVIXL::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
1344 HandleCondition(comp);
1345}
1346
1347void LocationsBuilderARMVIXL::VisitGreaterThan(HGreaterThan* comp) {
1348 HandleCondition(comp);
1349}
1350
1351void InstructionCodeGeneratorARMVIXL::VisitGreaterThan(HGreaterThan* comp) {
1352 HandleCondition(comp);
1353}
1354
1355void LocationsBuilderARMVIXL::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
1356 HandleCondition(comp);
1357}
1358
1359void InstructionCodeGeneratorARMVIXL::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
1360 HandleCondition(comp);
1361}
1362
1363void LocationsBuilderARMVIXL::VisitBelow(HBelow* comp) {
1364 HandleCondition(comp);
1365}
1366
1367void InstructionCodeGeneratorARMVIXL::VisitBelow(HBelow* comp) {
1368 HandleCondition(comp);
1369}
1370
1371void LocationsBuilderARMVIXL::VisitBelowOrEqual(HBelowOrEqual* comp) {
1372 HandleCondition(comp);
1373}
1374
1375void InstructionCodeGeneratorARMVIXL::VisitBelowOrEqual(HBelowOrEqual* comp) {
1376 HandleCondition(comp);
1377}
1378
1379void LocationsBuilderARMVIXL::VisitAbove(HAbove* comp) {
1380 HandleCondition(comp);
1381}
1382
1383void InstructionCodeGeneratorARMVIXL::VisitAbove(HAbove* comp) {
1384 HandleCondition(comp);
1385}
1386
1387void LocationsBuilderARMVIXL::VisitAboveOrEqual(HAboveOrEqual* comp) {
1388 HandleCondition(comp);
1389}
1390
1391void InstructionCodeGeneratorARMVIXL::VisitAboveOrEqual(HAboveOrEqual* comp) {
1392 HandleCondition(comp);
1393}
1394
1395void LocationsBuilderARMVIXL::VisitIntConstant(HIntConstant* constant) {
1396 LocationSummary* locations =
1397 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
1398 locations->SetOut(Location::ConstantLocation(constant));
1399}
1400
1401void InstructionCodeGeneratorARMVIXL::VisitIntConstant(HIntConstant* constant ATTRIBUTE_UNUSED) {
1402 // Will be generated at use site.
1403}
1404
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001405void LocationsBuilderARMVIXL::VisitNullConstant(HNullConstant* constant) {
1406 LocationSummary* locations =
1407 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
1408 locations->SetOut(Location::ConstantLocation(constant));
1409}
1410
1411void InstructionCodeGeneratorARMVIXL::VisitNullConstant(HNullConstant* constant ATTRIBUTE_UNUSED) {
1412 // Will be generated at use site.
1413}
1414
Scott Wakelingfe885462016-09-22 10:24:38 +01001415void LocationsBuilderARMVIXL::VisitLongConstant(HLongConstant* constant) {
1416 LocationSummary* locations =
1417 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
1418 locations->SetOut(Location::ConstantLocation(constant));
1419}
1420
1421void InstructionCodeGeneratorARMVIXL::VisitLongConstant(HLongConstant* constant ATTRIBUTE_UNUSED) {
1422 // Will be generated at use site.
1423}
1424
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01001425void LocationsBuilderARMVIXL::VisitFloatConstant(HFloatConstant* constant) {
1426 LocationSummary* locations =
1427 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
1428 locations->SetOut(Location::ConstantLocation(constant));
1429}
1430
Scott Wakelingc34dba72016-10-03 10:14:44 +01001431void InstructionCodeGeneratorARMVIXL::VisitFloatConstant(
1432 HFloatConstant* constant ATTRIBUTE_UNUSED) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01001433 // Will be generated at use site.
1434}
1435
1436void LocationsBuilderARMVIXL::VisitDoubleConstant(HDoubleConstant* constant) {
1437 LocationSummary* locations =
1438 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
1439 locations->SetOut(Location::ConstantLocation(constant));
1440}
1441
Scott Wakelingc34dba72016-10-03 10:14:44 +01001442void InstructionCodeGeneratorARMVIXL::VisitDoubleConstant(
1443 HDoubleConstant* constant ATTRIBUTE_UNUSED) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01001444 // Will be generated at use site.
1445}
1446
Scott Wakelingfe885462016-09-22 10:24:38 +01001447void LocationsBuilderARMVIXL::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
1448 memory_barrier->SetLocations(nullptr);
1449}
1450
1451void InstructionCodeGeneratorARMVIXL::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
1452 codegen_->GenerateMemoryBarrier(memory_barrier->GetBarrierKind());
1453}
1454
1455void LocationsBuilderARMVIXL::VisitReturnVoid(HReturnVoid* ret) {
1456 ret->SetLocations(nullptr);
1457}
1458
1459void InstructionCodeGeneratorARMVIXL::VisitReturnVoid(HReturnVoid* ret ATTRIBUTE_UNUSED) {
1460 codegen_->GenerateFrameExit();
1461}
1462
1463void LocationsBuilderARMVIXL::VisitReturn(HReturn* ret) {
1464 LocationSummary* locations =
1465 new (GetGraph()->GetArena()) LocationSummary(ret, LocationSummary::kNoCall);
1466 locations->SetInAt(0, parameter_visitor_.GetReturnLocation(ret->InputAt(0)->GetType()));
1467}
1468
1469void InstructionCodeGeneratorARMVIXL::VisitReturn(HReturn* ret ATTRIBUTE_UNUSED) {
1470 codegen_->GenerateFrameExit();
1471}
1472
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001473void LocationsBuilderARMVIXL::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
1474 // Explicit clinit checks triggered by static invokes must have been pruned by
1475 // art::PrepareForRegisterAllocation.
1476 DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
1477
Anton Kirilov5ec62182016-10-13 20:16:02 +01001478 IntrinsicLocationsBuilderARMVIXL intrinsic(codegen_);
1479 if (intrinsic.TryDispatch(invoke)) {
1480 if (invoke->GetLocations()->CanCall() && invoke->HasPcRelativeDexCache()) {
1481 invoke->GetLocations()->SetInAt(invoke->GetSpecialInputIndex(), Location::Any());
1482 }
1483 return;
1484 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001485
1486 HandleInvoke(invoke);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01001487
1488 // TODO(VIXL): invoke->HasPcRelativeDexCache()
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001489}
1490
Anton Kirilov5ec62182016-10-13 20:16:02 +01001491static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorARMVIXL* codegen) {
1492 if (invoke->GetLocations()->Intrinsified()) {
1493 IntrinsicCodeGeneratorARMVIXL intrinsic(codegen);
1494 intrinsic.Dispatch(invoke);
1495 return true;
1496 }
1497 return false;
1498}
1499
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001500void InstructionCodeGeneratorARMVIXL::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
1501 // Explicit clinit checks triggered by static invokes must have been pruned by
1502 // art::PrepareForRegisterAllocation.
1503 DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
1504
Anton Kirilov5ec62182016-10-13 20:16:02 +01001505 if (TryGenerateIntrinsicCode(invoke, codegen_)) {
1506 return;
1507 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001508
1509 LocationSummary* locations = invoke->GetLocations();
1510 DCHECK(locations->HasTemps());
1511 codegen_->GenerateStaticOrDirectCall(invoke, locations->GetTemp(0));
1512 // TODO(VIXL): If necessary, use a scope to ensure we record the pc info immediately after the
1513 // previous instruction.
1514 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
1515}
1516
1517void LocationsBuilderARMVIXL::HandleInvoke(HInvoke* invoke) {
1518 InvokeDexCallingConventionVisitorARM calling_convention_visitor;
1519 CodeGenerator::CreateCommonInvokeLocationSummary(invoke, &calling_convention_visitor);
1520}
1521
1522void LocationsBuilderARMVIXL::VisitInvokeVirtual(HInvokeVirtual* invoke) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01001523 IntrinsicLocationsBuilderARMVIXL intrinsic(codegen_);
1524 if (intrinsic.TryDispatch(invoke)) {
1525 return;
1526 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001527
1528 HandleInvoke(invoke);
1529}
1530
1531void InstructionCodeGeneratorARMVIXL::VisitInvokeVirtual(HInvokeVirtual* invoke) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01001532 if (TryGenerateIntrinsicCode(invoke, codegen_)) {
1533 return;
1534 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001535
1536 codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0));
1537 DCHECK(!codegen_->IsLeafMethod());
1538 // TODO(VIXL): If necessary, use a scope to ensure we record the pc info immediately after the
1539 // previous instruction.
1540 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
1541}
1542
Artem Serov02109dd2016-09-23 17:17:54 +01001543void LocationsBuilderARMVIXL::VisitNeg(HNeg* neg) {
1544 LocationSummary* locations =
1545 new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall);
1546 switch (neg->GetResultType()) {
1547 case Primitive::kPrimInt: {
1548 locations->SetInAt(0, Location::RequiresRegister());
1549 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
1550 break;
1551 }
1552 case Primitive::kPrimLong: {
1553 locations->SetInAt(0, Location::RequiresRegister());
1554 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
1555 break;
1556 }
1557
1558 case Primitive::kPrimFloat:
1559 case Primitive::kPrimDouble:
1560 locations->SetInAt(0, Location::RequiresFpuRegister());
1561 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
1562 break;
1563
1564 default:
1565 LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
1566 }
1567}
1568
1569void InstructionCodeGeneratorARMVIXL::VisitNeg(HNeg* neg) {
1570 LocationSummary* locations = neg->GetLocations();
1571 Location out = locations->Out();
1572 Location in = locations->InAt(0);
1573 switch (neg->GetResultType()) {
1574 case Primitive::kPrimInt:
1575 __ Rsb(OutputRegister(neg), InputRegisterAt(neg, 0), 0);
1576 break;
1577
1578 case Primitive::kPrimLong:
1579 // out.lo = 0 - in.lo (and update the carry/borrow (C) flag)
1580 __ Rsbs(LowRegisterFrom(out), LowRegisterFrom(in), 0);
1581 // We cannot emit an RSC (Reverse Subtract with Carry)
1582 // instruction here, as it does not exist in the Thumb-2
1583 // instruction set. We use the following approach
1584 // using SBC and SUB instead.
1585 //
1586 // out.hi = -C
1587 __ Sbc(HighRegisterFrom(out), HighRegisterFrom(out), HighRegisterFrom(out));
1588 // out.hi = out.hi - in.hi
1589 __ Sub(HighRegisterFrom(out), HighRegisterFrom(out), HighRegisterFrom(in));
1590 break;
1591
1592 case Primitive::kPrimFloat:
1593 case Primitive::kPrimDouble:
Anton Kirilove28d9ae2016-10-25 18:17:23 +01001594 // TODO(VIXL): Consider introducing an InputVRegister()
1595 // helper function (equivalent to InputRegister()).
Artem Serov02109dd2016-09-23 17:17:54 +01001596 __ Vneg(OutputVRegister(neg), InputVRegisterAt(neg, 0));
1597 break;
1598
1599 default:
1600 LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
1601 }
1602}
1603
Scott Wakelingfe885462016-09-22 10:24:38 +01001604void LocationsBuilderARMVIXL::VisitTypeConversion(HTypeConversion* conversion) {
1605 Primitive::Type result_type = conversion->GetResultType();
1606 Primitive::Type input_type = conversion->GetInputType();
1607 DCHECK_NE(result_type, input_type);
1608
1609 // The float-to-long, double-to-long and long-to-float type conversions
1610 // rely on a call to the runtime.
1611 LocationSummary::CallKind call_kind =
1612 (((input_type == Primitive::kPrimFloat || input_type == Primitive::kPrimDouble)
1613 && result_type == Primitive::kPrimLong)
1614 || (input_type == Primitive::kPrimLong && result_type == Primitive::kPrimFloat))
1615 ? LocationSummary::kCallOnMainOnly
1616 : LocationSummary::kNoCall;
1617 LocationSummary* locations =
1618 new (GetGraph()->GetArena()) LocationSummary(conversion, call_kind);
1619
1620 // The Java language does not allow treating boolean as an integral type but
1621 // our bit representation makes it safe.
1622
1623 switch (result_type) {
1624 case Primitive::kPrimByte:
1625 switch (input_type) {
1626 case Primitive::kPrimLong:
1627 // Type conversion from long to byte is a result of code transformations.
1628 case Primitive::kPrimBoolean:
1629 // Boolean input is a result of code transformations.
1630 case Primitive::kPrimShort:
1631 case Primitive::kPrimInt:
1632 case Primitive::kPrimChar:
1633 // Processing a Dex `int-to-byte' instruction.
1634 locations->SetInAt(0, Location::RequiresRegister());
1635 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
1636 break;
1637
1638 default:
1639 LOG(FATAL) << "Unexpected type conversion from " << input_type
1640 << " to " << result_type;
1641 }
1642 break;
1643
1644 case Primitive::kPrimShort:
1645 switch (input_type) {
1646 case Primitive::kPrimLong:
1647 // Type conversion from long to short is a result of code transformations.
1648 case Primitive::kPrimBoolean:
1649 // Boolean input is a result of code transformations.
1650 case Primitive::kPrimByte:
1651 case Primitive::kPrimInt:
1652 case Primitive::kPrimChar:
1653 // Processing a Dex `int-to-short' instruction.
1654 locations->SetInAt(0, Location::RequiresRegister());
1655 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
1656 break;
1657
1658 default:
1659 LOG(FATAL) << "Unexpected type conversion from " << input_type
1660 << " to " << result_type;
1661 }
1662 break;
1663
1664 case Primitive::kPrimInt:
1665 switch (input_type) {
1666 case Primitive::kPrimLong:
1667 // Processing a Dex `long-to-int' instruction.
1668 locations->SetInAt(0, Location::Any());
1669 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
1670 break;
1671
1672 case Primitive::kPrimFloat:
1673 // Processing a Dex `float-to-int' instruction.
1674 locations->SetInAt(0, Location::RequiresFpuRegister());
1675 locations->SetOut(Location::RequiresRegister());
1676 locations->AddTemp(Location::RequiresFpuRegister());
1677 break;
1678
1679 case Primitive::kPrimDouble:
1680 // Processing a Dex `double-to-int' instruction.
1681 locations->SetInAt(0, Location::RequiresFpuRegister());
1682 locations->SetOut(Location::RequiresRegister());
1683 locations->AddTemp(Location::RequiresFpuRegister());
1684 break;
1685
1686 default:
1687 LOG(FATAL) << "Unexpected type conversion from " << input_type
1688 << " to " << result_type;
1689 }
1690 break;
1691
1692 case Primitive::kPrimLong:
1693 switch (input_type) {
1694 case Primitive::kPrimBoolean:
1695 // Boolean input is a result of code transformations.
1696 case Primitive::kPrimByte:
1697 case Primitive::kPrimShort:
1698 case Primitive::kPrimInt:
1699 case Primitive::kPrimChar:
1700 // Processing a Dex `int-to-long' instruction.
1701 locations->SetInAt(0, Location::RequiresRegister());
1702 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
1703 break;
1704
1705 case Primitive::kPrimFloat: {
1706 // Processing a Dex `float-to-long' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001707 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1708 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
1709 locations->SetOut(LocationFrom(r0, r1));
Scott Wakelingfe885462016-09-22 10:24:38 +01001710 break;
1711 }
1712
1713 case Primitive::kPrimDouble: {
1714 // Processing a Dex `double-to-long' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001715 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1716 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0),
1717 calling_convention.GetFpuRegisterAt(1)));
1718 locations->SetOut(LocationFrom(r0, r1));
Scott Wakelingfe885462016-09-22 10:24:38 +01001719 break;
1720 }
1721
1722 default:
1723 LOG(FATAL) << "Unexpected type conversion from " << input_type
1724 << " to " << result_type;
1725 }
1726 break;
1727
1728 case Primitive::kPrimChar:
1729 switch (input_type) {
1730 case Primitive::kPrimLong:
1731 // Type conversion from long to char is a result of code transformations.
1732 case Primitive::kPrimBoolean:
1733 // Boolean input is a result of code transformations.
1734 case Primitive::kPrimByte:
1735 case Primitive::kPrimShort:
1736 case Primitive::kPrimInt:
1737 // Processing a Dex `int-to-char' instruction.
1738 locations->SetInAt(0, Location::RequiresRegister());
1739 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
1740 break;
1741
1742 default:
1743 LOG(FATAL) << "Unexpected type conversion from " << input_type
1744 << " to " << result_type;
1745 }
1746 break;
1747
1748 case Primitive::kPrimFloat:
1749 switch (input_type) {
1750 case Primitive::kPrimBoolean:
1751 // Boolean input is a result of code transformations.
1752 case Primitive::kPrimByte:
1753 case Primitive::kPrimShort:
1754 case Primitive::kPrimInt:
1755 case Primitive::kPrimChar:
1756 // Processing a Dex `int-to-float' instruction.
1757 locations->SetInAt(0, Location::RequiresRegister());
1758 locations->SetOut(Location::RequiresFpuRegister());
1759 break;
1760
1761 case Primitive::kPrimLong: {
1762 // Processing a Dex `long-to-float' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001763 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1764 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0),
1765 calling_convention.GetRegisterAt(1)));
1766 locations->SetOut(LocationFrom(calling_convention.GetFpuRegisterAt(0)));
Scott Wakelingfe885462016-09-22 10:24:38 +01001767 break;
1768 }
1769
1770 case Primitive::kPrimDouble:
1771 // Processing a Dex `double-to-float' instruction.
1772 locations->SetInAt(0, Location::RequiresFpuRegister());
1773 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
1774 break;
1775
1776 default:
1777 LOG(FATAL) << "Unexpected type conversion from " << input_type
1778 << " to " << result_type;
1779 };
1780 break;
1781
1782 case Primitive::kPrimDouble:
1783 switch (input_type) {
1784 case Primitive::kPrimBoolean:
1785 // Boolean input is a result of code transformations.
1786 case Primitive::kPrimByte:
1787 case Primitive::kPrimShort:
1788 case Primitive::kPrimInt:
1789 case Primitive::kPrimChar:
1790 // Processing a Dex `int-to-double' instruction.
1791 locations->SetInAt(0, Location::RequiresRegister());
1792 locations->SetOut(Location::RequiresFpuRegister());
1793 break;
1794
1795 case Primitive::kPrimLong:
1796 // Processing a Dex `long-to-double' instruction.
1797 locations->SetInAt(0, Location::RequiresRegister());
1798 locations->SetOut(Location::RequiresFpuRegister());
1799 locations->AddTemp(Location::RequiresFpuRegister());
1800 locations->AddTemp(Location::RequiresFpuRegister());
1801 break;
1802
1803 case Primitive::kPrimFloat:
1804 // Processing a Dex `float-to-double' instruction.
1805 locations->SetInAt(0, Location::RequiresFpuRegister());
1806 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
1807 break;
1808
1809 default:
1810 LOG(FATAL) << "Unexpected type conversion from " << input_type
1811 << " to " << result_type;
1812 };
1813 break;
1814
1815 default:
1816 LOG(FATAL) << "Unexpected type conversion from " << input_type
1817 << " to " << result_type;
1818 }
1819}
1820
1821void InstructionCodeGeneratorARMVIXL::VisitTypeConversion(HTypeConversion* conversion) {
1822 LocationSummary* locations = conversion->GetLocations();
1823 Location out = locations->Out();
1824 Location in = locations->InAt(0);
1825 Primitive::Type result_type = conversion->GetResultType();
1826 Primitive::Type input_type = conversion->GetInputType();
1827 DCHECK_NE(result_type, input_type);
1828 switch (result_type) {
1829 case Primitive::kPrimByte:
1830 switch (input_type) {
1831 case Primitive::kPrimLong:
1832 // Type conversion from long to byte is a result of code transformations.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001833 __ Sbfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 8);
Scott Wakelingfe885462016-09-22 10:24:38 +01001834 break;
1835 case Primitive::kPrimBoolean:
1836 // Boolean input is a result of code transformations.
1837 case Primitive::kPrimShort:
1838 case Primitive::kPrimInt:
1839 case Primitive::kPrimChar:
1840 // Processing a Dex `int-to-byte' instruction.
1841 __ Sbfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 8);
1842 break;
1843
1844 default:
1845 LOG(FATAL) << "Unexpected type conversion from " << input_type
1846 << " to " << result_type;
1847 }
1848 break;
1849
1850 case Primitive::kPrimShort:
1851 switch (input_type) {
1852 case Primitive::kPrimLong:
1853 // Type conversion from long to short is a result of code transformations.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001854 __ Sbfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 16);
Scott Wakelingfe885462016-09-22 10:24:38 +01001855 break;
1856 case Primitive::kPrimBoolean:
1857 // Boolean input is a result of code transformations.
1858 case Primitive::kPrimByte:
1859 case Primitive::kPrimInt:
1860 case Primitive::kPrimChar:
1861 // Processing a Dex `int-to-short' instruction.
1862 __ Sbfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 16);
1863 break;
1864
1865 default:
1866 LOG(FATAL) << "Unexpected type conversion from " << input_type
1867 << " to " << result_type;
1868 }
1869 break;
1870
1871 case Primitive::kPrimInt:
1872 switch (input_type) {
1873 case Primitive::kPrimLong:
1874 // Processing a Dex `long-to-int' instruction.
1875 DCHECK(out.IsRegister());
1876 if (in.IsRegisterPair()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001877 __ Mov(OutputRegister(conversion), LowRegisterFrom(in));
Scott Wakelingfe885462016-09-22 10:24:38 +01001878 } else if (in.IsDoubleStackSlot()) {
1879 GetAssembler()->LoadFromOffset(kLoadWord,
1880 OutputRegister(conversion),
1881 sp,
1882 in.GetStackIndex());
1883 } else {
1884 DCHECK(in.IsConstant());
1885 DCHECK(in.GetConstant()->IsLongConstant());
1886 int64_t value = in.GetConstant()->AsLongConstant()->GetValue();
1887 __ Mov(OutputRegister(conversion), static_cast<int32_t>(value));
1888 }
1889 break;
1890
1891 case Primitive::kPrimFloat: {
1892 // Processing a Dex `float-to-int' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001893 vixl32::SRegister temp = LowSRegisterFrom(locations->GetTemp(0));
Scott Wakelingfe885462016-09-22 10:24:38 +01001894 __ Vcvt(I32, F32, temp, InputSRegisterAt(conversion, 0));
1895 __ Vmov(OutputRegister(conversion), temp);
1896 break;
1897 }
1898
1899 case Primitive::kPrimDouble: {
1900 // Processing a Dex `double-to-int' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001901 vixl32::SRegister temp_s = LowSRegisterFrom(locations->GetTemp(0));
Scott Wakelingc34dba72016-10-03 10:14:44 +01001902 __ Vcvt(I32, F64, temp_s, DRegisterFrom(in));
Scott Wakelingfe885462016-09-22 10:24:38 +01001903 __ Vmov(OutputRegister(conversion), temp_s);
1904 break;
1905 }
1906
1907 default:
1908 LOG(FATAL) << "Unexpected type conversion from " << input_type
1909 << " to " << result_type;
1910 }
1911 break;
1912
1913 case Primitive::kPrimLong:
1914 switch (input_type) {
1915 case Primitive::kPrimBoolean:
1916 // Boolean input is a result of code transformations.
1917 case Primitive::kPrimByte:
1918 case Primitive::kPrimShort:
1919 case Primitive::kPrimInt:
1920 case Primitive::kPrimChar:
1921 // Processing a Dex `int-to-long' instruction.
1922 DCHECK(out.IsRegisterPair());
1923 DCHECK(in.IsRegister());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001924 __ Mov(LowRegisterFrom(out), InputRegisterAt(conversion, 0));
Scott Wakelingfe885462016-09-22 10:24:38 +01001925 // Sign extension.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001926 __ Asr(HighRegisterFrom(out), LowRegisterFrom(out), 31);
Scott Wakelingfe885462016-09-22 10:24:38 +01001927 break;
1928
1929 case Primitive::kPrimFloat:
1930 // Processing a Dex `float-to-long' instruction.
1931 codegen_->InvokeRuntime(kQuickF2l, conversion, conversion->GetDexPc());
1932 CheckEntrypointTypes<kQuickF2l, int64_t, float>();
1933 break;
1934
1935 case Primitive::kPrimDouble:
1936 // Processing a Dex `double-to-long' instruction.
1937 codegen_->InvokeRuntime(kQuickD2l, conversion, conversion->GetDexPc());
1938 CheckEntrypointTypes<kQuickD2l, int64_t, double>();
1939 break;
1940
1941 default:
1942 LOG(FATAL) << "Unexpected type conversion from " << input_type
1943 << " to " << result_type;
1944 }
1945 break;
1946
1947 case Primitive::kPrimChar:
1948 switch (input_type) {
1949 case Primitive::kPrimLong:
1950 // Type conversion from long to char is a result of code transformations.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001951 __ Ubfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 16);
Scott Wakelingfe885462016-09-22 10:24:38 +01001952 break;
1953 case Primitive::kPrimBoolean:
1954 // Boolean input is a result of code transformations.
1955 case Primitive::kPrimByte:
1956 case Primitive::kPrimShort:
1957 case Primitive::kPrimInt:
1958 // Processing a Dex `int-to-char' instruction.
1959 __ Ubfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 16);
1960 break;
1961
1962 default:
1963 LOG(FATAL) << "Unexpected type conversion from " << input_type
1964 << " to " << result_type;
1965 }
1966 break;
1967
1968 case Primitive::kPrimFloat:
1969 switch (input_type) {
1970 case Primitive::kPrimBoolean:
1971 // Boolean input is a result of code transformations.
1972 case Primitive::kPrimByte:
1973 case Primitive::kPrimShort:
1974 case Primitive::kPrimInt:
1975 case Primitive::kPrimChar: {
1976 // Processing a Dex `int-to-float' instruction.
1977 __ Vmov(OutputSRegister(conversion), InputRegisterAt(conversion, 0));
1978 __ Vcvt(F32, I32, OutputSRegister(conversion), OutputSRegister(conversion));
1979 break;
1980 }
1981
1982 case Primitive::kPrimLong:
1983 // Processing a Dex `long-to-float' instruction.
1984 codegen_->InvokeRuntime(kQuickL2f, conversion, conversion->GetDexPc());
1985 CheckEntrypointTypes<kQuickL2f, float, int64_t>();
1986 break;
1987
1988 case Primitive::kPrimDouble:
1989 // Processing a Dex `double-to-float' instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01001990 __ Vcvt(F32, F64, OutputSRegister(conversion), DRegisterFrom(in));
Scott Wakelingfe885462016-09-22 10:24:38 +01001991 break;
1992
1993 default:
1994 LOG(FATAL) << "Unexpected type conversion from " << input_type
1995 << " to " << result_type;
1996 };
1997 break;
1998
1999 case Primitive::kPrimDouble:
2000 switch (input_type) {
2001 case Primitive::kPrimBoolean:
2002 // Boolean input is a result of code transformations.
2003 case Primitive::kPrimByte:
2004 case Primitive::kPrimShort:
2005 case Primitive::kPrimInt:
2006 case Primitive::kPrimChar: {
2007 // Processing a Dex `int-to-double' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002008 __ Vmov(LowSRegisterFrom(out), InputRegisterAt(conversion, 0));
Scott Wakelingc34dba72016-10-03 10:14:44 +01002009 __ Vcvt(F64, I32, DRegisterFrom(out), LowSRegisterFrom(out));
Scott Wakelingfe885462016-09-22 10:24:38 +01002010 break;
2011 }
2012
2013 case Primitive::kPrimLong: {
2014 // Processing a Dex `long-to-double' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002015 vixl32::Register low = LowRegisterFrom(in);
2016 vixl32::Register high = HighRegisterFrom(in);
Scott Wakelingfe885462016-09-22 10:24:38 +01002017
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002018 vixl32::SRegister out_s = LowSRegisterFrom(out);
Scott Wakelingc34dba72016-10-03 10:14:44 +01002019 vixl32::DRegister out_d = DRegisterFrom(out);
Scott Wakelingfe885462016-09-22 10:24:38 +01002020
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002021 vixl32::SRegister temp_s = LowSRegisterFrom(locations->GetTemp(0));
Scott Wakelingc34dba72016-10-03 10:14:44 +01002022 vixl32::DRegister temp_d = DRegisterFrom(locations->GetTemp(0));
Scott Wakelingfe885462016-09-22 10:24:38 +01002023
Scott Wakelingc34dba72016-10-03 10:14:44 +01002024 vixl32::DRegister constant_d = DRegisterFrom(locations->GetTemp(0));
Scott Wakelingfe885462016-09-22 10:24:38 +01002025
2026 // temp_d = int-to-double(high)
2027 __ Vmov(temp_s, high);
2028 __ Vcvt(F64, I32, temp_d, temp_s);
2029 // constant_d = k2Pow32EncodingForDouble
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002030 __ Vmov(constant_d, bit_cast<double, int64_t>(k2Pow32EncodingForDouble));
Scott Wakelingfe885462016-09-22 10:24:38 +01002031 // out_d = unsigned-to-double(low)
2032 __ Vmov(out_s, low);
2033 __ Vcvt(F64, U32, out_d, out_s);
2034 // out_d += temp_d * constant_d
2035 __ Vmla(F64, out_d, temp_d, constant_d);
2036 break;
2037 }
2038
2039 case Primitive::kPrimFloat:
2040 // Processing a Dex `float-to-double' instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01002041 __ Vcvt(F64, F32, DRegisterFrom(out), InputSRegisterAt(conversion, 0));
Scott Wakelingfe885462016-09-22 10:24:38 +01002042 break;
2043
2044 default:
2045 LOG(FATAL) << "Unexpected type conversion from " << input_type
2046 << " to " << result_type;
2047 };
2048 break;
2049
2050 default:
2051 LOG(FATAL) << "Unexpected type conversion from " << input_type
2052 << " to " << result_type;
2053 }
2054}
2055
2056void LocationsBuilderARMVIXL::VisitAdd(HAdd* add) {
2057 LocationSummary* locations =
2058 new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall);
2059 switch (add->GetResultType()) {
2060 case Primitive::kPrimInt: {
2061 locations->SetInAt(0, Location::RequiresRegister());
2062 locations->SetInAt(1, Location::RegisterOrConstant(add->InputAt(1)));
2063 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2064 break;
2065 }
2066
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002067 // TODO(VIXL): https://android-review.googlesource.com/#/c/254144/
Scott Wakelingfe885462016-09-22 10:24:38 +01002068 case Primitive::kPrimLong: {
2069 locations->SetInAt(0, Location::RequiresRegister());
2070 locations->SetInAt(1, Location::RequiresRegister());
2071 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2072 break;
2073 }
2074
2075 case Primitive::kPrimFloat:
2076 case Primitive::kPrimDouble: {
2077 locations->SetInAt(0, Location::RequiresFpuRegister());
2078 locations->SetInAt(1, Location::RequiresFpuRegister());
2079 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2080 break;
2081 }
2082
2083 default:
2084 LOG(FATAL) << "Unexpected add type " << add->GetResultType();
2085 }
2086}
2087
2088void InstructionCodeGeneratorARMVIXL::VisitAdd(HAdd* add) {
2089 LocationSummary* locations = add->GetLocations();
2090 Location out = locations->Out();
2091 Location first = locations->InAt(0);
2092 Location second = locations->InAt(1);
2093
2094 switch (add->GetResultType()) {
2095 case Primitive::kPrimInt: {
2096 __ Add(OutputRegister(add), InputRegisterAt(add, 0), InputOperandAt(add, 1));
2097 }
2098 break;
2099
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002100 // TODO(VIXL): https://android-review.googlesource.com/#/c/254144/
Scott Wakelingfe885462016-09-22 10:24:38 +01002101 case Primitive::kPrimLong: {
2102 DCHECK(second.IsRegisterPair());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002103 __ Adds(LowRegisterFrom(out), LowRegisterFrom(first), LowRegisterFrom(second));
2104 __ Adc(HighRegisterFrom(out), HighRegisterFrom(first), HighRegisterFrom(second));
Scott Wakelingfe885462016-09-22 10:24:38 +01002105 break;
2106 }
2107
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002108 case Primitive::kPrimFloat:
Scott Wakelingfe885462016-09-22 10:24:38 +01002109 case Primitive::kPrimDouble:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002110 __ Vadd(OutputVRegister(add), InputVRegisterAt(add, 0), InputVRegisterAt(add, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01002111 break;
2112
2113 default:
2114 LOG(FATAL) << "Unexpected add type " << add->GetResultType();
2115 }
2116}
2117
2118void LocationsBuilderARMVIXL::VisitSub(HSub* sub) {
2119 LocationSummary* locations =
2120 new (GetGraph()->GetArena()) LocationSummary(sub, LocationSummary::kNoCall);
2121 switch (sub->GetResultType()) {
2122 case Primitive::kPrimInt: {
2123 locations->SetInAt(0, Location::RequiresRegister());
2124 locations->SetInAt(1, Location::RegisterOrConstant(sub->InputAt(1)));
2125 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2126 break;
2127 }
2128
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002129 // TODO(VIXL): https://android-review.googlesource.com/#/c/254144/
Scott Wakelingfe885462016-09-22 10:24:38 +01002130 case Primitive::kPrimLong: {
2131 locations->SetInAt(0, Location::RequiresRegister());
2132 locations->SetInAt(1, Location::RequiresRegister());
2133 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2134 break;
2135 }
2136 case Primitive::kPrimFloat:
2137 case Primitive::kPrimDouble: {
2138 locations->SetInAt(0, Location::RequiresFpuRegister());
2139 locations->SetInAt(1, Location::RequiresFpuRegister());
2140 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2141 break;
2142 }
2143 default:
2144 LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
2145 }
2146}
2147
2148void InstructionCodeGeneratorARMVIXL::VisitSub(HSub* sub) {
2149 LocationSummary* locations = sub->GetLocations();
2150 Location out = locations->Out();
2151 Location first = locations->InAt(0);
2152 Location second = locations->InAt(1);
2153 switch (sub->GetResultType()) {
2154 case Primitive::kPrimInt: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002155 __ Sub(OutputRegister(sub), InputRegisterAt(sub, 0), InputOperandAt(sub, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01002156 break;
2157 }
2158
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002159 // TODO(VIXL): https://android-review.googlesource.com/#/c/254144/
Scott Wakelingfe885462016-09-22 10:24:38 +01002160 case Primitive::kPrimLong: {
2161 DCHECK(second.IsRegisterPair());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002162 __ Subs(LowRegisterFrom(out), LowRegisterFrom(first), LowRegisterFrom(second));
2163 __ Sbc(HighRegisterFrom(out), HighRegisterFrom(first), HighRegisterFrom(second));
Scott Wakelingfe885462016-09-22 10:24:38 +01002164 break;
2165 }
2166
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002167 case Primitive::kPrimFloat:
2168 case Primitive::kPrimDouble:
2169 __ Vsub(OutputVRegister(sub), InputVRegisterAt(sub, 0), InputVRegisterAt(sub, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01002170 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01002171
2172 default:
2173 LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
2174 }
2175}
2176
2177void LocationsBuilderARMVIXL::VisitMul(HMul* mul) {
2178 LocationSummary* locations =
2179 new (GetGraph()->GetArena()) LocationSummary(mul, LocationSummary::kNoCall);
2180 switch (mul->GetResultType()) {
2181 case Primitive::kPrimInt:
2182 case Primitive::kPrimLong: {
2183 locations->SetInAt(0, Location::RequiresRegister());
2184 locations->SetInAt(1, Location::RequiresRegister());
2185 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2186 break;
2187 }
2188
2189 case Primitive::kPrimFloat:
2190 case Primitive::kPrimDouble: {
2191 locations->SetInAt(0, Location::RequiresFpuRegister());
2192 locations->SetInAt(1, Location::RequiresFpuRegister());
2193 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2194 break;
2195 }
2196
2197 default:
2198 LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
2199 }
2200}
2201
2202void InstructionCodeGeneratorARMVIXL::VisitMul(HMul* mul) {
2203 LocationSummary* locations = mul->GetLocations();
2204 Location out = locations->Out();
2205 Location first = locations->InAt(0);
2206 Location second = locations->InAt(1);
2207 switch (mul->GetResultType()) {
2208 case Primitive::kPrimInt: {
2209 __ Mul(OutputRegister(mul), InputRegisterAt(mul, 0), InputRegisterAt(mul, 1));
2210 break;
2211 }
2212 case Primitive::kPrimLong: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002213 vixl32::Register out_hi = HighRegisterFrom(out);
2214 vixl32::Register out_lo = LowRegisterFrom(out);
2215 vixl32::Register in1_hi = HighRegisterFrom(first);
2216 vixl32::Register in1_lo = LowRegisterFrom(first);
2217 vixl32::Register in2_hi = HighRegisterFrom(second);
2218 vixl32::Register in2_lo = LowRegisterFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01002219
2220 // Extra checks to protect caused by the existence of R1_R2.
2221 // The algorithm is wrong if out.hi is either in1.lo or in2.lo:
2222 // (e.g. in1=r0_r1, in2=r2_r3 and out=r1_r2);
2223 DCHECK_NE(out_hi.GetCode(), in1_lo.GetCode());
2224 DCHECK_NE(out_hi.GetCode(), in2_lo.GetCode());
2225
2226 // input: in1 - 64 bits, in2 - 64 bits
2227 // output: out
2228 // formula: out.hi : out.lo = (in1.lo * in2.hi + in1.hi * in2.lo)* 2^32 + in1.lo * in2.lo
2229 // parts: out.hi = in1.lo * in2.hi + in1.hi * in2.lo + (in1.lo * in2.lo)[63:32]
2230 // parts: out.lo = (in1.lo * in2.lo)[31:0]
2231
2232 UseScratchRegisterScope temps(GetVIXLAssembler());
2233 vixl32::Register temp = temps.Acquire();
2234 // temp <- in1.lo * in2.hi
2235 __ Mul(temp, in1_lo, in2_hi);
2236 // out.hi <- in1.lo * in2.hi + in1.hi * in2.lo
2237 __ Mla(out_hi, in1_hi, in2_lo, temp);
2238 // out.lo <- (in1.lo * in2.lo)[31:0];
2239 __ Umull(out_lo, temp, in1_lo, in2_lo);
2240 // out.hi <- in2.hi * in1.lo + in2.lo * in1.hi + (in1.lo * in2.lo)[63:32]
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002241 __ Add(out_hi, out_hi, temp);
Scott Wakelingfe885462016-09-22 10:24:38 +01002242 break;
2243 }
2244
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002245 case Primitive::kPrimFloat:
2246 case Primitive::kPrimDouble:
2247 __ Vmul(OutputVRegister(mul), InputVRegisterAt(mul, 0), InputVRegisterAt(mul, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01002248 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01002249
2250 default:
2251 LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
2252 }
2253}
2254
Scott Wakelingfe885462016-09-22 10:24:38 +01002255void InstructionCodeGeneratorARMVIXL::DivRemOneOrMinusOne(HBinaryOperation* instruction) {
2256 DCHECK(instruction->IsDiv() || instruction->IsRem());
2257 DCHECK(instruction->GetResultType() == Primitive::kPrimInt);
2258
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002259 Location second = instruction->GetLocations()->InAt(1);
Scott Wakelingfe885462016-09-22 10:24:38 +01002260 DCHECK(second.IsConstant());
2261
2262 vixl32::Register out = OutputRegister(instruction);
2263 vixl32::Register dividend = InputRegisterAt(instruction, 0);
2264 int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
2265 DCHECK(imm == 1 || imm == -1);
2266
2267 if (instruction->IsRem()) {
2268 __ Mov(out, 0);
2269 } else {
2270 if (imm == 1) {
2271 __ Mov(out, dividend);
2272 } else {
2273 __ Rsb(out, dividend, 0);
2274 }
2275 }
2276}
2277
2278void InstructionCodeGeneratorARMVIXL::DivRemByPowerOfTwo(HBinaryOperation* instruction) {
2279 DCHECK(instruction->IsDiv() || instruction->IsRem());
2280 DCHECK(instruction->GetResultType() == Primitive::kPrimInt);
2281
2282 LocationSummary* locations = instruction->GetLocations();
2283 Location second = locations->InAt(1);
2284 DCHECK(second.IsConstant());
2285
2286 vixl32::Register out = OutputRegister(instruction);
2287 vixl32::Register dividend = InputRegisterAt(instruction, 0);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002288 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
Scott Wakelingfe885462016-09-22 10:24:38 +01002289 int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
2290 uint32_t abs_imm = static_cast<uint32_t>(AbsOrMin(imm));
2291 int ctz_imm = CTZ(abs_imm);
2292
2293 if (ctz_imm == 1) {
2294 __ Lsr(temp, dividend, 32 - ctz_imm);
2295 } else {
2296 __ Asr(temp, dividend, 31);
2297 __ Lsr(temp, temp, 32 - ctz_imm);
2298 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002299 __ Add(out, temp, dividend);
Scott Wakelingfe885462016-09-22 10:24:38 +01002300
2301 if (instruction->IsDiv()) {
2302 __ Asr(out, out, ctz_imm);
2303 if (imm < 0) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002304 __ Rsb(out, out, 0);
Scott Wakelingfe885462016-09-22 10:24:38 +01002305 }
2306 } else {
2307 __ Ubfx(out, out, 0, ctz_imm);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002308 __ Sub(out, out, temp);
Scott Wakelingfe885462016-09-22 10:24:38 +01002309 }
2310}
2311
2312void InstructionCodeGeneratorARMVIXL::GenerateDivRemWithAnyConstant(HBinaryOperation* instruction) {
2313 DCHECK(instruction->IsDiv() || instruction->IsRem());
2314 DCHECK(instruction->GetResultType() == Primitive::kPrimInt);
2315
2316 LocationSummary* locations = instruction->GetLocations();
2317 Location second = locations->InAt(1);
2318 DCHECK(second.IsConstant());
2319
2320 vixl32::Register out = OutputRegister(instruction);
2321 vixl32::Register dividend = InputRegisterAt(instruction, 0);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002322 vixl32::Register temp1 = RegisterFrom(locations->GetTemp(0));
2323 vixl32::Register temp2 = RegisterFrom(locations->GetTemp(1));
Scott Wakelingfe885462016-09-22 10:24:38 +01002324 int64_t imm = second.GetConstant()->AsIntConstant()->GetValue();
2325
2326 int64_t magic;
2327 int shift;
2328 CalculateMagicAndShiftForDivRem(imm, false /* is_long */, &magic, &shift);
2329
2330 __ Mov(temp1, magic);
2331 __ Smull(temp2, temp1, dividend, temp1);
2332
2333 if (imm > 0 && magic < 0) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002334 __ Add(temp1, temp1, dividend);
Scott Wakelingfe885462016-09-22 10:24:38 +01002335 } else if (imm < 0 && magic > 0) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002336 __ Sub(temp1, temp1, dividend);
Scott Wakelingfe885462016-09-22 10:24:38 +01002337 }
2338
2339 if (shift != 0) {
2340 __ Asr(temp1, temp1, shift);
2341 }
2342
2343 if (instruction->IsDiv()) {
2344 __ Sub(out, temp1, Operand(temp1, vixl32::Shift(ASR), 31));
2345 } else {
2346 __ Sub(temp1, temp1, Operand(temp1, vixl32::Shift(ASR), 31));
2347 // TODO: Strength reduction for mls.
2348 __ Mov(temp2, imm);
2349 __ Mls(out, temp1, temp2, dividend);
2350 }
2351}
2352
2353void InstructionCodeGeneratorARMVIXL::GenerateDivRemConstantIntegral(
2354 HBinaryOperation* instruction) {
2355 DCHECK(instruction->IsDiv() || instruction->IsRem());
2356 DCHECK(instruction->GetResultType() == Primitive::kPrimInt);
2357
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002358 Location second = instruction->GetLocations()->InAt(1);
Scott Wakelingfe885462016-09-22 10:24:38 +01002359 DCHECK(second.IsConstant());
2360
2361 int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
2362 if (imm == 0) {
2363 // Do not generate anything. DivZeroCheck would prevent any code to be executed.
2364 } else if (imm == 1 || imm == -1) {
2365 DivRemOneOrMinusOne(instruction);
2366 } else if (IsPowerOfTwo(AbsOrMin(imm))) {
2367 DivRemByPowerOfTwo(instruction);
2368 } else {
2369 DCHECK(imm <= -2 || imm >= 2);
2370 GenerateDivRemWithAnyConstant(instruction);
2371 }
2372}
2373
2374void LocationsBuilderARMVIXL::VisitDiv(HDiv* div) {
2375 LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
2376 if (div->GetResultType() == Primitive::kPrimLong) {
2377 // pLdiv runtime call.
2378 call_kind = LocationSummary::kCallOnMainOnly;
2379 } else if (div->GetResultType() == Primitive::kPrimInt && div->InputAt(1)->IsConstant()) {
2380 // sdiv will be replaced by other instruction sequence.
2381 } else if (div->GetResultType() == Primitive::kPrimInt &&
2382 !codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
2383 // pIdivmod runtime call.
2384 call_kind = LocationSummary::kCallOnMainOnly;
2385 }
2386
2387 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(div, call_kind);
2388
2389 switch (div->GetResultType()) {
2390 case Primitive::kPrimInt: {
2391 if (div->InputAt(1)->IsConstant()) {
2392 locations->SetInAt(0, Location::RequiresRegister());
2393 locations->SetInAt(1, Location::ConstantLocation(div->InputAt(1)->AsConstant()));
2394 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2395 int32_t value = div->InputAt(1)->AsIntConstant()->GetValue();
2396 if (value == 1 || value == 0 || value == -1) {
2397 // No temp register required.
2398 } else {
2399 locations->AddTemp(Location::RequiresRegister());
2400 if (!IsPowerOfTwo(AbsOrMin(value))) {
2401 locations->AddTemp(Location::RequiresRegister());
2402 }
2403 }
2404 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
2405 locations->SetInAt(0, Location::RequiresRegister());
2406 locations->SetInAt(1, Location::RequiresRegister());
2407 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2408 } else {
2409 TODO_VIXL32(FATAL);
2410 }
2411 break;
2412 }
2413 case Primitive::kPrimLong: {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01002414 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2415 locations->SetInAt(0, LocationFrom(
2416 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
2417 locations->SetInAt(1, LocationFrom(
2418 calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3)));
2419 locations->SetOut(LocationFrom(r0, r1));
Scott Wakelingfe885462016-09-22 10:24:38 +01002420 break;
2421 }
2422 case Primitive::kPrimFloat:
2423 case Primitive::kPrimDouble: {
2424 locations->SetInAt(0, Location::RequiresFpuRegister());
2425 locations->SetInAt(1, Location::RequiresFpuRegister());
2426 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2427 break;
2428 }
2429
2430 default:
2431 LOG(FATAL) << "Unexpected div type " << div->GetResultType();
2432 }
2433}
2434
2435void InstructionCodeGeneratorARMVIXL::VisitDiv(HDiv* div) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01002436 Location lhs = div->GetLocations()->InAt(0);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002437 Location rhs = div->GetLocations()->InAt(1);
Scott Wakelingfe885462016-09-22 10:24:38 +01002438
2439 switch (div->GetResultType()) {
2440 case Primitive::kPrimInt: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002441 if (rhs.IsConstant()) {
Scott Wakelingfe885462016-09-22 10:24:38 +01002442 GenerateDivRemConstantIntegral(div);
2443 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
2444 __ Sdiv(OutputRegister(div), InputRegisterAt(div, 0), InputRegisterAt(div, 1));
2445 } else {
2446 TODO_VIXL32(FATAL);
2447 }
2448 break;
2449 }
2450
2451 case Primitive::kPrimLong: {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01002452 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2453 DCHECK(calling_convention.GetRegisterAt(0).Is(LowRegisterFrom(lhs)));
2454 DCHECK(calling_convention.GetRegisterAt(1).Is(HighRegisterFrom(lhs)));
2455 DCHECK(calling_convention.GetRegisterAt(2).Is(LowRegisterFrom(rhs)));
2456 DCHECK(calling_convention.GetRegisterAt(3).Is(HighRegisterFrom(rhs)));
2457 DCHECK(LowRegisterFrom(div->GetLocations()->Out()).Is(r0));
2458 DCHECK(HighRegisterFrom(div->GetLocations()->Out()).Is(r1));
2459
2460 codegen_->InvokeRuntime(kQuickLdiv, div, div->GetDexPc());
2461 CheckEntrypointTypes<kQuickLdiv, int64_t, int64_t, int64_t>();
Scott Wakelingfe885462016-09-22 10:24:38 +01002462 break;
2463 }
2464
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002465 case Primitive::kPrimFloat:
2466 case Primitive::kPrimDouble:
2467 __ Vdiv(OutputVRegister(div), InputVRegisterAt(div, 0), InputVRegisterAt(div, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01002468 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01002469
2470 default:
2471 LOG(FATAL) << "Unexpected div type " << div->GetResultType();
2472 }
2473}
2474
2475void LocationsBuilderARMVIXL::VisitDivZeroCheck(HDivZeroCheck* instruction) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002476 // TODO(VIXL): https://android-review.googlesource.com/#/c/275337/
Scott Wakelingfe885462016-09-22 10:24:38 +01002477 LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
2478 ? LocationSummary::kCallOnSlowPath
2479 : LocationSummary::kNoCall;
2480 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
2481 locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0)));
2482 if (instruction->HasUses()) {
2483 locations->SetOut(Location::SameAsFirstInput());
2484 }
2485}
2486
2487void InstructionCodeGeneratorARMVIXL::VisitDivZeroCheck(HDivZeroCheck* instruction) {
2488 DivZeroCheckSlowPathARMVIXL* slow_path =
2489 new (GetGraph()->GetArena()) DivZeroCheckSlowPathARMVIXL(instruction);
2490 codegen_->AddSlowPath(slow_path);
2491
2492 LocationSummary* locations = instruction->GetLocations();
2493 Location value = locations->InAt(0);
2494
2495 switch (instruction->GetType()) {
2496 case Primitive::kPrimBoolean:
2497 case Primitive::kPrimByte:
2498 case Primitive::kPrimChar:
2499 case Primitive::kPrimShort:
2500 case Primitive::kPrimInt: {
2501 if (value.IsRegister()) {
2502 __ Cbz(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
2503 } else {
2504 DCHECK(value.IsConstant()) << value;
2505 if (value.GetConstant()->AsIntConstant()->GetValue() == 0) {
2506 __ B(slow_path->GetEntryLabel());
2507 }
2508 }
2509 break;
2510 }
2511 case Primitive::kPrimLong: {
2512 if (value.IsRegisterPair()) {
2513 UseScratchRegisterScope temps(GetVIXLAssembler());
2514 vixl32::Register temp = temps.Acquire();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002515 __ Orrs(temp, LowRegisterFrom(value), HighRegisterFrom(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01002516 __ B(eq, slow_path->GetEntryLabel());
2517 } else {
2518 DCHECK(value.IsConstant()) << value;
2519 if (value.GetConstant()->AsLongConstant()->GetValue() == 0) {
2520 __ B(slow_path->GetEntryLabel());
2521 }
2522 }
2523 break;
2524 }
2525 default:
2526 LOG(FATAL) << "Unexpected type for HDivZeroCheck " << instruction->GetType();
2527 }
2528}
2529
Artem Serov02109dd2016-09-23 17:17:54 +01002530void InstructionCodeGeneratorARMVIXL::HandleIntegerRotate(HRor* ror) {
2531 LocationSummary* locations = ror->GetLocations();
2532 vixl32::Register in = InputRegisterAt(ror, 0);
2533 Location rhs = locations->InAt(1);
2534 vixl32::Register out = OutputRegister(ror);
2535
2536 if (rhs.IsConstant()) {
2537 // Arm32 and Thumb2 assemblers require a rotation on the interval [1,31],
2538 // so map all rotations to a +ve. equivalent in that range.
2539 // (e.g. left *or* right by -2 bits == 30 bits in the same direction.)
2540 uint32_t rot = CodeGenerator::GetInt32ValueOf(rhs.GetConstant()) & 0x1F;
2541 if (rot) {
2542 // Rotate, mapping left rotations to right equivalents if necessary.
2543 // (e.g. left by 2 bits == right by 30.)
2544 __ Ror(out, in, rot);
2545 } else if (!out.Is(in)) {
2546 __ Mov(out, in);
2547 }
2548 } else {
2549 __ Ror(out, in, RegisterFrom(rhs));
2550 }
2551}
2552
2553// Gain some speed by mapping all Long rotates onto equivalent pairs of Integer
2554// rotates by swapping input regs (effectively rotating by the first 32-bits of
2555// a larger rotation) or flipping direction (thus treating larger right/left
2556// rotations as sub-word sized rotations in the other direction) as appropriate.
2557void InstructionCodeGeneratorARMVIXL::HandleLongRotate(HRor* ror) {
2558 LocationSummary* locations = ror->GetLocations();
2559 vixl32::Register in_reg_lo = LowRegisterFrom(locations->InAt(0));
2560 vixl32::Register in_reg_hi = HighRegisterFrom(locations->InAt(0));
2561 Location rhs = locations->InAt(1);
2562 vixl32::Register out_reg_lo = LowRegisterFrom(locations->Out());
2563 vixl32::Register out_reg_hi = HighRegisterFrom(locations->Out());
2564
2565 if (rhs.IsConstant()) {
2566 uint64_t rot = CodeGenerator::GetInt64ValueOf(rhs.GetConstant());
2567 // Map all rotations to +ve. equivalents on the interval [0,63].
2568 rot &= kMaxLongShiftDistance;
2569 // For rotates over a word in size, 'pre-rotate' by 32-bits to keep rotate
2570 // logic below to a simple pair of binary orr.
2571 // (e.g. 34 bits == in_reg swap + 2 bits right.)
2572 if (rot >= kArmBitsPerWord) {
2573 rot -= kArmBitsPerWord;
2574 std::swap(in_reg_hi, in_reg_lo);
2575 }
2576 // Rotate, or mov to out for zero or word size rotations.
2577 if (rot != 0u) {
2578 __ Lsr(out_reg_hi, in_reg_hi, rot);
2579 __ Orr(out_reg_hi, out_reg_hi, Operand(in_reg_lo, ShiftType::LSL, kArmBitsPerWord - rot));
2580 __ Lsr(out_reg_lo, in_reg_lo, rot);
2581 __ Orr(out_reg_lo, out_reg_lo, Operand(in_reg_hi, ShiftType::LSL, kArmBitsPerWord - rot));
2582 } else {
2583 __ Mov(out_reg_lo, in_reg_lo);
2584 __ Mov(out_reg_hi, in_reg_hi);
2585 }
2586 } else {
2587 vixl32::Register shift_right = RegisterFrom(locations->GetTemp(0));
2588 vixl32::Register shift_left = RegisterFrom(locations->GetTemp(1));
2589 vixl32::Label end;
2590 vixl32::Label shift_by_32_plus_shift_right;
2591
2592 __ And(shift_right, RegisterFrom(rhs), 0x1F);
2593 __ Lsrs(shift_left, RegisterFrom(rhs), 6);
2594 // TODO(VIXL): Check that flags are kept after "vixl32::LeaveFlags" enabled.
2595 __ Rsb(shift_left, shift_right, kArmBitsPerWord);
2596 __ B(cc, &shift_by_32_plus_shift_right);
2597
2598 // out_reg_hi = (reg_hi << shift_left) | (reg_lo >> shift_right).
2599 // out_reg_lo = (reg_lo << shift_left) | (reg_hi >> shift_right).
2600 __ Lsl(out_reg_hi, in_reg_hi, shift_left);
2601 __ Lsr(out_reg_lo, in_reg_lo, shift_right);
2602 __ Add(out_reg_hi, out_reg_hi, out_reg_lo);
2603 __ Lsl(out_reg_lo, in_reg_lo, shift_left);
2604 __ Lsr(shift_left, in_reg_hi, shift_right);
2605 __ Add(out_reg_lo, out_reg_lo, shift_left);
2606 __ B(&end);
2607
2608 __ Bind(&shift_by_32_plus_shift_right); // Shift by 32+shift_right.
2609 // out_reg_hi = (reg_hi >> shift_right) | (reg_lo << shift_left).
2610 // out_reg_lo = (reg_lo >> shift_right) | (reg_hi << shift_left).
2611 __ Lsr(out_reg_hi, in_reg_hi, shift_right);
2612 __ Lsl(out_reg_lo, in_reg_lo, shift_left);
2613 __ Add(out_reg_hi, out_reg_hi, out_reg_lo);
2614 __ Lsr(out_reg_lo, in_reg_lo, shift_right);
2615 __ Lsl(shift_right, in_reg_hi, shift_left);
2616 __ Add(out_reg_lo, out_reg_lo, shift_right);
2617
2618 __ Bind(&end);
2619 }
2620}
2621
2622void LocationsBuilderARMVIXL::VisitRor(HRor* ror) {
2623 LocationSummary* locations =
2624 new (GetGraph()->GetArena()) LocationSummary(ror, LocationSummary::kNoCall);
2625 switch (ror->GetResultType()) {
2626 case Primitive::kPrimInt: {
2627 locations->SetInAt(0, Location::RequiresRegister());
2628 locations->SetInAt(1, Location::RegisterOrConstant(ror->InputAt(1)));
2629 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2630 break;
2631 }
2632 case Primitive::kPrimLong: {
2633 locations->SetInAt(0, Location::RequiresRegister());
2634 if (ror->InputAt(1)->IsConstant()) {
2635 locations->SetInAt(1, Location::ConstantLocation(ror->InputAt(1)->AsConstant()));
2636 } else {
2637 locations->SetInAt(1, Location::RequiresRegister());
2638 locations->AddTemp(Location::RequiresRegister());
2639 locations->AddTemp(Location::RequiresRegister());
2640 }
2641 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
2642 break;
2643 }
2644 default:
2645 LOG(FATAL) << "Unexpected operation type " << ror->GetResultType();
2646 }
2647}
2648
2649void InstructionCodeGeneratorARMVIXL::VisitRor(HRor* ror) {
2650 Primitive::Type type = ror->GetResultType();
2651 switch (type) {
2652 case Primitive::kPrimInt: {
2653 HandleIntegerRotate(ror);
2654 break;
2655 }
2656 case Primitive::kPrimLong: {
2657 HandleLongRotate(ror);
2658 break;
2659 }
2660 default:
2661 LOG(FATAL) << "Unexpected operation type " << type;
2662 UNREACHABLE();
2663 }
2664}
2665
Artem Serov02d37832016-10-25 15:25:33 +01002666void LocationsBuilderARMVIXL::HandleShift(HBinaryOperation* op) {
2667 DCHECK(op->IsShl() || op->IsShr() || op->IsUShr());
2668
2669 LocationSummary* locations =
2670 new (GetGraph()->GetArena()) LocationSummary(op, LocationSummary::kNoCall);
2671
2672 switch (op->GetResultType()) {
2673 case Primitive::kPrimInt: {
2674 locations->SetInAt(0, Location::RequiresRegister());
2675 if (op->InputAt(1)->IsConstant()) {
2676 locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant()));
2677 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2678 } else {
2679 locations->SetInAt(1, Location::RequiresRegister());
2680 // Make the output overlap, as it will be used to hold the masked
2681 // second input.
2682 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
2683 }
2684 break;
2685 }
2686 case Primitive::kPrimLong: {
2687 locations->SetInAt(0, Location::RequiresRegister());
2688 if (op->InputAt(1)->IsConstant()) {
2689 locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant()));
2690 // For simplicity, use kOutputOverlap even though we only require that low registers
2691 // don't clash with high registers which the register allocator currently guarantees.
2692 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
2693 } else {
2694 locations->SetInAt(1, Location::RequiresRegister());
2695 locations->AddTemp(Location::RequiresRegister());
2696 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
2697 }
2698 break;
2699 }
2700 default:
2701 LOG(FATAL) << "Unexpected operation type " << op->GetResultType();
2702 }
2703}
2704
2705void InstructionCodeGeneratorARMVIXL::HandleShift(HBinaryOperation* op) {
2706 DCHECK(op->IsShl() || op->IsShr() || op->IsUShr());
2707
2708 LocationSummary* locations = op->GetLocations();
2709 Location out = locations->Out();
2710 Location first = locations->InAt(0);
2711 Location second = locations->InAt(1);
2712
2713 Primitive::Type type = op->GetResultType();
2714 switch (type) {
2715 case Primitive::kPrimInt: {
2716 vixl32::Register out_reg = OutputRegister(op);
2717 vixl32::Register first_reg = InputRegisterAt(op, 0);
2718 if (second.IsRegister()) {
2719 vixl32::Register second_reg = RegisterFrom(second);
2720 // ARM doesn't mask the shift count so we need to do it ourselves.
2721 __ And(out_reg, second_reg, kMaxIntShiftDistance);
2722 if (op->IsShl()) {
2723 __ Lsl(out_reg, first_reg, out_reg);
2724 } else if (op->IsShr()) {
2725 __ Asr(out_reg, first_reg, out_reg);
2726 } else {
2727 __ Lsr(out_reg, first_reg, out_reg);
2728 }
2729 } else {
2730 int32_t cst = second.GetConstant()->AsIntConstant()->GetValue();
2731 uint32_t shift_value = cst & kMaxIntShiftDistance;
2732 if (shift_value == 0) { // ARM does not support shifting with 0 immediate.
2733 __ Mov(out_reg, first_reg);
2734 } else if (op->IsShl()) {
2735 __ Lsl(out_reg, first_reg, shift_value);
2736 } else if (op->IsShr()) {
2737 __ Asr(out_reg, first_reg, shift_value);
2738 } else {
2739 __ Lsr(out_reg, first_reg, shift_value);
2740 }
2741 }
2742 break;
2743 }
2744 case Primitive::kPrimLong: {
2745 vixl32::Register o_h = HighRegisterFrom(out);
2746 vixl32::Register o_l = LowRegisterFrom(out);
2747
2748 vixl32::Register high = HighRegisterFrom(first);
2749 vixl32::Register low = LowRegisterFrom(first);
2750
2751 if (second.IsRegister()) {
2752 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
2753
2754 vixl32::Register second_reg = RegisterFrom(second);
2755
2756 if (op->IsShl()) {
2757 __ And(o_l, second_reg, kMaxLongShiftDistance);
2758 // Shift the high part
2759 __ Lsl(o_h, high, o_l);
2760 // Shift the low part and `or` what overflew on the high part
2761 __ Rsb(temp, o_l, kArmBitsPerWord);
2762 __ Lsr(temp, low, temp);
2763 __ Orr(o_h, o_h, temp);
2764 // If the shift is > 32 bits, override the high part
2765 __ Subs(temp, o_l, kArmBitsPerWord);
2766 {
2767 AssemblerAccurateScope guard(GetVIXLAssembler(),
2768 3 * kArmInstrMaxSizeInBytes,
2769 CodeBufferCheckScope::kMaximumSize);
2770 __ it(pl);
2771 __ lsl(pl, o_h, low, temp);
2772 }
2773 // Shift the low part
2774 __ Lsl(o_l, low, o_l);
2775 } else if (op->IsShr()) {
2776 __ And(o_h, second_reg, kMaxLongShiftDistance);
2777 // Shift the low part
2778 __ Lsr(o_l, low, o_h);
2779 // Shift the high part and `or` what underflew on the low part
2780 __ Rsb(temp, o_h, kArmBitsPerWord);
2781 __ Lsl(temp, high, temp);
2782 __ Orr(o_l, o_l, temp);
2783 // If the shift is > 32 bits, override the low part
2784 __ Subs(temp, o_h, kArmBitsPerWord);
2785 {
2786 AssemblerAccurateScope guard(GetVIXLAssembler(),
2787 3 * kArmInstrMaxSizeInBytes,
2788 CodeBufferCheckScope::kMaximumSize);
2789 __ it(pl);
2790 __ asr(pl, o_l, high, temp);
2791 }
2792 // Shift the high part
2793 __ Asr(o_h, high, o_h);
2794 } else {
2795 __ And(o_h, second_reg, kMaxLongShiftDistance);
2796 // same as Shr except we use `Lsr`s and not `Asr`s
2797 __ Lsr(o_l, low, o_h);
2798 __ Rsb(temp, o_h, kArmBitsPerWord);
2799 __ Lsl(temp, high, temp);
2800 __ Orr(o_l, o_l, temp);
2801 __ Subs(temp, o_h, kArmBitsPerWord);
2802 {
2803 AssemblerAccurateScope guard(GetVIXLAssembler(),
2804 3 * kArmInstrMaxSizeInBytes,
2805 CodeBufferCheckScope::kMaximumSize);
2806 __ it(pl);
2807 __ lsr(pl, o_l, high, temp);
2808 }
2809 __ Lsr(o_h, high, o_h);
2810 }
2811 } else {
2812 // Register allocator doesn't create partial overlap.
2813 DCHECK(!o_l.Is(high));
2814 DCHECK(!o_h.Is(low));
2815 int32_t cst = second.GetConstant()->AsIntConstant()->GetValue();
2816 uint32_t shift_value = cst & kMaxLongShiftDistance;
2817 if (shift_value > 32) {
2818 if (op->IsShl()) {
2819 __ Lsl(o_h, low, shift_value - 32);
2820 __ Mov(o_l, 0);
2821 } else if (op->IsShr()) {
2822 __ Asr(o_l, high, shift_value - 32);
2823 __ Asr(o_h, high, 31);
2824 } else {
2825 __ Lsr(o_l, high, shift_value - 32);
2826 __ Mov(o_h, 0);
2827 }
2828 } else if (shift_value == 32) {
2829 if (op->IsShl()) {
2830 __ Mov(o_h, low);
2831 __ Mov(o_l, 0);
2832 } else if (op->IsShr()) {
2833 __ Mov(o_l, high);
2834 __ Asr(o_h, high, 31);
2835 } else {
2836 __ Mov(o_l, high);
2837 __ Mov(o_h, 0);
2838 }
2839 } else if (shift_value == 1) {
2840 if (op->IsShl()) {
2841 __ Lsls(o_l, low, 1);
2842 __ Adc(o_h, high, high);
2843 } else if (op->IsShr()) {
2844 __ Asrs(o_h, high, 1);
2845 __ Rrx(o_l, low);
2846 } else {
2847 __ Lsrs(o_h, high, 1);
2848 __ Rrx(o_l, low);
2849 }
2850 } else {
2851 DCHECK(2 <= shift_value && shift_value < 32) << shift_value;
2852 if (op->IsShl()) {
2853 __ Lsl(o_h, high, shift_value);
2854 __ Orr(o_h, o_h, Operand(low, ShiftType::LSR, 32 - shift_value));
2855 __ Lsl(o_l, low, shift_value);
2856 } else if (op->IsShr()) {
2857 __ Lsr(o_l, low, shift_value);
2858 __ Orr(o_l, o_l, Operand(high, ShiftType::LSL, 32 - shift_value));
2859 __ Asr(o_h, high, shift_value);
2860 } else {
2861 __ Lsr(o_l, low, shift_value);
2862 __ Orr(o_l, o_l, Operand(high, ShiftType::LSL, 32 - shift_value));
2863 __ Lsr(o_h, high, shift_value);
2864 }
2865 }
2866 }
2867 break;
2868 }
2869 default:
2870 LOG(FATAL) << "Unexpected operation type " << type;
2871 UNREACHABLE();
2872 }
2873}
2874
2875void LocationsBuilderARMVIXL::VisitShl(HShl* shl) {
2876 HandleShift(shl);
2877}
2878
2879void InstructionCodeGeneratorARMVIXL::VisitShl(HShl* shl) {
2880 HandleShift(shl);
2881}
2882
2883void LocationsBuilderARMVIXL::VisitShr(HShr* shr) {
2884 HandleShift(shr);
2885}
2886
2887void InstructionCodeGeneratorARMVIXL::VisitShr(HShr* shr) {
2888 HandleShift(shr);
2889}
2890
2891void LocationsBuilderARMVIXL::VisitUShr(HUShr* ushr) {
2892 HandleShift(ushr);
2893}
2894
2895void InstructionCodeGeneratorARMVIXL::VisitUShr(HUShr* ushr) {
2896 HandleShift(ushr);
2897}
2898
2899void LocationsBuilderARMVIXL::VisitNewInstance(HNewInstance* instruction) {
2900 LocationSummary* locations =
2901 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
2902 if (instruction->IsStringAlloc()) {
2903 locations->AddTemp(LocationFrom(kMethodRegister));
2904 } else {
2905 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2906 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
2907 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
2908 }
2909 locations->SetOut(LocationFrom(r0));
2910}
2911
2912void InstructionCodeGeneratorARMVIXL::VisitNewInstance(HNewInstance* instruction) {
2913 // Note: if heap poisoning is enabled, the entry point takes cares
2914 // of poisoning the reference.
2915 if (instruction->IsStringAlloc()) {
2916 // String is allocated through StringFactory. Call NewEmptyString entry point.
2917 vixl32::Register temp = RegisterFrom(instruction->GetLocations()->GetTemp(0));
2918 MemberOffset code_offset = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize);
2919 GetAssembler()->LoadFromOffset(kLoadWord, temp, tr, QUICK_ENTRY_POINT(pNewEmptyString));
2920 GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, code_offset.Int32Value());
2921 AssemblerAccurateScope aas(GetVIXLAssembler(),
2922 kArmInstrMaxSizeInBytes,
2923 CodeBufferCheckScope::kMaximumSize);
2924 __ blx(lr);
2925 codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
2926 } else {
2927 codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
2928 CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, ArtMethod*>();
2929 }
2930}
2931
2932void LocationsBuilderARMVIXL::VisitNewArray(HNewArray* instruction) {
2933 LocationSummary* locations =
2934 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
2935 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2936 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(0)));
2937 locations->SetOut(LocationFrom(r0));
2938 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(1)));
2939 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(2)));
2940}
2941
2942void InstructionCodeGeneratorARMVIXL::VisitNewArray(HNewArray* instruction) {
2943 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2944 __ Mov(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex());
2945 // Note: if heap poisoning is enabled, the entry point takes cares
2946 // of poisoning the reference.
2947 codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
2948 CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>();
2949}
2950
2951void LocationsBuilderARMVIXL::VisitParameterValue(HParameterValue* instruction) {
2952 LocationSummary* locations =
2953 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
2954 Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
2955 if (location.IsStackSlot()) {
2956 location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
2957 } else if (location.IsDoubleStackSlot()) {
2958 location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
2959 }
2960 locations->SetOut(location);
2961}
2962
2963void InstructionCodeGeneratorARMVIXL::VisitParameterValue(
2964 HParameterValue* instruction ATTRIBUTE_UNUSED) {
2965 // Nothing to do, the parameter is already at its location.
2966}
2967
2968void LocationsBuilderARMVIXL::VisitCurrentMethod(HCurrentMethod* instruction) {
2969 LocationSummary* locations =
2970 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
2971 locations->SetOut(LocationFrom(kMethodRegister));
2972}
2973
2974void InstructionCodeGeneratorARMVIXL::VisitCurrentMethod(
2975 HCurrentMethod* instruction ATTRIBUTE_UNUSED) {
2976 // Nothing to do, the method is already at its location.
2977}
2978
2979void LocationsBuilderARMVIXL::VisitNot(HNot* not_) {
2980 LocationSummary* locations =
2981 new (GetGraph()->GetArena()) LocationSummary(not_, LocationSummary::kNoCall);
2982 locations->SetInAt(0, Location::RequiresRegister());
2983 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2984}
2985
2986void InstructionCodeGeneratorARMVIXL::VisitNot(HNot* not_) {
2987 LocationSummary* locations = not_->GetLocations();
2988 Location out = locations->Out();
2989 Location in = locations->InAt(0);
2990 switch (not_->GetResultType()) {
2991 case Primitive::kPrimInt:
2992 __ Mvn(OutputRegister(not_), InputRegisterAt(not_, 0));
2993 break;
2994
2995 case Primitive::kPrimLong:
2996 __ Mvn(LowRegisterFrom(out), LowRegisterFrom(in));
2997 __ Mvn(HighRegisterFrom(out), HighRegisterFrom(in));
2998 break;
2999
3000 default:
3001 LOG(FATAL) << "Unimplemented type for not operation " << not_->GetResultType();
3002 }
3003}
3004
Scott Wakelingc34dba72016-10-03 10:14:44 +01003005void LocationsBuilderARMVIXL::VisitBooleanNot(HBooleanNot* bool_not) {
3006 LocationSummary* locations =
3007 new (GetGraph()->GetArena()) LocationSummary(bool_not, LocationSummary::kNoCall);
3008 locations->SetInAt(0, Location::RequiresRegister());
3009 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3010}
3011
3012void InstructionCodeGeneratorARMVIXL::VisitBooleanNot(HBooleanNot* bool_not) {
3013 __ Eor(OutputRegister(bool_not), InputRegister(bool_not), 1);
3014}
3015
Artem Serov02d37832016-10-25 15:25:33 +01003016void LocationsBuilderARMVIXL::VisitCompare(HCompare* compare) {
3017 LocationSummary* locations =
3018 new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall);
3019 switch (compare->InputAt(0)->GetType()) {
3020 case Primitive::kPrimBoolean:
3021 case Primitive::kPrimByte:
3022 case Primitive::kPrimShort:
3023 case Primitive::kPrimChar:
3024 case Primitive::kPrimInt:
3025 case Primitive::kPrimLong: {
3026 locations->SetInAt(0, Location::RequiresRegister());
3027 locations->SetInAt(1, Location::RequiresRegister());
3028 // Output overlaps because it is written before doing the low comparison.
3029 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
3030 break;
3031 }
3032 case Primitive::kPrimFloat:
3033 case Primitive::kPrimDouble: {
3034 locations->SetInAt(0, Location::RequiresFpuRegister());
3035 locations->SetInAt(1, ArithmeticZeroOrFpuRegister(compare->InputAt(1)));
3036 locations->SetOut(Location::RequiresRegister());
3037 break;
3038 }
3039 default:
3040 LOG(FATAL) << "Unexpected type for compare operation " << compare->InputAt(0)->GetType();
3041 }
3042}
3043
3044void InstructionCodeGeneratorARMVIXL::VisitCompare(HCompare* compare) {
3045 LocationSummary* locations = compare->GetLocations();
3046 vixl32::Register out = OutputRegister(compare);
3047 Location left = locations->InAt(0);
3048 Location right = locations->InAt(1);
3049
3050 vixl32::Label less, greater, done;
3051 Primitive::Type type = compare->InputAt(0)->GetType();
3052 vixl32::Condition less_cond = vixl32::Condition(kNone);
3053 switch (type) {
3054 case Primitive::kPrimBoolean:
3055 case Primitive::kPrimByte:
3056 case Primitive::kPrimShort:
3057 case Primitive::kPrimChar:
3058 case Primitive::kPrimInt: {
3059 // Emit move to `out` before the `Cmp`, as `Mov` might affect the status flags.
3060 __ Mov(out, 0);
3061 __ Cmp(RegisterFrom(left), RegisterFrom(right)); // Signed compare.
3062 less_cond = lt;
3063 break;
3064 }
3065 case Primitive::kPrimLong: {
3066 __ Cmp(HighRegisterFrom(left), HighRegisterFrom(right)); // Signed compare.
3067 __ B(lt, &less);
3068 __ B(gt, &greater);
3069 // Emit move to `out` before the last `Cmp`, as `Mov` might affect the status flags.
3070 __ Mov(out, 0);
3071 __ Cmp(LowRegisterFrom(left), LowRegisterFrom(right)); // Unsigned compare.
3072 less_cond = lo;
3073 break;
3074 }
3075 case Primitive::kPrimFloat:
3076 case Primitive::kPrimDouble: {
3077 __ Mov(out, 0);
3078 GenerateVcmp(compare);
3079 // To branch on the FP compare result we transfer FPSCR to APSR (encoded as PC in VMRS).
3080 __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
3081 less_cond = ARMFPCondition(kCondLT, compare->IsGtBias());
3082 break;
3083 }
3084 default:
3085 LOG(FATAL) << "Unexpected compare type " << type;
3086 UNREACHABLE();
3087 }
3088
3089 __ B(eq, &done);
3090 __ B(less_cond, &less);
3091
3092 __ Bind(&greater);
3093 __ Mov(out, 1);
3094 __ B(&done);
3095
3096 __ Bind(&less);
3097 __ Mov(out, -1);
3098
3099 __ Bind(&done);
3100}
3101
3102void LocationsBuilderARMVIXL::VisitPhi(HPhi* instruction) {
3103 LocationSummary* locations =
3104 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
3105 for (size_t i = 0, e = locations->GetInputCount(); i < e; ++i) {
3106 locations->SetInAt(i, Location::Any());
3107 }
3108 locations->SetOut(Location::Any());
3109}
3110
3111void InstructionCodeGeneratorARMVIXL::VisitPhi(HPhi* instruction ATTRIBUTE_UNUSED) {
3112 LOG(FATAL) << "Unreachable";
3113}
3114
3115void CodeGeneratorARMVIXL::GenerateMemoryBarrier(MemBarrierKind kind) {
3116 // TODO (ported from quick): revisit ARM barrier kinds.
3117 DmbOptions flavor = DmbOptions::ISH; // Quiet C++ warnings.
3118 switch (kind) {
3119 case MemBarrierKind::kAnyStore:
3120 case MemBarrierKind::kLoadAny:
3121 case MemBarrierKind::kAnyAny: {
3122 flavor = DmbOptions::ISH;
3123 break;
3124 }
3125 case MemBarrierKind::kStoreStore: {
3126 flavor = DmbOptions::ISHST;
3127 break;
3128 }
3129 default:
3130 LOG(FATAL) << "Unexpected memory barrier " << kind;
3131 }
3132 __ Dmb(flavor);
3133}
3134
3135void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicLoad(vixl32::Register addr,
3136 uint32_t offset,
3137 vixl32::Register out_lo,
3138 vixl32::Register out_hi) {
3139 UseScratchRegisterScope temps(GetVIXLAssembler());
3140 if (offset != 0) {
3141 vixl32::Register temp = temps.Acquire();
3142 __ Add(temp, addr, offset);
3143 addr = temp;
3144 }
3145 __ Ldrexd(out_lo, out_hi, addr);
3146}
3147
3148void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicStore(vixl32::Register addr,
3149 uint32_t offset,
3150 vixl32::Register value_lo,
3151 vixl32::Register value_hi,
3152 vixl32::Register temp1,
3153 vixl32::Register temp2,
3154 HInstruction* instruction) {
3155 UseScratchRegisterScope temps(GetVIXLAssembler());
3156 vixl32::Label fail;
3157 if (offset != 0) {
3158 vixl32::Register temp = temps.Acquire();
3159 __ Add(temp, addr, offset);
3160 addr = temp;
3161 }
3162 __ Bind(&fail);
3163 // We need a load followed by store. (The address used in a STREX instruction must
3164 // be the same as the address in the most recently executed LDREX instruction.)
3165 __ Ldrexd(temp1, temp2, addr);
3166 codegen_->MaybeRecordImplicitNullCheck(instruction);
3167 __ Strexd(temp1, value_lo, value_hi, addr);
3168 __ Cbnz(temp1, &fail);
3169}
Artem Serov02109dd2016-09-23 17:17:54 +01003170
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003171void LocationsBuilderARMVIXL::HandleFieldSet(
3172 HInstruction* instruction, const FieldInfo& field_info) {
3173 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
3174
3175 LocationSummary* locations =
3176 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
3177 locations->SetInAt(0, Location::RequiresRegister());
3178
3179 Primitive::Type field_type = field_info.GetFieldType();
3180 if (Primitive::IsFloatingPointType(field_type)) {
3181 locations->SetInAt(1, Location::RequiresFpuRegister());
3182 } else {
3183 locations->SetInAt(1, Location::RequiresRegister());
3184 }
3185
3186 bool is_wide = field_type == Primitive::kPrimLong || field_type == Primitive::kPrimDouble;
3187 bool generate_volatile = field_info.IsVolatile()
3188 && is_wide
3189 && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
3190 bool needs_write_barrier =
3191 CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1));
3192 // Temporary registers for the write barrier.
3193 // TODO: consider renaming StoreNeedsWriteBarrier to StoreNeedsGCMark.
3194 if (needs_write_barrier) {
3195 locations->AddTemp(Location::RequiresRegister()); // Possibly used for reference poisoning too.
3196 locations->AddTemp(Location::RequiresRegister());
3197 } else if (generate_volatile) {
3198 // ARM encoding have some additional constraints for ldrexd/strexd:
3199 // - registers need to be consecutive
3200 // - the first register should be even but not R14.
3201 // We don't test for ARM yet, and the assertion makes sure that we
3202 // revisit this if we ever enable ARM encoding.
3203 DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet());
3204
3205 locations->AddTemp(Location::RequiresRegister());
3206 locations->AddTemp(Location::RequiresRegister());
3207 if (field_type == Primitive::kPrimDouble) {
3208 // For doubles we need two more registers to copy the value.
3209 locations->AddTemp(LocationFrom(r2));
3210 locations->AddTemp(LocationFrom(r3));
3211 }
3212 }
3213}
3214
3215void InstructionCodeGeneratorARMVIXL::HandleFieldSet(HInstruction* instruction,
3216 const FieldInfo& field_info,
3217 bool value_can_be_null) {
3218 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
3219
3220 LocationSummary* locations = instruction->GetLocations();
3221 vixl32::Register base = InputRegisterAt(instruction, 0);
3222 Location value = locations->InAt(1);
3223
3224 bool is_volatile = field_info.IsVolatile();
3225 bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
3226 Primitive::Type field_type = field_info.GetFieldType();
3227 uint32_t offset = field_info.GetFieldOffset().Uint32Value();
3228 bool needs_write_barrier =
3229 CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1));
3230
3231 if (is_volatile) {
3232 codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
3233 }
3234
3235 switch (field_type) {
3236 case Primitive::kPrimBoolean:
3237 case Primitive::kPrimByte: {
3238 GetAssembler()->StoreToOffset(kStoreByte, RegisterFrom(value), base, offset);
3239 break;
3240 }
3241
3242 case Primitive::kPrimShort:
3243 case Primitive::kPrimChar: {
3244 GetAssembler()->StoreToOffset(kStoreHalfword, RegisterFrom(value), base, offset);
3245 break;
3246 }
3247
3248 case Primitive::kPrimInt:
3249 case Primitive::kPrimNot: {
3250 if (kPoisonHeapReferences && needs_write_barrier) {
3251 // Note that in the case where `value` is a null reference,
3252 // we do not enter this block, as a null reference does not
3253 // need poisoning.
3254 DCHECK_EQ(field_type, Primitive::kPrimNot);
3255 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
3256 __ Mov(temp, RegisterFrom(value));
3257 GetAssembler()->PoisonHeapReference(temp);
3258 GetAssembler()->StoreToOffset(kStoreWord, temp, base, offset);
3259 } else {
3260 GetAssembler()->StoreToOffset(kStoreWord, RegisterFrom(value), base, offset);
3261 }
3262 break;
3263 }
3264
3265 case Primitive::kPrimLong: {
3266 if (is_volatile && !atomic_ldrd_strd) {
3267 GenerateWideAtomicStore(base,
3268 offset,
3269 LowRegisterFrom(value),
3270 HighRegisterFrom(value),
3271 RegisterFrom(locations->GetTemp(0)),
3272 RegisterFrom(locations->GetTemp(1)),
3273 instruction);
3274 } else {
3275 GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), base, offset);
3276 codegen_->MaybeRecordImplicitNullCheck(instruction);
3277 }
3278 break;
3279 }
3280
3281 case Primitive::kPrimFloat: {
3282 GetAssembler()->StoreSToOffset(SRegisterFrom(value), base, offset);
3283 break;
3284 }
3285
3286 case Primitive::kPrimDouble: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01003287 vixl32::DRegister value_reg = DRegisterFrom(value);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003288 if (is_volatile && !atomic_ldrd_strd) {
3289 vixl32::Register value_reg_lo = RegisterFrom(locations->GetTemp(0));
3290 vixl32::Register value_reg_hi = RegisterFrom(locations->GetTemp(1));
3291
3292 __ Vmov(value_reg_lo, value_reg_hi, value_reg);
3293
3294 GenerateWideAtomicStore(base,
3295 offset,
3296 value_reg_lo,
3297 value_reg_hi,
3298 RegisterFrom(locations->GetTemp(2)),
3299 RegisterFrom(locations->GetTemp(3)),
3300 instruction);
3301 } else {
3302 GetAssembler()->StoreDToOffset(value_reg, base, offset);
3303 codegen_->MaybeRecordImplicitNullCheck(instruction);
3304 }
3305 break;
3306 }
3307
3308 case Primitive::kPrimVoid:
3309 LOG(FATAL) << "Unreachable type " << field_type;
3310 UNREACHABLE();
3311 }
3312
3313 // Longs and doubles are handled in the switch.
3314 if (field_type != Primitive::kPrimLong && field_type != Primitive::kPrimDouble) {
3315 codegen_->MaybeRecordImplicitNullCheck(instruction);
3316 }
3317
3318 if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
3319 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
3320 vixl32::Register card = RegisterFrom(locations->GetTemp(1));
3321 codegen_->MarkGCCard(temp, card, base, RegisterFrom(value), value_can_be_null);
3322 }
3323
3324 if (is_volatile) {
3325 codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
3326 }
3327}
3328
Artem Serov02d37832016-10-25 15:25:33 +01003329void LocationsBuilderARMVIXL::HandleFieldGet(HInstruction* instruction,
3330 const FieldInfo& field_info) {
3331 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
3332
3333 bool object_field_get_with_read_barrier =
3334 kEmitCompilerReadBarrier && (field_info.GetFieldType() == Primitive::kPrimNot);
3335 LocationSummary* locations =
3336 new (GetGraph()->GetArena()) LocationSummary(instruction,
3337 object_field_get_with_read_barrier ?
3338 LocationSummary::kCallOnSlowPath :
3339 LocationSummary::kNoCall);
3340 if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
3341 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
3342 }
3343 locations->SetInAt(0, Location::RequiresRegister());
3344
3345 bool volatile_for_double = field_info.IsVolatile()
3346 && (field_info.GetFieldType() == Primitive::kPrimDouble)
3347 && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
3348 // The output overlaps in case of volatile long: we don't want the
3349 // code generated by GenerateWideAtomicLoad to overwrite the
3350 // object's location. Likewise, in the case of an object field get
3351 // with read barriers enabled, we do not want the load to overwrite
3352 // the object's location, as we need it to emit the read barrier.
3353 bool overlap = (field_info.IsVolatile() && (field_info.GetFieldType() == Primitive::kPrimLong)) ||
3354 object_field_get_with_read_barrier;
3355
3356 if (Primitive::IsFloatingPointType(instruction->GetType())) {
3357 locations->SetOut(Location::RequiresFpuRegister());
3358 } else {
3359 locations->SetOut(Location::RequiresRegister(),
3360 (overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap));
3361 }
3362 if (volatile_for_double) {
3363 // ARM encoding have some additional constraints for ldrexd/strexd:
3364 // - registers need to be consecutive
3365 // - the first register should be even but not R14.
3366 // We don't test for ARM yet, and the assertion makes sure that we
3367 // revisit this if we ever enable ARM encoding.
3368 DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet());
3369 locations->AddTemp(Location::RequiresRegister());
3370 locations->AddTemp(Location::RequiresRegister());
3371 } else if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
3372 // We need a temporary register for the read barrier marking slow
3373 // path in CodeGeneratorARM::GenerateFieldLoadWithBakerReadBarrier.
3374 locations->AddTemp(Location::RequiresRegister());
3375 }
3376}
3377
3378Location LocationsBuilderARMVIXL::ArithmeticZeroOrFpuRegister(HInstruction* input) {
3379 DCHECK(Primitive::IsFloatingPointType(input->GetType())) << input->GetType();
3380 if ((input->IsFloatConstant() && (input->AsFloatConstant()->IsArithmeticZero())) ||
3381 (input->IsDoubleConstant() && (input->AsDoubleConstant()->IsArithmeticZero()))) {
3382 return Location::ConstantLocation(input->AsConstant());
3383 } else {
3384 return Location::RequiresFpuRegister();
3385 }
3386}
3387
Artem Serov02109dd2016-09-23 17:17:54 +01003388Location LocationsBuilderARMVIXL::ArmEncodableConstantOrRegister(HInstruction* constant,
3389 Opcode opcode) {
3390 DCHECK(!Primitive::IsFloatingPointType(constant->GetType()));
3391 if (constant->IsConstant() &&
3392 CanEncodeConstantAsImmediate(constant->AsConstant(), opcode)) {
3393 return Location::ConstantLocation(constant->AsConstant());
3394 }
3395 return Location::RequiresRegister();
3396}
3397
3398bool LocationsBuilderARMVIXL::CanEncodeConstantAsImmediate(HConstant* input_cst,
3399 Opcode opcode) {
3400 uint64_t value = static_cast<uint64_t>(Int64FromConstant(input_cst));
3401 if (Primitive::Is64BitType(input_cst->GetType())) {
3402 Opcode high_opcode = opcode;
3403 SetCc low_set_cc = kCcDontCare;
3404 switch (opcode) {
3405 case SUB:
3406 // Flip the operation to an ADD.
3407 value = -value;
3408 opcode = ADD;
3409 FALLTHROUGH_INTENDED;
3410 case ADD:
3411 if (Low32Bits(value) == 0u) {
3412 return CanEncodeConstantAsImmediate(High32Bits(value), opcode, kCcDontCare);
3413 }
3414 high_opcode = ADC;
3415 low_set_cc = kCcSet;
3416 break;
3417 default:
3418 break;
3419 }
3420 return CanEncodeConstantAsImmediate(Low32Bits(value), opcode, low_set_cc) &&
3421 CanEncodeConstantAsImmediate(High32Bits(value), high_opcode, kCcDontCare);
3422 } else {
3423 return CanEncodeConstantAsImmediate(Low32Bits(value), opcode);
3424 }
3425}
3426
3427// TODO(VIXL): Replace art::arm::SetCc` with `vixl32::FlagsUpdate after flags set optimization
3428// enabled.
3429bool LocationsBuilderARMVIXL::CanEncodeConstantAsImmediate(uint32_t value,
3430 Opcode opcode,
3431 SetCc set_cc) {
3432 ArmVIXLAssembler* assembler = codegen_->GetAssembler();
3433 if (assembler->ShifterOperandCanHold(opcode, value, set_cc)) {
3434 return true;
3435 }
3436 Opcode neg_opcode = kNoOperand;
3437 switch (opcode) {
3438 case AND: neg_opcode = BIC; value = ~value; break;
3439 case ORR: neg_opcode = ORN; value = ~value; break;
3440 case ADD: neg_opcode = SUB; value = -value; break;
3441 case ADC: neg_opcode = SBC; value = ~value; break;
3442 case SUB: neg_opcode = ADD; value = -value; break;
3443 case SBC: neg_opcode = ADC; value = ~value; break;
3444 default:
3445 return false;
3446 }
3447 return assembler->ShifterOperandCanHold(neg_opcode, value, set_cc);
3448}
3449
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003450void InstructionCodeGeneratorARMVIXL::HandleFieldGet(HInstruction* instruction,
3451 const FieldInfo& field_info) {
3452 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
3453
3454 LocationSummary* locations = instruction->GetLocations();
3455 vixl32::Register base = InputRegisterAt(instruction, 0);
3456 Location out = locations->Out();
3457 bool is_volatile = field_info.IsVolatile();
3458 bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
3459 Primitive::Type field_type = field_info.GetFieldType();
3460 uint32_t offset = field_info.GetFieldOffset().Uint32Value();
3461
3462 switch (field_type) {
3463 case Primitive::kPrimBoolean:
3464 GetAssembler()->LoadFromOffset(kLoadUnsignedByte, RegisterFrom(out), base, offset);
3465 break;
3466
3467 case Primitive::kPrimByte:
3468 GetAssembler()->LoadFromOffset(kLoadSignedByte, RegisterFrom(out), base, offset);
3469 break;
3470
3471 case Primitive::kPrimShort:
3472 GetAssembler()->LoadFromOffset(kLoadSignedHalfword, RegisterFrom(out), base, offset);
3473 break;
3474
3475 case Primitive::kPrimChar:
3476 GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, RegisterFrom(out), base, offset);
3477 break;
3478
3479 case Primitive::kPrimInt:
3480 GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(out), base, offset);
3481 break;
3482
3483 case Primitive::kPrimNot: {
3484 // /* HeapReference<Object> */ out = *(base + offset)
3485 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
3486 TODO_VIXL32(FATAL);
3487 } else {
3488 GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(out), base, offset);
3489 // TODO(VIXL): Scope to guarantee the position immediately after the load.
3490 codegen_->MaybeRecordImplicitNullCheck(instruction);
3491 if (is_volatile) {
3492 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
3493 }
3494 // If read barriers are enabled, emit read barriers other than
3495 // Baker's using a slow path (and also unpoison the loaded
3496 // reference, if heap poisoning is enabled).
3497 codegen_->MaybeGenerateReadBarrierSlow(instruction, out, out, locations->InAt(0), offset);
3498 }
3499 break;
3500 }
3501
3502 case Primitive::kPrimLong:
3503 if (is_volatile && !atomic_ldrd_strd) {
3504 GenerateWideAtomicLoad(base, offset, LowRegisterFrom(out), HighRegisterFrom(out));
3505 } else {
3506 GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out), base, offset);
3507 }
3508 break;
3509
3510 case Primitive::kPrimFloat:
3511 GetAssembler()->LoadSFromOffset(SRegisterFrom(out), base, offset);
3512 break;
3513
3514 case Primitive::kPrimDouble: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01003515 vixl32::DRegister out_dreg = DRegisterFrom(out);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003516 if (is_volatile && !atomic_ldrd_strd) {
3517 vixl32::Register lo = RegisterFrom(locations->GetTemp(0));
3518 vixl32::Register hi = RegisterFrom(locations->GetTemp(1));
3519 GenerateWideAtomicLoad(base, offset, lo, hi);
3520 // TODO(VIXL): Do we need to be immediately after the ldrexd instruction? If so we need a
3521 // scope.
3522 codegen_->MaybeRecordImplicitNullCheck(instruction);
3523 __ Vmov(out_dreg, lo, hi);
3524 } else {
3525 GetAssembler()->LoadDFromOffset(out_dreg, base, offset);
3526 // TODO(VIXL): Scope to guarantee the position immediately after the load.
3527 codegen_->MaybeRecordImplicitNullCheck(instruction);
3528 }
3529 break;
3530 }
3531
3532 case Primitive::kPrimVoid:
3533 LOG(FATAL) << "Unreachable type " << field_type;
3534 UNREACHABLE();
3535 }
3536
3537 if (field_type == Primitive::kPrimNot || field_type == Primitive::kPrimDouble) {
3538 // Potential implicit null checks, in the case of reference or
3539 // double fields, are handled in the previous switch statement.
3540 } else {
3541 // Address cases other than reference and double that may require an implicit null check.
3542 codegen_->MaybeRecordImplicitNullCheck(instruction);
3543 }
3544
3545 if (is_volatile) {
3546 if (field_type == Primitive::kPrimNot) {
3547 // Memory barriers, in the case of references, are also handled
3548 // in the previous switch statement.
3549 } else {
3550 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
3551 }
3552 }
3553}
3554
3555void LocationsBuilderARMVIXL::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
3556 HandleFieldSet(instruction, instruction->GetFieldInfo());
3557}
3558
3559void InstructionCodeGeneratorARMVIXL::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
3560 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
3561}
3562
3563void LocationsBuilderARMVIXL::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
3564 HandleFieldGet(instruction, instruction->GetFieldInfo());
3565}
3566
3567void InstructionCodeGeneratorARMVIXL::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
3568 HandleFieldGet(instruction, instruction->GetFieldInfo());
3569}
3570
3571void LocationsBuilderARMVIXL::VisitStaticFieldGet(HStaticFieldGet* instruction) {
3572 HandleFieldGet(instruction, instruction->GetFieldInfo());
3573}
3574
3575void InstructionCodeGeneratorARMVIXL::VisitStaticFieldGet(HStaticFieldGet* instruction) {
3576 HandleFieldGet(instruction, instruction->GetFieldInfo());
3577}
3578
Scott Wakelingc34dba72016-10-03 10:14:44 +01003579void LocationsBuilderARMVIXL::VisitStaticFieldSet(HStaticFieldSet* instruction) {
3580 HandleFieldSet(instruction, instruction->GetFieldInfo());
3581}
3582
3583void InstructionCodeGeneratorARMVIXL::VisitStaticFieldSet(HStaticFieldSet* instruction) {
3584 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
3585}
3586
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003587void LocationsBuilderARMVIXL::VisitNullCheck(HNullCheck* instruction) {
3588 // TODO(VIXL): https://android-review.googlesource.com/#/c/275337/
3589 LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
3590 ? LocationSummary::kCallOnSlowPath
3591 : LocationSummary::kNoCall;
3592 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
3593 locations->SetInAt(0, Location::RequiresRegister());
3594 if (instruction->HasUses()) {
3595 locations->SetOut(Location::SameAsFirstInput());
3596 }
3597}
3598
3599void CodeGeneratorARMVIXL::GenerateImplicitNullCheck(HNullCheck* instruction) {
3600 if (CanMoveNullCheckToUser(instruction)) {
3601 return;
3602 }
3603
3604 UseScratchRegisterScope temps(GetVIXLAssembler());
3605 AssemblerAccurateScope aas(GetVIXLAssembler(),
3606 kArmInstrMaxSizeInBytes,
3607 CodeBufferCheckScope::kMaximumSize);
3608 __ ldr(temps.Acquire(), MemOperand(InputRegisterAt(instruction, 0)));
3609 RecordPcInfo(instruction, instruction->GetDexPc());
3610}
3611
3612void CodeGeneratorARMVIXL::GenerateExplicitNullCheck(HNullCheck* instruction) {
3613 NullCheckSlowPathARMVIXL* slow_path =
3614 new (GetGraph()->GetArena()) NullCheckSlowPathARMVIXL(instruction);
3615 AddSlowPath(slow_path);
3616 __ Cbz(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
3617}
3618
3619void InstructionCodeGeneratorARMVIXL::VisitNullCheck(HNullCheck* instruction) {
3620 codegen_->GenerateNullCheck(instruction);
3621}
3622
Scott Wakelingc34dba72016-10-03 10:14:44 +01003623static LoadOperandType GetLoadOperandType(Primitive::Type type) {
3624 switch (type) {
3625 case Primitive::kPrimNot:
3626 return kLoadWord;
3627 case Primitive::kPrimBoolean:
3628 return kLoadUnsignedByte;
3629 case Primitive::kPrimByte:
3630 return kLoadSignedByte;
3631 case Primitive::kPrimChar:
3632 return kLoadUnsignedHalfword;
3633 case Primitive::kPrimShort:
3634 return kLoadSignedHalfword;
3635 case Primitive::kPrimInt:
3636 return kLoadWord;
3637 case Primitive::kPrimLong:
3638 return kLoadWordPair;
3639 case Primitive::kPrimFloat:
3640 return kLoadSWord;
3641 case Primitive::kPrimDouble:
3642 return kLoadDWord;
3643 default:
3644 LOG(FATAL) << "Unreachable type " << type;
3645 UNREACHABLE();
3646 }
3647}
3648
3649static StoreOperandType GetStoreOperandType(Primitive::Type type) {
3650 switch (type) {
3651 case Primitive::kPrimNot:
3652 return kStoreWord;
3653 case Primitive::kPrimBoolean:
3654 case Primitive::kPrimByte:
3655 return kStoreByte;
3656 case Primitive::kPrimChar:
3657 case Primitive::kPrimShort:
3658 return kStoreHalfword;
3659 case Primitive::kPrimInt:
3660 return kStoreWord;
3661 case Primitive::kPrimLong:
3662 return kStoreWordPair;
3663 case Primitive::kPrimFloat:
3664 return kStoreSWord;
3665 case Primitive::kPrimDouble:
3666 return kStoreDWord;
3667 default:
3668 LOG(FATAL) << "Unreachable type " << type;
3669 UNREACHABLE();
3670 }
3671}
3672
3673void CodeGeneratorARMVIXL::LoadFromShiftedRegOffset(Primitive::Type type,
3674 Location out_loc,
3675 vixl32::Register base,
3676 vixl32::Register reg_index,
3677 vixl32::Condition cond) {
3678 uint32_t shift_count = Primitive::ComponentSizeShift(type);
3679 MemOperand mem_address(base, reg_index, vixl32::LSL, shift_count);
3680
3681 switch (type) {
3682 case Primitive::kPrimByte:
3683 __ Ldrsb(cond, RegisterFrom(out_loc), mem_address);
3684 break;
3685 case Primitive::kPrimBoolean:
3686 __ Ldrb(cond, RegisterFrom(out_loc), mem_address);
3687 break;
3688 case Primitive::kPrimShort:
3689 __ Ldrsh(cond, RegisterFrom(out_loc), mem_address);
3690 break;
3691 case Primitive::kPrimChar:
3692 __ Ldrh(cond, RegisterFrom(out_loc), mem_address);
3693 break;
3694 case Primitive::kPrimNot:
3695 case Primitive::kPrimInt:
3696 __ Ldr(cond, RegisterFrom(out_loc), mem_address);
3697 break;
3698 // T32 doesn't support LoadFromShiftedRegOffset mem address mode for these types.
3699 case Primitive::kPrimLong:
3700 case Primitive::kPrimFloat:
3701 case Primitive::kPrimDouble:
3702 default:
3703 LOG(FATAL) << "Unreachable type " << type;
3704 UNREACHABLE();
3705 }
3706}
3707
3708void CodeGeneratorARMVIXL::StoreToShiftedRegOffset(Primitive::Type type,
3709 Location loc,
3710 vixl32::Register base,
3711 vixl32::Register reg_index,
3712 vixl32::Condition cond) {
3713 uint32_t shift_count = Primitive::ComponentSizeShift(type);
3714 MemOperand mem_address(base, reg_index, vixl32::LSL, shift_count);
3715
3716 switch (type) {
3717 case Primitive::kPrimByte:
3718 case Primitive::kPrimBoolean:
3719 __ Strb(cond, RegisterFrom(loc), mem_address);
3720 break;
3721 case Primitive::kPrimShort:
3722 case Primitive::kPrimChar:
3723 __ Strh(cond, RegisterFrom(loc), mem_address);
3724 break;
3725 case Primitive::kPrimNot:
3726 case Primitive::kPrimInt:
3727 __ Str(cond, RegisterFrom(loc), mem_address);
3728 break;
3729 // T32 doesn't support StoreToShiftedRegOffset mem address mode for these types.
3730 case Primitive::kPrimLong:
3731 case Primitive::kPrimFloat:
3732 case Primitive::kPrimDouble:
3733 default:
3734 LOG(FATAL) << "Unreachable type " << type;
3735 UNREACHABLE();
3736 }
3737}
3738
3739void LocationsBuilderARMVIXL::VisitArrayGet(HArrayGet* instruction) {
3740 bool object_array_get_with_read_barrier =
3741 kEmitCompilerReadBarrier && (instruction->GetType() == Primitive::kPrimNot);
3742 LocationSummary* locations =
3743 new (GetGraph()->GetArena()) LocationSummary(instruction,
3744 object_array_get_with_read_barrier ?
3745 LocationSummary::kCallOnSlowPath :
3746 LocationSummary::kNoCall);
3747 if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
3748 TODO_VIXL32(FATAL);
3749 }
3750 locations->SetInAt(0, Location::RequiresRegister());
3751 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
3752 if (Primitive::IsFloatingPointType(instruction->GetType())) {
3753 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3754 } else {
3755 // The output overlaps in the case of an object array get with
3756 // read barriers enabled: we do not want the move to overwrite the
3757 // array's location, as we need it to emit the read barrier.
3758 locations->SetOut(
3759 Location::RequiresRegister(),
3760 object_array_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap);
3761 }
3762 // We need a temporary register for the read barrier marking slow
3763 // path in CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier.
3764 // Also need for String compression feature.
3765 if ((object_array_get_with_read_barrier && kUseBakerReadBarrier)
3766 || (mirror::kUseStringCompression && instruction->IsStringCharAt())) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01003767 locations->AddTemp(Location::RequiresRegister());
Scott Wakelingc34dba72016-10-03 10:14:44 +01003768 }
3769}
3770
3771void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) {
3772 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
3773 LocationSummary* locations = instruction->GetLocations();
3774 Location obj_loc = locations->InAt(0);
3775 vixl32::Register obj = InputRegisterAt(instruction, 0);
3776 Location index = locations->InAt(1);
3777 Location out_loc = locations->Out();
3778 uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
3779 Primitive::Type type = instruction->GetType();
3780 const bool maybe_compressed_char_at = mirror::kUseStringCompression &&
3781 instruction->IsStringCharAt();
3782 HInstruction* array_instr = instruction->GetArray();
3783 bool has_intermediate_address = array_instr->IsIntermediateAddress();
3784 // The read barrier instrumentation does not support the HIntermediateAddress instruction yet.
3785 DCHECK(!(has_intermediate_address && kEmitCompilerReadBarrier));
3786
3787 switch (type) {
3788 case Primitive::kPrimBoolean:
3789 case Primitive::kPrimByte:
3790 case Primitive::kPrimShort:
3791 case Primitive::kPrimChar:
3792 case Primitive::kPrimInt: {
3793 if (index.IsConstant()) {
3794 int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue();
3795 if (maybe_compressed_char_at) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01003796 vixl32::Register length = temps.Acquire();
3797 vixl32::Label uncompressed_load, done;
3798 uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
3799 GetAssembler()->LoadFromOffset(kLoadWord, length, obj, count_offset);
3800 codegen_->MaybeRecordImplicitNullCheck(instruction);
3801 __ Cmp(length, 0);
3802 __ B(ge, &uncompressed_load);
3803 GetAssembler()->LoadFromOffset(kLoadUnsignedByte,
3804 RegisterFrom(out_loc),
3805 obj,
3806 data_offset + const_index);
3807 __ B(&done);
3808 __ Bind(&uncompressed_load);
3809 GetAssembler()->LoadFromOffset(GetLoadOperandType(Primitive::kPrimChar),
3810 RegisterFrom(out_loc),
3811 obj,
3812 data_offset + (const_index << 1));
3813 __ Bind(&done);
Scott Wakelingc34dba72016-10-03 10:14:44 +01003814 } else {
3815 uint32_t full_offset = data_offset + (const_index << Primitive::ComponentSizeShift(type));
3816
3817 LoadOperandType load_type = GetLoadOperandType(type);
3818 GetAssembler()->LoadFromOffset(load_type, RegisterFrom(out_loc), obj, full_offset);
3819 }
3820 } else {
3821 vixl32::Register temp = temps.Acquire();
3822
3823 if (has_intermediate_address) {
3824 TODO_VIXL32(FATAL);
3825 } else {
3826 __ Add(temp, obj, data_offset);
3827 }
3828 if (maybe_compressed_char_at) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01003829 vixl32::Label uncompressed_load, done;
3830 uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
3831 vixl32::Register length = RegisterFrom(locations->GetTemp(0));
3832 GetAssembler()->LoadFromOffset(kLoadWord, length, obj, count_offset);
3833 codegen_->MaybeRecordImplicitNullCheck(instruction);
3834 __ Cmp(length, 0);
3835 __ B(ge, &uncompressed_load);
3836 __ Ldrb(RegisterFrom(out_loc), MemOperand(temp, RegisterFrom(index), vixl32::LSL, 0));
3837 __ B(&done);
3838 __ Bind(&uncompressed_load);
3839 __ Ldrh(RegisterFrom(out_loc), MemOperand(temp, RegisterFrom(index), vixl32::LSL, 1));
3840 __ Bind(&done);
Scott Wakelingc34dba72016-10-03 10:14:44 +01003841 } else {
3842 codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index));
3843 }
3844 }
3845 break;
3846 }
3847
3848 case Primitive::kPrimNot: {
3849 static_assert(
3850 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
3851 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
3852 // /* HeapReference<Object> */ out =
3853 // *(obj + data_offset + index * sizeof(HeapReference<Object>))
3854 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
3855 TODO_VIXL32(FATAL);
3856 } else {
3857 vixl32::Register out = OutputRegister(instruction);
3858 if (index.IsConstant()) {
3859 size_t offset =
3860 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
3861 GetAssembler()->LoadFromOffset(kLoadWord, out, obj, offset);
3862 codegen_->MaybeRecordImplicitNullCheck(instruction);
3863 // If read barriers are enabled, emit read barriers other than
3864 // Baker's using a slow path (and also unpoison the loaded
3865 // reference, if heap poisoning is enabled).
3866 codegen_->MaybeGenerateReadBarrierSlow(instruction, out_loc, out_loc, obj_loc, offset);
3867 } else {
3868 vixl32::Register temp = temps.Acquire();
3869
3870 if (has_intermediate_address) {
3871 TODO_VIXL32(FATAL);
3872 } else {
3873 __ Add(temp, obj, data_offset);
3874 }
3875 codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index));
3876
3877 codegen_->MaybeRecordImplicitNullCheck(instruction);
3878 // If read barriers are enabled, emit read barriers other than
3879 // Baker's using a slow path (and also unpoison the loaded
3880 // reference, if heap poisoning is enabled).
3881 codegen_->MaybeGenerateReadBarrierSlow(
3882 instruction, out_loc, out_loc, obj_loc, data_offset, index);
3883 }
3884 }
3885 break;
3886 }
3887
3888 case Primitive::kPrimLong: {
3889 if (index.IsConstant()) {
3890 size_t offset =
3891 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
3892 GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out_loc), obj, offset);
3893 } else {
3894 vixl32::Register temp = temps.Acquire();
3895 __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
3896 GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out_loc), temp, data_offset);
3897 }
3898 break;
3899 }
3900
3901 case Primitive::kPrimFloat: {
3902 vixl32::SRegister out = SRegisterFrom(out_loc);
3903 if (index.IsConstant()) {
3904 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
3905 GetAssembler()->LoadSFromOffset(out, obj, offset);
3906 } else {
3907 vixl32::Register temp = temps.Acquire();
3908 __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_4));
3909 GetAssembler()->LoadSFromOffset(out, temp, data_offset);
3910 }
3911 break;
3912 }
3913
3914 case Primitive::kPrimDouble: {
3915 if (index.IsConstant()) {
3916 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
3917 GetAssembler()->LoadDFromOffset(DRegisterFrom(out_loc), obj, offset);
3918 } else {
3919 vixl32::Register temp = temps.Acquire();
3920 __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
3921 GetAssembler()->LoadDFromOffset(DRegisterFrom(out_loc), temp, data_offset);
3922 }
3923 break;
3924 }
3925
3926 case Primitive::kPrimVoid:
3927 LOG(FATAL) << "Unreachable type " << type;
3928 UNREACHABLE();
3929 }
3930
3931 if (type == Primitive::kPrimNot) {
3932 // Potential implicit null checks, in the case of reference
3933 // arrays, are handled in the previous switch statement.
3934 } else if (!maybe_compressed_char_at) {
3935 codegen_->MaybeRecordImplicitNullCheck(instruction);
3936 }
3937}
3938
3939void LocationsBuilderARMVIXL::VisitArraySet(HArraySet* instruction) {
3940 Primitive::Type value_type = instruction->GetComponentType();
3941
3942 bool needs_write_barrier =
3943 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
3944 bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck();
3945
3946 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(
3947 instruction,
3948 may_need_runtime_call_for_type_check ?
3949 LocationSummary::kCallOnSlowPath :
3950 LocationSummary::kNoCall);
3951
3952 locations->SetInAt(0, Location::RequiresRegister());
3953 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
3954 if (Primitive::IsFloatingPointType(value_type)) {
3955 locations->SetInAt(2, Location::RequiresFpuRegister());
3956 } else {
3957 locations->SetInAt(2, Location::RequiresRegister());
3958 }
3959 if (needs_write_barrier) {
3960 // Temporary registers for the write barrier.
3961 locations->AddTemp(Location::RequiresRegister()); // Possibly used for ref. poisoning too.
3962 locations->AddTemp(Location::RequiresRegister());
3963 }
3964}
3965
3966void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) {
3967 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
3968 LocationSummary* locations = instruction->GetLocations();
3969 vixl32::Register array = InputRegisterAt(instruction, 0);
3970 Location index = locations->InAt(1);
3971 Primitive::Type value_type = instruction->GetComponentType();
3972 bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck();
3973 bool needs_write_barrier =
3974 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
3975 uint32_t data_offset =
3976 mirror::Array::DataOffset(Primitive::ComponentSize(value_type)).Uint32Value();
3977 Location value_loc = locations->InAt(2);
3978 HInstruction* array_instr = instruction->GetArray();
3979 bool has_intermediate_address = array_instr->IsIntermediateAddress();
3980 // The read barrier instrumentation does not support the HIntermediateAddress instruction yet.
3981 DCHECK(!(has_intermediate_address && kEmitCompilerReadBarrier));
3982
3983 switch (value_type) {
3984 case Primitive::kPrimBoolean:
3985 case Primitive::kPrimByte:
3986 case Primitive::kPrimShort:
3987 case Primitive::kPrimChar:
3988 case Primitive::kPrimInt: {
3989 if (index.IsConstant()) {
3990 int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue();
3991 uint32_t full_offset =
3992 data_offset + (const_index << Primitive::ComponentSizeShift(value_type));
3993 StoreOperandType store_type = GetStoreOperandType(value_type);
3994 GetAssembler()->StoreToOffset(store_type, RegisterFrom(value_loc), array, full_offset);
3995 } else {
3996 vixl32::Register temp = temps.Acquire();
3997
3998 if (has_intermediate_address) {
3999 TODO_VIXL32(FATAL);
4000 } else {
4001 __ Add(temp, array, data_offset);
4002 }
4003 codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
4004 }
4005 break;
4006 }
4007
4008 case Primitive::kPrimNot: {
4009 vixl32::Register value = RegisterFrom(value_loc);
4010 // TryExtractArrayAccessAddress optimization is never applied for non-primitive ArraySet.
4011 // See the comment in instruction_simplifier_shared.cc.
4012 DCHECK(!has_intermediate_address);
4013
4014 if (instruction->InputAt(2)->IsNullConstant()) {
4015 // Just setting null.
4016 if (index.IsConstant()) {
4017 size_t offset =
4018 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
4019 GetAssembler()->StoreToOffset(kStoreWord, value, array, offset);
4020 } else {
4021 DCHECK(index.IsRegister()) << index;
4022 vixl32::Register temp = temps.Acquire();
4023 __ Add(temp, array, data_offset);
4024 codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
4025 }
4026 codegen_->MaybeRecordImplicitNullCheck(instruction);
4027 DCHECK(!needs_write_barrier);
4028 DCHECK(!may_need_runtime_call_for_type_check);
4029 break;
4030 }
4031
4032 DCHECK(needs_write_barrier);
4033 Location temp1_loc = locations->GetTemp(0);
4034 vixl32::Register temp1 = RegisterFrom(temp1_loc);
4035 Location temp2_loc = locations->GetTemp(1);
4036 vixl32::Register temp2 = RegisterFrom(temp2_loc);
4037 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
4038 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
4039 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
4040 vixl32::Label done;
4041 SlowPathCodeARMVIXL* slow_path = nullptr;
4042
4043 if (may_need_runtime_call_for_type_check) {
4044 slow_path = new (GetGraph()->GetArena()) ArraySetSlowPathARMVIXL(instruction);
4045 codegen_->AddSlowPath(slow_path);
4046 if (instruction->GetValueCanBeNull()) {
4047 vixl32::Label non_zero;
4048 __ Cbnz(value, &non_zero);
4049 if (index.IsConstant()) {
4050 size_t offset =
4051 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
4052 GetAssembler()->StoreToOffset(kStoreWord, value, array, offset);
4053 } else {
4054 DCHECK(index.IsRegister()) << index;
4055 vixl32::Register temp = temps.Acquire();
4056 __ Add(temp, array, data_offset);
4057 codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
4058 }
4059 codegen_->MaybeRecordImplicitNullCheck(instruction);
4060 __ B(&done);
4061 __ Bind(&non_zero);
4062 }
4063
4064 // Note that when read barriers are enabled, the type checks
4065 // are performed without read barriers. This is fine, even in
4066 // the case where a class object is in the from-space after
4067 // the flip, as a comparison involving such a type would not
4068 // produce a false positive; it may of course produce a false
4069 // negative, in which case we would take the ArraySet slow
4070 // path.
4071
4072 // /* HeapReference<Class> */ temp1 = array->klass_
4073 GetAssembler()->LoadFromOffset(kLoadWord, temp1, array, class_offset);
4074 codegen_->MaybeRecordImplicitNullCheck(instruction);
4075 GetAssembler()->MaybeUnpoisonHeapReference(temp1);
4076
4077 // /* HeapReference<Class> */ temp1 = temp1->component_type_
4078 GetAssembler()->LoadFromOffset(kLoadWord, temp1, temp1, component_offset);
4079 // /* HeapReference<Class> */ temp2 = value->klass_
4080 GetAssembler()->LoadFromOffset(kLoadWord, temp2, value, class_offset);
4081 // If heap poisoning is enabled, no need to unpoison `temp1`
4082 // nor `temp2`, as we are comparing two poisoned references.
4083 __ Cmp(temp1, temp2);
4084
4085 if (instruction->StaticTypeOfArrayIsObjectArray()) {
4086 vixl32::Label do_put;
4087 __ B(eq, &do_put);
4088 // If heap poisoning is enabled, the `temp1` reference has
4089 // not been unpoisoned yet; unpoison it now.
4090 GetAssembler()->MaybeUnpoisonHeapReference(temp1);
4091
4092 // /* HeapReference<Class> */ temp1 = temp1->super_class_
4093 GetAssembler()->LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
4094 // If heap poisoning is enabled, no need to unpoison
4095 // `temp1`, as we are comparing against null below.
4096 __ Cbnz(temp1, slow_path->GetEntryLabel());
4097 __ Bind(&do_put);
4098 } else {
4099 __ B(ne, slow_path->GetEntryLabel());
4100 }
4101 }
4102
4103 vixl32::Register source = value;
4104 if (kPoisonHeapReferences) {
4105 // Note that in the case where `value` is a null reference,
4106 // we do not enter this block, as a null reference does not
4107 // need poisoning.
4108 DCHECK_EQ(value_type, Primitive::kPrimNot);
4109 __ Mov(temp1, value);
4110 GetAssembler()->PoisonHeapReference(temp1);
4111 source = temp1;
4112 }
4113
4114 if (index.IsConstant()) {
4115 size_t offset =
4116 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
4117 GetAssembler()->StoreToOffset(kStoreWord, source, array, offset);
4118 } else {
4119 DCHECK(index.IsRegister()) << index;
4120
4121 vixl32::Register temp = temps.Acquire();
4122 __ Add(temp, array, data_offset);
4123 codegen_->StoreToShiftedRegOffset(value_type,
4124 LocationFrom(source),
4125 temp,
4126 RegisterFrom(index));
4127 }
4128
4129 if (!may_need_runtime_call_for_type_check) {
4130 codegen_->MaybeRecordImplicitNullCheck(instruction);
4131 }
4132
4133 codegen_->MarkGCCard(temp1, temp2, array, value, instruction->GetValueCanBeNull());
4134
4135 if (done.IsReferenced()) {
4136 __ Bind(&done);
4137 }
4138
4139 if (slow_path != nullptr) {
4140 __ Bind(slow_path->GetExitLabel());
4141 }
4142
4143 break;
4144 }
4145
4146 case Primitive::kPrimLong: {
4147 Location value = locations->InAt(2);
4148 if (index.IsConstant()) {
4149 size_t offset =
4150 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
4151 GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), array, offset);
4152 } else {
4153 vixl32::Register temp = temps.Acquire();
4154 __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
4155 GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), temp, data_offset);
4156 }
4157 break;
4158 }
4159
4160 case Primitive::kPrimFloat: {
4161 Location value = locations->InAt(2);
4162 DCHECK(value.IsFpuRegister());
4163 if (index.IsConstant()) {
4164 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
4165 GetAssembler()->StoreSToOffset(SRegisterFrom(value), array, offset);
4166 } else {
4167 vixl32::Register temp = temps.Acquire();
4168 __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_4));
4169 GetAssembler()->StoreSToOffset(SRegisterFrom(value), temp, data_offset);
4170 }
4171 break;
4172 }
4173
4174 case Primitive::kPrimDouble: {
4175 Location value = locations->InAt(2);
4176 DCHECK(value.IsFpuRegisterPair());
4177 if (index.IsConstant()) {
4178 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
4179 GetAssembler()->StoreDToOffset(DRegisterFrom(value), array, offset);
4180 } else {
4181 vixl32::Register temp = temps.Acquire();
4182 __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
4183 GetAssembler()->StoreDToOffset(DRegisterFrom(value), temp, data_offset);
4184 }
4185 break;
4186 }
4187
4188 case Primitive::kPrimVoid:
4189 LOG(FATAL) << "Unreachable type " << value_type;
4190 UNREACHABLE();
4191 }
4192
4193 // Objects are handled in the switch.
4194 if (value_type != Primitive::kPrimNot) {
4195 codegen_->MaybeRecordImplicitNullCheck(instruction);
4196 }
4197}
4198
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004199void LocationsBuilderARMVIXL::VisitArrayLength(HArrayLength* instruction) {
4200 LocationSummary* locations =
4201 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
4202 locations->SetInAt(0, Location::RequiresRegister());
4203 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4204}
4205
4206void InstructionCodeGeneratorARMVIXL::VisitArrayLength(HArrayLength* instruction) {
4207 uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction);
4208 vixl32::Register obj = InputRegisterAt(instruction, 0);
4209 vixl32::Register out = OutputRegister(instruction);
4210 GetAssembler()->LoadFromOffset(kLoadWord, out, obj, offset);
4211 codegen_->MaybeRecordImplicitNullCheck(instruction);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004212 // Mask out compression flag from String's array length.
4213 if (mirror::kUseStringCompression && instruction->IsStringLength()) {
4214 __ Bic(out, out, 1u << 31);
4215 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004216}
4217
Scott Wakelingc34dba72016-10-03 10:14:44 +01004218void LocationsBuilderARMVIXL::VisitBoundsCheck(HBoundsCheck* instruction) {
4219 RegisterSet caller_saves = RegisterSet::Empty();
4220 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4221 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0)));
4222 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(1)));
4223 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction, caller_saves);
4224 locations->SetInAt(0, Location::RequiresRegister());
4225 locations->SetInAt(1, Location::RequiresRegister());
4226}
4227
4228void InstructionCodeGeneratorARMVIXL::VisitBoundsCheck(HBoundsCheck* instruction) {
4229 SlowPathCodeARMVIXL* slow_path =
4230 new (GetGraph()->GetArena()) BoundsCheckSlowPathARMVIXL(instruction);
4231 codegen_->AddSlowPath(slow_path);
4232
4233 vixl32::Register index = InputRegisterAt(instruction, 0);
4234 vixl32::Register length = InputRegisterAt(instruction, 1);
4235
4236 __ Cmp(index, length);
4237 __ B(hs, slow_path->GetEntryLabel());
4238}
4239
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004240void CodeGeneratorARMVIXL::MarkGCCard(vixl32::Register temp,
4241 vixl32::Register card,
4242 vixl32::Register object,
4243 vixl32::Register value,
4244 bool can_be_null) {
4245 vixl32::Label is_null;
4246 if (can_be_null) {
4247 __ Cbz(value, &is_null);
4248 }
4249 GetAssembler()->LoadFromOffset(
4250 kLoadWord, card, tr, Thread::CardTableOffset<kArmPointerSize>().Int32Value());
4251 __ Lsr(temp, object, gc::accounting::CardTable::kCardShift);
4252 __ Strb(card, MemOperand(card, temp));
4253 if (can_be_null) {
4254 __ Bind(&is_null);
4255 }
4256}
4257
Scott Wakelingfe885462016-09-22 10:24:38 +01004258void LocationsBuilderARMVIXL::VisitParallelMove(HParallelMove* instruction ATTRIBUTE_UNUSED) {
4259 LOG(FATAL) << "Unreachable";
4260}
4261
4262void InstructionCodeGeneratorARMVIXL::VisitParallelMove(HParallelMove* instruction) {
4263 codegen_->GetMoveResolver()->EmitNativeCode(instruction);
4264}
4265
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004266void LocationsBuilderARMVIXL::VisitSuspendCheck(HSuspendCheck* instruction) {
4267 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
4268 // TODO(VIXL): https://android-review.googlesource.com/#/c/275337/ and related.
4269}
4270
4271void InstructionCodeGeneratorARMVIXL::VisitSuspendCheck(HSuspendCheck* instruction) {
4272 HBasicBlock* block = instruction->GetBlock();
4273 if (block->GetLoopInformation() != nullptr) {
4274 DCHECK(block->GetLoopInformation()->GetSuspendCheck() == instruction);
4275 // The back edge will generate the suspend check.
4276 return;
4277 }
4278 if (block->IsEntryBlock() && instruction->GetNext()->IsGoto()) {
4279 // The goto will generate the suspend check.
4280 return;
4281 }
4282 GenerateSuspendCheck(instruction, nullptr);
4283}
4284
4285void InstructionCodeGeneratorARMVIXL::GenerateSuspendCheck(HSuspendCheck* instruction,
4286 HBasicBlock* successor) {
4287 SuspendCheckSlowPathARMVIXL* slow_path =
4288 down_cast<SuspendCheckSlowPathARMVIXL*>(instruction->GetSlowPath());
4289 if (slow_path == nullptr) {
4290 slow_path = new (GetGraph()->GetArena()) SuspendCheckSlowPathARMVIXL(instruction, successor);
4291 instruction->SetSlowPath(slow_path);
4292 codegen_->AddSlowPath(slow_path);
4293 if (successor != nullptr) {
4294 DCHECK(successor->IsLoopHeader());
4295 codegen_->ClearSpillSlotsFromLoopPhisInStackMap(instruction);
4296 }
4297 } else {
4298 DCHECK_EQ(slow_path->GetSuccessor(), successor);
4299 }
4300
4301 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
4302 vixl32::Register temp = temps.Acquire();
4303 GetAssembler()->LoadFromOffset(
4304 kLoadUnsignedHalfword, temp, tr, Thread::ThreadFlagsOffset<kArmPointerSize>().Int32Value());
4305 if (successor == nullptr) {
4306 __ Cbnz(temp, slow_path->GetEntryLabel());
4307 __ Bind(slow_path->GetReturnLabel());
4308 } else {
4309 __ Cbz(temp, codegen_->GetLabelOf(successor));
4310 __ B(slow_path->GetEntryLabel());
4311 }
4312}
4313
Scott Wakelingfe885462016-09-22 10:24:38 +01004314ArmVIXLAssembler* ParallelMoveResolverARMVIXL::GetAssembler() const {
4315 return codegen_->GetAssembler();
4316}
4317
4318void ParallelMoveResolverARMVIXL::EmitMove(size_t index) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004319 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
Scott Wakelingfe885462016-09-22 10:24:38 +01004320 MoveOperands* move = moves_[index];
4321 Location source = move->GetSource();
4322 Location destination = move->GetDestination();
4323
4324 if (source.IsRegister()) {
4325 if (destination.IsRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004326 __ Mov(RegisterFrom(destination), RegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01004327 } else if (destination.IsFpuRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004328 __ Vmov(SRegisterFrom(destination), RegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01004329 } else {
4330 DCHECK(destination.IsStackSlot());
4331 GetAssembler()->StoreToOffset(kStoreWord,
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004332 RegisterFrom(source),
Scott Wakelingfe885462016-09-22 10:24:38 +01004333 sp,
4334 destination.GetStackIndex());
4335 }
4336 } else if (source.IsStackSlot()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004337 if (destination.IsRegister()) {
4338 GetAssembler()->LoadFromOffset(kLoadWord,
4339 RegisterFrom(destination),
4340 sp,
4341 source.GetStackIndex());
4342 } else if (destination.IsFpuRegister()) {
4343 GetAssembler()->LoadSFromOffset(SRegisterFrom(destination), sp, source.GetStackIndex());
4344 } else {
4345 DCHECK(destination.IsStackSlot());
4346 vixl32::Register temp = temps.Acquire();
4347 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, source.GetStackIndex());
4348 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
4349 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004350 } else if (source.IsFpuRegister()) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01004351 if (destination.IsRegister()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01004352 __ Vmov(RegisterFrom(destination), SRegisterFrom(source));
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01004353 } else if (destination.IsFpuRegister()) {
4354 __ Vmov(SRegisterFrom(destination), SRegisterFrom(source));
4355 } else {
4356 DCHECK(destination.IsStackSlot());
4357 GetAssembler()->StoreSToOffset(SRegisterFrom(source), sp, destination.GetStackIndex());
4358 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004359 } else if (source.IsDoubleStackSlot()) {
Alexandre Rames9c19bd62016-10-24 11:50:32 +01004360 if (destination.IsDoubleStackSlot()) {
4361 vixl32::DRegister temp = temps.AcquireD();
4362 GetAssembler()->LoadDFromOffset(temp, sp, source.GetStackIndex());
4363 GetAssembler()->StoreDToOffset(temp, sp, destination.GetStackIndex());
4364 } else if (destination.IsRegisterPair()) {
4365 DCHECK(ExpectedPairLayout(destination));
4366 GetAssembler()->LoadFromOffset(
4367 kLoadWordPair, LowRegisterFrom(destination), sp, source.GetStackIndex());
4368 } else {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01004369 DCHECK(destination.IsFpuRegisterPair()) << destination;
4370 GetAssembler()->LoadDFromOffset(DRegisterFrom(destination), sp, source.GetStackIndex());
Alexandre Rames9c19bd62016-10-24 11:50:32 +01004371 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004372 } else if (source.IsRegisterPair()) {
4373 if (destination.IsRegisterPair()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004374 __ Mov(LowRegisterFrom(destination), LowRegisterFrom(source));
4375 __ Mov(HighRegisterFrom(destination), HighRegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01004376 } else if (destination.IsFpuRegisterPair()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01004377 __ Vmov(DRegisterFrom(destination), LowRegisterFrom(source), HighRegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01004378 } else {
4379 DCHECK(destination.IsDoubleStackSlot()) << destination;
4380 DCHECK(ExpectedPairLayout(source));
4381 GetAssembler()->StoreToOffset(kStoreWordPair,
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004382 LowRegisterFrom(source),
Scott Wakelingfe885462016-09-22 10:24:38 +01004383 sp,
4384 destination.GetStackIndex());
4385 }
4386 } else if (source.IsFpuRegisterPair()) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01004387 if (destination.IsRegisterPair()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01004388 __ Vmov(LowRegisterFrom(destination), HighRegisterFrom(destination), DRegisterFrom(source));
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01004389 } else if (destination.IsFpuRegisterPair()) {
4390 __ Vmov(DRegisterFrom(destination), DRegisterFrom(source));
4391 } else {
4392 DCHECK(destination.IsDoubleStackSlot()) << destination;
4393 GetAssembler()->StoreDToOffset(DRegisterFrom(source), sp, destination.GetStackIndex());
4394 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004395 } else {
4396 DCHECK(source.IsConstant()) << source;
4397 HConstant* constant = source.GetConstant();
4398 if (constant->IsIntConstant() || constant->IsNullConstant()) {
4399 int32_t value = CodeGenerator::GetInt32ValueOf(constant);
4400 if (destination.IsRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004401 __ Mov(RegisterFrom(destination), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01004402 } else {
4403 DCHECK(destination.IsStackSlot());
Scott Wakelingfe885462016-09-22 10:24:38 +01004404 vixl32::Register temp = temps.Acquire();
4405 __ Mov(temp, value);
4406 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
4407 }
4408 } else if (constant->IsLongConstant()) {
4409 int64_t value = constant->AsLongConstant()->GetValue();
4410 if (destination.IsRegisterPair()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004411 __ Mov(LowRegisterFrom(destination), Low32Bits(value));
4412 __ Mov(HighRegisterFrom(destination), High32Bits(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01004413 } else {
4414 DCHECK(destination.IsDoubleStackSlot()) << destination;
Scott Wakelingfe885462016-09-22 10:24:38 +01004415 vixl32::Register temp = temps.Acquire();
4416 __ Mov(temp, Low32Bits(value));
4417 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
4418 __ Mov(temp, High32Bits(value));
4419 GetAssembler()->StoreToOffset(kStoreWord,
4420 temp,
4421 sp,
4422 destination.GetHighStackIndex(kArmWordSize));
4423 }
4424 } else if (constant->IsDoubleConstant()) {
4425 double value = constant->AsDoubleConstant()->GetValue();
4426 if (destination.IsFpuRegisterPair()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01004427 __ Vmov(DRegisterFrom(destination), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01004428 } else {
4429 DCHECK(destination.IsDoubleStackSlot()) << destination;
4430 uint64_t int_value = bit_cast<uint64_t, double>(value);
Scott Wakelingfe885462016-09-22 10:24:38 +01004431 vixl32::Register temp = temps.Acquire();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004432 __ Mov(temp, Low32Bits(int_value));
Scott Wakelingfe885462016-09-22 10:24:38 +01004433 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004434 __ Mov(temp, High32Bits(int_value));
Scott Wakelingfe885462016-09-22 10:24:38 +01004435 GetAssembler()->StoreToOffset(kStoreWord,
4436 temp,
4437 sp,
4438 destination.GetHighStackIndex(kArmWordSize));
4439 }
4440 } else {
4441 DCHECK(constant->IsFloatConstant()) << constant->DebugName();
4442 float value = constant->AsFloatConstant()->GetValue();
4443 if (destination.IsFpuRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004444 __ Vmov(SRegisterFrom(destination), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01004445 } else {
4446 DCHECK(destination.IsStackSlot());
Scott Wakelingfe885462016-09-22 10:24:38 +01004447 vixl32::Register temp = temps.Acquire();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004448 __ Mov(temp, bit_cast<int32_t, float>(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01004449 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
4450 }
4451 }
4452 }
4453}
4454
Alexandre Rames9c19bd62016-10-24 11:50:32 +01004455void ParallelMoveResolverARMVIXL::Exchange(vixl32::Register reg, int mem) {
4456 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
4457 vixl32::Register temp = temps.Acquire();
4458 __ Mov(temp, reg);
4459 GetAssembler()->LoadFromOffset(kLoadWord, reg, sp, mem);
4460 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, mem);
Scott Wakelingfe885462016-09-22 10:24:38 +01004461}
4462
Alexandre Rames9c19bd62016-10-24 11:50:32 +01004463void ParallelMoveResolverARMVIXL::Exchange(int mem1, int mem2) {
4464 // TODO(VIXL32): Double check the performance of this implementation.
4465 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
4466 vixl32::Register temp = temps.Acquire();
4467 vixl32::SRegister temp_s = temps.AcquireS();
4468
4469 __ Ldr(temp, MemOperand(sp, mem1));
4470 __ Vldr(temp_s, MemOperand(sp, mem2));
4471 __ Str(temp, MemOperand(sp, mem2));
4472 __ Vstr(temp_s, MemOperand(sp, mem1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004473}
4474
Alexandre Rames9c19bd62016-10-24 11:50:32 +01004475void ParallelMoveResolverARMVIXL::EmitSwap(size_t index) {
4476 MoveOperands* move = moves_[index];
4477 Location source = move->GetSource();
4478 Location destination = move->GetDestination();
4479 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
4480
4481 if (source.IsRegister() && destination.IsRegister()) {
4482 vixl32::Register temp = temps.Acquire();
4483 DCHECK(!RegisterFrom(source).Is(temp));
4484 DCHECK(!RegisterFrom(destination).Is(temp));
4485 __ Mov(temp, RegisterFrom(destination));
4486 __ Mov(RegisterFrom(destination), RegisterFrom(source));
4487 __ Mov(RegisterFrom(source), temp);
4488 } else if (source.IsRegister() && destination.IsStackSlot()) {
4489 Exchange(RegisterFrom(source), destination.GetStackIndex());
4490 } else if (source.IsStackSlot() && destination.IsRegister()) {
4491 Exchange(RegisterFrom(destination), source.GetStackIndex());
4492 } else if (source.IsStackSlot() && destination.IsStackSlot()) {
4493 TODO_VIXL32(FATAL);
4494 } else if (source.IsFpuRegister() && destination.IsFpuRegister()) {
4495 TODO_VIXL32(FATAL);
4496 } else if (source.IsRegisterPair() && destination.IsRegisterPair()) {
4497 vixl32::DRegister temp = temps.AcquireD();
4498 __ Vmov(temp, LowRegisterFrom(source), HighRegisterFrom(source));
4499 __ Mov(LowRegisterFrom(source), LowRegisterFrom(destination));
4500 __ Mov(HighRegisterFrom(source), HighRegisterFrom(destination));
4501 __ Vmov(LowRegisterFrom(destination), HighRegisterFrom(destination), temp);
4502 } else if (source.IsRegisterPair() || destination.IsRegisterPair()) {
4503 vixl32::Register low_reg = LowRegisterFrom(source.IsRegisterPair() ? source : destination);
4504 int mem = source.IsRegisterPair() ? destination.GetStackIndex() : source.GetStackIndex();
4505 DCHECK(ExpectedPairLayout(source.IsRegisterPair() ? source : destination));
4506 vixl32::DRegister temp = temps.AcquireD();
4507 __ Vmov(temp, low_reg, vixl32::Register(low_reg.GetCode() + 1));
4508 GetAssembler()->LoadFromOffset(kLoadWordPair, low_reg, sp, mem);
4509 GetAssembler()->StoreDToOffset(temp, sp, mem);
4510 } else if (source.IsFpuRegisterPair() && destination.IsFpuRegisterPair()) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004511 vixl32::DRegister first = DRegisterFrom(source);
4512 vixl32::DRegister second = DRegisterFrom(destination);
4513 vixl32::DRegister temp = temps.AcquireD();
4514 __ Vmov(temp, first);
4515 __ Vmov(first, second);
4516 __ Vmov(second, temp);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01004517 } else if (source.IsFpuRegisterPair() || destination.IsFpuRegisterPair()) {
4518 TODO_VIXL32(FATAL);
4519 } else if (source.IsFpuRegister() || destination.IsFpuRegister()) {
4520 TODO_VIXL32(FATAL);
4521 } else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) {
4522 vixl32::DRegister temp1 = temps.AcquireD();
4523 vixl32::DRegister temp2 = temps.AcquireD();
4524 __ Vldr(temp1, MemOperand(sp, source.GetStackIndex()));
4525 __ Vldr(temp2, MemOperand(sp, destination.GetStackIndex()));
4526 __ Vstr(temp1, MemOperand(sp, destination.GetStackIndex()));
4527 __ Vstr(temp2, MemOperand(sp, source.GetStackIndex()));
4528 } else {
4529 LOG(FATAL) << "Unimplemented" << source << " <-> " << destination;
4530 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004531}
4532
4533void ParallelMoveResolverARMVIXL::SpillScratch(int reg ATTRIBUTE_UNUSED) {
4534 TODO_VIXL32(FATAL);
4535}
4536
4537void ParallelMoveResolverARMVIXL::RestoreScratch(int reg ATTRIBUTE_UNUSED) {
4538 TODO_VIXL32(FATAL);
4539}
4540
Artem Serov02d37832016-10-25 15:25:33 +01004541// Check if the desired_class_load_kind is supported. If it is, return it,
4542// otherwise return a fall-back kind that should be used instead.
4543HLoadClass::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadClassKind(
4544 HLoadClass::LoadKind desired_class_load_kind ATTRIBUTE_UNUSED) {
4545 // TODO(VIXL): Implement optimized code paths.
4546 return HLoadClass::LoadKind::kDexCacheViaMethod;
4547}
4548
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004549void LocationsBuilderARMVIXL::VisitLoadClass(HLoadClass* cls) {
4550 if (cls->NeedsAccessCheck()) {
4551 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4552 CodeGenerator::CreateLoadClassLocationSummary(
4553 cls,
4554 LocationFrom(calling_convention.GetRegisterAt(0)),
4555 LocationFrom(r0),
4556 /* code_generator_supports_read_barrier */ true);
4557 return;
4558 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004559
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004560 // TODO(VIXL): read barrier code.
4561 LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || kEmitCompilerReadBarrier)
4562 ? LocationSummary::kCallOnSlowPath
4563 : LocationSummary::kNoCall;
4564 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
4565 HLoadClass::LoadKind load_kind = cls->GetLoadKind();
4566 if (load_kind == HLoadClass::LoadKind::kReferrersClass ||
4567 load_kind == HLoadClass::LoadKind::kDexCacheViaMethod ||
4568 load_kind == HLoadClass::LoadKind::kDexCachePcRelative) {
4569 locations->SetInAt(0, Location::RequiresRegister());
4570 }
4571 locations->SetOut(Location::RequiresRegister());
4572}
4573
4574void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) {
4575 LocationSummary* locations = cls->GetLocations();
4576 if (cls->NeedsAccessCheck()) {
4577 codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex());
4578 codegen_->InvokeRuntime(kQuickInitializeTypeAndVerifyAccess, cls, cls->GetDexPc());
4579 CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>();
4580 return;
4581 }
4582
4583 Location out_loc = locations->Out();
4584 vixl32::Register out = OutputRegister(cls);
4585
4586 // TODO(VIXL): read barrier code.
4587 bool generate_null_check = false;
4588 switch (cls->GetLoadKind()) {
4589 case HLoadClass::LoadKind::kReferrersClass: {
4590 DCHECK(!cls->CanCallRuntime());
4591 DCHECK(!cls->MustGenerateClinitCheck());
4592 // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
4593 vixl32::Register current_method = InputRegisterAt(cls, 0);
4594 GenerateGcRootFieldLoad(cls,
4595 out_loc,
4596 current_method,
Roland Levillain00468f32016-10-27 18:02:48 +01004597 ArtMethod::DeclaringClassOffset().Int32Value(),
4598 kEmitCompilerReadBarrier);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004599 break;
4600 }
4601 case HLoadClass::LoadKind::kDexCacheViaMethod: {
4602 // /* GcRoot<mirror::Class>[] */ out =
4603 // current_method.ptr_sized_fields_->dex_cache_resolved_types_
4604 vixl32::Register current_method = InputRegisterAt(cls, 0);
4605 const int32_t resolved_types_offset =
4606 ArtMethod::DexCacheResolvedTypesOffset(kArmPointerSize).Int32Value();
4607 GetAssembler()->LoadFromOffset(kLoadWord, out, current_method, resolved_types_offset);
4608 // /* GcRoot<mirror::Class> */ out = out[type_index]
4609 size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex());
Roland Levillain00468f32016-10-27 18:02:48 +01004610 GenerateGcRootFieldLoad(cls, out_loc, out, offset, kEmitCompilerReadBarrier);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004611 generate_null_check = !cls->IsInDexCache();
4612 break;
4613 }
4614 default:
4615 TODO_VIXL32(FATAL);
4616 }
4617
4618 if (generate_null_check || cls->MustGenerateClinitCheck()) {
4619 DCHECK(cls->CanCallRuntime());
4620 LoadClassSlowPathARMVIXL* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARMVIXL(
4621 cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
4622 codegen_->AddSlowPath(slow_path);
4623 if (generate_null_check) {
4624 __ Cbz(out, slow_path->GetEntryLabel());
4625 }
4626 if (cls->MustGenerateClinitCheck()) {
4627 GenerateClassInitializationCheck(slow_path, out);
4628 } else {
4629 __ Bind(slow_path->GetExitLabel());
4630 }
4631 }
4632}
4633
Artem Serov02d37832016-10-25 15:25:33 +01004634void LocationsBuilderARMVIXL::VisitClinitCheck(HClinitCheck* check) {
4635 LocationSummary* locations =
4636 new (GetGraph()->GetArena()) LocationSummary(check, LocationSummary::kCallOnSlowPath);
4637 locations->SetInAt(0, Location::RequiresRegister());
4638 if (check->HasUses()) {
4639 locations->SetOut(Location::SameAsFirstInput());
4640 }
4641}
4642
4643void InstructionCodeGeneratorARMVIXL::VisitClinitCheck(HClinitCheck* check) {
4644 // We assume the class is not null.
4645 LoadClassSlowPathARMVIXL* slow_path =
4646 new (GetGraph()->GetArena()) LoadClassSlowPathARMVIXL(check->GetLoadClass(),
4647 check,
4648 check->GetDexPc(),
4649 /* do_clinit */ true);
4650 codegen_->AddSlowPath(slow_path);
4651 GenerateClassInitializationCheck(slow_path, InputRegisterAt(check, 0));
4652}
4653
4654void InstructionCodeGeneratorARMVIXL::GenerateClassInitializationCheck(
4655 LoadClassSlowPathARMVIXL* slow_path, vixl32::Register class_reg) {
4656 UseScratchRegisterScope temps(GetVIXLAssembler());
4657 vixl32::Register temp = temps.Acquire();
4658 GetAssembler()->LoadFromOffset(kLoadWord,
4659 temp,
4660 class_reg,
4661 mirror::Class::StatusOffset().Int32Value());
4662 __ Cmp(temp, mirror::Class::kStatusInitialized);
4663 __ B(lt, slow_path->GetEntryLabel());
4664 // Even if the initialized flag is set, we may be in a situation where caches are not synced
4665 // properly. Therefore, we do a memory fence.
4666 __ Dmb(ISH);
4667 __ Bind(slow_path->GetExitLabel());
4668}
4669
4670// Check if the desired_string_load_kind is supported. If it is, return it,
4671// otherwise return a fall-back kind that should be used instead.
4672HLoadString::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadStringKind(
4673 HLoadString::LoadKind desired_string_load_kind ATTRIBUTE_UNUSED) {
4674 // TODO(VIXL): Implement optimized code paths. For now we always use the simpler fallback code.
4675 return HLoadString::LoadKind::kDexCacheViaMethod;
4676}
4677
4678void LocationsBuilderARMVIXL::VisitLoadString(HLoadString* load) {
4679 LocationSummary::CallKind call_kind = load->NeedsEnvironment()
4680 ? LocationSummary::kCallOnMainOnly
4681 : LocationSummary::kNoCall;
4682 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
4683
4684 // TODO(VIXL): Implement optimized code paths.
4685 // See InstructionCodeGeneratorARMVIXL::VisitLoadString.
4686 HLoadString::LoadKind load_kind = load->GetLoadKind();
4687 if (load_kind == HLoadString::LoadKind::kDexCacheViaMethod) {
4688 locations->SetInAt(0, Location::RequiresRegister());
4689 // TODO(VIXL): Use InvokeRuntimeCallingConventionARMVIXL instead.
4690 locations->SetOut(LocationFrom(r0));
4691 } else {
4692 locations->SetOut(Location::RequiresRegister());
4693 }
4694}
4695
4696void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) {
4697 // TODO(VIXL): Implement optimized code paths.
4698 // We implemented the simplest solution to get first ART tests passing, we deferred the
4699 // optimized path until later, we should implement it using ARM64 implementation as a
4700 // reference. The same related to LocationsBuilderARMVIXL::VisitLoadString.
4701
4702 // TODO: Re-add the compiler code to do string dex cache lookup again.
4703 DCHECK_EQ(load->GetLoadKind(), HLoadString::LoadKind::kDexCacheViaMethod);
4704 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4705 __ Mov(calling_convention.GetRegisterAt(0), load->GetStringIndex());
4706 codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc());
4707 CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
4708}
4709
4710static int32_t GetExceptionTlsOffset() {
4711 return Thread::ExceptionOffset<kArmPointerSize>().Int32Value();
4712}
4713
4714void LocationsBuilderARMVIXL::VisitLoadException(HLoadException* load) {
4715 LocationSummary* locations =
4716 new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kNoCall);
4717 locations->SetOut(Location::RequiresRegister());
4718}
4719
4720void InstructionCodeGeneratorARMVIXL::VisitLoadException(HLoadException* load) {
4721 vixl32::Register out = OutputRegister(load);
4722 GetAssembler()->LoadFromOffset(kLoadWord, out, tr, GetExceptionTlsOffset());
4723}
4724
4725
4726void LocationsBuilderARMVIXL::VisitClearException(HClearException* clear) {
4727 new (GetGraph()->GetArena()) LocationSummary(clear, LocationSummary::kNoCall);
4728}
4729
4730void InstructionCodeGeneratorARMVIXL::VisitClearException(HClearException* clear ATTRIBUTE_UNUSED) {
4731 UseScratchRegisterScope temps(GetVIXLAssembler());
4732 vixl32::Register temp = temps.Acquire();
4733 __ Mov(temp, 0);
4734 GetAssembler()->StoreToOffset(kStoreWord, temp, tr, GetExceptionTlsOffset());
4735}
4736
4737void LocationsBuilderARMVIXL::VisitThrow(HThrow* instruction) {
4738 LocationSummary* locations =
4739 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
4740 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4741 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
4742}
4743
4744void InstructionCodeGeneratorARMVIXL::VisitThrow(HThrow* instruction) {
4745 codegen_->InvokeRuntime(kQuickDeliverException, instruction, instruction->GetDexPc());
4746 CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
4747}
4748
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004749static bool TypeCheckNeedsATemporary(TypeCheckKind type_check_kind) {
4750 return kEmitCompilerReadBarrier &&
4751 (kUseBakerReadBarrier ||
4752 type_check_kind == TypeCheckKind::kAbstractClassCheck ||
4753 type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
4754 type_check_kind == TypeCheckKind::kArrayObjectCheck);
4755}
4756
4757void LocationsBuilderARMVIXL::VisitCheckCast(HCheckCast* instruction) {
4758 LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
4759 bool throws_into_catch = instruction->CanThrowIntoCatchBlock();
4760
4761 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
4762 switch (type_check_kind) {
4763 case TypeCheckKind::kExactCheck:
4764 case TypeCheckKind::kAbstractClassCheck:
4765 case TypeCheckKind::kClassHierarchyCheck:
4766 case TypeCheckKind::kArrayObjectCheck:
4767 call_kind = (throws_into_catch || kEmitCompilerReadBarrier) ?
4768 LocationSummary::kCallOnSlowPath :
4769 LocationSummary::kNoCall; // In fact, call on a fatal (non-returning) slow path.
4770 break;
4771 case TypeCheckKind::kArrayCheck:
4772 case TypeCheckKind::kUnresolvedCheck:
4773 case TypeCheckKind::kInterfaceCheck:
4774 call_kind = LocationSummary::kCallOnSlowPath;
4775 break;
4776 }
4777
4778 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
4779 locations->SetInAt(0, Location::RequiresRegister());
4780 locations->SetInAt(1, Location::RequiresRegister());
4781 // Note that TypeCheckSlowPathARM uses this "temp" register too.
4782 locations->AddTemp(Location::RequiresRegister());
4783 // When read barriers are enabled, we need an additional temporary
4784 // register for some cases.
4785 if (TypeCheckNeedsATemporary(type_check_kind)) {
4786 locations->AddTemp(Location::RequiresRegister());
4787 }
4788}
4789
4790void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) {
4791 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
4792 LocationSummary* locations = instruction->GetLocations();
4793 Location obj_loc = locations->InAt(0);
4794 vixl32::Register obj = InputRegisterAt(instruction, 0);
4795 vixl32::Register cls = InputRegisterAt(instruction, 1);
4796 Location temp_loc = locations->GetTemp(0);
4797 vixl32::Register temp = RegisterFrom(temp_loc);
4798 Location maybe_temp2_loc = TypeCheckNeedsATemporary(type_check_kind) ?
4799 locations->GetTemp(1) :
4800 Location::NoLocation();
4801 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
4802
4803 bool is_type_check_slow_path_fatal =
4804 (type_check_kind == TypeCheckKind::kExactCheck ||
4805 type_check_kind == TypeCheckKind::kAbstractClassCheck ||
4806 type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
4807 type_check_kind == TypeCheckKind::kArrayObjectCheck) &&
4808 !instruction->CanThrowIntoCatchBlock();
4809 SlowPathCodeARMVIXL* type_check_slow_path =
4810 new (GetGraph()->GetArena()) TypeCheckSlowPathARMVIXL(instruction,
4811 is_type_check_slow_path_fatal);
4812 codegen_->AddSlowPath(type_check_slow_path);
4813
4814 vixl32::Label done;
4815 // Avoid null check if we know obj is not null.
4816 if (instruction->MustDoNullCheck()) {
4817 __ Cbz(obj, &done);
4818 }
4819
4820 // /* HeapReference<Class> */ temp = obj->klass_
4821 GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc);
4822
4823 switch (type_check_kind) {
4824 case TypeCheckKind::kExactCheck:
4825 case TypeCheckKind::kArrayCheck: {
4826 __ Cmp(temp, cls);
4827 // Jump to slow path for throwing the exception or doing a
4828 // more involved array check.
4829 __ B(ne, type_check_slow_path->GetEntryLabel());
4830 break;
4831 }
4832
4833 case TypeCheckKind::kAbstractClassCheck: {
4834 TODO_VIXL32(FATAL);
4835 break;
4836 }
4837
4838 case TypeCheckKind::kClassHierarchyCheck: {
4839 TODO_VIXL32(FATAL);
4840 break;
4841 }
4842
4843 case TypeCheckKind::kArrayObjectCheck: {
4844 TODO_VIXL32(FATAL);
4845 break;
4846 }
4847
4848 case TypeCheckKind::kUnresolvedCheck:
4849 case TypeCheckKind::kInterfaceCheck:
4850 TODO_VIXL32(FATAL);
4851 break;
4852 }
4853 __ Bind(&done);
4854
4855 __ Bind(type_check_slow_path->GetExitLabel());
4856}
4857
Artem Serov02109dd2016-09-23 17:17:54 +01004858void LocationsBuilderARMVIXL::VisitAnd(HAnd* instruction) {
4859 HandleBitwiseOperation(instruction, AND);
4860}
4861
4862void LocationsBuilderARMVIXL::VisitOr(HOr* instruction) {
4863 HandleBitwiseOperation(instruction, ORR);
4864}
4865
4866void LocationsBuilderARMVIXL::VisitXor(HXor* instruction) {
4867 HandleBitwiseOperation(instruction, EOR);
4868}
4869
4870void LocationsBuilderARMVIXL::HandleBitwiseOperation(HBinaryOperation* instruction, Opcode opcode) {
4871 LocationSummary* locations =
4872 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
4873 DCHECK(instruction->GetResultType() == Primitive::kPrimInt
4874 || instruction->GetResultType() == Primitive::kPrimLong);
4875 // Note: GVN reorders commutative operations to have the constant on the right hand side.
4876 locations->SetInAt(0, Location::RequiresRegister());
4877 locations->SetInAt(1, ArmEncodableConstantOrRegister(instruction->InputAt(1), opcode));
4878 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4879}
4880
4881void InstructionCodeGeneratorARMVIXL::VisitAnd(HAnd* instruction) {
4882 HandleBitwiseOperation(instruction);
4883}
4884
4885void InstructionCodeGeneratorARMVIXL::VisitOr(HOr* instruction) {
4886 HandleBitwiseOperation(instruction);
4887}
4888
4889void InstructionCodeGeneratorARMVIXL::VisitXor(HXor* instruction) {
4890 HandleBitwiseOperation(instruction);
4891}
4892
4893// TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
4894void InstructionCodeGeneratorARMVIXL::GenerateAndConst(vixl32::Register out,
4895 vixl32::Register first,
4896 uint32_t value) {
4897 // Optimize special cases for individual halfs of `and-long` (`and` is simplified earlier).
4898 if (value == 0xffffffffu) {
4899 if (!out.Is(first)) {
4900 __ Mov(out, first);
4901 }
4902 return;
4903 }
4904 if (value == 0u) {
4905 __ Mov(out, 0);
4906 return;
4907 }
4908 if (GetAssembler()->ShifterOperandCanHold(AND, value)) {
4909 __ And(out, first, value);
4910 } else {
4911 DCHECK(GetAssembler()->ShifterOperandCanHold(BIC, ~value));
4912 __ Bic(out, first, ~value);
4913 }
4914}
4915
4916// TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
4917void InstructionCodeGeneratorARMVIXL::GenerateOrrConst(vixl32::Register out,
4918 vixl32::Register first,
4919 uint32_t value) {
4920 // Optimize special cases for individual halfs of `or-long` (`or` is simplified earlier).
4921 if (value == 0u) {
4922 if (!out.Is(first)) {
4923 __ Mov(out, first);
4924 }
4925 return;
4926 }
4927 if (value == 0xffffffffu) {
4928 __ Mvn(out, 0);
4929 return;
4930 }
4931 if (GetAssembler()->ShifterOperandCanHold(ORR, value)) {
4932 __ Orr(out, first, value);
4933 } else {
4934 DCHECK(GetAssembler()->ShifterOperandCanHold(ORN, ~value));
4935 __ Orn(out, first, ~value);
4936 }
4937}
4938
4939// TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
4940void InstructionCodeGeneratorARMVIXL::GenerateEorConst(vixl32::Register out,
4941 vixl32::Register first,
4942 uint32_t value) {
4943 // Optimize special case for individual halfs of `xor-long` (`xor` is simplified earlier).
4944 if (value == 0u) {
4945 if (!out.Is(first)) {
4946 __ Mov(out, first);
4947 }
4948 return;
4949 }
4950 __ Eor(out, first, value);
4951}
4952
4953void InstructionCodeGeneratorARMVIXL::HandleBitwiseOperation(HBinaryOperation* instruction) {
4954 LocationSummary* locations = instruction->GetLocations();
4955 Location first = locations->InAt(0);
4956 Location second = locations->InAt(1);
4957 Location out = locations->Out();
4958
4959 if (second.IsConstant()) {
4960 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
4961 uint32_t value_low = Low32Bits(value);
4962 if (instruction->GetResultType() == Primitive::kPrimInt) {
4963 vixl32::Register first_reg = InputRegisterAt(instruction, 0);
4964 vixl32::Register out_reg = OutputRegister(instruction);
4965 if (instruction->IsAnd()) {
4966 GenerateAndConst(out_reg, first_reg, value_low);
4967 } else if (instruction->IsOr()) {
4968 GenerateOrrConst(out_reg, first_reg, value_low);
4969 } else {
4970 DCHECK(instruction->IsXor());
4971 GenerateEorConst(out_reg, first_reg, value_low);
4972 }
4973 } else {
4974 DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong);
4975 uint32_t value_high = High32Bits(value);
4976 vixl32::Register first_low = LowRegisterFrom(first);
4977 vixl32::Register first_high = HighRegisterFrom(first);
4978 vixl32::Register out_low = LowRegisterFrom(out);
4979 vixl32::Register out_high = HighRegisterFrom(out);
4980 if (instruction->IsAnd()) {
4981 GenerateAndConst(out_low, first_low, value_low);
4982 GenerateAndConst(out_high, first_high, value_high);
4983 } else if (instruction->IsOr()) {
4984 GenerateOrrConst(out_low, first_low, value_low);
4985 GenerateOrrConst(out_high, first_high, value_high);
4986 } else {
4987 DCHECK(instruction->IsXor());
4988 GenerateEorConst(out_low, first_low, value_low);
4989 GenerateEorConst(out_high, first_high, value_high);
4990 }
4991 }
4992 return;
4993 }
4994
4995 if (instruction->GetResultType() == Primitive::kPrimInt) {
4996 vixl32::Register first_reg = InputRegisterAt(instruction, 0);
4997 vixl32::Register second_reg = InputRegisterAt(instruction, 1);
4998 vixl32::Register out_reg = OutputRegister(instruction);
4999 if (instruction->IsAnd()) {
5000 __ And(out_reg, first_reg, second_reg);
5001 } else if (instruction->IsOr()) {
5002 __ Orr(out_reg, first_reg, second_reg);
5003 } else {
5004 DCHECK(instruction->IsXor());
5005 __ Eor(out_reg, first_reg, second_reg);
5006 }
5007 } else {
5008 DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong);
5009 vixl32::Register first_low = LowRegisterFrom(first);
5010 vixl32::Register first_high = HighRegisterFrom(first);
5011 vixl32::Register second_low = LowRegisterFrom(second);
5012 vixl32::Register second_high = HighRegisterFrom(second);
5013 vixl32::Register out_low = LowRegisterFrom(out);
5014 vixl32::Register out_high = HighRegisterFrom(out);
5015 if (instruction->IsAnd()) {
5016 __ And(out_low, first_low, second_low);
5017 __ And(out_high, first_high, second_high);
5018 } else if (instruction->IsOr()) {
5019 __ Orr(out_low, first_low, second_low);
5020 __ Orr(out_high, first_high, second_high);
5021 } else {
5022 DCHECK(instruction->IsXor());
5023 __ Eor(out_low, first_low, second_low);
5024 __ Eor(out_high, first_high, second_high);
5025 }
5026 }
5027}
5028
Anton Kirilove28d9ae2016-10-25 18:17:23 +01005029void InstructionCodeGeneratorARMVIXL::GenerateReferenceLoadTwoRegisters(
5030 HInstruction* instruction ATTRIBUTE_UNUSED,
5031 Location out,
5032 Location obj,
5033 uint32_t offset,
5034 Location maybe_temp ATTRIBUTE_UNUSED) {
5035 vixl32::Register out_reg = RegisterFrom(out);
5036 vixl32::Register obj_reg = RegisterFrom(obj);
5037 if (kEmitCompilerReadBarrier) {
5038 TODO_VIXL32(FATAL);
5039 } else {
5040 // Plain load with no read barrier.
5041 // /* HeapReference<Object> */ out = *(obj + offset)
5042 GetAssembler()->LoadFromOffset(kLoadWord, out_reg, obj_reg, offset);
5043 GetAssembler()->MaybeUnpoisonHeapReference(out_reg);
5044 }
5045}
5046
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005047void InstructionCodeGeneratorARMVIXL::GenerateGcRootFieldLoad(
5048 HInstruction* instruction ATTRIBUTE_UNUSED,
5049 Location root,
5050 vixl32::Register obj,
5051 uint32_t offset,
5052 bool requires_read_barrier) {
5053 vixl32::Register root_reg = RegisterFrom(root);
5054 if (requires_read_barrier) {
5055 TODO_VIXL32(FATAL);
5056 } else {
5057 // Plain GC root load with no read barrier.
5058 // /* GcRoot<mirror::Object> */ root = *(obj + offset)
5059 GetAssembler()->LoadFromOffset(kLoadWord, root_reg, obj, offset);
5060 // Note that GC roots are not affected by heap poisoning, thus we
5061 // do not have to unpoison `root_reg` here.
5062 }
5063}
5064
Roland Levillain844e6532016-11-03 16:09:47 +00005065void CodeGeneratorARMVIXL::GenerateReadBarrierSlow(HInstruction* instruction ATTRIBUTE_UNUSED,
5066 Location out ATTRIBUTE_UNUSED,
5067 Location ref ATTRIBUTE_UNUSED,
5068 Location obj ATTRIBUTE_UNUSED,
5069 uint32_t offset ATTRIBUTE_UNUSED,
5070 Location index ATTRIBUTE_UNUSED) {
5071 TODO_VIXL32(FATAL);
5072}
5073
Artem Serov02d37832016-10-25 15:25:33 +01005074void CodeGeneratorARMVIXL::MaybeGenerateReadBarrierSlow(HInstruction* instruction ATTRIBUTE_UNUSED,
5075 Location out,
5076 Location ref ATTRIBUTE_UNUSED,
5077 Location obj ATTRIBUTE_UNUSED,
5078 uint32_t offset ATTRIBUTE_UNUSED,
5079 Location index ATTRIBUTE_UNUSED) {
5080 if (kEmitCompilerReadBarrier) {
5081 DCHECK(!kUseBakerReadBarrier);
5082 TODO_VIXL32(FATAL);
5083 } else if (kPoisonHeapReferences) {
5084 GetAssembler()->UnpoisonHeapReference(RegisterFrom(out));
5085 }
5086}
5087
5088// Check if the desired_dispatch_info is supported. If it is, return it,
5089// otherwise return a fall-back info that should be used instead.
5090HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARMVIXL::GetSupportedInvokeStaticOrDirectDispatch(
5091 const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info ATTRIBUTE_UNUSED,
5092 HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
5093 // TODO(VIXL): Implement optimized code paths.
5094 return {
5095 HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod,
5096 HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
5097 0u,
5098 0u
5099 };
5100}
5101
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005102vixl32::Register CodeGeneratorARMVIXL::GetInvokeStaticOrDirectExtraParameter(
5103 HInvokeStaticOrDirect* invoke, vixl32::Register temp) {
5104 DCHECK_EQ(invoke->InputCount(), invoke->GetNumberOfArguments() + 1u);
5105 Location location = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
5106 if (!invoke->GetLocations()->Intrinsified()) {
5107 return RegisterFrom(location);
5108 }
5109 // For intrinsics we allow any location, so it may be on the stack.
5110 if (!location.IsRegister()) {
5111 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, location.GetStackIndex());
5112 return temp;
5113 }
5114 // For register locations, check if the register was saved. If so, get it from the stack.
5115 // Note: There is a chance that the register was saved but not overwritten, so we could
5116 // save one load. However, since this is just an intrinsic slow path we prefer this
5117 // simple and more robust approach rather that trying to determine if that's the case.
5118 SlowPathCode* slow_path = GetCurrentSlowPath();
5119 DCHECK(slow_path != nullptr); // For intrinsified invokes the call is emitted on the slow path.
5120 if (slow_path->IsCoreRegisterSaved(RegisterFrom(location).GetCode())) {
5121 int stack_offset = slow_path->GetStackOffsetOfCoreRegister(RegisterFrom(location).GetCode());
5122 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, stack_offset);
5123 return temp;
5124 }
5125 return RegisterFrom(location);
5126}
5127
5128void CodeGeneratorARMVIXL::GenerateStaticOrDirectCall(
5129 HInvokeStaticOrDirect* invoke, Location temp) {
5130 Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp.
5131 vixl32::Register temp_reg = RegisterFrom(temp);
5132
5133 switch (invoke->GetMethodLoadKind()) {
5134 case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: {
5135 uint32_t offset =
5136 GetThreadOffset<kArmPointerSize>(invoke->GetStringInitEntryPoint()).Int32Value();
5137 // temp = thread->string_init_entrypoint
5138 GetAssembler()->LoadFromOffset(kLoadWord, temp_reg, tr, offset);
5139 break;
5140 }
5141 case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: {
5142 Location current_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
5143 vixl32::Register method_reg;
5144 if (current_method.IsRegister()) {
5145 method_reg = RegisterFrom(current_method);
5146 } else {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01005147 DCHECK(invoke->GetLocations()->Intrinsified());
5148 DCHECK(!current_method.IsValid());
5149 method_reg = temp_reg;
5150 GetAssembler()->LoadFromOffset(kLoadWord, temp_reg, sp, kCurrentMethodStackOffset);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005151 }
5152 // /* ArtMethod*[] */ temp = temp.ptr_sized_fields_->dex_cache_resolved_methods_;
5153 GetAssembler()->LoadFromOffset(
5154 kLoadWord,
5155 temp_reg,
5156 method_reg,
5157 ArtMethod::DexCacheResolvedMethodsOffset(kArmPointerSize).Int32Value());
5158 // temp = temp[index_in_cache];
5159 // Note: Don't use invoke->GetTargetMethod() as it may point to a different dex file.
5160 uint32_t index_in_cache = invoke->GetDexMethodIndex();
5161 GetAssembler()->LoadFromOffset(
5162 kLoadWord, temp_reg, temp_reg, CodeGenerator::GetCachePointerOffset(index_in_cache));
5163 break;
5164 }
5165 default:
5166 TODO_VIXL32(FATAL);
5167 }
5168
5169 // TODO(VIXL): Support `CodePtrLocation` values other than `kCallArtMethod`.
5170 if (invoke->GetCodePtrLocation() != HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod) {
5171 TODO_VIXL32(FATAL);
5172 }
5173
5174 // LR = callee_method->entry_point_from_quick_compiled_code_
5175 GetAssembler()->LoadFromOffset(
5176 kLoadWord,
5177 lr,
5178 RegisterFrom(callee_method),
5179 ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value());
5180 // LR()
5181 __ Blx(lr);
5182
5183 DCHECK(!IsLeafMethod());
5184}
5185
5186void CodeGeneratorARMVIXL::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp_location) {
5187 vixl32::Register temp = RegisterFrom(temp_location);
5188 uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
5189 invoke->GetVTableIndex(), kArmPointerSize).Uint32Value();
5190
5191 // Use the calling convention instead of the location of the receiver, as
5192 // intrinsics may have put the receiver in a different register. In the intrinsics
5193 // slow path, the arguments have been moved to the right place, so here we are
5194 // guaranteed that the receiver is the first register of the calling convention.
5195 InvokeDexCallingConventionARMVIXL calling_convention;
5196 vixl32::Register receiver = calling_convention.GetRegisterAt(0);
5197 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
5198 // /* HeapReference<Class> */ temp = receiver->klass_
5199 GetAssembler()->LoadFromOffset(kLoadWord, temp, receiver, class_offset);
5200 MaybeRecordImplicitNullCheck(invoke);
5201 // Instead of simply (possibly) unpoisoning `temp` here, we should
5202 // emit a read barrier for the previous class reference load.
5203 // However this is not required in practice, as this is an
5204 // intermediate/temporary reference and because the current
5205 // concurrent copying collector keeps the from-space memory
5206 // intact/accessible until the end of the marking phase (the
5207 // concurrent copying collector may not in the future).
5208 GetAssembler()->MaybeUnpoisonHeapReference(temp);
5209
5210 // temp = temp->GetMethodAt(method_offset);
5211 uint32_t entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(
5212 kArmPointerSize).Int32Value();
5213 GetAssembler()->LoadFromOffset(kLoadWord, temp, temp, method_offset);
5214 // LR = temp->GetEntryPoint();
5215 GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, entry_point);
5216 // LR();
5217 __ Blx(lr);
5218}
5219
Artem Serov02d37832016-10-25 15:25:33 +01005220// Copy the result of a call into the given target.
Anton Kirilove28d9ae2016-10-25 18:17:23 +01005221void CodeGeneratorARMVIXL::MoveFromReturnRegister(Location trg, Primitive::Type type) {
5222 if (!trg.IsValid()) {
5223 DCHECK_EQ(type, Primitive::kPrimVoid);
5224 return;
5225 }
5226
5227 DCHECK_NE(type, Primitive::kPrimVoid);
5228
5229 Location return_loc = InvokeDexCallingConventionVisitorARM().GetReturnLocation(type);
5230 if (return_loc.Equals(trg)) {
5231 return;
5232 }
5233
5234 // TODO: Consider pairs in the parallel move resolver, then this could be nicely merged
5235 // with the last branch.
5236 if (type == Primitive::kPrimLong) {
5237 TODO_VIXL32(FATAL);
5238 } else if (type == Primitive::kPrimDouble) {
5239 TODO_VIXL32(FATAL);
5240 } else {
5241 // Let the parallel move resolver take care of all of this.
5242 HParallelMove parallel_move(GetGraph()->GetArena());
5243 parallel_move.AddMove(return_loc, trg, type, nullptr);
5244 GetMoveResolver()->EmitNativeCode(&parallel_move);
5245 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005246}
Scott Wakelingfe885462016-09-22 10:24:38 +01005247
5248#undef __
5249#undef QUICK_ENTRY_POINT
5250#undef TODO_VIXL32
5251
5252} // namespace arm
5253} // namespace art