blob: be7a37f267001af7404be783621172989c3b1a1e [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;
Scott Wakelingb77051e2016-11-21 19:46:00 +000050using helpers::Int32ConstantFrom;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010051using helpers::LocationFrom;
52using helpers::LowRegisterFrom;
53using helpers::LowSRegisterFrom;
54using helpers::OutputRegister;
55using helpers::OutputSRegister;
56using helpers::OutputVRegister;
57using helpers::RegisterFrom;
58using helpers::SRegisterFrom;
Scott Wakelingfe885462016-09-22 10:24:38 +010059
60using RegisterList = vixl32::RegisterList;
61
62static bool ExpectedPairLayout(Location location) {
63 // We expected this for both core and fpu register pairs.
64 return ((location.low() & 1) == 0) && (location.low() + 1 == location.high());
65}
66
Anton Kirilove28d9ae2016-10-25 18:17:23 +010067static constexpr int kCurrentMethodStackOffset = 0;
Scott Wakelingfe885462016-09-22 10:24:38 +010068static constexpr size_t kArmInstrMaxSizeInBytes = 4u;
Artem Serov551b28f2016-10-18 19:11:30 +010069static constexpr uint32_t kPackedSwitchCompareJumpThreshold = 7;
Scott Wakelingfe885462016-09-22 10:24:38 +010070
71#ifdef __
72#error "ARM Codegen VIXL macro-assembler macro already defined."
73#endif
74
Scott Wakelingfe885462016-09-22 10:24:38 +010075// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
76#define __ down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler()-> // NOLINT
77#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArmPointerSize, x).Int32Value()
78
79// Marker that code is yet to be, and must, be implemented.
80#define TODO_VIXL32(level) LOG(level) << __PRETTY_FUNCTION__ << " unimplemented "
81
Scott Wakelinga7812ae2016-10-17 10:03:36 +010082// SaveLiveRegisters and RestoreLiveRegisters from SlowPathCodeARM operate on sets of S registers,
83// for each live D registers they treat two corresponding S registers as live ones.
84//
85// Two following functions (SaveContiguousSRegisterList, RestoreContiguousSRegisterList) build
86// from a list of contiguous S registers a list of contiguous D registers (processing first/last
87// S registers corner cases) and save/restore this new list treating them as D registers.
88// - decreasing code size
89// - avoiding hazards on Cortex-A57, when a pair of S registers for an actual live D register is
90// restored and then used in regular non SlowPath code as D register.
91//
92// For the following example (v means the S register is live):
93// D names: | D0 | D1 | D2 | D4 | ...
94// S names: | S0 | S1 | S2 | S3 | S4 | S5 | S6 | S7 | ...
95// Live? | | v | v | v | v | v | v | | ...
96//
97// S1 and S6 will be saved/restored independently; D registers list (D1, D2) will be processed
98// as D registers.
99//
100// TODO(VIXL): All this code should be unnecessary once the VIXL AArch32 backend provides helpers
101// for lists of floating-point registers.
102static size_t SaveContiguousSRegisterList(size_t first,
103 size_t last,
104 CodeGenerator* codegen,
105 size_t stack_offset) {
106 static_assert(kSRegSizeInBytes == kArmWordSize, "Broken assumption on reg/word sizes.");
107 static_assert(kDRegSizeInBytes == 2 * kArmWordSize, "Broken assumption on reg/word sizes.");
108 DCHECK_LE(first, last);
109 if ((first == last) && (first == 0)) {
110 __ Vstr(vixl32::SRegister(first), MemOperand(sp, stack_offset));
111 return stack_offset + kSRegSizeInBytes;
112 }
113 if (first % 2 == 1) {
114 __ Vstr(vixl32::SRegister(first++), MemOperand(sp, stack_offset));
115 stack_offset += kSRegSizeInBytes;
116 }
117
118 bool save_last = false;
119 if (last % 2 == 0) {
120 save_last = true;
121 --last;
122 }
123
124 if (first < last) {
125 vixl32::DRegister d_reg = vixl32::DRegister(first / 2);
126 DCHECK_EQ((last - first + 1) % 2, 0u);
127 size_t number_of_d_regs = (last - first + 1) / 2;
128
129 if (number_of_d_regs == 1) {
130 __ Vstr(d_reg, MemOperand(sp, stack_offset));
131 } else if (number_of_d_regs > 1) {
132 UseScratchRegisterScope temps(down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler());
133 vixl32::Register base = sp;
134 if (stack_offset != 0) {
135 base = temps.Acquire();
Scott Wakelingb77051e2016-11-21 19:46:00 +0000136 __ Add(base, sp, Operand::From(stack_offset));
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100137 }
138 __ Vstm(F64, base, NO_WRITE_BACK, DRegisterList(d_reg, number_of_d_regs));
139 }
140 stack_offset += number_of_d_regs * kDRegSizeInBytes;
141 }
142
143 if (save_last) {
144 __ Vstr(vixl32::SRegister(last + 1), MemOperand(sp, stack_offset));
145 stack_offset += kSRegSizeInBytes;
146 }
147
148 return stack_offset;
149}
150
151static size_t RestoreContiguousSRegisterList(size_t first,
152 size_t last,
153 CodeGenerator* codegen,
154 size_t stack_offset) {
155 static_assert(kSRegSizeInBytes == kArmWordSize, "Broken assumption on reg/word sizes.");
156 static_assert(kDRegSizeInBytes == 2 * kArmWordSize, "Broken assumption on reg/word sizes.");
157 DCHECK_LE(first, last);
158 if ((first == last) && (first == 0)) {
159 __ Vldr(vixl32::SRegister(first), MemOperand(sp, stack_offset));
160 return stack_offset + kSRegSizeInBytes;
161 }
162 if (first % 2 == 1) {
163 __ Vldr(vixl32::SRegister(first++), MemOperand(sp, stack_offset));
164 stack_offset += kSRegSizeInBytes;
165 }
166
167 bool restore_last = false;
168 if (last % 2 == 0) {
169 restore_last = true;
170 --last;
171 }
172
173 if (first < last) {
174 vixl32::DRegister d_reg = vixl32::DRegister(first / 2);
175 DCHECK_EQ((last - first + 1) % 2, 0u);
176 size_t number_of_d_regs = (last - first + 1) / 2;
177 if (number_of_d_regs == 1) {
178 __ Vldr(d_reg, MemOperand(sp, stack_offset));
179 } else if (number_of_d_regs > 1) {
180 UseScratchRegisterScope temps(down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler());
181 vixl32::Register base = sp;
182 if (stack_offset != 0) {
183 base = temps.Acquire();
Scott Wakelingb77051e2016-11-21 19:46:00 +0000184 __ Add(base, sp, Operand::From(stack_offset));
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100185 }
186 __ Vldm(F64, base, NO_WRITE_BACK, DRegisterList(d_reg, number_of_d_regs));
187 }
188 stack_offset += number_of_d_regs * kDRegSizeInBytes;
189 }
190
191 if (restore_last) {
192 __ Vldr(vixl32::SRegister(last + 1), MemOperand(sp, stack_offset));
193 stack_offset += kSRegSizeInBytes;
194 }
195
196 return stack_offset;
197}
198
199void SlowPathCodeARMVIXL::SaveLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
200 size_t stack_offset = codegen->GetFirstRegisterSlotInSlowPath();
201 size_t orig_offset = stack_offset;
202
203 const uint32_t core_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ true);
204 for (uint32_t i : LowToHighBits(core_spills)) {
205 // If the register holds an object, update the stack mask.
206 if (locations->RegisterContainsObject(i)) {
207 locations->SetStackBit(stack_offset / kVRegSize);
208 }
209 DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
210 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
211 saved_core_stack_offsets_[i] = stack_offset;
212 stack_offset += kArmWordSize;
213 }
214
215 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
216 arm_codegen->GetAssembler()->StoreRegisterList(core_spills, orig_offset);
217
218 uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ false);
219 orig_offset = stack_offset;
220 for (uint32_t i : LowToHighBits(fp_spills)) {
221 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
222 saved_fpu_stack_offsets_[i] = stack_offset;
223 stack_offset += kArmWordSize;
224 }
225
226 stack_offset = orig_offset;
227 while (fp_spills != 0u) {
228 uint32_t begin = CTZ(fp_spills);
229 uint32_t tmp = fp_spills + (1u << begin);
230 fp_spills &= tmp; // Clear the contiguous range of 1s.
231 uint32_t end = (tmp == 0u) ? 32u : CTZ(tmp); // CTZ(0) is undefined.
232 stack_offset = SaveContiguousSRegisterList(begin, end - 1, codegen, stack_offset);
233 }
234 DCHECK_LE(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
235}
236
237void SlowPathCodeARMVIXL::RestoreLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
238 size_t stack_offset = codegen->GetFirstRegisterSlotInSlowPath();
239 size_t orig_offset = stack_offset;
240
241 const uint32_t core_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ true);
242 for (uint32_t i : LowToHighBits(core_spills)) {
243 DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
244 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
245 stack_offset += kArmWordSize;
246 }
247
248 // TODO(VIXL): Check the coherency of stack_offset after this with a test.
249 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
250 arm_codegen->GetAssembler()->LoadRegisterList(core_spills, orig_offset);
251
252 uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ false);
253 while (fp_spills != 0u) {
254 uint32_t begin = CTZ(fp_spills);
255 uint32_t tmp = fp_spills + (1u << begin);
256 fp_spills &= tmp; // Clear the contiguous range of 1s.
257 uint32_t end = (tmp == 0u) ? 32u : CTZ(tmp); // CTZ(0) is undefined.
258 stack_offset = RestoreContiguousSRegisterList(begin, end - 1, codegen, stack_offset);
259 }
260 DCHECK_LE(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
261}
262
263class NullCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
264 public:
265 explicit NullCheckSlowPathARMVIXL(HNullCheck* instruction) : SlowPathCodeARMVIXL(instruction) {}
266
267 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
268 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
269 __ Bind(GetEntryLabel());
270 if (instruction_->CanThrowIntoCatchBlock()) {
271 // Live registers will be restored in the catch block if caught.
272 SaveLiveRegisters(codegen, instruction_->GetLocations());
273 }
274 arm_codegen->InvokeRuntime(kQuickThrowNullPointer,
275 instruction_,
276 instruction_->GetDexPc(),
277 this);
278 CheckEntrypointTypes<kQuickThrowNullPointer, void, void>();
279 }
280
281 bool IsFatal() const OVERRIDE { return true; }
282
283 const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathARMVIXL"; }
284
285 private:
286 DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARMVIXL);
287};
288
Scott Wakelingfe885462016-09-22 10:24:38 +0100289class DivZeroCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
290 public:
291 explicit DivZeroCheckSlowPathARMVIXL(HDivZeroCheck* instruction)
292 : SlowPathCodeARMVIXL(instruction) {}
293
294 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100295 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
Scott Wakelingfe885462016-09-22 10:24:38 +0100296 __ Bind(GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100297 arm_codegen->InvokeRuntime(kQuickThrowDivZero, instruction_, instruction_->GetDexPc(), this);
Scott Wakelingfe885462016-09-22 10:24:38 +0100298 CheckEntrypointTypes<kQuickThrowDivZero, void, void>();
299 }
300
301 bool IsFatal() const OVERRIDE { return true; }
302
303 const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathARMVIXL"; }
304
305 private:
306 DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathARMVIXL);
307};
308
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100309class SuspendCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
310 public:
311 SuspendCheckSlowPathARMVIXL(HSuspendCheck* instruction, HBasicBlock* successor)
312 : SlowPathCodeARMVIXL(instruction), successor_(successor) {}
313
314 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
315 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
316 __ Bind(GetEntryLabel());
317 arm_codegen->InvokeRuntime(kQuickTestSuspend, instruction_, instruction_->GetDexPc(), this);
318 CheckEntrypointTypes<kQuickTestSuspend, void, void>();
319 if (successor_ == nullptr) {
320 __ B(GetReturnLabel());
321 } else {
322 __ B(arm_codegen->GetLabelOf(successor_));
323 }
324 }
325
326 vixl32::Label* GetReturnLabel() {
327 DCHECK(successor_ == nullptr);
328 return &return_label_;
329 }
330
331 HBasicBlock* GetSuccessor() const {
332 return successor_;
333 }
334
335 const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathARMVIXL"; }
336
337 private:
338 // If not null, the block to branch to after the suspend check.
339 HBasicBlock* const successor_;
340
341 // If `successor_` is null, the label to branch to after the suspend check.
342 vixl32::Label return_label_;
343
344 DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathARMVIXL);
345};
346
Scott Wakelingc34dba72016-10-03 10:14:44 +0100347class BoundsCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
348 public:
349 explicit BoundsCheckSlowPathARMVIXL(HBoundsCheck* instruction)
350 : SlowPathCodeARMVIXL(instruction) {}
351
352 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
353 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
354 LocationSummary* locations = instruction_->GetLocations();
355
356 __ Bind(GetEntryLabel());
357 if (instruction_->CanThrowIntoCatchBlock()) {
358 // Live registers will be restored in the catch block if caught.
359 SaveLiveRegisters(codegen, instruction_->GetLocations());
360 }
361 // We're moving two locations to locations that could overlap, so we need a parallel
362 // move resolver.
363 InvokeRuntimeCallingConventionARMVIXL calling_convention;
364 codegen->EmitParallelMoves(
365 locations->InAt(0),
366 LocationFrom(calling_convention.GetRegisterAt(0)),
367 Primitive::kPrimInt,
368 locations->InAt(1),
369 LocationFrom(calling_convention.GetRegisterAt(1)),
370 Primitive::kPrimInt);
371 QuickEntrypointEnum entrypoint = instruction_->AsBoundsCheck()->IsStringCharAt()
372 ? kQuickThrowStringBounds
373 : kQuickThrowArrayBounds;
374 arm_codegen->InvokeRuntime(entrypoint, instruction_, instruction_->GetDexPc(), this);
375 CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>();
376 CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>();
377 }
378
379 bool IsFatal() const OVERRIDE { return true; }
380
381 const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathARMVIXL"; }
382
383 private:
384 DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARMVIXL);
385};
386
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100387class LoadClassSlowPathARMVIXL : public SlowPathCodeARMVIXL {
388 public:
389 LoadClassSlowPathARMVIXL(HLoadClass* cls, HInstruction* at, uint32_t dex_pc, bool do_clinit)
390 : SlowPathCodeARMVIXL(at), cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) {
391 DCHECK(at->IsLoadClass() || at->IsClinitCheck());
392 }
393
394 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
395 LocationSummary* locations = at_->GetLocations();
396
397 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
398 __ Bind(GetEntryLabel());
399 SaveLiveRegisters(codegen, locations);
400
401 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Andreas Gampea5b09a62016-11-17 15:21:22 -0800402 __ Mov(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex().index_);
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100403 QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage
404 : kQuickInitializeType;
405 arm_codegen->InvokeRuntime(entrypoint, at_, dex_pc_, this);
406 if (do_clinit_) {
407 CheckEntrypointTypes<kQuickInitializeStaticStorage, void*, uint32_t>();
408 } else {
409 CheckEntrypointTypes<kQuickInitializeType, void*, uint32_t>();
410 }
411
412 // Move the class to the desired location.
413 Location out = locations->Out();
414 if (out.IsValid()) {
415 DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
416 arm_codegen->Move32(locations->Out(), LocationFrom(r0));
417 }
418 RestoreLiveRegisters(codegen, locations);
419 __ B(GetExitLabel());
420 }
421
422 const char* GetDescription() const OVERRIDE { return "LoadClassSlowPathARMVIXL"; }
423
424 private:
425 // The class this slow path will load.
426 HLoadClass* const cls_;
427
428 // The instruction where this slow path is happening.
429 // (Might be the load class or an initialization check).
430 HInstruction* const at_;
431
432 // The dex PC of `at_`.
433 const uint32_t dex_pc_;
434
435 // Whether to initialize the class.
436 const bool do_clinit_;
437
438 DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathARMVIXL);
439};
440
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100441class TypeCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
442 public:
443 TypeCheckSlowPathARMVIXL(HInstruction* instruction, bool is_fatal)
444 : SlowPathCodeARMVIXL(instruction), is_fatal_(is_fatal) {}
445
446 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
447 LocationSummary* locations = instruction_->GetLocations();
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100448 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_) {
Artem Serovcfbe9132016-10-14 15:58:56 +0100455 SaveLiveRegisters(codegen, locations);
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100456 }
457
458 // We're moving two locations to locations that could overlap, so we need a parallel
459 // move resolver.
460 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100461
Mathieu Chartier9fd8c602016-11-14 14:38:53 -0800462 codegen->EmitParallelMoves(locations->InAt(0),
Mathieu Chartierb99f4d62016-11-07 16:17:26 -0800463 LocationFrom(calling_convention.GetRegisterAt(0)),
464 Primitive::kPrimNot,
Mathieu Chartier9fd8c602016-11-14 14:38:53 -0800465 locations->InAt(1),
Mathieu Chartierb99f4d62016-11-07 16:17:26 -0800466 LocationFrom(calling_convention.GetRegisterAt(1)),
467 Primitive::kPrimNot);
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100468 if (instruction_->IsInstanceOf()) {
Artem Serovcfbe9132016-10-14 15:58:56 +0100469 arm_codegen->InvokeRuntime(kQuickInstanceofNonTrivial,
470 instruction_,
471 instruction_->GetDexPc(),
472 this);
Mathieu Chartier9fd8c602016-11-14 14:38:53 -0800473 CheckEntrypointTypes<kQuickInstanceofNonTrivial, size_t, mirror::Object*, mirror::Class*>();
Artem Serovcfbe9132016-10-14 15:58:56 +0100474 arm_codegen->Move32(locations->Out(), LocationFrom(r0));
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100475 } else {
476 DCHECK(instruction_->IsCheckCast());
Mathieu Chartierb99f4d62016-11-07 16:17:26 -0800477 arm_codegen->InvokeRuntime(kQuickCheckInstanceOf,
478 instruction_,
479 instruction_->GetDexPc(),
480 this);
481 CheckEntrypointTypes<kQuickCheckInstanceOf, void, mirror::Object*, mirror::Class*>();
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100482 }
483
484 if (!is_fatal_) {
Artem Serovcfbe9132016-10-14 15:58:56 +0100485 RestoreLiveRegisters(codegen, locations);
486 __ B(GetExitLabel());
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100487 }
488 }
489
490 const char* GetDescription() const OVERRIDE { return "TypeCheckSlowPathARMVIXL"; }
491
492 bool IsFatal() const OVERRIDE { return is_fatal_; }
493
494 private:
495 const bool is_fatal_;
496
497 DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathARMVIXL);
498};
499
Scott Wakelingc34dba72016-10-03 10:14:44 +0100500class DeoptimizationSlowPathARMVIXL : public SlowPathCodeARMVIXL {
501 public:
502 explicit DeoptimizationSlowPathARMVIXL(HDeoptimize* instruction)
503 : SlowPathCodeARMVIXL(instruction) {}
504
505 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
506 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
507 __ Bind(GetEntryLabel());
508 arm_codegen->InvokeRuntime(kQuickDeoptimize, instruction_, instruction_->GetDexPc(), this);
509 CheckEntrypointTypes<kQuickDeoptimize, void, void>();
510 }
511
512 const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathARMVIXL"; }
513
514 private:
515 DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathARMVIXL);
516};
517
518class ArraySetSlowPathARMVIXL : public SlowPathCodeARMVIXL {
519 public:
520 explicit ArraySetSlowPathARMVIXL(HInstruction* instruction) : SlowPathCodeARMVIXL(instruction) {}
521
522 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
523 LocationSummary* locations = instruction_->GetLocations();
524 __ Bind(GetEntryLabel());
525 SaveLiveRegisters(codegen, locations);
526
527 InvokeRuntimeCallingConventionARMVIXL calling_convention;
528 HParallelMove parallel_move(codegen->GetGraph()->GetArena());
529 parallel_move.AddMove(
530 locations->InAt(0),
531 LocationFrom(calling_convention.GetRegisterAt(0)),
532 Primitive::kPrimNot,
533 nullptr);
534 parallel_move.AddMove(
535 locations->InAt(1),
536 LocationFrom(calling_convention.GetRegisterAt(1)),
537 Primitive::kPrimInt,
538 nullptr);
539 parallel_move.AddMove(
540 locations->InAt(2),
541 LocationFrom(calling_convention.GetRegisterAt(2)),
542 Primitive::kPrimNot,
543 nullptr);
544 codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
545
546 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
547 arm_codegen->InvokeRuntime(kQuickAputObject, instruction_, instruction_->GetDexPc(), this);
548 CheckEntrypointTypes<kQuickAputObject, void, mirror::Array*, int32_t, mirror::Object*>();
549 RestoreLiveRegisters(codegen, locations);
550 __ B(GetExitLabel());
551 }
552
553 const char* GetDescription() const OVERRIDE { return "ArraySetSlowPathARMVIXL"; }
554
555 private:
556 DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathARMVIXL);
557};
558
559
Scott Wakelingfe885462016-09-22 10:24:38 +0100560inline vixl32::Condition ARMCondition(IfCondition cond) {
561 switch (cond) {
562 case kCondEQ: return eq;
563 case kCondNE: return ne;
564 case kCondLT: return lt;
565 case kCondLE: return le;
566 case kCondGT: return gt;
567 case kCondGE: return ge;
568 case kCondB: return lo;
569 case kCondBE: return ls;
570 case kCondA: return hi;
571 case kCondAE: return hs;
572 }
573 LOG(FATAL) << "Unreachable";
574 UNREACHABLE();
575}
576
577// Maps signed condition to unsigned condition.
578inline vixl32::Condition ARMUnsignedCondition(IfCondition cond) {
579 switch (cond) {
580 case kCondEQ: return eq;
581 case kCondNE: return ne;
582 // Signed to unsigned.
583 case kCondLT: return lo;
584 case kCondLE: return ls;
585 case kCondGT: return hi;
586 case kCondGE: return hs;
587 // Unsigned remain unchanged.
588 case kCondB: return lo;
589 case kCondBE: return ls;
590 case kCondA: return hi;
591 case kCondAE: return hs;
592 }
593 LOG(FATAL) << "Unreachable";
594 UNREACHABLE();
595}
596
597inline vixl32::Condition ARMFPCondition(IfCondition cond, bool gt_bias) {
598 // The ARM condition codes can express all the necessary branches, see the
599 // "Meaning (floating-point)" column in the table A8-1 of the ARMv7 reference manual.
600 // There is no dex instruction or HIR that would need the missing conditions
601 // "equal or unordered" or "not equal".
602 switch (cond) {
603 case kCondEQ: return eq;
604 case kCondNE: return ne /* unordered */;
605 case kCondLT: return gt_bias ? cc : lt /* unordered */;
606 case kCondLE: return gt_bias ? ls : le /* unordered */;
607 case kCondGT: return gt_bias ? hi /* unordered */ : gt;
608 case kCondGE: return gt_bias ? cs /* unordered */ : ge;
609 default:
610 LOG(FATAL) << "UNREACHABLE";
611 UNREACHABLE();
612 }
613}
614
Scott Wakelingfe885462016-09-22 10:24:38 +0100615void CodeGeneratorARMVIXL::DumpCoreRegister(std::ostream& stream, int reg) const {
616 stream << vixl32::Register(reg);
617}
618
619void CodeGeneratorARMVIXL::DumpFloatingPointRegister(std::ostream& stream, int reg) const {
620 stream << vixl32::SRegister(reg);
621}
622
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100623static uint32_t ComputeSRegisterListMask(const SRegisterList& regs) {
Scott Wakelingfe885462016-09-22 10:24:38 +0100624 uint32_t mask = 0;
625 for (uint32_t i = regs.GetFirstSRegister().GetCode();
626 i <= regs.GetLastSRegister().GetCode();
627 ++i) {
628 mask |= (1 << i);
629 }
630 return mask;
631}
632
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100633size_t CodeGeneratorARMVIXL::RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
634 GetAssembler()->LoadSFromOffset(vixl32::SRegister(reg_id), sp, stack_index);
635 return kArmWordSize;
636}
637
Scott Wakelingfe885462016-09-22 10:24:38 +0100638#undef __
639
640CodeGeneratorARMVIXL::CodeGeneratorARMVIXL(HGraph* graph,
641 const ArmInstructionSetFeatures& isa_features,
642 const CompilerOptions& compiler_options,
643 OptimizingCompilerStats* stats)
644 : CodeGenerator(graph,
645 kNumberOfCoreRegisters,
646 kNumberOfSRegisters,
647 kNumberOfRegisterPairs,
648 kCoreCalleeSaves.GetList(),
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100649 ComputeSRegisterListMask(kFpuCalleeSaves),
Scott Wakelingfe885462016-09-22 10:24:38 +0100650 compiler_options,
651 stats),
652 block_labels_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
Artem Serov551b28f2016-10-18 19:11:30 +0100653 jump_tables_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
Scott Wakelingfe885462016-09-22 10:24:38 +0100654 location_builder_(graph, this),
655 instruction_visitor_(graph, this),
656 move_resolver_(graph->GetArena(), this),
657 assembler_(graph->GetArena()),
658 isa_features_(isa_features) {
659 // Always save the LR register to mimic Quick.
660 AddAllocatedRegister(Location::RegisterLocation(LR));
Alexandre Rames9c19bd62016-10-24 11:50:32 +0100661 // Give d14 and d15 as scratch registers to VIXL.
662 // They are removed from the register allocator in `SetupBlockedRegisters()`.
663 // TODO(VIXL): We need two scratch D registers for `EmitSwap` when swapping two double stack
664 // slots. If that is sufficiently rare, and we have pressure on FP registers, we could instead
665 // spill in `EmitSwap`. But if we actually are guaranteed to have 32 D registers, we could give
666 // d30 and d31 to VIXL to avoid removing registers from the allocator. If that is the case, we may
667 // also want to investigate giving those 14 other D registers to the allocator.
668 GetVIXLAssembler()->GetScratchVRegisterList()->Combine(d14);
669 GetVIXLAssembler()->GetScratchVRegisterList()->Combine(d15);
Scott Wakelingfe885462016-09-22 10:24:38 +0100670}
671
Artem Serov551b28f2016-10-18 19:11:30 +0100672void JumpTableARMVIXL::EmitTable(CodeGeneratorARMVIXL* codegen) {
673 uint32_t num_entries = switch_instr_->GetNumEntries();
674 DCHECK_GE(num_entries, kPackedSwitchCompareJumpThreshold);
675
676 // We are about to use the assembler to place literals directly. Make sure we have enough
Scott Wakelingb77051e2016-11-21 19:46:00 +0000677 // underlying code buffer and we have generated a jump table of the right size, using
678 // codegen->GetVIXLAssembler()->GetBuffer().Align();
Artem Serov551b28f2016-10-18 19:11:30 +0100679 AssemblerAccurateScope aas(codegen->GetVIXLAssembler(),
680 num_entries * sizeof(int32_t),
681 CodeBufferCheckScope::kMaximumSize);
682 // TODO(VIXL): Check that using lower case bind is fine here.
683 codegen->GetVIXLAssembler()->bind(&table_start_);
Artem Serov09a940d2016-11-11 16:15:11 +0000684 for (uint32_t i = 0; i < num_entries; i++) {
685 codegen->GetVIXLAssembler()->place(bb_addresses_[i].get());
686 }
687}
688
689void JumpTableARMVIXL::FixTable(CodeGeneratorARMVIXL* codegen) {
690 uint32_t num_entries = switch_instr_->GetNumEntries();
691 DCHECK_GE(num_entries, kPackedSwitchCompareJumpThreshold);
692
Artem Serov551b28f2016-10-18 19:11:30 +0100693 const ArenaVector<HBasicBlock*>& successors = switch_instr_->GetBlock()->GetSuccessors();
694 for (uint32_t i = 0; i < num_entries; i++) {
695 vixl32::Label* target_label = codegen->GetLabelOf(successors[i]);
696 DCHECK(target_label->IsBound());
697 int32_t jump_offset = target_label->GetLocation() - table_start_.GetLocation();
698 // When doing BX to address we need to have lower bit set to 1 in T32.
699 if (codegen->GetVIXLAssembler()->IsUsingT32()) {
700 jump_offset++;
701 }
702 DCHECK_GT(jump_offset, std::numeric_limits<int32_t>::min());
703 DCHECK_LE(jump_offset, std::numeric_limits<int32_t>::max());
Artem Serov09a940d2016-11-11 16:15:11 +0000704
Scott Wakelingb77051e2016-11-21 19:46:00 +0000705 bb_addresses_[i].get()->UpdateValue(jump_offset, codegen->GetVIXLAssembler()->GetBuffer());
Artem Serov551b28f2016-10-18 19:11:30 +0100706 }
707}
708
Artem Serov09a940d2016-11-11 16:15:11 +0000709void CodeGeneratorARMVIXL::FixJumpTables() {
Artem Serov551b28f2016-10-18 19:11:30 +0100710 for (auto&& jump_table : jump_tables_) {
Artem Serov09a940d2016-11-11 16:15:11 +0000711 jump_table->FixTable(this);
Artem Serov551b28f2016-10-18 19:11:30 +0100712 }
713}
714
Andreas Gampeca620d72016-11-08 08:09:33 -0800715#define __ reinterpret_cast<ArmVIXLAssembler*>(GetAssembler())->GetVIXLAssembler()-> // NOLINT
Scott Wakelingfe885462016-09-22 10:24:38 +0100716
717void CodeGeneratorARMVIXL::Finalize(CodeAllocator* allocator) {
Artem Serov09a940d2016-11-11 16:15:11 +0000718 FixJumpTables();
Scott Wakelingfe885462016-09-22 10:24:38 +0100719 GetAssembler()->FinalizeCode();
720 CodeGenerator::Finalize(allocator);
721}
722
723void CodeGeneratorARMVIXL::SetupBlockedRegisters() const {
Scott Wakelingfe885462016-09-22 10:24:38 +0100724 // Stack register, LR and PC are always reserved.
725 blocked_core_registers_[SP] = true;
726 blocked_core_registers_[LR] = true;
727 blocked_core_registers_[PC] = true;
728
729 // Reserve thread register.
730 blocked_core_registers_[TR] = true;
731
732 // Reserve temp register.
733 blocked_core_registers_[IP] = true;
734
Alexandre Rames9c19bd62016-10-24 11:50:32 +0100735 // Registers s28-s31 (d14-d15) are left to VIXL for scratch registers.
736 // (They are given to the `MacroAssembler` in `CodeGeneratorARMVIXL::CodeGeneratorARMVIXL`.)
737 blocked_fpu_registers_[28] = true;
738 blocked_fpu_registers_[29] = true;
739 blocked_fpu_registers_[30] = true;
740 blocked_fpu_registers_[31] = true;
741
Scott Wakelingfe885462016-09-22 10:24:38 +0100742 if (GetGraph()->IsDebuggable()) {
743 // Stubs do not save callee-save floating point registers. If the graph
744 // is debuggable, we need to deal with these registers differently. For
745 // now, just block them.
746 for (uint32_t i = kFpuCalleeSaves.GetFirstSRegister().GetCode();
747 i <= kFpuCalleeSaves.GetLastSRegister().GetCode();
748 ++i) {
749 blocked_fpu_registers_[i] = true;
750 }
751 }
Scott Wakelingfe885462016-09-22 10:24:38 +0100752}
753
Scott Wakelingfe885462016-09-22 10:24:38 +0100754InstructionCodeGeneratorARMVIXL::InstructionCodeGeneratorARMVIXL(HGraph* graph,
755 CodeGeneratorARMVIXL* codegen)
756 : InstructionCodeGenerator(graph, codegen),
757 assembler_(codegen->GetAssembler()),
758 codegen_(codegen) {}
759
760void CodeGeneratorARMVIXL::ComputeSpillMask() {
761 core_spill_mask_ = allocated_registers_.GetCoreRegisters() & core_callee_save_mask_;
762 DCHECK_NE(core_spill_mask_, 0u) << "At least the return address register must be saved";
763 // There is no easy instruction to restore just the PC on thumb2. We spill and
764 // restore another arbitrary register.
765 core_spill_mask_ |= (1 << kCoreAlwaysSpillRegister.GetCode());
766 fpu_spill_mask_ = allocated_registers_.GetFloatingPointRegisters() & fpu_callee_save_mask_;
767 // We use vpush and vpop for saving and restoring floating point registers, which take
768 // a SRegister and the number of registers to save/restore after that SRegister. We
769 // therefore update the `fpu_spill_mask_` to also contain those registers not allocated,
770 // but in the range.
771 if (fpu_spill_mask_ != 0) {
772 uint32_t least_significant_bit = LeastSignificantBit(fpu_spill_mask_);
773 uint32_t most_significant_bit = MostSignificantBit(fpu_spill_mask_);
774 for (uint32_t i = least_significant_bit + 1 ; i < most_significant_bit; ++i) {
775 fpu_spill_mask_ |= (1 << i);
776 }
777 }
778}
779
780void CodeGeneratorARMVIXL::GenerateFrameEntry() {
781 bool skip_overflow_check =
782 IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kArm);
783 DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks());
784 __ Bind(&frame_entry_label_);
785
786 if (HasEmptyFrame()) {
787 return;
788 }
789
Scott Wakelingfe885462016-09-22 10:24:38 +0100790 if (!skip_overflow_check) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100791 UseScratchRegisterScope temps(GetVIXLAssembler());
792 vixl32::Register temp = temps.Acquire();
Scott Wakelingfe885462016-09-22 10:24:38 +0100793 __ Sub(temp, sp, static_cast<int32_t>(GetStackOverflowReservedBytes(kArm)));
794 // The load must immediately precede RecordPcInfo.
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100795 AssemblerAccurateScope aas(GetVIXLAssembler(),
796 kArmInstrMaxSizeInBytes,
797 CodeBufferCheckScope::kMaximumSize);
798 __ ldr(temp, MemOperand(temp));
799 RecordPcInfo(nullptr, 0);
Scott Wakelingfe885462016-09-22 10:24:38 +0100800 }
801
802 __ Push(RegisterList(core_spill_mask_));
803 GetAssembler()->cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(core_spill_mask_));
804 GetAssembler()->cfi().RelOffsetForMany(DWARFReg(kMethodRegister),
805 0,
806 core_spill_mask_,
807 kArmWordSize);
808 if (fpu_spill_mask_ != 0) {
809 uint32_t first = LeastSignificantBit(fpu_spill_mask_);
810
811 // Check that list is contiguous.
812 DCHECK_EQ(fpu_spill_mask_ >> CTZ(fpu_spill_mask_), ~0u >> (32 - POPCOUNT(fpu_spill_mask_)));
813
814 __ Vpush(SRegisterList(vixl32::SRegister(first), POPCOUNT(fpu_spill_mask_)));
815 GetAssembler()->cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(fpu_spill_mask_));
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100816 GetAssembler()->cfi().RelOffsetForMany(DWARFReg(s0), 0, fpu_spill_mask_, kArmWordSize);
Scott Wakelingfe885462016-09-22 10:24:38 +0100817 }
818 int adjust = GetFrameSize() - FrameEntrySpillSize();
819 __ Sub(sp, sp, adjust);
820 GetAssembler()->cfi().AdjustCFAOffset(adjust);
821 GetAssembler()->StoreToOffset(kStoreWord, kMethodRegister, sp, 0);
822}
823
824void CodeGeneratorARMVIXL::GenerateFrameExit() {
825 if (HasEmptyFrame()) {
826 __ Bx(lr);
827 return;
828 }
829 GetAssembler()->cfi().RememberState();
830 int adjust = GetFrameSize() - FrameEntrySpillSize();
831 __ Add(sp, sp, adjust);
832 GetAssembler()->cfi().AdjustCFAOffset(-adjust);
833 if (fpu_spill_mask_ != 0) {
834 uint32_t first = LeastSignificantBit(fpu_spill_mask_);
835
836 // Check that list is contiguous.
837 DCHECK_EQ(fpu_spill_mask_ >> CTZ(fpu_spill_mask_), ~0u >> (32 - POPCOUNT(fpu_spill_mask_)));
838
839 __ Vpop(SRegisterList(vixl32::SRegister(first), POPCOUNT(fpu_spill_mask_)));
840 GetAssembler()->cfi().AdjustCFAOffset(
841 -static_cast<int>(kArmWordSize) * POPCOUNT(fpu_spill_mask_));
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100842 GetAssembler()->cfi().RestoreMany(DWARFReg(vixl32::SRegister(0)), fpu_spill_mask_);
Scott Wakelingfe885462016-09-22 10:24:38 +0100843 }
844 // Pop LR into PC to return.
845 DCHECK_NE(core_spill_mask_ & (1 << kLrCode), 0U);
846 uint32_t pop_mask = (core_spill_mask_ & (~(1 << kLrCode))) | 1 << kPcCode;
847 __ Pop(RegisterList(pop_mask));
848 GetAssembler()->cfi().RestoreState();
849 GetAssembler()->cfi().DefCFAOffset(GetFrameSize());
850}
851
852void CodeGeneratorARMVIXL::Bind(HBasicBlock* block) {
853 __ Bind(GetLabelOf(block));
854}
855
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100856void CodeGeneratorARMVIXL::Move32(Location destination, Location source) {
857 if (source.Equals(destination)) {
858 return;
859 }
860 if (destination.IsRegister()) {
861 if (source.IsRegister()) {
862 __ Mov(RegisterFrom(destination), RegisterFrom(source));
863 } else if (source.IsFpuRegister()) {
864 __ Vmov(RegisterFrom(destination), SRegisterFrom(source));
865 } else {
866 GetAssembler()->LoadFromOffset(kLoadWord,
867 RegisterFrom(destination),
868 sp,
869 source.GetStackIndex());
870 }
871 } else if (destination.IsFpuRegister()) {
872 if (source.IsRegister()) {
873 __ Vmov(SRegisterFrom(destination), RegisterFrom(source));
874 } else if (source.IsFpuRegister()) {
875 __ Vmov(SRegisterFrom(destination), SRegisterFrom(source));
876 } else {
877 GetAssembler()->LoadSFromOffset(SRegisterFrom(destination), sp, source.GetStackIndex());
878 }
879 } else {
880 DCHECK(destination.IsStackSlot()) << destination;
881 if (source.IsRegister()) {
882 GetAssembler()->StoreToOffset(kStoreWord,
883 RegisterFrom(source),
884 sp,
885 destination.GetStackIndex());
886 } else if (source.IsFpuRegister()) {
887 GetAssembler()->StoreSToOffset(SRegisterFrom(source), sp, destination.GetStackIndex());
888 } else {
889 DCHECK(source.IsStackSlot()) << source;
890 UseScratchRegisterScope temps(GetVIXLAssembler());
891 vixl32::Register temp = temps.Acquire();
892 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, source.GetStackIndex());
893 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
894 }
895 }
896}
897
Artem Serovcfbe9132016-10-14 15:58:56 +0100898void CodeGeneratorARMVIXL::MoveConstant(Location location, int32_t value) {
899 DCHECK(location.IsRegister());
900 __ Mov(RegisterFrom(location), value);
Scott Wakelingfe885462016-09-22 10:24:38 +0100901}
902
903void CodeGeneratorARMVIXL::MoveLocation(Location dst, Location src, Primitive::Type dst_type) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100904 // TODO(VIXL): Maybe refactor to have the 'move' implementation here and use it in
905 // `ParallelMoveResolverARMVIXL::EmitMove`, as is done in the `arm64` backend.
906 HParallelMove move(GetGraph()->GetArena());
907 move.AddMove(src, dst, dst_type, nullptr);
908 GetMoveResolver()->EmitNativeCode(&move);
Scott Wakelingfe885462016-09-22 10:24:38 +0100909}
910
Artem Serovcfbe9132016-10-14 15:58:56 +0100911void CodeGeneratorARMVIXL::AddLocationAsTemp(Location location, LocationSummary* locations) {
912 if (location.IsRegister()) {
913 locations->AddTemp(location);
914 } else if (location.IsRegisterPair()) {
915 locations->AddTemp(LocationFrom(LowRegisterFrom(location)));
916 locations->AddTemp(LocationFrom(HighRegisterFrom(location)));
917 } else {
918 UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location;
919 }
Scott Wakelingfe885462016-09-22 10:24:38 +0100920}
921
922void CodeGeneratorARMVIXL::InvokeRuntime(QuickEntrypointEnum entrypoint,
923 HInstruction* instruction,
924 uint32_t dex_pc,
925 SlowPathCode* slow_path) {
926 ValidateInvokeRuntime(entrypoint, instruction, slow_path);
927 GenerateInvokeRuntime(GetThreadOffset<kArmPointerSize>(entrypoint).Int32Value());
928 if (EntrypointRequiresStackMap(entrypoint)) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100929 // TODO(VIXL): If necessary, use a scope to ensure we record the pc info immediately after the
930 // previous instruction.
Scott Wakelingfe885462016-09-22 10:24:38 +0100931 RecordPcInfo(instruction, dex_pc, slow_path);
932 }
933}
934
935void CodeGeneratorARMVIXL::InvokeRuntimeWithoutRecordingPcInfo(int32_t entry_point_offset,
936 HInstruction* instruction,
937 SlowPathCode* slow_path) {
938 ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction, slow_path);
939 GenerateInvokeRuntime(entry_point_offset);
940}
941
942void CodeGeneratorARMVIXL::GenerateInvokeRuntime(int32_t entry_point_offset) {
943 GetAssembler()->LoadFromOffset(kLoadWord, lr, tr, entry_point_offset);
944 __ Blx(lr);
945}
946
Scott Wakelingfe885462016-09-22 10:24:38 +0100947void InstructionCodeGeneratorARMVIXL::HandleGoto(HInstruction* got, HBasicBlock* successor) {
948 DCHECK(!successor->IsExitBlock());
949 HBasicBlock* block = got->GetBlock();
950 HInstruction* previous = got->GetPrevious();
951 HLoopInformation* info = block->GetLoopInformation();
952
953 if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) {
954 codegen_->ClearSpillSlotsFromLoopPhisInStackMap(info->GetSuspendCheck());
955 GenerateSuspendCheck(info->GetSuspendCheck(), successor);
956 return;
957 }
958 if (block->IsEntryBlock() && (previous != nullptr) && previous->IsSuspendCheck()) {
959 GenerateSuspendCheck(previous->AsSuspendCheck(), nullptr);
960 }
961 if (!codegen_->GoesToNextBlock(block, successor)) {
962 __ B(codegen_->GetLabelOf(successor));
963 }
964}
965
966void LocationsBuilderARMVIXL::VisitGoto(HGoto* got) {
967 got->SetLocations(nullptr);
968}
969
970void InstructionCodeGeneratorARMVIXL::VisitGoto(HGoto* got) {
971 HandleGoto(got, got->GetSuccessor());
972}
973
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100974void LocationsBuilderARMVIXL::VisitTryBoundary(HTryBoundary* try_boundary) {
975 try_boundary->SetLocations(nullptr);
976}
977
978void InstructionCodeGeneratorARMVIXL::VisitTryBoundary(HTryBoundary* try_boundary) {
979 HBasicBlock* successor = try_boundary->GetNormalFlowSuccessor();
980 if (!successor->IsExitBlock()) {
981 HandleGoto(try_boundary, successor);
982 }
983}
984
Scott Wakelingfe885462016-09-22 10:24:38 +0100985void LocationsBuilderARMVIXL::VisitExit(HExit* exit) {
986 exit->SetLocations(nullptr);
987}
988
989void InstructionCodeGeneratorARMVIXL::VisitExit(HExit* exit ATTRIBUTE_UNUSED) {
990}
991
992void InstructionCodeGeneratorARMVIXL::GenerateVcmp(HInstruction* instruction) {
993 Primitive::Type type = instruction->InputAt(0)->GetType();
994 Location lhs_loc = instruction->GetLocations()->InAt(0);
995 Location rhs_loc = instruction->GetLocations()->InAt(1);
996 if (rhs_loc.IsConstant()) {
997 // 0.0 is the only immediate that can be encoded directly in
998 // a VCMP instruction.
999 //
1000 // Both the JLS (section 15.20.1) and the JVMS (section 6.5)
1001 // specify that in a floating-point comparison, positive zero
1002 // and negative zero are considered equal, so we can use the
1003 // literal 0.0 for both cases here.
1004 //
1005 // Note however that some methods (Float.equal, Float.compare,
1006 // Float.compareTo, Double.equal, Double.compare,
1007 // Double.compareTo, Math.max, Math.min, StrictMath.max,
1008 // StrictMath.min) consider 0.0 to be (strictly) greater than
1009 // -0.0. So if we ever translate calls to these methods into a
1010 // HCompare instruction, we must handle the -0.0 case with
1011 // care here.
1012 DCHECK(rhs_loc.GetConstant()->IsArithmeticZero());
1013 if (type == Primitive::kPrimFloat) {
1014 __ Vcmp(F32, InputSRegisterAt(instruction, 0), 0.0);
1015 } else {
1016 DCHECK_EQ(type, Primitive::kPrimDouble);
Scott Wakelingc34dba72016-10-03 10:14:44 +01001017 __ Vcmp(F64, DRegisterFrom(lhs_loc), 0.0);
Scott Wakelingfe885462016-09-22 10:24:38 +01001018 }
1019 } else {
1020 if (type == Primitive::kPrimFloat) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001021 __ Vcmp(InputSRegisterAt(instruction, 0), InputSRegisterAt(instruction, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01001022 } else {
1023 DCHECK_EQ(type, Primitive::kPrimDouble);
Scott Wakelingc34dba72016-10-03 10:14:44 +01001024 __ Vcmp(DRegisterFrom(lhs_loc), DRegisterFrom(rhs_loc));
Scott Wakelingfe885462016-09-22 10:24:38 +01001025 }
1026 }
1027}
1028
1029void InstructionCodeGeneratorARMVIXL::GenerateFPJumps(HCondition* cond,
1030 vixl32::Label* true_label,
1031 vixl32::Label* false_label ATTRIBUTE_UNUSED) {
1032 // To branch on the result of the FP compare we transfer FPSCR to APSR (encoded as PC in VMRS).
1033 __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
1034 __ B(ARMFPCondition(cond->GetCondition(), cond->IsGtBias()), true_label);
1035}
1036
1037void InstructionCodeGeneratorARMVIXL::GenerateLongComparesAndJumps(HCondition* cond,
1038 vixl32::Label* true_label,
1039 vixl32::Label* false_label) {
1040 LocationSummary* locations = cond->GetLocations();
1041 Location left = locations->InAt(0);
1042 Location right = locations->InAt(1);
1043 IfCondition if_cond = cond->GetCondition();
1044
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001045 vixl32::Register left_high = HighRegisterFrom(left);
1046 vixl32::Register left_low = LowRegisterFrom(left);
Scott Wakelingfe885462016-09-22 10:24:38 +01001047 IfCondition true_high_cond = if_cond;
1048 IfCondition false_high_cond = cond->GetOppositeCondition();
1049 vixl32::Condition final_condition = ARMUnsignedCondition(if_cond); // unsigned on lower part
1050
1051 // Set the conditions for the test, remembering that == needs to be
1052 // decided using the low words.
1053 // TODO: consider avoiding jumps with temporary and CMP low+SBC high
1054 switch (if_cond) {
1055 case kCondEQ:
1056 case kCondNE:
1057 // Nothing to do.
1058 break;
1059 case kCondLT:
1060 false_high_cond = kCondGT;
1061 break;
1062 case kCondLE:
1063 true_high_cond = kCondLT;
1064 break;
1065 case kCondGT:
1066 false_high_cond = kCondLT;
1067 break;
1068 case kCondGE:
1069 true_high_cond = kCondGT;
1070 break;
1071 case kCondB:
1072 false_high_cond = kCondA;
1073 break;
1074 case kCondBE:
1075 true_high_cond = kCondB;
1076 break;
1077 case kCondA:
1078 false_high_cond = kCondB;
1079 break;
1080 case kCondAE:
1081 true_high_cond = kCondA;
1082 break;
1083 }
1084 if (right.IsConstant()) {
1085 int64_t value = right.GetConstant()->AsLongConstant()->GetValue();
1086 int32_t val_low = Low32Bits(value);
1087 int32_t val_high = High32Bits(value);
1088
1089 __ Cmp(left_high, val_high);
1090 if (if_cond == kCondNE) {
1091 __ B(ARMCondition(true_high_cond), true_label);
1092 } else if (if_cond == kCondEQ) {
1093 __ B(ARMCondition(false_high_cond), false_label);
1094 } else {
1095 __ B(ARMCondition(true_high_cond), true_label);
1096 __ B(ARMCondition(false_high_cond), false_label);
1097 }
1098 // Must be equal high, so compare the lows.
1099 __ Cmp(left_low, val_low);
1100 } else {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001101 vixl32::Register right_high = HighRegisterFrom(right);
1102 vixl32::Register right_low = LowRegisterFrom(right);
Scott Wakelingfe885462016-09-22 10:24:38 +01001103
1104 __ Cmp(left_high, right_high);
1105 if (if_cond == kCondNE) {
1106 __ B(ARMCondition(true_high_cond), true_label);
1107 } else if (if_cond == kCondEQ) {
1108 __ B(ARMCondition(false_high_cond), false_label);
1109 } else {
1110 __ B(ARMCondition(true_high_cond), true_label);
1111 __ B(ARMCondition(false_high_cond), false_label);
1112 }
1113 // Must be equal high, so compare the lows.
1114 __ Cmp(left_low, right_low);
1115 }
1116 // The last comparison might be unsigned.
1117 // TODO: optimize cases where this is always true/false
1118 __ B(final_condition, true_label);
1119}
1120
1121void InstructionCodeGeneratorARMVIXL::GenerateCompareTestAndBranch(HCondition* condition,
1122 vixl32::Label* true_target_in,
1123 vixl32::Label* false_target_in) {
1124 // Generated branching requires both targets to be explicit. If either of the
1125 // targets is nullptr (fallthrough) use and bind `fallthrough` instead.
1126 vixl32::Label fallthrough;
1127 vixl32::Label* true_target = (true_target_in == nullptr) ? &fallthrough : true_target_in;
1128 vixl32::Label* false_target = (false_target_in == nullptr) ? &fallthrough : false_target_in;
1129
1130 Primitive::Type type = condition->InputAt(0)->GetType();
1131 switch (type) {
1132 case Primitive::kPrimLong:
1133 GenerateLongComparesAndJumps(condition, true_target, false_target);
1134 break;
1135 case Primitive::kPrimFloat:
1136 case Primitive::kPrimDouble:
1137 GenerateVcmp(condition);
1138 GenerateFPJumps(condition, true_target, false_target);
1139 break;
1140 default:
1141 LOG(FATAL) << "Unexpected compare type " << type;
1142 }
1143
1144 if (false_target != &fallthrough) {
1145 __ B(false_target);
1146 }
1147
1148 if (true_target_in == nullptr || false_target_in == nullptr) {
1149 __ Bind(&fallthrough);
1150 }
1151}
1152
1153void InstructionCodeGeneratorARMVIXL::GenerateTestAndBranch(HInstruction* instruction,
1154 size_t condition_input_index,
1155 vixl32::Label* true_target,
xueliang.zhongf51bc622016-11-04 09:23:32 +00001156 vixl32::Label* false_target,
1157 bool far_target) {
Scott Wakelingfe885462016-09-22 10:24:38 +01001158 HInstruction* cond = instruction->InputAt(condition_input_index);
1159
1160 if (true_target == nullptr && false_target == nullptr) {
1161 // Nothing to do. The code always falls through.
1162 return;
1163 } else if (cond->IsIntConstant()) {
1164 // Constant condition, statically compared against "true" (integer value 1).
1165 if (cond->AsIntConstant()->IsTrue()) {
1166 if (true_target != nullptr) {
1167 __ B(true_target);
1168 }
1169 } else {
1170 DCHECK(cond->AsIntConstant()->IsFalse()) << cond->AsIntConstant()->GetValue();
1171 if (false_target != nullptr) {
1172 __ B(false_target);
1173 }
1174 }
1175 return;
1176 }
1177
1178 // The following code generates these patterns:
1179 // (1) true_target == nullptr && false_target != nullptr
1180 // - opposite condition true => branch to false_target
1181 // (2) true_target != nullptr && false_target == nullptr
1182 // - condition true => branch to true_target
1183 // (3) true_target != nullptr && false_target != nullptr
1184 // - condition true => branch to true_target
1185 // - branch to false_target
1186 if (IsBooleanValueOrMaterializedCondition(cond)) {
1187 // Condition has been materialized, compare the output to 0.
1188 if (kIsDebugBuild) {
1189 Location cond_val = instruction->GetLocations()->InAt(condition_input_index);
1190 DCHECK(cond_val.IsRegister());
1191 }
1192 if (true_target == nullptr) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00001193 __ CompareAndBranchIfZero(InputRegisterAt(instruction, condition_input_index),
1194 false_target,
1195 far_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01001196 } else {
xueliang.zhongf51bc622016-11-04 09:23:32 +00001197 __ CompareAndBranchIfNonZero(InputRegisterAt(instruction, condition_input_index),
1198 true_target,
1199 far_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01001200 }
1201 } else {
1202 // Condition has not been materialized. Use its inputs as the comparison and
1203 // its condition as the branch condition.
1204 HCondition* condition = cond->AsCondition();
1205
1206 // If this is a long or FP comparison that has been folded into
1207 // the HCondition, generate the comparison directly.
1208 Primitive::Type type = condition->InputAt(0)->GetType();
1209 if (type == Primitive::kPrimLong || Primitive::IsFloatingPointType(type)) {
1210 GenerateCompareTestAndBranch(condition, true_target, false_target);
1211 return;
1212 }
1213
1214 LocationSummary* locations = cond->GetLocations();
1215 DCHECK(locations->InAt(0).IsRegister());
1216 vixl32::Register left = InputRegisterAt(cond, 0);
1217 Location right = locations->InAt(1);
1218 if (right.IsRegister()) {
1219 __ Cmp(left, InputRegisterAt(cond, 1));
1220 } else {
1221 DCHECK(right.IsConstant());
1222 __ Cmp(left, CodeGenerator::GetInt32ValueOf(right.GetConstant()));
1223 }
1224 if (true_target == nullptr) {
1225 __ B(ARMCondition(condition->GetOppositeCondition()), false_target);
1226 } else {
1227 __ B(ARMCondition(condition->GetCondition()), true_target);
1228 }
1229 }
1230
1231 // If neither branch falls through (case 3), the conditional branch to `true_target`
1232 // was already emitted (case 2) and we need to emit a jump to `false_target`.
1233 if (true_target != nullptr && false_target != nullptr) {
1234 __ B(false_target);
1235 }
1236}
1237
1238void LocationsBuilderARMVIXL::VisitIf(HIf* if_instr) {
1239 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr);
1240 if (IsBooleanValueOrMaterializedCondition(if_instr->InputAt(0))) {
1241 locations->SetInAt(0, Location::RequiresRegister());
1242 }
1243}
1244
1245void InstructionCodeGeneratorARMVIXL::VisitIf(HIf* if_instr) {
1246 HBasicBlock* true_successor = if_instr->IfTrueSuccessor();
1247 HBasicBlock* false_successor = if_instr->IfFalseSuccessor();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001248 vixl32::Label* true_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), true_successor) ?
1249 nullptr : codegen_->GetLabelOf(true_successor);
1250 vixl32::Label* false_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), false_successor) ?
1251 nullptr : codegen_->GetLabelOf(false_successor);
Scott Wakelingfe885462016-09-22 10:24:38 +01001252 GenerateTestAndBranch(if_instr, /* condition_input_index */ 0, true_target, false_target);
1253}
1254
Scott Wakelingc34dba72016-10-03 10:14:44 +01001255void LocationsBuilderARMVIXL::VisitDeoptimize(HDeoptimize* deoptimize) {
1256 LocationSummary* locations = new (GetGraph()->GetArena())
1257 LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath);
1258 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
1259 if (IsBooleanValueOrMaterializedCondition(deoptimize->InputAt(0))) {
1260 locations->SetInAt(0, Location::RequiresRegister());
1261 }
1262}
1263
1264void InstructionCodeGeneratorARMVIXL::VisitDeoptimize(HDeoptimize* deoptimize) {
1265 SlowPathCodeARMVIXL* slow_path =
1266 deopt_slow_paths_.NewSlowPath<DeoptimizationSlowPathARMVIXL>(deoptimize);
1267 GenerateTestAndBranch(deoptimize,
1268 /* condition_input_index */ 0,
1269 slow_path->GetEntryLabel(),
1270 /* false_target */ nullptr);
1271}
1272
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001273void LocationsBuilderARMVIXL::VisitSelect(HSelect* select) {
1274 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select);
1275 if (Primitive::IsFloatingPointType(select->GetType())) {
1276 locations->SetInAt(0, Location::RequiresFpuRegister());
1277 locations->SetInAt(1, Location::RequiresFpuRegister());
1278 } else {
1279 locations->SetInAt(0, Location::RequiresRegister());
1280 locations->SetInAt(1, Location::RequiresRegister());
1281 }
1282 if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) {
1283 locations->SetInAt(2, Location::RequiresRegister());
1284 }
1285 locations->SetOut(Location::SameAsFirstInput());
1286}
1287
1288void InstructionCodeGeneratorARMVIXL::VisitSelect(HSelect* select) {
1289 LocationSummary* locations = select->GetLocations();
1290 vixl32::Label false_target;
1291 GenerateTestAndBranch(select,
1292 /* condition_input_index */ 2,
1293 /* true_target */ nullptr,
xueliang.zhongf51bc622016-11-04 09:23:32 +00001294 &false_target,
1295 /* far_target */ false);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001296 codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType());
1297 __ Bind(&false_target);
1298}
1299
Artem Serov551b28f2016-10-18 19:11:30 +01001300void LocationsBuilderARMVIXL::VisitNativeDebugInfo(HNativeDebugInfo* info) {
1301 new (GetGraph()->GetArena()) LocationSummary(info);
1302}
1303
1304void InstructionCodeGeneratorARMVIXL::VisitNativeDebugInfo(HNativeDebugInfo*) {
1305 // MaybeRecordNativeDebugInfo is already called implicitly in CodeGenerator::Compile.
1306}
1307
Scott Wakelingfe885462016-09-22 10:24:38 +01001308void CodeGeneratorARMVIXL::GenerateNop() {
1309 __ Nop();
1310}
1311
1312void LocationsBuilderARMVIXL::HandleCondition(HCondition* cond) {
1313 LocationSummary* locations =
1314 new (GetGraph()->GetArena()) LocationSummary(cond, LocationSummary::kNoCall);
1315 // Handle the long/FP comparisons made in instruction simplification.
1316 switch (cond->InputAt(0)->GetType()) {
1317 case Primitive::kPrimLong:
1318 locations->SetInAt(0, Location::RequiresRegister());
1319 locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
1320 if (!cond->IsEmittedAtUseSite()) {
1321 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
1322 }
1323 break;
1324
Scott Wakelingfe885462016-09-22 10:24:38 +01001325 case Primitive::kPrimFloat:
1326 case Primitive::kPrimDouble:
1327 locations->SetInAt(0, Location::RequiresFpuRegister());
Artem Serov657022c2016-11-23 14:19:38 +00001328 locations->SetInAt(1, ArithmeticZeroOrFpuRegister(cond->InputAt(1)));
Scott Wakelingfe885462016-09-22 10:24:38 +01001329 if (!cond->IsEmittedAtUseSite()) {
1330 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
1331 }
1332 break;
1333
1334 default:
1335 locations->SetInAt(0, Location::RequiresRegister());
1336 locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
1337 if (!cond->IsEmittedAtUseSite()) {
1338 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
1339 }
1340 }
1341}
1342
1343void InstructionCodeGeneratorARMVIXL::HandleCondition(HCondition* cond) {
1344 if (cond->IsEmittedAtUseSite()) {
1345 return;
1346 }
1347
Artem Serov657022c2016-11-23 14:19:38 +00001348 Location right = cond->GetLocations()->InAt(1);
Scott Wakelingfe885462016-09-22 10:24:38 +01001349 vixl32::Register out = OutputRegister(cond);
1350 vixl32::Label true_label, false_label;
1351
1352 switch (cond->InputAt(0)->GetType()) {
1353 default: {
1354 // Integer case.
Artem Serov657022c2016-11-23 14:19:38 +00001355 if (right.IsRegister()) {
1356 __ Cmp(InputRegisterAt(cond, 0), InputOperandAt(cond, 1));
1357 } else {
1358 DCHECK(right.IsConstant());
1359 __ Cmp(InputRegisterAt(cond, 0),
1360 CodeGenerator::GetInt32ValueOf(right.GetConstant()));
1361 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001362 AssemblerAccurateScope aas(GetVIXLAssembler(),
1363 kArmInstrMaxSizeInBytes * 3u,
1364 CodeBufferCheckScope::kMaximumSize);
1365 __ ite(ARMCondition(cond->GetCondition()));
1366 __ mov(ARMCondition(cond->GetCondition()), OutputRegister(cond), 1);
1367 __ mov(ARMCondition(cond->GetOppositeCondition()), OutputRegister(cond), 0);
Scott Wakelingfe885462016-09-22 10:24:38 +01001368 return;
1369 }
1370 case Primitive::kPrimLong:
1371 GenerateLongComparesAndJumps(cond, &true_label, &false_label);
1372 break;
1373 case Primitive::kPrimFloat:
1374 case Primitive::kPrimDouble:
1375 GenerateVcmp(cond);
1376 GenerateFPJumps(cond, &true_label, &false_label);
1377 break;
1378 }
1379
1380 // Convert the jumps into the result.
1381 vixl32::Label done_label;
1382
1383 // False case: result = 0.
1384 __ Bind(&false_label);
1385 __ Mov(out, 0);
1386 __ B(&done_label);
1387
1388 // True case: result = 1.
1389 __ Bind(&true_label);
1390 __ Mov(out, 1);
1391 __ Bind(&done_label);
1392}
1393
1394void LocationsBuilderARMVIXL::VisitEqual(HEqual* comp) {
1395 HandleCondition(comp);
1396}
1397
1398void InstructionCodeGeneratorARMVIXL::VisitEqual(HEqual* comp) {
1399 HandleCondition(comp);
1400}
1401
1402void LocationsBuilderARMVIXL::VisitNotEqual(HNotEqual* comp) {
1403 HandleCondition(comp);
1404}
1405
1406void InstructionCodeGeneratorARMVIXL::VisitNotEqual(HNotEqual* comp) {
1407 HandleCondition(comp);
1408}
1409
1410void LocationsBuilderARMVIXL::VisitLessThan(HLessThan* comp) {
1411 HandleCondition(comp);
1412}
1413
1414void InstructionCodeGeneratorARMVIXL::VisitLessThan(HLessThan* comp) {
1415 HandleCondition(comp);
1416}
1417
1418void LocationsBuilderARMVIXL::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
1419 HandleCondition(comp);
1420}
1421
1422void InstructionCodeGeneratorARMVIXL::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
1423 HandleCondition(comp);
1424}
1425
1426void LocationsBuilderARMVIXL::VisitGreaterThan(HGreaterThan* comp) {
1427 HandleCondition(comp);
1428}
1429
1430void InstructionCodeGeneratorARMVIXL::VisitGreaterThan(HGreaterThan* comp) {
1431 HandleCondition(comp);
1432}
1433
1434void LocationsBuilderARMVIXL::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
1435 HandleCondition(comp);
1436}
1437
1438void InstructionCodeGeneratorARMVIXL::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
1439 HandleCondition(comp);
1440}
1441
1442void LocationsBuilderARMVIXL::VisitBelow(HBelow* comp) {
1443 HandleCondition(comp);
1444}
1445
1446void InstructionCodeGeneratorARMVIXL::VisitBelow(HBelow* comp) {
1447 HandleCondition(comp);
1448}
1449
1450void LocationsBuilderARMVIXL::VisitBelowOrEqual(HBelowOrEqual* comp) {
1451 HandleCondition(comp);
1452}
1453
1454void InstructionCodeGeneratorARMVIXL::VisitBelowOrEqual(HBelowOrEqual* comp) {
1455 HandleCondition(comp);
1456}
1457
1458void LocationsBuilderARMVIXL::VisitAbove(HAbove* comp) {
1459 HandleCondition(comp);
1460}
1461
1462void InstructionCodeGeneratorARMVIXL::VisitAbove(HAbove* comp) {
1463 HandleCondition(comp);
1464}
1465
1466void LocationsBuilderARMVIXL::VisitAboveOrEqual(HAboveOrEqual* comp) {
1467 HandleCondition(comp);
1468}
1469
1470void InstructionCodeGeneratorARMVIXL::VisitAboveOrEqual(HAboveOrEqual* comp) {
1471 HandleCondition(comp);
1472}
1473
1474void LocationsBuilderARMVIXL::VisitIntConstant(HIntConstant* constant) {
1475 LocationSummary* locations =
1476 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
1477 locations->SetOut(Location::ConstantLocation(constant));
1478}
1479
1480void InstructionCodeGeneratorARMVIXL::VisitIntConstant(HIntConstant* constant ATTRIBUTE_UNUSED) {
1481 // Will be generated at use site.
1482}
1483
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001484void LocationsBuilderARMVIXL::VisitNullConstant(HNullConstant* constant) {
1485 LocationSummary* locations =
1486 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
1487 locations->SetOut(Location::ConstantLocation(constant));
1488}
1489
1490void InstructionCodeGeneratorARMVIXL::VisitNullConstant(HNullConstant* constant ATTRIBUTE_UNUSED) {
1491 // Will be generated at use site.
1492}
1493
Scott Wakelingfe885462016-09-22 10:24:38 +01001494void LocationsBuilderARMVIXL::VisitLongConstant(HLongConstant* constant) {
1495 LocationSummary* locations =
1496 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
1497 locations->SetOut(Location::ConstantLocation(constant));
1498}
1499
1500void InstructionCodeGeneratorARMVIXL::VisitLongConstant(HLongConstant* constant ATTRIBUTE_UNUSED) {
1501 // Will be generated at use site.
1502}
1503
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01001504void LocationsBuilderARMVIXL::VisitFloatConstant(HFloatConstant* constant) {
1505 LocationSummary* locations =
1506 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
1507 locations->SetOut(Location::ConstantLocation(constant));
1508}
1509
Scott Wakelingc34dba72016-10-03 10:14:44 +01001510void InstructionCodeGeneratorARMVIXL::VisitFloatConstant(
1511 HFloatConstant* constant ATTRIBUTE_UNUSED) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01001512 // Will be generated at use site.
1513}
1514
1515void LocationsBuilderARMVIXL::VisitDoubleConstant(HDoubleConstant* constant) {
1516 LocationSummary* locations =
1517 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
1518 locations->SetOut(Location::ConstantLocation(constant));
1519}
1520
Scott Wakelingc34dba72016-10-03 10:14:44 +01001521void InstructionCodeGeneratorARMVIXL::VisitDoubleConstant(
1522 HDoubleConstant* constant ATTRIBUTE_UNUSED) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01001523 // Will be generated at use site.
1524}
1525
Scott Wakelingfe885462016-09-22 10:24:38 +01001526void LocationsBuilderARMVIXL::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
1527 memory_barrier->SetLocations(nullptr);
1528}
1529
1530void InstructionCodeGeneratorARMVIXL::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
1531 codegen_->GenerateMemoryBarrier(memory_barrier->GetBarrierKind());
1532}
1533
1534void LocationsBuilderARMVIXL::VisitReturnVoid(HReturnVoid* ret) {
1535 ret->SetLocations(nullptr);
1536}
1537
1538void InstructionCodeGeneratorARMVIXL::VisitReturnVoid(HReturnVoid* ret ATTRIBUTE_UNUSED) {
1539 codegen_->GenerateFrameExit();
1540}
1541
1542void LocationsBuilderARMVIXL::VisitReturn(HReturn* ret) {
1543 LocationSummary* locations =
1544 new (GetGraph()->GetArena()) LocationSummary(ret, LocationSummary::kNoCall);
1545 locations->SetInAt(0, parameter_visitor_.GetReturnLocation(ret->InputAt(0)->GetType()));
1546}
1547
1548void InstructionCodeGeneratorARMVIXL::VisitReturn(HReturn* ret ATTRIBUTE_UNUSED) {
1549 codegen_->GenerateFrameExit();
1550}
1551
Artem Serovcfbe9132016-10-14 15:58:56 +01001552void LocationsBuilderARMVIXL::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
1553 // The trampoline uses the same calling convention as dex calling conventions,
1554 // except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain
1555 // the method_idx.
1556 HandleInvoke(invoke);
1557}
1558
1559void InstructionCodeGeneratorARMVIXL::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
1560 codegen_->GenerateInvokeUnresolvedRuntimeCall(invoke);
1561}
1562
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001563void LocationsBuilderARMVIXL::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
1564 // Explicit clinit checks triggered by static invokes must have been pruned by
1565 // art::PrepareForRegisterAllocation.
1566 DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
1567
Anton Kirilov5ec62182016-10-13 20:16:02 +01001568 IntrinsicLocationsBuilderARMVIXL intrinsic(codegen_);
1569 if (intrinsic.TryDispatch(invoke)) {
1570 if (invoke->GetLocations()->CanCall() && invoke->HasPcRelativeDexCache()) {
1571 invoke->GetLocations()->SetInAt(invoke->GetSpecialInputIndex(), Location::Any());
1572 }
1573 return;
1574 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001575
1576 HandleInvoke(invoke);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01001577
1578 // TODO(VIXL): invoke->HasPcRelativeDexCache()
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001579}
1580
Anton Kirilov5ec62182016-10-13 20:16:02 +01001581static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorARMVIXL* codegen) {
1582 if (invoke->GetLocations()->Intrinsified()) {
1583 IntrinsicCodeGeneratorARMVIXL intrinsic(codegen);
1584 intrinsic.Dispatch(invoke);
1585 return true;
1586 }
1587 return false;
1588}
1589
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001590void InstructionCodeGeneratorARMVIXL::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
1591 // Explicit clinit checks triggered by static invokes must have been pruned by
1592 // art::PrepareForRegisterAllocation.
1593 DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
1594
Anton Kirilov5ec62182016-10-13 20:16:02 +01001595 if (TryGenerateIntrinsicCode(invoke, codegen_)) {
1596 return;
1597 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001598
1599 LocationSummary* locations = invoke->GetLocations();
1600 DCHECK(locations->HasTemps());
1601 codegen_->GenerateStaticOrDirectCall(invoke, locations->GetTemp(0));
1602 // TODO(VIXL): If necessary, use a scope to ensure we record the pc info immediately after the
1603 // previous instruction.
1604 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
1605}
1606
1607void LocationsBuilderARMVIXL::HandleInvoke(HInvoke* invoke) {
1608 InvokeDexCallingConventionVisitorARM calling_convention_visitor;
1609 CodeGenerator::CreateCommonInvokeLocationSummary(invoke, &calling_convention_visitor);
1610}
1611
1612void LocationsBuilderARMVIXL::VisitInvokeVirtual(HInvokeVirtual* invoke) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01001613 IntrinsicLocationsBuilderARMVIXL intrinsic(codegen_);
1614 if (intrinsic.TryDispatch(invoke)) {
1615 return;
1616 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001617
1618 HandleInvoke(invoke);
1619}
1620
1621void InstructionCodeGeneratorARMVIXL::VisitInvokeVirtual(HInvokeVirtual* invoke) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01001622 if (TryGenerateIntrinsicCode(invoke, codegen_)) {
1623 return;
1624 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001625
1626 codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0));
1627 DCHECK(!codegen_->IsLeafMethod());
1628 // TODO(VIXL): If necessary, use a scope to ensure we record the pc info immediately after the
1629 // previous instruction.
1630 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
1631}
1632
Artem Serovcfbe9132016-10-14 15:58:56 +01001633void LocationsBuilderARMVIXL::VisitInvokeInterface(HInvokeInterface* invoke) {
1634 HandleInvoke(invoke);
1635 // Add the hidden argument.
1636 invoke->GetLocations()->AddTemp(LocationFrom(r12));
1637}
1638
1639void InstructionCodeGeneratorARMVIXL::VisitInvokeInterface(HInvokeInterface* invoke) {
1640 // TODO: b/18116999, our IMTs can miss an IncompatibleClassChangeError.
1641 LocationSummary* locations = invoke->GetLocations();
1642 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
1643 vixl32::Register hidden_reg = RegisterFrom(locations->GetTemp(1));
1644 Location receiver = locations->InAt(0);
1645 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
1646
1647 DCHECK(!receiver.IsStackSlot());
1648
1649 // /* HeapReference<Class> */ temp = receiver->klass_
1650 GetAssembler()->LoadFromOffset(kLoadWord, temp, RegisterFrom(receiver), class_offset);
1651
1652 codegen_->MaybeRecordImplicitNullCheck(invoke);
1653 // Instead of simply (possibly) unpoisoning `temp` here, we should
1654 // emit a read barrier for the previous class reference load.
1655 // However this is not required in practice, as this is an
1656 // intermediate/temporary reference and because the current
1657 // concurrent copying collector keeps the from-space memory
1658 // intact/accessible until the end of the marking phase (the
1659 // concurrent copying collector may not in the future).
1660 GetAssembler()->MaybeUnpoisonHeapReference(temp);
1661 GetAssembler()->LoadFromOffset(kLoadWord,
1662 temp,
1663 temp,
1664 mirror::Class::ImtPtrOffset(kArmPointerSize).Uint32Value());
1665 uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement(
1666 invoke->GetImtIndex(), kArmPointerSize));
1667 // temp = temp->GetImtEntryAt(method_offset);
1668 GetAssembler()->LoadFromOffset(kLoadWord, temp, temp, method_offset);
1669 uint32_t entry_point =
1670 ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value();
1671 // LR = temp->GetEntryPoint();
1672 GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, entry_point);
1673
1674 // Set the hidden (in r12) argument. It is done here, right before a BLX to prevent other
1675 // instruction from clobbering it as they might use r12 as a scratch register.
1676 DCHECK(hidden_reg.Is(r12));
Scott Wakelingb77051e2016-11-21 19:46:00 +00001677
1678 {
1679 // The VIXL macro assembler may clobber any of the scratch registers that are available to it,
1680 // so it checks if the application is using them (by passing them to the macro assembler
1681 // methods). The following application of UseScratchRegisterScope corrects VIXL's notion of
1682 // what is available, and is the opposite of the standard usage: Instead of requesting a
1683 // temporary location, it imposes an external constraint (i.e. a specific register is reserved
1684 // for the hidden argument). Note that this works even if VIXL needs a scratch register itself
1685 // (to materialize the constant), since the destination register becomes available for such use
1686 // internally for the duration of the macro instruction.
1687 UseScratchRegisterScope temps(GetVIXLAssembler());
1688 temps.Exclude(hidden_reg);
1689 __ Mov(hidden_reg, invoke->GetDexMethodIndex());
1690 }
Artem Serovcfbe9132016-10-14 15:58:56 +01001691
1692 {
1693 AssemblerAccurateScope aas(GetVIXLAssembler(),
1694 kArmInstrMaxSizeInBytes,
1695 CodeBufferCheckScope::kMaximumSize);
1696 // LR();
1697 __ blx(lr);
1698 DCHECK(!codegen_->IsLeafMethod());
1699 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
1700 }
1701}
1702
Artem Serov02109dd2016-09-23 17:17:54 +01001703void LocationsBuilderARMVIXL::VisitNeg(HNeg* neg) {
1704 LocationSummary* locations =
1705 new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall);
1706 switch (neg->GetResultType()) {
1707 case Primitive::kPrimInt: {
1708 locations->SetInAt(0, Location::RequiresRegister());
1709 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
1710 break;
1711 }
1712 case Primitive::kPrimLong: {
1713 locations->SetInAt(0, Location::RequiresRegister());
1714 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
1715 break;
1716 }
1717
1718 case Primitive::kPrimFloat:
1719 case Primitive::kPrimDouble:
1720 locations->SetInAt(0, Location::RequiresFpuRegister());
1721 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
1722 break;
1723
1724 default:
1725 LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
1726 }
1727}
1728
1729void InstructionCodeGeneratorARMVIXL::VisitNeg(HNeg* neg) {
1730 LocationSummary* locations = neg->GetLocations();
1731 Location out = locations->Out();
1732 Location in = locations->InAt(0);
1733 switch (neg->GetResultType()) {
1734 case Primitive::kPrimInt:
1735 __ Rsb(OutputRegister(neg), InputRegisterAt(neg, 0), 0);
1736 break;
1737
1738 case Primitive::kPrimLong:
1739 // out.lo = 0 - in.lo (and update the carry/borrow (C) flag)
1740 __ Rsbs(LowRegisterFrom(out), LowRegisterFrom(in), 0);
1741 // We cannot emit an RSC (Reverse Subtract with Carry)
1742 // instruction here, as it does not exist in the Thumb-2
1743 // instruction set. We use the following approach
1744 // using SBC and SUB instead.
1745 //
1746 // out.hi = -C
1747 __ Sbc(HighRegisterFrom(out), HighRegisterFrom(out), HighRegisterFrom(out));
1748 // out.hi = out.hi - in.hi
1749 __ Sub(HighRegisterFrom(out), HighRegisterFrom(out), HighRegisterFrom(in));
1750 break;
1751
1752 case Primitive::kPrimFloat:
1753 case Primitive::kPrimDouble:
Anton Kirilove28d9ae2016-10-25 18:17:23 +01001754 // TODO(VIXL): Consider introducing an InputVRegister()
1755 // helper function (equivalent to InputRegister()).
Artem Serov02109dd2016-09-23 17:17:54 +01001756 __ Vneg(OutputVRegister(neg), InputVRegisterAt(neg, 0));
1757 break;
1758
1759 default:
1760 LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
1761 }
1762}
1763
Scott Wakelingfe885462016-09-22 10:24:38 +01001764void LocationsBuilderARMVIXL::VisitTypeConversion(HTypeConversion* conversion) {
1765 Primitive::Type result_type = conversion->GetResultType();
1766 Primitive::Type input_type = conversion->GetInputType();
1767 DCHECK_NE(result_type, input_type);
1768
1769 // The float-to-long, double-to-long and long-to-float type conversions
1770 // rely on a call to the runtime.
1771 LocationSummary::CallKind call_kind =
1772 (((input_type == Primitive::kPrimFloat || input_type == Primitive::kPrimDouble)
1773 && result_type == Primitive::kPrimLong)
1774 || (input_type == Primitive::kPrimLong && result_type == Primitive::kPrimFloat))
1775 ? LocationSummary::kCallOnMainOnly
1776 : LocationSummary::kNoCall;
1777 LocationSummary* locations =
1778 new (GetGraph()->GetArena()) LocationSummary(conversion, call_kind);
1779
1780 // The Java language does not allow treating boolean as an integral type but
1781 // our bit representation makes it safe.
1782
1783 switch (result_type) {
1784 case Primitive::kPrimByte:
1785 switch (input_type) {
1786 case Primitive::kPrimLong:
1787 // Type conversion from long to byte is a result of code transformations.
1788 case Primitive::kPrimBoolean:
1789 // Boolean input is a result of code transformations.
1790 case Primitive::kPrimShort:
1791 case Primitive::kPrimInt:
1792 case Primitive::kPrimChar:
1793 // Processing a Dex `int-to-byte' instruction.
1794 locations->SetInAt(0, Location::RequiresRegister());
1795 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
1796 break;
1797
1798 default:
1799 LOG(FATAL) << "Unexpected type conversion from " << input_type
1800 << " to " << result_type;
1801 }
1802 break;
1803
1804 case Primitive::kPrimShort:
1805 switch (input_type) {
1806 case Primitive::kPrimLong:
1807 // Type conversion from long to short is a result of code transformations.
1808 case Primitive::kPrimBoolean:
1809 // Boolean input is a result of code transformations.
1810 case Primitive::kPrimByte:
1811 case Primitive::kPrimInt:
1812 case Primitive::kPrimChar:
1813 // Processing a Dex `int-to-short' instruction.
1814 locations->SetInAt(0, Location::RequiresRegister());
1815 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
1816 break;
1817
1818 default:
1819 LOG(FATAL) << "Unexpected type conversion from " << input_type
1820 << " to " << result_type;
1821 }
1822 break;
1823
1824 case Primitive::kPrimInt:
1825 switch (input_type) {
1826 case Primitive::kPrimLong:
1827 // Processing a Dex `long-to-int' instruction.
1828 locations->SetInAt(0, Location::Any());
1829 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
1830 break;
1831
1832 case Primitive::kPrimFloat:
1833 // Processing a Dex `float-to-int' instruction.
1834 locations->SetInAt(0, Location::RequiresFpuRegister());
1835 locations->SetOut(Location::RequiresRegister());
1836 locations->AddTemp(Location::RequiresFpuRegister());
1837 break;
1838
1839 case Primitive::kPrimDouble:
1840 // Processing a Dex `double-to-int' instruction.
1841 locations->SetInAt(0, Location::RequiresFpuRegister());
1842 locations->SetOut(Location::RequiresRegister());
1843 locations->AddTemp(Location::RequiresFpuRegister());
1844 break;
1845
1846 default:
1847 LOG(FATAL) << "Unexpected type conversion from " << input_type
1848 << " to " << result_type;
1849 }
1850 break;
1851
1852 case Primitive::kPrimLong:
1853 switch (input_type) {
1854 case Primitive::kPrimBoolean:
1855 // Boolean input is a result of code transformations.
1856 case Primitive::kPrimByte:
1857 case Primitive::kPrimShort:
1858 case Primitive::kPrimInt:
1859 case Primitive::kPrimChar:
1860 // Processing a Dex `int-to-long' instruction.
1861 locations->SetInAt(0, Location::RequiresRegister());
1862 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
1863 break;
1864
1865 case Primitive::kPrimFloat: {
1866 // Processing a Dex `float-to-long' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001867 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1868 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
1869 locations->SetOut(LocationFrom(r0, r1));
Scott Wakelingfe885462016-09-22 10:24:38 +01001870 break;
1871 }
1872
1873 case Primitive::kPrimDouble: {
1874 // Processing a Dex `double-to-long' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001875 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1876 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0),
1877 calling_convention.GetFpuRegisterAt(1)));
1878 locations->SetOut(LocationFrom(r0, r1));
Scott Wakelingfe885462016-09-22 10:24:38 +01001879 break;
1880 }
1881
1882 default:
1883 LOG(FATAL) << "Unexpected type conversion from " << input_type
1884 << " to " << result_type;
1885 }
1886 break;
1887
1888 case Primitive::kPrimChar:
1889 switch (input_type) {
1890 case Primitive::kPrimLong:
1891 // Type conversion from long to char is a result of code transformations.
1892 case Primitive::kPrimBoolean:
1893 // Boolean input is a result of code transformations.
1894 case Primitive::kPrimByte:
1895 case Primitive::kPrimShort:
1896 case Primitive::kPrimInt:
1897 // Processing a Dex `int-to-char' instruction.
1898 locations->SetInAt(0, Location::RequiresRegister());
1899 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
1900 break;
1901
1902 default:
1903 LOG(FATAL) << "Unexpected type conversion from " << input_type
1904 << " to " << result_type;
1905 }
1906 break;
1907
1908 case Primitive::kPrimFloat:
1909 switch (input_type) {
1910 case Primitive::kPrimBoolean:
1911 // Boolean input is a result of code transformations.
1912 case Primitive::kPrimByte:
1913 case Primitive::kPrimShort:
1914 case Primitive::kPrimInt:
1915 case Primitive::kPrimChar:
1916 // Processing a Dex `int-to-float' instruction.
1917 locations->SetInAt(0, Location::RequiresRegister());
1918 locations->SetOut(Location::RequiresFpuRegister());
1919 break;
1920
1921 case Primitive::kPrimLong: {
1922 // Processing a Dex `long-to-float' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001923 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1924 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0),
1925 calling_convention.GetRegisterAt(1)));
1926 locations->SetOut(LocationFrom(calling_convention.GetFpuRegisterAt(0)));
Scott Wakelingfe885462016-09-22 10:24:38 +01001927 break;
1928 }
1929
1930 case Primitive::kPrimDouble:
1931 // Processing a Dex `double-to-float' instruction.
1932 locations->SetInAt(0, Location::RequiresFpuRegister());
1933 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
1934 break;
1935
1936 default:
1937 LOG(FATAL) << "Unexpected type conversion from " << input_type
1938 << " to " << result_type;
1939 };
1940 break;
1941
1942 case Primitive::kPrimDouble:
1943 switch (input_type) {
1944 case Primitive::kPrimBoolean:
1945 // Boolean input is a result of code transformations.
1946 case Primitive::kPrimByte:
1947 case Primitive::kPrimShort:
1948 case Primitive::kPrimInt:
1949 case Primitive::kPrimChar:
1950 // Processing a Dex `int-to-double' instruction.
1951 locations->SetInAt(0, Location::RequiresRegister());
1952 locations->SetOut(Location::RequiresFpuRegister());
1953 break;
1954
1955 case Primitive::kPrimLong:
1956 // Processing a Dex `long-to-double' instruction.
1957 locations->SetInAt(0, Location::RequiresRegister());
1958 locations->SetOut(Location::RequiresFpuRegister());
1959 locations->AddTemp(Location::RequiresFpuRegister());
1960 locations->AddTemp(Location::RequiresFpuRegister());
1961 break;
1962
1963 case Primitive::kPrimFloat:
1964 // Processing a Dex `float-to-double' instruction.
1965 locations->SetInAt(0, Location::RequiresFpuRegister());
1966 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
1967 break;
1968
1969 default:
1970 LOG(FATAL) << "Unexpected type conversion from " << input_type
1971 << " to " << result_type;
1972 };
1973 break;
1974
1975 default:
1976 LOG(FATAL) << "Unexpected type conversion from " << input_type
1977 << " to " << result_type;
1978 }
1979}
1980
1981void InstructionCodeGeneratorARMVIXL::VisitTypeConversion(HTypeConversion* conversion) {
1982 LocationSummary* locations = conversion->GetLocations();
1983 Location out = locations->Out();
1984 Location in = locations->InAt(0);
1985 Primitive::Type result_type = conversion->GetResultType();
1986 Primitive::Type input_type = conversion->GetInputType();
1987 DCHECK_NE(result_type, input_type);
1988 switch (result_type) {
1989 case Primitive::kPrimByte:
1990 switch (input_type) {
1991 case Primitive::kPrimLong:
1992 // Type conversion from long to byte is a result of code transformations.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001993 __ Sbfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 8);
Scott Wakelingfe885462016-09-22 10:24:38 +01001994 break;
1995 case Primitive::kPrimBoolean:
1996 // Boolean input is a result of code transformations.
1997 case Primitive::kPrimShort:
1998 case Primitive::kPrimInt:
1999 case Primitive::kPrimChar:
2000 // Processing a Dex `int-to-byte' instruction.
2001 __ Sbfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 8);
2002 break;
2003
2004 default:
2005 LOG(FATAL) << "Unexpected type conversion from " << input_type
2006 << " to " << result_type;
2007 }
2008 break;
2009
2010 case Primitive::kPrimShort:
2011 switch (input_type) {
2012 case Primitive::kPrimLong:
2013 // Type conversion from long to short is a result of code transformations.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002014 __ Sbfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 16);
Scott Wakelingfe885462016-09-22 10:24:38 +01002015 break;
2016 case Primitive::kPrimBoolean:
2017 // Boolean input is a result of code transformations.
2018 case Primitive::kPrimByte:
2019 case Primitive::kPrimInt:
2020 case Primitive::kPrimChar:
2021 // Processing a Dex `int-to-short' instruction.
2022 __ Sbfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 16);
2023 break;
2024
2025 default:
2026 LOG(FATAL) << "Unexpected type conversion from " << input_type
2027 << " to " << result_type;
2028 }
2029 break;
2030
2031 case Primitive::kPrimInt:
2032 switch (input_type) {
2033 case Primitive::kPrimLong:
2034 // Processing a Dex `long-to-int' instruction.
2035 DCHECK(out.IsRegister());
2036 if (in.IsRegisterPair()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002037 __ Mov(OutputRegister(conversion), LowRegisterFrom(in));
Scott Wakelingfe885462016-09-22 10:24:38 +01002038 } else if (in.IsDoubleStackSlot()) {
2039 GetAssembler()->LoadFromOffset(kLoadWord,
2040 OutputRegister(conversion),
2041 sp,
2042 in.GetStackIndex());
2043 } else {
2044 DCHECK(in.IsConstant());
2045 DCHECK(in.GetConstant()->IsLongConstant());
2046 int64_t value = in.GetConstant()->AsLongConstant()->GetValue();
2047 __ Mov(OutputRegister(conversion), static_cast<int32_t>(value));
2048 }
2049 break;
2050
2051 case Primitive::kPrimFloat: {
2052 // Processing a Dex `float-to-int' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002053 vixl32::SRegister temp = LowSRegisterFrom(locations->GetTemp(0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01002054 __ Vcvt(S32, F32, temp, InputSRegisterAt(conversion, 0));
Scott Wakelingfe885462016-09-22 10:24:38 +01002055 __ Vmov(OutputRegister(conversion), temp);
2056 break;
2057 }
2058
2059 case Primitive::kPrimDouble: {
2060 // Processing a Dex `double-to-int' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002061 vixl32::SRegister temp_s = LowSRegisterFrom(locations->GetTemp(0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01002062 __ Vcvt(S32, F64, temp_s, DRegisterFrom(in));
Scott Wakelingfe885462016-09-22 10:24:38 +01002063 __ Vmov(OutputRegister(conversion), temp_s);
2064 break;
2065 }
2066
2067 default:
2068 LOG(FATAL) << "Unexpected type conversion from " << input_type
2069 << " to " << result_type;
2070 }
2071 break;
2072
2073 case Primitive::kPrimLong:
2074 switch (input_type) {
2075 case Primitive::kPrimBoolean:
2076 // Boolean input is a result of code transformations.
2077 case Primitive::kPrimByte:
2078 case Primitive::kPrimShort:
2079 case Primitive::kPrimInt:
2080 case Primitive::kPrimChar:
2081 // Processing a Dex `int-to-long' instruction.
2082 DCHECK(out.IsRegisterPair());
2083 DCHECK(in.IsRegister());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002084 __ Mov(LowRegisterFrom(out), InputRegisterAt(conversion, 0));
Scott Wakelingfe885462016-09-22 10:24:38 +01002085 // Sign extension.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002086 __ Asr(HighRegisterFrom(out), LowRegisterFrom(out), 31);
Scott Wakelingfe885462016-09-22 10:24:38 +01002087 break;
2088
2089 case Primitive::kPrimFloat:
2090 // Processing a Dex `float-to-long' instruction.
2091 codegen_->InvokeRuntime(kQuickF2l, conversion, conversion->GetDexPc());
2092 CheckEntrypointTypes<kQuickF2l, int64_t, float>();
2093 break;
2094
2095 case Primitive::kPrimDouble:
2096 // Processing a Dex `double-to-long' instruction.
2097 codegen_->InvokeRuntime(kQuickD2l, conversion, conversion->GetDexPc());
2098 CheckEntrypointTypes<kQuickD2l, int64_t, double>();
2099 break;
2100
2101 default:
2102 LOG(FATAL) << "Unexpected type conversion from " << input_type
2103 << " to " << result_type;
2104 }
2105 break;
2106
2107 case Primitive::kPrimChar:
2108 switch (input_type) {
2109 case Primitive::kPrimLong:
2110 // Type conversion from long to char is a result of code transformations.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002111 __ Ubfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 16);
Scott Wakelingfe885462016-09-22 10:24:38 +01002112 break;
2113 case Primitive::kPrimBoolean:
2114 // Boolean input is a result of code transformations.
2115 case Primitive::kPrimByte:
2116 case Primitive::kPrimShort:
2117 case Primitive::kPrimInt:
2118 // Processing a Dex `int-to-char' instruction.
2119 __ Ubfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 16);
2120 break;
2121
2122 default:
2123 LOG(FATAL) << "Unexpected type conversion from " << input_type
2124 << " to " << result_type;
2125 }
2126 break;
2127
2128 case Primitive::kPrimFloat:
2129 switch (input_type) {
2130 case Primitive::kPrimBoolean:
2131 // Boolean input is a result of code transformations.
2132 case Primitive::kPrimByte:
2133 case Primitive::kPrimShort:
2134 case Primitive::kPrimInt:
2135 case Primitive::kPrimChar: {
2136 // Processing a Dex `int-to-float' instruction.
2137 __ Vmov(OutputSRegister(conversion), InputRegisterAt(conversion, 0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01002138 __ Vcvt(F32, S32, OutputSRegister(conversion), OutputSRegister(conversion));
Scott Wakelingfe885462016-09-22 10:24:38 +01002139 break;
2140 }
2141
2142 case Primitive::kPrimLong:
2143 // Processing a Dex `long-to-float' instruction.
2144 codegen_->InvokeRuntime(kQuickL2f, conversion, conversion->GetDexPc());
2145 CheckEntrypointTypes<kQuickL2f, float, int64_t>();
2146 break;
2147
2148 case Primitive::kPrimDouble:
2149 // Processing a Dex `double-to-float' instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01002150 __ Vcvt(F32, F64, OutputSRegister(conversion), DRegisterFrom(in));
Scott Wakelingfe885462016-09-22 10:24:38 +01002151 break;
2152
2153 default:
2154 LOG(FATAL) << "Unexpected type conversion from " << input_type
2155 << " to " << result_type;
2156 };
2157 break;
2158
2159 case Primitive::kPrimDouble:
2160 switch (input_type) {
2161 case Primitive::kPrimBoolean:
2162 // Boolean input is a result of code transformations.
2163 case Primitive::kPrimByte:
2164 case Primitive::kPrimShort:
2165 case Primitive::kPrimInt:
2166 case Primitive::kPrimChar: {
2167 // Processing a Dex `int-to-double' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002168 __ Vmov(LowSRegisterFrom(out), InputRegisterAt(conversion, 0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01002169 __ Vcvt(F64, S32, DRegisterFrom(out), LowSRegisterFrom(out));
Scott Wakelingfe885462016-09-22 10:24:38 +01002170 break;
2171 }
2172
2173 case Primitive::kPrimLong: {
2174 // Processing a Dex `long-to-double' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002175 vixl32::Register low = LowRegisterFrom(in);
2176 vixl32::Register high = HighRegisterFrom(in);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002177 vixl32::SRegister out_s = LowSRegisterFrom(out);
Scott Wakelingc34dba72016-10-03 10:14:44 +01002178 vixl32::DRegister out_d = DRegisterFrom(out);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002179 vixl32::SRegister temp_s = LowSRegisterFrom(locations->GetTemp(0));
Scott Wakelingc34dba72016-10-03 10:14:44 +01002180 vixl32::DRegister temp_d = DRegisterFrom(locations->GetTemp(0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01002181 vixl32::DRegister constant_d = DRegisterFrom(locations->GetTemp(1));
Scott Wakelingfe885462016-09-22 10:24:38 +01002182
2183 // temp_d = int-to-double(high)
2184 __ Vmov(temp_s, high);
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01002185 __ Vcvt(F64, S32, temp_d, temp_s);
Scott Wakelingfe885462016-09-22 10:24:38 +01002186 // constant_d = k2Pow32EncodingForDouble
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002187 __ Vmov(constant_d, bit_cast<double, int64_t>(k2Pow32EncodingForDouble));
Scott Wakelingfe885462016-09-22 10:24:38 +01002188 // out_d = unsigned-to-double(low)
2189 __ Vmov(out_s, low);
2190 __ Vcvt(F64, U32, out_d, out_s);
2191 // out_d += temp_d * constant_d
2192 __ Vmla(F64, out_d, temp_d, constant_d);
2193 break;
2194 }
2195
2196 case Primitive::kPrimFloat:
2197 // Processing a Dex `float-to-double' instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01002198 __ Vcvt(F64, F32, DRegisterFrom(out), InputSRegisterAt(conversion, 0));
Scott Wakelingfe885462016-09-22 10:24:38 +01002199 break;
2200
2201 default:
2202 LOG(FATAL) << "Unexpected type conversion from " << input_type
2203 << " to " << result_type;
2204 };
2205 break;
2206
2207 default:
2208 LOG(FATAL) << "Unexpected type conversion from " << input_type
2209 << " to " << result_type;
2210 }
2211}
2212
2213void LocationsBuilderARMVIXL::VisitAdd(HAdd* add) {
2214 LocationSummary* locations =
2215 new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall);
2216 switch (add->GetResultType()) {
2217 case Primitive::kPrimInt: {
2218 locations->SetInAt(0, Location::RequiresRegister());
2219 locations->SetInAt(1, Location::RegisterOrConstant(add->InputAt(1)));
2220 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2221 break;
2222 }
2223
Scott Wakelingfe885462016-09-22 10:24:38 +01002224 case Primitive::kPrimLong: {
2225 locations->SetInAt(0, Location::RequiresRegister());
Anton Kirilovdda43962016-11-21 19:55:20 +00002226 locations->SetInAt(1, ArmEncodableConstantOrRegister(add->InputAt(1), ADD));
Scott Wakelingfe885462016-09-22 10:24:38 +01002227 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2228 break;
2229 }
2230
2231 case Primitive::kPrimFloat:
2232 case Primitive::kPrimDouble: {
2233 locations->SetInAt(0, Location::RequiresFpuRegister());
2234 locations->SetInAt(1, Location::RequiresFpuRegister());
2235 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2236 break;
2237 }
2238
2239 default:
2240 LOG(FATAL) << "Unexpected add type " << add->GetResultType();
2241 }
2242}
2243
2244void InstructionCodeGeneratorARMVIXL::VisitAdd(HAdd* add) {
2245 LocationSummary* locations = add->GetLocations();
2246 Location out = locations->Out();
2247 Location first = locations->InAt(0);
2248 Location second = locations->InAt(1);
2249
2250 switch (add->GetResultType()) {
2251 case Primitive::kPrimInt: {
2252 __ Add(OutputRegister(add), InputRegisterAt(add, 0), InputOperandAt(add, 1));
2253 }
2254 break;
2255
Scott Wakelingfe885462016-09-22 10:24:38 +01002256 case Primitive::kPrimLong: {
Anton Kirilovdda43962016-11-21 19:55:20 +00002257 if (second.IsConstant()) {
2258 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
2259 GenerateAddLongConst(out, first, value);
2260 } else {
2261 DCHECK(second.IsRegisterPair());
2262 __ Adds(LowRegisterFrom(out), LowRegisterFrom(first), LowRegisterFrom(second));
2263 __ Adc(HighRegisterFrom(out), HighRegisterFrom(first), HighRegisterFrom(second));
2264 }
Scott Wakelingfe885462016-09-22 10:24:38 +01002265 break;
2266 }
2267
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002268 case Primitive::kPrimFloat:
Scott Wakelingfe885462016-09-22 10:24:38 +01002269 case Primitive::kPrimDouble:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002270 __ Vadd(OutputVRegister(add), InputVRegisterAt(add, 0), InputVRegisterAt(add, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01002271 break;
2272
2273 default:
2274 LOG(FATAL) << "Unexpected add type " << add->GetResultType();
2275 }
2276}
2277
2278void LocationsBuilderARMVIXL::VisitSub(HSub* sub) {
2279 LocationSummary* locations =
2280 new (GetGraph()->GetArena()) LocationSummary(sub, LocationSummary::kNoCall);
2281 switch (sub->GetResultType()) {
2282 case Primitive::kPrimInt: {
2283 locations->SetInAt(0, Location::RequiresRegister());
2284 locations->SetInAt(1, Location::RegisterOrConstant(sub->InputAt(1)));
2285 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2286 break;
2287 }
2288
Scott Wakelingfe885462016-09-22 10:24:38 +01002289 case Primitive::kPrimLong: {
2290 locations->SetInAt(0, Location::RequiresRegister());
Anton Kirilovdda43962016-11-21 19:55:20 +00002291 locations->SetInAt(1, ArmEncodableConstantOrRegister(sub->InputAt(1), SUB));
Scott Wakelingfe885462016-09-22 10:24:38 +01002292 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2293 break;
2294 }
2295 case Primitive::kPrimFloat:
2296 case Primitive::kPrimDouble: {
2297 locations->SetInAt(0, Location::RequiresFpuRegister());
2298 locations->SetInAt(1, Location::RequiresFpuRegister());
2299 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2300 break;
2301 }
2302 default:
2303 LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
2304 }
2305}
2306
2307void InstructionCodeGeneratorARMVIXL::VisitSub(HSub* sub) {
2308 LocationSummary* locations = sub->GetLocations();
2309 Location out = locations->Out();
2310 Location first = locations->InAt(0);
2311 Location second = locations->InAt(1);
2312 switch (sub->GetResultType()) {
2313 case Primitive::kPrimInt: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002314 __ Sub(OutputRegister(sub), InputRegisterAt(sub, 0), InputOperandAt(sub, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01002315 break;
2316 }
2317
Scott Wakelingfe885462016-09-22 10:24:38 +01002318 case Primitive::kPrimLong: {
Anton Kirilovdda43962016-11-21 19:55:20 +00002319 if (second.IsConstant()) {
2320 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
2321 GenerateAddLongConst(out, first, -value);
2322 } else {
2323 DCHECK(second.IsRegisterPair());
2324 __ Subs(LowRegisterFrom(out), LowRegisterFrom(first), LowRegisterFrom(second));
2325 __ Sbc(HighRegisterFrom(out), HighRegisterFrom(first), HighRegisterFrom(second));
2326 }
Scott Wakelingfe885462016-09-22 10:24:38 +01002327 break;
2328 }
2329
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002330 case Primitive::kPrimFloat:
2331 case Primitive::kPrimDouble:
2332 __ Vsub(OutputVRegister(sub), InputVRegisterAt(sub, 0), InputVRegisterAt(sub, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01002333 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01002334
2335 default:
2336 LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
2337 }
2338}
2339
2340void LocationsBuilderARMVIXL::VisitMul(HMul* mul) {
2341 LocationSummary* locations =
2342 new (GetGraph()->GetArena()) LocationSummary(mul, LocationSummary::kNoCall);
2343 switch (mul->GetResultType()) {
2344 case Primitive::kPrimInt:
2345 case Primitive::kPrimLong: {
2346 locations->SetInAt(0, Location::RequiresRegister());
2347 locations->SetInAt(1, Location::RequiresRegister());
2348 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2349 break;
2350 }
2351
2352 case Primitive::kPrimFloat:
2353 case Primitive::kPrimDouble: {
2354 locations->SetInAt(0, Location::RequiresFpuRegister());
2355 locations->SetInAt(1, Location::RequiresFpuRegister());
2356 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2357 break;
2358 }
2359
2360 default:
2361 LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
2362 }
2363}
2364
2365void InstructionCodeGeneratorARMVIXL::VisitMul(HMul* mul) {
2366 LocationSummary* locations = mul->GetLocations();
2367 Location out = locations->Out();
2368 Location first = locations->InAt(0);
2369 Location second = locations->InAt(1);
2370 switch (mul->GetResultType()) {
2371 case Primitive::kPrimInt: {
2372 __ Mul(OutputRegister(mul), InputRegisterAt(mul, 0), InputRegisterAt(mul, 1));
2373 break;
2374 }
2375 case Primitive::kPrimLong: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002376 vixl32::Register out_hi = HighRegisterFrom(out);
2377 vixl32::Register out_lo = LowRegisterFrom(out);
2378 vixl32::Register in1_hi = HighRegisterFrom(first);
2379 vixl32::Register in1_lo = LowRegisterFrom(first);
2380 vixl32::Register in2_hi = HighRegisterFrom(second);
2381 vixl32::Register in2_lo = LowRegisterFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01002382
2383 // Extra checks to protect caused by the existence of R1_R2.
2384 // The algorithm is wrong if out.hi is either in1.lo or in2.lo:
2385 // (e.g. in1=r0_r1, in2=r2_r3 and out=r1_r2);
2386 DCHECK_NE(out_hi.GetCode(), in1_lo.GetCode());
2387 DCHECK_NE(out_hi.GetCode(), in2_lo.GetCode());
2388
2389 // input: in1 - 64 bits, in2 - 64 bits
2390 // output: out
2391 // formula: out.hi : out.lo = (in1.lo * in2.hi + in1.hi * in2.lo)* 2^32 + in1.lo * in2.lo
2392 // parts: out.hi = in1.lo * in2.hi + in1.hi * in2.lo + (in1.lo * in2.lo)[63:32]
2393 // parts: out.lo = (in1.lo * in2.lo)[31:0]
2394
2395 UseScratchRegisterScope temps(GetVIXLAssembler());
2396 vixl32::Register temp = temps.Acquire();
2397 // temp <- in1.lo * in2.hi
2398 __ Mul(temp, in1_lo, in2_hi);
2399 // out.hi <- in1.lo * in2.hi + in1.hi * in2.lo
2400 __ Mla(out_hi, in1_hi, in2_lo, temp);
2401 // out.lo <- (in1.lo * in2.lo)[31:0];
2402 __ Umull(out_lo, temp, in1_lo, in2_lo);
2403 // out.hi <- in2.hi * in1.lo + in2.lo * in1.hi + (in1.lo * in2.lo)[63:32]
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002404 __ Add(out_hi, out_hi, temp);
Scott Wakelingfe885462016-09-22 10:24:38 +01002405 break;
2406 }
2407
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002408 case Primitive::kPrimFloat:
2409 case Primitive::kPrimDouble:
2410 __ Vmul(OutputVRegister(mul), InputVRegisterAt(mul, 0), InputVRegisterAt(mul, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01002411 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01002412
2413 default:
2414 LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
2415 }
2416}
2417
Scott Wakelingfe885462016-09-22 10:24:38 +01002418void InstructionCodeGeneratorARMVIXL::DivRemOneOrMinusOne(HBinaryOperation* instruction) {
2419 DCHECK(instruction->IsDiv() || instruction->IsRem());
2420 DCHECK(instruction->GetResultType() == Primitive::kPrimInt);
2421
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002422 Location second = instruction->GetLocations()->InAt(1);
Scott Wakelingfe885462016-09-22 10:24:38 +01002423 DCHECK(second.IsConstant());
2424
2425 vixl32::Register out = OutputRegister(instruction);
2426 vixl32::Register dividend = InputRegisterAt(instruction, 0);
2427 int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
2428 DCHECK(imm == 1 || imm == -1);
2429
2430 if (instruction->IsRem()) {
2431 __ Mov(out, 0);
2432 } else {
2433 if (imm == 1) {
2434 __ Mov(out, dividend);
2435 } else {
2436 __ Rsb(out, dividend, 0);
2437 }
2438 }
2439}
2440
2441void InstructionCodeGeneratorARMVIXL::DivRemByPowerOfTwo(HBinaryOperation* instruction) {
2442 DCHECK(instruction->IsDiv() || instruction->IsRem());
2443 DCHECK(instruction->GetResultType() == Primitive::kPrimInt);
2444
2445 LocationSummary* locations = instruction->GetLocations();
2446 Location second = locations->InAt(1);
2447 DCHECK(second.IsConstant());
2448
2449 vixl32::Register out = OutputRegister(instruction);
2450 vixl32::Register dividend = InputRegisterAt(instruction, 0);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002451 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
Scott Wakelingfe885462016-09-22 10:24:38 +01002452 int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
2453 uint32_t abs_imm = static_cast<uint32_t>(AbsOrMin(imm));
2454 int ctz_imm = CTZ(abs_imm);
2455
2456 if (ctz_imm == 1) {
2457 __ Lsr(temp, dividend, 32 - ctz_imm);
2458 } else {
2459 __ Asr(temp, dividend, 31);
2460 __ Lsr(temp, temp, 32 - ctz_imm);
2461 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002462 __ Add(out, temp, dividend);
Scott Wakelingfe885462016-09-22 10:24:38 +01002463
2464 if (instruction->IsDiv()) {
2465 __ Asr(out, out, ctz_imm);
2466 if (imm < 0) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002467 __ Rsb(out, out, 0);
Scott Wakelingfe885462016-09-22 10:24:38 +01002468 }
2469 } else {
2470 __ Ubfx(out, out, 0, ctz_imm);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002471 __ Sub(out, out, temp);
Scott Wakelingfe885462016-09-22 10:24:38 +01002472 }
2473}
2474
2475void InstructionCodeGeneratorARMVIXL::GenerateDivRemWithAnyConstant(HBinaryOperation* instruction) {
2476 DCHECK(instruction->IsDiv() || instruction->IsRem());
2477 DCHECK(instruction->GetResultType() == Primitive::kPrimInt);
2478
2479 LocationSummary* locations = instruction->GetLocations();
2480 Location second = locations->InAt(1);
2481 DCHECK(second.IsConstant());
2482
2483 vixl32::Register out = OutputRegister(instruction);
2484 vixl32::Register dividend = InputRegisterAt(instruction, 0);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002485 vixl32::Register temp1 = RegisterFrom(locations->GetTemp(0));
2486 vixl32::Register temp2 = RegisterFrom(locations->GetTemp(1));
Scott Wakelingb77051e2016-11-21 19:46:00 +00002487 int32_t imm = Int32ConstantFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01002488
2489 int64_t magic;
2490 int shift;
2491 CalculateMagicAndShiftForDivRem(imm, false /* is_long */, &magic, &shift);
2492
Anton Kirilovdda43962016-11-21 19:55:20 +00002493 // TODO(VIXL): Change the static cast to Operand::From() after VIXL is fixed.
2494 __ Mov(temp1, static_cast<int32_t>(magic));
Scott Wakelingfe885462016-09-22 10:24:38 +01002495 __ Smull(temp2, temp1, dividend, temp1);
2496
2497 if (imm > 0 && magic < 0) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002498 __ Add(temp1, temp1, dividend);
Scott Wakelingfe885462016-09-22 10:24:38 +01002499 } else if (imm < 0 && magic > 0) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002500 __ Sub(temp1, temp1, dividend);
Scott Wakelingfe885462016-09-22 10:24:38 +01002501 }
2502
2503 if (shift != 0) {
2504 __ Asr(temp1, temp1, shift);
2505 }
2506
2507 if (instruction->IsDiv()) {
2508 __ Sub(out, temp1, Operand(temp1, vixl32::Shift(ASR), 31));
2509 } else {
2510 __ Sub(temp1, temp1, Operand(temp1, vixl32::Shift(ASR), 31));
2511 // TODO: Strength reduction for mls.
2512 __ Mov(temp2, imm);
2513 __ Mls(out, temp1, temp2, dividend);
2514 }
2515}
2516
2517void InstructionCodeGeneratorARMVIXL::GenerateDivRemConstantIntegral(
2518 HBinaryOperation* instruction) {
2519 DCHECK(instruction->IsDiv() || instruction->IsRem());
2520 DCHECK(instruction->GetResultType() == Primitive::kPrimInt);
2521
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002522 Location second = instruction->GetLocations()->InAt(1);
Scott Wakelingfe885462016-09-22 10:24:38 +01002523 DCHECK(second.IsConstant());
2524
2525 int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
2526 if (imm == 0) {
2527 // Do not generate anything. DivZeroCheck would prevent any code to be executed.
2528 } else if (imm == 1 || imm == -1) {
2529 DivRemOneOrMinusOne(instruction);
2530 } else if (IsPowerOfTwo(AbsOrMin(imm))) {
2531 DivRemByPowerOfTwo(instruction);
2532 } else {
2533 DCHECK(imm <= -2 || imm >= 2);
2534 GenerateDivRemWithAnyConstant(instruction);
2535 }
2536}
2537
2538void LocationsBuilderARMVIXL::VisitDiv(HDiv* div) {
2539 LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
2540 if (div->GetResultType() == Primitive::kPrimLong) {
2541 // pLdiv runtime call.
2542 call_kind = LocationSummary::kCallOnMainOnly;
2543 } else if (div->GetResultType() == Primitive::kPrimInt && div->InputAt(1)->IsConstant()) {
2544 // sdiv will be replaced by other instruction sequence.
2545 } else if (div->GetResultType() == Primitive::kPrimInt &&
2546 !codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
2547 // pIdivmod runtime call.
2548 call_kind = LocationSummary::kCallOnMainOnly;
2549 }
2550
2551 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(div, call_kind);
2552
2553 switch (div->GetResultType()) {
2554 case Primitive::kPrimInt: {
2555 if (div->InputAt(1)->IsConstant()) {
2556 locations->SetInAt(0, Location::RequiresRegister());
2557 locations->SetInAt(1, Location::ConstantLocation(div->InputAt(1)->AsConstant()));
2558 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2559 int32_t value = div->InputAt(1)->AsIntConstant()->GetValue();
2560 if (value == 1 || value == 0 || value == -1) {
2561 // No temp register required.
2562 } else {
2563 locations->AddTemp(Location::RequiresRegister());
2564 if (!IsPowerOfTwo(AbsOrMin(value))) {
2565 locations->AddTemp(Location::RequiresRegister());
2566 }
2567 }
2568 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
2569 locations->SetInAt(0, Location::RequiresRegister());
2570 locations->SetInAt(1, Location::RequiresRegister());
2571 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2572 } else {
Artem Serov551b28f2016-10-18 19:11:30 +01002573 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2574 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
2575 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
2576 // Note: divrem will compute both the quotient and the remainder as the pair R0 and R1, but
2577 // we only need the former.
2578 locations->SetOut(LocationFrom(r0));
Scott Wakelingfe885462016-09-22 10:24:38 +01002579 }
2580 break;
2581 }
2582 case Primitive::kPrimLong: {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01002583 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2584 locations->SetInAt(0, LocationFrom(
2585 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
2586 locations->SetInAt(1, LocationFrom(
2587 calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3)));
2588 locations->SetOut(LocationFrom(r0, r1));
Scott Wakelingfe885462016-09-22 10:24:38 +01002589 break;
2590 }
2591 case Primitive::kPrimFloat:
2592 case Primitive::kPrimDouble: {
2593 locations->SetInAt(0, Location::RequiresFpuRegister());
2594 locations->SetInAt(1, Location::RequiresFpuRegister());
2595 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2596 break;
2597 }
2598
2599 default:
2600 LOG(FATAL) << "Unexpected div type " << div->GetResultType();
2601 }
2602}
2603
2604void InstructionCodeGeneratorARMVIXL::VisitDiv(HDiv* div) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01002605 Location lhs = div->GetLocations()->InAt(0);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002606 Location rhs = div->GetLocations()->InAt(1);
Scott Wakelingfe885462016-09-22 10:24:38 +01002607
2608 switch (div->GetResultType()) {
2609 case Primitive::kPrimInt: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002610 if (rhs.IsConstant()) {
Scott Wakelingfe885462016-09-22 10:24:38 +01002611 GenerateDivRemConstantIntegral(div);
2612 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
2613 __ Sdiv(OutputRegister(div), InputRegisterAt(div, 0), InputRegisterAt(div, 1));
2614 } else {
Artem Serov551b28f2016-10-18 19:11:30 +01002615 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2616 DCHECK(calling_convention.GetRegisterAt(0).Is(RegisterFrom(lhs)));
2617 DCHECK(calling_convention.GetRegisterAt(1).Is(RegisterFrom(rhs)));
2618 DCHECK(r0.Is(OutputRegister(div)));
2619
2620 codegen_->InvokeRuntime(kQuickIdivmod, div, div->GetDexPc());
2621 CheckEntrypointTypes<kQuickIdivmod, int32_t, int32_t, int32_t>();
Scott Wakelingfe885462016-09-22 10:24:38 +01002622 }
2623 break;
2624 }
2625
2626 case Primitive::kPrimLong: {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01002627 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2628 DCHECK(calling_convention.GetRegisterAt(0).Is(LowRegisterFrom(lhs)));
2629 DCHECK(calling_convention.GetRegisterAt(1).Is(HighRegisterFrom(lhs)));
2630 DCHECK(calling_convention.GetRegisterAt(2).Is(LowRegisterFrom(rhs)));
2631 DCHECK(calling_convention.GetRegisterAt(3).Is(HighRegisterFrom(rhs)));
2632 DCHECK(LowRegisterFrom(div->GetLocations()->Out()).Is(r0));
2633 DCHECK(HighRegisterFrom(div->GetLocations()->Out()).Is(r1));
2634
2635 codegen_->InvokeRuntime(kQuickLdiv, div, div->GetDexPc());
2636 CheckEntrypointTypes<kQuickLdiv, int64_t, int64_t, int64_t>();
Scott Wakelingfe885462016-09-22 10:24:38 +01002637 break;
2638 }
2639
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002640 case Primitive::kPrimFloat:
2641 case Primitive::kPrimDouble:
2642 __ Vdiv(OutputVRegister(div), InputVRegisterAt(div, 0), InputVRegisterAt(div, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01002643 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01002644
2645 default:
2646 LOG(FATAL) << "Unexpected div type " << div->GetResultType();
2647 }
2648}
2649
Artem Serov551b28f2016-10-18 19:11:30 +01002650void LocationsBuilderARMVIXL::VisitRem(HRem* rem) {
2651 Primitive::Type type = rem->GetResultType();
2652
2653 // Most remainders are implemented in the runtime.
2654 LocationSummary::CallKind call_kind = LocationSummary::kCallOnMainOnly;
2655 if (rem->GetResultType() == Primitive::kPrimInt && rem->InputAt(1)->IsConstant()) {
2656 // sdiv will be replaced by other instruction sequence.
2657 call_kind = LocationSummary::kNoCall;
2658 } else if ((rem->GetResultType() == Primitive::kPrimInt)
2659 && codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
2660 // Have hardware divide instruction for int, do it with three instructions.
2661 call_kind = LocationSummary::kNoCall;
2662 }
2663
2664 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(rem, call_kind);
2665
2666 switch (type) {
2667 case Primitive::kPrimInt: {
2668 if (rem->InputAt(1)->IsConstant()) {
2669 locations->SetInAt(0, Location::RequiresRegister());
2670 locations->SetInAt(1, Location::ConstantLocation(rem->InputAt(1)->AsConstant()));
2671 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2672 int32_t value = rem->InputAt(1)->AsIntConstant()->GetValue();
2673 if (value == 1 || value == 0 || value == -1) {
2674 // No temp register required.
2675 } else {
2676 locations->AddTemp(Location::RequiresRegister());
2677 if (!IsPowerOfTwo(AbsOrMin(value))) {
2678 locations->AddTemp(Location::RequiresRegister());
2679 }
2680 }
2681 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
2682 locations->SetInAt(0, Location::RequiresRegister());
2683 locations->SetInAt(1, Location::RequiresRegister());
2684 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2685 locations->AddTemp(Location::RequiresRegister());
2686 } else {
2687 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2688 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
2689 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
2690 // Note: divrem will compute both the quotient and the remainder as the pair R0 and R1, but
2691 // we only need the latter.
2692 locations->SetOut(LocationFrom(r1));
2693 }
2694 break;
2695 }
2696 case Primitive::kPrimLong: {
2697 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2698 locations->SetInAt(0, LocationFrom(
2699 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
2700 locations->SetInAt(1, LocationFrom(
2701 calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3)));
2702 // The runtime helper puts the output in R2,R3.
2703 locations->SetOut(LocationFrom(r2, r3));
2704 break;
2705 }
2706 case Primitive::kPrimFloat: {
2707 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2708 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
2709 locations->SetInAt(1, LocationFrom(calling_convention.GetFpuRegisterAt(1)));
2710 locations->SetOut(LocationFrom(s0));
2711 break;
2712 }
2713
2714 case Primitive::kPrimDouble: {
2715 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2716 locations->SetInAt(0, LocationFrom(
2717 calling_convention.GetFpuRegisterAt(0), calling_convention.GetFpuRegisterAt(1)));
2718 locations->SetInAt(1, LocationFrom(
2719 calling_convention.GetFpuRegisterAt(2), calling_convention.GetFpuRegisterAt(3)));
2720 locations->SetOut(LocationFrom(s0, s1));
2721 break;
2722 }
2723
2724 default:
2725 LOG(FATAL) << "Unexpected rem type " << type;
2726 }
2727}
2728
2729void InstructionCodeGeneratorARMVIXL::VisitRem(HRem* rem) {
2730 LocationSummary* locations = rem->GetLocations();
2731 Location second = locations->InAt(1);
2732
2733 Primitive::Type type = rem->GetResultType();
2734 switch (type) {
2735 case Primitive::kPrimInt: {
2736 vixl32::Register reg1 = InputRegisterAt(rem, 0);
2737 vixl32::Register out_reg = OutputRegister(rem);
2738 if (second.IsConstant()) {
2739 GenerateDivRemConstantIntegral(rem);
2740 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
2741 vixl32::Register reg2 = RegisterFrom(second);
2742 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
2743
2744 // temp = reg1 / reg2 (integer division)
2745 // dest = reg1 - temp * reg2
2746 __ Sdiv(temp, reg1, reg2);
2747 __ Mls(out_reg, temp, reg2, reg1);
2748 } else {
2749 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2750 DCHECK(reg1.Is(calling_convention.GetRegisterAt(0)));
2751 DCHECK(RegisterFrom(second).Is(calling_convention.GetRegisterAt(1)));
2752 DCHECK(out_reg.Is(r1));
2753
2754 codegen_->InvokeRuntime(kQuickIdivmod, rem, rem->GetDexPc());
2755 CheckEntrypointTypes<kQuickIdivmod, int32_t, int32_t, int32_t>();
2756 }
2757 break;
2758 }
2759
2760 case Primitive::kPrimLong: {
2761 codegen_->InvokeRuntime(kQuickLmod, rem, rem->GetDexPc());
2762 CheckEntrypointTypes<kQuickLmod, int64_t, int64_t, int64_t>();
2763 break;
2764 }
2765
2766 case Primitive::kPrimFloat: {
2767 codegen_->InvokeRuntime(kQuickFmodf, rem, rem->GetDexPc());
2768 CheckEntrypointTypes<kQuickFmodf, float, float, float>();
2769 break;
2770 }
2771
2772 case Primitive::kPrimDouble: {
2773 codegen_->InvokeRuntime(kQuickFmod, rem, rem->GetDexPc());
2774 CheckEntrypointTypes<kQuickFmod, double, double, double>();
2775 break;
2776 }
2777
2778 default:
2779 LOG(FATAL) << "Unexpected rem type " << type;
2780 }
2781}
2782
2783
Scott Wakelingfe885462016-09-22 10:24:38 +01002784void LocationsBuilderARMVIXL::VisitDivZeroCheck(HDivZeroCheck* instruction) {
Artem Serov657022c2016-11-23 14:19:38 +00002785 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
Scott Wakelingfe885462016-09-22 10:24:38 +01002786 locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0)));
Scott Wakelingfe885462016-09-22 10:24:38 +01002787}
2788
2789void InstructionCodeGeneratorARMVIXL::VisitDivZeroCheck(HDivZeroCheck* instruction) {
2790 DivZeroCheckSlowPathARMVIXL* slow_path =
2791 new (GetGraph()->GetArena()) DivZeroCheckSlowPathARMVIXL(instruction);
2792 codegen_->AddSlowPath(slow_path);
2793
2794 LocationSummary* locations = instruction->GetLocations();
2795 Location value = locations->InAt(0);
2796
2797 switch (instruction->GetType()) {
2798 case Primitive::kPrimBoolean:
2799 case Primitive::kPrimByte:
2800 case Primitive::kPrimChar:
2801 case Primitive::kPrimShort:
2802 case Primitive::kPrimInt: {
2803 if (value.IsRegister()) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00002804 __ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
Scott Wakelingfe885462016-09-22 10:24:38 +01002805 } else {
2806 DCHECK(value.IsConstant()) << value;
2807 if (value.GetConstant()->AsIntConstant()->GetValue() == 0) {
2808 __ B(slow_path->GetEntryLabel());
2809 }
2810 }
2811 break;
2812 }
2813 case Primitive::kPrimLong: {
2814 if (value.IsRegisterPair()) {
2815 UseScratchRegisterScope temps(GetVIXLAssembler());
2816 vixl32::Register temp = temps.Acquire();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002817 __ Orrs(temp, LowRegisterFrom(value), HighRegisterFrom(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01002818 __ B(eq, slow_path->GetEntryLabel());
2819 } else {
2820 DCHECK(value.IsConstant()) << value;
2821 if (value.GetConstant()->AsLongConstant()->GetValue() == 0) {
2822 __ B(slow_path->GetEntryLabel());
2823 }
2824 }
2825 break;
2826 }
2827 default:
2828 LOG(FATAL) << "Unexpected type for HDivZeroCheck " << instruction->GetType();
2829 }
2830}
2831
Artem Serov02109dd2016-09-23 17:17:54 +01002832void InstructionCodeGeneratorARMVIXL::HandleIntegerRotate(HRor* ror) {
2833 LocationSummary* locations = ror->GetLocations();
2834 vixl32::Register in = InputRegisterAt(ror, 0);
2835 Location rhs = locations->InAt(1);
2836 vixl32::Register out = OutputRegister(ror);
2837
2838 if (rhs.IsConstant()) {
2839 // Arm32 and Thumb2 assemblers require a rotation on the interval [1,31],
2840 // so map all rotations to a +ve. equivalent in that range.
2841 // (e.g. left *or* right by -2 bits == 30 bits in the same direction.)
2842 uint32_t rot = CodeGenerator::GetInt32ValueOf(rhs.GetConstant()) & 0x1F;
2843 if (rot) {
2844 // Rotate, mapping left rotations to right equivalents if necessary.
2845 // (e.g. left by 2 bits == right by 30.)
2846 __ Ror(out, in, rot);
2847 } else if (!out.Is(in)) {
2848 __ Mov(out, in);
2849 }
2850 } else {
2851 __ Ror(out, in, RegisterFrom(rhs));
2852 }
2853}
2854
2855// Gain some speed by mapping all Long rotates onto equivalent pairs of Integer
2856// rotates by swapping input regs (effectively rotating by the first 32-bits of
2857// a larger rotation) or flipping direction (thus treating larger right/left
2858// rotations as sub-word sized rotations in the other direction) as appropriate.
2859void InstructionCodeGeneratorARMVIXL::HandleLongRotate(HRor* ror) {
2860 LocationSummary* locations = ror->GetLocations();
2861 vixl32::Register in_reg_lo = LowRegisterFrom(locations->InAt(0));
2862 vixl32::Register in_reg_hi = HighRegisterFrom(locations->InAt(0));
2863 Location rhs = locations->InAt(1);
2864 vixl32::Register out_reg_lo = LowRegisterFrom(locations->Out());
2865 vixl32::Register out_reg_hi = HighRegisterFrom(locations->Out());
2866
2867 if (rhs.IsConstant()) {
2868 uint64_t rot = CodeGenerator::GetInt64ValueOf(rhs.GetConstant());
2869 // Map all rotations to +ve. equivalents on the interval [0,63].
2870 rot &= kMaxLongShiftDistance;
2871 // For rotates over a word in size, 'pre-rotate' by 32-bits to keep rotate
2872 // logic below to a simple pair of binary orr.
2873 // (e.g. 34 bits == in_reg swap + 2 bits right.)
2874 if (rot >= kArmBitsPerWord) {
2875 rot -= kArmBitsPerWord;
2876 std::swap(in_reg_hi, in_reg_lo);
2877 }
2878 // Rotate, or mov to out for zero or word size rotations.
2879 if (rot != 0u) {
Scott Wakelingb77051e2016-11-21 19:46:00 +00002880 __ Lsr(out_reg_hi, in_reg_hi, Operand::From(rot));
Artem Serov02109dd2016-09-23 17:17:54 +01002881 __ Orr(out_reg_hi, out_reg_hi, Operand(in_reg_lo, ShiftType::LSL, kArmBitsPerWord - rot));
Scott Wakelingb77051e2016-11-21 19:46:00 +00002882 __ Lsr(out_reg_lo, in_reg_lo, Operand::From(rot));
Artem Serov02109dd2016-09-23 17:17:54 +01002883 __ Orr(out_reg_lo, out_reg_lo, Operand(in_reg_hi, ShiftType::LSL, kArmBitsPerWord - rot));
2884 } else {
2885 __ Mov(out_reg_lo, in_reg_lo);
2886 __ Mov(out_reg_hi, in_reg_hi);
2887 }
2888 } else {
2889 vixl32::Register shift_right = RegisterFrom(locations->GetTemp(0));
2890 vixl32::Register shift_left = RegisterFrom(locations->GetTemp(1));
2891 vixl32::Label end;
2892 vixl32::Label shift_by_32_plus_shift_right;
2893
2894 __ And(shift_right, RegisterFrom(rhs), 0x1F);
2895 __ Lsrs(shift_left, RegisterFrom(rhs), 6);
2896 // TODO(VIXL): Check that flags are kept after "vixl32::LeaveFlags" enabled.
Scott Wakelingb77051e2016-11-21 19:46:00 +00002897 __ Rsb(shift_left, shift_right, Operand::From(kArmBitsPerWord));
Artem Serov02109dd2016-09-23 17:17:54 +01002898 __ B(cc, &shift_by_32_plus_shift_right);
2899
2900 // out_reg_hi = (reg_hi << shift_left) | (reg_lo >> shift_right).
2901 // out_reg_lo = (reg_lo << shift_left) | (reg_hi >> shift_right).
2902 __ Lsl(out_reg_hi, in_reg_hi, shift_left);
2903 __ Lsr(out_reg_lo, in_reg_lo, shift_right);
2904 __ Add(out_reg_hi, out_reg_hi, out_reg_lo);
2905 __ Lsl(out_reg_lo, in_reg_lo, shift_left);
2906 __ Lsr(shift_left, in_reg_hi, shift_right);
2907 __ Add(out_reg_lo, out_reg_lo, shift_left);
2908 __ B(&end);
2909
2910 __ Bind(&shift_by_32_plus_shift_right); // Shift by 32+shift_right.
2911 // out_reg_hi = (reg_hi >> shift_right) | (reg_lo << shift_left).
2912 // out_reg_lo = (reg_lo >> shift_right) | (reg_hi << shift_left).
2913 __ Lsr(out_reg_hi, in_reg_hi, shift_right);
2914 __ Lsl(out_reg_lo, in_reg_lo, shift_left);
2915 __ Add(out_reg_hi, out_reg_hi, out_reg_lo);
2916 __ Lsr(out_reg_lo, in_reg_lo, shift_right);
2917 __ Lsl(shift_right, in_reg_hi, shift_left);
2918 __ Add(out_reg_lo, out_reg_lo, shift_right);
2919
2920 __ Bind(&end);
2921 }
2922}
2923
2924void LocationsBuilderARMVIXL::VisitRor(HRor* ror) {
2925 LocationSummary* locations =
2926 new (GetGraph()->GetArena()) LocationSummary(ror, LocationSummary::kNoCall);
2927 switch (ror->GetResultType()) {
2928 case Primitive::kPrimInt: {
2929 locations->SetInAt(0, Location::RequiresRegister());
2930 locations->SetInAt(1, Location::RegisterOrConstant(ror->InputAt(1)));
2931 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2932 break;
2933 }
2934 case Primitive::kPrimLong: {
2935 locations->SetInAt(0, Location::RequiresRegister());
2936 if (ror->InputAt(1)->IsConstant()) {
2937 locations->SetInAt(1, Location::ConstantLocation(ror->InputAt(1)->AsConstant()));
2938 } else {
2939 locations->SetInAt(1, Location::RequiresRegister());
2940 locations->AddTemp(Location::RequiresRegister());
2941 locations->AddTemp(Location::RequiresRegister());
2942 }
2943 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
2944 break;
2945 }
2946 default:
2947 LOG(FATAL) << "Unexpected operation type " << ror->GetResultType();
2948 }
2949}
2950
2951void InstructionCodeGeneratorARMVIXL::VisitRor(HRor* ror) {
2952 Primitive::Type type = ror->GetResultType();
2953 switch (type) {
2954 case Primitive::kPrimInt: {
2955 HandleIntegerRotate(ror);
2956 break;
2957 }
2958 case Primitive::kPrimLong: {
2959 HandleLongRotate(ror);
2960 break;
2961 }
2962 default:
2963 LOG(FATAL) << "Unexpected operation type " << type;
2964 UNREACHABLE();
2965 }
2966}
2967
Artem Serov02d37832016-10-25 15:25:33 +01002968void LocationsBuilderARMVIXL::HandleShift(HBinaryOperation* op) {
2969 DCHECK(op->IsShl() || op->IsShr() || op->IsUShr());
2970
2971 LocationSummary* locations =
2972 new (GetGraph()->GetArena()) LocationSummary(op, LocationSummary::kNoCall);
2973
2974 switch (op->GetResultType()) {
2975 case Primitive::kPrimInt: {
2976 locations->SetInAt(0, Location::RequiresRegister());
2977 if (op->InputAt(1)->IsConstant()) {
2978 locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant()));
2979 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2980 } else {
2981 locations->SetInAt(1, Location::RequiresRegister());
2982 // Make the output overlap, as it will be used to hold the masked
2983 // second input.
2984 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
2985 }
2986 break;
2987 }
2988 case Primitive::kPrimLong: {
2989 locations->SetInAt(0, Location::RequiresRegister());
2990 if (op->InputAt(1)->IsConstant()) {
2991 locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant()));
2992 // For simplicity, use kOutputOverlap even though we only require that low registers
2993 // don't clash with high registers which the register allocator currently guarantees.
2994 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
2995 } else {
2996 locations->SetInAt(1, Location::RequiresRegister());
2997 locations->AddTemp(Location::RequiresRegister());
2998 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
2999 }
3000 break;
3001 }
3002 default:
3003 LOG(FATAL) << "Unexpected operation type " << op->GetResultType();
3004 }
3005}
3006
3007void InstructionCodeGeneratorARMVIXL::HandleShift(HBinaryOperation* op) {
3008 DCHECK(op->IsShl() || op->IsShr() || op->IsUShr());
3009
3010 LocationSummary* locations = op->GetLocations();
3011 Location out = locations->Out();
3012 Location first = locations->InAt(0);
3013 Location second = locations->InAt(1);
3014
3015 Primitive::Type type = op->GetResultType();
3016 switch (type) {
3017 case Primitive::kPrimInt: {
3018 vixl32::Register out_reg = OutputRegister(op);
3019 vixl32::Register first_reg = InputRegisterAt(op, 0);
3020 if (second.IsRegister()) {
3021 vixl32::Register second_reg = RegisterFrom(second);
3022 // ARM doesn't mask the shift count so we need to do it ourselves.
3023 __ And(out_reg, second_reg, kMaxIntShiftDistance);
3024 if (op->IsShl()) {
3025 __ Lsl(out_reg, first_reg, out_reg);
3026 } else if (op->IsShr()) {
3027 __ Asr(out_reg, first_reg, out_reg);
3028 } else {
3029 __ Lsr(out_reg, first_reg, out_reg);
3030 }
3031 } else {
3032 int32_t cst = second.GetConstant()->AsIntConstant()->GetValue();
3033 uint32_t shift_value = cst & kMaxIntShiftDistance;
3034 if (shift_value == 0) { // ARM does not support shifting with 0 immediate.
3035 __ Mov(out_reg, first_reg);
3036 } else if (op->IsShl()) {
3037 __ Lsl(out_reg, first_reg, shift_value);
3038 } else if (op->IsShr()) {
3039 __ Asr(out_reg, first_reg, shift_value);
3040 } else {
3041 __ Lsr(out_reg, first_reg, shift_value);
3042 }
3043 }
3044 break;
3045 }
3046 case Primitive::kPrimLong: {
3047 vixl32::Register o_h = HighRegisterFrom(out);
3048 vixl32::Register o_l = LowRegisterFrom(out);
3049
3050 vixl32::Register high = HighRegisterFrom(first);
3051 vixl32::Register low = LowRegisterFrom(first);
3052
3053 if (second.IsRegister()) {
3054 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
3055
3056 vixl32::Register second_reg = RegisterFrom(second);
3057
3058 if (op->IsShl()) {
3059 __ And(o_l, second_reg, kMaxLongShiftDistance);
3060 // Shift the high part
3061 __ Lsl(o_h, high, o_l);
3062 // Shift the low part and `or` what overflew on the high part
Scott Wakelingb77051e2016-11-21 19:46:00 +00003063 __ Rsb(temp, o_l, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01003064 __ Lsr(temp, low, temp);
3065 __ Orr(o_h, o_h, temp);
3066 // If the shift is > 32 bits, override the high part
Scott Wakelingb77051e2016-11-21 19:46:00 +00003067 __ Subs(temp, o_l, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01003068 {
3069 AssemblerAccurateScope guard(GetVIXLAssembler(),
3070 3 * kArmInstrMaxSizeInBytes,
3071 CodeBufferCheckScope::kMaximumSize);
3072 __ it(pl);
3073 __ lsl(pl, o_h, low, temp);
3074 }
3075 // Shift the low part
3076 __ Lsl(o_l, low, o_l);
3077 } else if (op->IsShr()) {
3078 __ And(o_h, second_reg, kMaxLongShiftDistance);
3079 // Shift the low part
3080 __ Lsr(o_l, low, o_h);
3081 // Shift the high part and `or` what underflew on the low part
Scott Wakelingb77051e2016-11-21 19:46:00 +00003082 __ Rsb(temp, o_h, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01003083 __ Lsl(temp, high, temp);
3084 __ Orr(o_l, o_l, temp);
3085 // If the shift is > 32 bits, override the low part
Scott Wakelingb77051e2016-11-21 19:46:00 +00003086 __ Subs(temp, o_h, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01003087 {
3088 AssemblerAccurateScope guard(GetVIXLAssembler(),
3089 3 * kArmInstrMaxSizeInBytes,
3090 CodeBufferCheckScope::kMaximumSize);
3091 __ it(pl);
3092 __ asr(pl, o_l, high, temp);
3093 }
3094 // Shift the high part
3095 __ Asr(o_h, high, o_h);
3096 } else {
3097 __ And(o_h, second_reg, kMaxLongShiftDistance);
3098 // same as Shr except we use `Lsr`s and not `Asr`s
3099 __ Lsr(o_l, low, o_h);
Scott Wakelingb77051e2016-11-21 19:46:00 +00003100 __ Rsb(temp, o_h, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01003101 __ Lsl(temp, high, temp);
3102 __ Orr(o_l, o_l, temp);
Scott Wakelingb77051e2016-11-21 19:46:00 +00003103 __ Subs(temp, o_h, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01003104 {
3105 AssemblerAccurateScope guard(GetVIXLAssembler(),
3106 3 * kArmInstrMaxSizeInBytes,
3107 CodeBufferCheckScope::kMaximumSize);
3108 __ it(pl);
3109 __ lsr(pl, o_l, high, temp);
3110 }
3111 __ Lsr(o_h, high, o_h);
3112 }
3113 } else {
3114 // Register allocator doesn't create partial overlap.
3115 DCHECK(!o_l.Is(high));
3116 DCHECK(!o_h.Is(low));
3117 int32_t cst = second.GetConstant()->AsIntConstant()->GetValue();
3118 uint32_t shift_value = cst & kMaxLongShiftDistance;
3119 if (shift_value > 32) {
3120 if (op->IsShl()) {
3121 __ Lsl(o_h, low, shift_value - 32);
3122 __ Mov(o_l, 0);
3123 } else if (op->IsShr()) {
3124 __ Asr(o_l, high, shift_value - 32);
3125 __ Asr(o_h, high, 31);
3126 } else {
3127 __ Lsr(o_l, high, shift_value - 32);
3128 __ Mov(o_h, 0);
3129 }
3130 } else if (shift_value == 32) {
3131 if (op->IsShl()) {
3132 __ Mov(o_h, low);
3133 __ Mov(o_l, 0);
3134 } else if (op->IsShr()) {
3135 __ Mov(o_l, high);
3136 __ Asr(o_h, high, 31);
3137 } else {
3138 __ Mov(o_l, high);
3139 __ Mov(o_h, 0);
3140 }
3141 } else if (shift_value == 1) {
3142 if (op->IsShl()) {
3143 __ Lsls(o_l, low, 1);
3144 __ Adc(o_h, high, high);
3145 } else if (op->IsShr()) {
3146 __ Asrs(o_h, high, 1);
3147 __ Rrx(o_l, low);
3148 } else {
3149 __ Lsrs(o_h, high, 1);
3150 __ Rrx(o_l, low);
3151 }
3152 } else {
3153 DCHECK(2 <= shift_value && shift_value < 32) << shift_value;
3154 if (op->IsShl()) {
3155 __ Lsl(o_h, high, shift_value);
3156 __ Orr(o_h, o_h, Operand(low, ShiftType::LSR, 32 - shift_value));
3157 __ Lsl(o_l, low, shift_value);
3158 } else if (op->IsShr()) {
3159 __ Lsr(o_l, low, shift_value);
3160 __ Orr(o_l, o_l, Operand(high, ShiftType::LSL, 32 - shift_value));
3161 __ Asr(o_h, high, shift_value);
3162 } else {
3163 __ Lsr(o_l, low, shift_value);
3164 __ Orr(o_l, o_l, Operand(high, ShiftType::LSL, 32 - shift_value));
3165 __ Lsr(o_h, high, shift_value);
3166 }
3167 }
3168 }
3169 break;
3170 }
3171 default:
3172 LOG(FATAL) << "Unexpected operation type " << type;
3173 UNREACHABLE();
3174 }
3175}
3176
3177void LocationsBuilderARMVIXL::VisitShl(HShl* shl) {
3178 HandleShift(shl);
3179}
3180
3181void InstructionCodeGeneratorARMVIXL::VisitShl(HShl* shl) {
3182 HandleShift(shl);
3183}
3184
3185void LocationsBuilderARMVIXL::VisitShr(HShr* shr) {
3186 HandleShift(shr);
3187}
3188
3189void InstructionCodeGeneratorARMVIXL::VisitShr(HShr* shr) {
3190 HandleShift(shr);
3191}
3192
3193void LocationsBuilderARMVIXL::VisitUShr(HUShr* ushr) {
3194 HandleShift(ushr);
3195}
3196
3197void InstructionCodeGeneratorARMVIXL::VisitUShr(HUShr* ushr) {
3198 HandleShift(ushr);
3199}
3200
3201void LocationsBuilderARMVIXL::VisitNewInstance(HNewInstance* instruction) {
3202 LocationSummary* locations =
3203 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
3204 if (instruction->IsStringAlloc()) {
3205 locations->AddTemp(LocationFrom(kMethodRegister));
3206 } else {
3207 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3208 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
3209 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
3210 }
3211 locations->SetOut(LocationFrom(r0));
3212}
3213
3214void InstructionCodeGeneratorARMVIXL::VisitNewInstance(HNewInstance* instruction) {
3215 // Note: if heap poisoning is enabled, the entry point takes cares
3216 // of poisoning the reference.
3217 if (instruction->IsStringAlloc()) {
3218 // String is allocated through StringFactory. Call NewEmptyString entry point.
3219 vixl32::Register temp = RegisterFrom(instruction->GetLocations()->GetTemp(0));
3220 MemberOffset code_offset = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize);
3221 GetAssembler()->LoadFromOffset(kLoadWord, temp, tr, QUICK_ENTRY_POINT(pNewEmptyString));
3222 GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, code_offset.Int32Value());
3223 AssemblerAccurateScope aas(GetVIXLAssembler(),
3224 kArmInstrMaxSizeInBytes,
3225 CodeBufferCheckScope::kMaximumSize);
3226 __ blx(lr);
3227 codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
3228 } else {
3229 codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
3230 CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, ArtMethod*>();
3231 }
3232}
3233
3234void LocationsBuilderARMVIXL::VisitNewArray(HNewArray* instruction) {
3235 LocationSummary* locations =
3236 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
3237 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3238 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(0)));
3239 locations->SetOut(LocationFrom(r0));
3240 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(1)));
3241 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(2)));
3242}
3243
3244void InstructionCodeGeneratorARMVIXL::VisitNewArray(HNewArray* instruction) {
3245 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Andreas Gampea5b09a62016-11-17 15:21:22 -08003246 __ Mov(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex().index_);
Artem Serov02d37832016-10-25 15:25:33 +01003247 // Note: if heap poisoning is enabled, the entry point takes cares
3248 // of poisoning the reference.
3249 codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
3250 CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>();
3251}
3252
3253void LocationsBuilderARMVIXL::VisitParameterValue(HParameterValue* instruction) {
3254 LocationSummary* locations =
3255 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
3256 Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
3257 if (location.IsStackSlot()) {
3258 location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
3259 } else if (location.IsDoubleStackSlot()) {
3260 location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
3261 }
3262 locations->SetOut(location);
3263}
3264
3265void InstructionCodeGeneratorARMVIXL::VisitParameterValue(
3266 HParameterValue* instruction ATTRIBUTE_UNUSED) {
3267 // Nothing to do, the parameter is already at its location.
3268}
3269
3270void LocationsBuilderARMVIXL::VisitCurrentMethod(HCurrentMethod* instruction) {
3271 LocationSummary* locations =
3272 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
3273 locations->SetOut(LocationFrom(kMethodRegister));
3274}
3275
3276void InstructionCodeGeneratorARMVIXL::VisitCurrentMethod(
3277 HCurrentMethod* instruction ATTRIBUTE_UNUSED) {
3278 // Nothing to do, the method is already at its location.
3279}
3280
3281void LocationsBuilderARMVIXL::VisitNot(HNot* not_) {
3282 LocationSummary* locations =
3283 new (GetGraph()->GetArena()) LocationSummary(not_, LocationSummary::kNoCall);
3284 locations->SetInAt(0, Location::RequiresRegister());
3285 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3286}
3287
3288void InstructionCodeGeneratorARMVIXL::VisitNot(HNot* not_) {
3289 LocationSummary* locations = not_->GetLocations();
3290 Location out = locations->Out();
3291 Location in = locations->InAt(0);
3292 switch (not_->GetResultType()) {
3293 case Primitive::kPrimInt:
3294 __ Mvn(OutputRegister(not_), InputRegisterAt(not_, 0));
3295 break;
3296
3297 case Primitive::kPrimLong:
3298 __ Mvn(LowRegisterFrom(out), LowRegisterFrom(in));
3299 __ Mvn(HighRegisterFrom(out), HighRegisterFrom(in));
3300 break;
3301
3302 default:
3303 LOG(FATAL) << "Unimplemented type for not operation " << not_->GetResultType();
3304 }
3305}
3306
Scott Wakelingc34dba72016-10-03 10:14:44 +01003307void LocationsBuilderARMVIXL::VisitBooleanNot(HBooleanNot* bool_not) {
3308 LocationSummary* locations =
3309 new (GetGraph()->GetArena()) LocationSummary(bool_not, LocationSummary::kNoCall);
3310 locations->SetInAt(0, Location::RequiresRegister());
3311 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3312}
3313
3314void InstructionCodeGeneratorARMVIXL::VisitBooleanNot(HBooleanNot* bool_not) {
3315 __ Eor(OutputRegister(bool_not), InputRegister(bool_not), 1);
3316}
3317
Artem Serov02d37832016-10-25 15:25:33 +01003318void LocationsBuilderARMVIXL::VisitCompare(HCompare* compare) {
3319 LocationSummary* locations =
3320 new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall);
3321 switch (compare->InputAt(0)->GetType()) {
3322 case Primitive::kPrimBoolean:
3323 case Primitive::kPrimByte:
3324 case Primitive::kPrimShort:
3325 case Primitive::kPrimChar:
3326 case Primitive::kPrimInt:
3327 case Primitive::kPrimLong: {
3328 locations->SetInAt(0, Location::RequiresRegister());
3329 locations->SetInAt(1, Location::RequiresRegister());
3330 // Output overlaps because it is written before doing the low comparison.
3331 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
3332 break;
3333 }
3334 case Primitive::kPrimFloat:
3335 case Primitive::kPrimDouble: {
3336 locations->SetInAt(0, Location::RequiresFpuRegister());
3337 locations->SetInAt(1, ArithmeticZeroOrFpuRegister(compare->InputAt(1)));
3338 locations->SetOut(Location::RequiresRegister());
3339 break;
3340 }
3341 default:
3342 LOG(FATAL) << "Unexpected type for compare operation " << compare->InputAt(0)->GetType();
3343 }
3344}
3345
3346void InstructionCodeGeneratorARMVIXL::VisitCompare(HCompare* compare) {
3347 LocationSummary* locations = compare->GetLocations();
3348 vixl32::Register out = OutputRegister(compare);
3349 Location left = locations->InAt(0);
3350 Location right = locations->InAt(1);
3351
3352 vixl32::Label less, greater, done;
3353 Primitive::Type type = compare->InputAt(0)->GetType();
3354 vixl32::Condition less_cond = vixl32::Condition(kNone);
3355 switch (type) {
3356 case Primitive::kPrimBoolean:
3357 case Primitive::kPrimByte:
3358 case Primitive::kPrimShort:
3359 case Primitive::kPrimChar:
3360 case Primitive::kPrimInt: {
3361 // Emit move to `out` before the `Cmp`, as `Mov` might affect the status flags.
3362 __ Mov(out, 0);
3363 __ Cmp(RegisterFrom(left), RegisterFrom(right)); // Signed compare.
3364 less_cond = lt;
3365 break;
3366 }
3367 case Primitive::kPrimLong: {
3368 __ Cmp(HighRegisterFrom(left), HighRegisterFrom(right)); // Signed compare.
3369 __ B(lt, &less);
3370 __ B(gt, &greater);
3371 // Emit move to `out` before the last `Cmp`, as `Mov` might affect the status flags.
3372 __ Mov(out, 0);
3373 __ Cmp(LowRegisterFrom(left), LowRegisterFrom(right)); // Unsigned compare.
3374 less_cond = lo;
3375 break;
3376 }
3377 case Primitive::kPrimFloat:
3378 case Primitive::kPrimDouble: {
3379 __ Mov(out, 0);
3380 GenerateVcmp(compare);
3381 // To branch on the FP compare result we transfer FPSCR to APSR (encoded as PC in VMRS).
3382 __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
3383 less_cond = ARMFPCondition(kCondLT, compare->IsGtBias());
3384 break;
3385 }
3386 default:
3387 LOG(FATAL) << "Unexpected compare type " << type;
3388 UNREACHABLE();
3389 }
3390
3391 __ B(eq, &done);
3392 __ B(less_cond, &less);
3393
3394 __ Bind(&greater);
3395 __ Mov(out, 1);
3396 __ B(&done);
3397
3398 __ Bind(&less);
3399 __ Mov(out, -1);
3400
3401 __ Bind(&done);
3402}
3403
3404void LocationsBuilderARMVIXL::VisitPhi(HPhi* instruction) {
3405 LocationSummary* locations =
3406 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
3407 for (size_t i = 0, e = locations->GetInputCount(); i < e; ++i) {
3408 locations->SetInAt(i, Location::Any());
3409 }
3410 locations->SetOut(Location::Any());
3411}
3412
3413void InstructionCodeGeneratorARMVIXL::VisitPhi(HPhi* instruction ATTRIBUTE_UNUSED) {
3414 LOG(FATAL) << "Unreachable";
3415}
3416
3417void CodeGeneratorARMVIXL::GenerateMemoryBarrier(MemBarrierKind kind) {
3418 // TODO (ported from quick): revisit ARM barrier kinds.
3419 DmbOptions flavor = DmbOptions::ISH; // Quiet C++ warnings.
3420 switch (kind) {
3421 case MemBarrierKind::kAnyStore:
3422 case MemBarrierKind::kLoadAny:
3423 case MemBarrierKind::kAnyAny: {
3424 flavor = DmbOptions::ISH;
3425 break;
3426 }
3427 case MemBarrierKind::kStoreStore: {
3428 flavor = DmbOptions::ISHST;
3429 break;
3430 }
3431 default:
3432 LOG(FATAL) << "Unexpected memory barrier " << kind;
3433 }
3434 __ Dmb(flavor);
3435}
3436
3437void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicLoad(vixl32::Register addr,
3438 uint32_t offset,
3439 vixl32::Register out_lo,
3440 vixl32::Register out_hi) {
3441 UseScratchRegisterScope temps(GetVIXLAssembler());
3442 if (offset != 0) {
3443 vixl32::Register temp = temps.Acquire();
3444 __ Add(temp, addr, offset);
3445 addr = temp;
3446 }
Scott Wakelingb77051e2016-11-21 19:46:00 +00003447 __ Ldrexd(out_lo, out_hi, MemOperand(addr));
Artem Serov02d37832016-10-25 15:25:33 +01003448}
3449
3450void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicStore(vixl32::Register addr,
3451 uint32_t offset,
3452 vixl32::Register value_lo,
3453 vixl32::Register value_hi,
3454 vixl32::Register temp1,
3455 vixl32::Register temp2,
3456 HInstruction* instruction) {
3457 UseScratchRegisterScope temps(GetVIXLAssembler());
3458 vixl32::Label fail;
3459 if (offset != 0) {
3460 vixl32::Register temp = temps.Acquire();
3461 __ Add(temp, addr, offset);
3462 addr = temp;
3463 }
3464 __ Bind(&fail);
3465 // We need a load followed by store. (The address used in a STREX instruction must
3466 // be the same as the address in the most recently executed LDREX instruction.)
Scott Wakelingb77051e2016-11-21 19:46:00 +00003467 __ Ldrexd(temp1, temp2, MemOperand(addr));
Artem Serov02d37832016-10-25 15:25:33 +01003468 codegen_->MaybeRecordImplicitNullCheck(instruction);
Scott Wakelingb77051e2016-11-21 19:46:00 +00003469 __ Strexd(temp1, value_lo, value_hi, MemOperand(addr));
xueliang.zhongf51bc622016-11-04 09:23:32 +00003470 __ CompareAndBranchIfNonZero(temp1, &fail);
Artem Serov02d37832016-10-25 15:25:33 +01003471}
Artem Serov02109dd2016-09-23 17:17:54 +01003472
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003473void LocationsBuilderARMVIXL::HandleFieldSet(
3474 HInstruction* instruction, const FieldInfo& field_info) {
3475 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
3476
3477 LocationSummary* locations =
3478 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
3479 locations->SetInAt(0, Location::RequiresRegister());
3480
3481 Primitive::Type field_type = field_info.GetFieldType();
3482 if (Primitive::IsFloatingPointType(field_type)) {
3483 locations->SetInAt(1, Location::RequiresFpuRegister());
3484 } else {
3485 locations->SetInAt(1, Location::RequiresRegister());
3486 }
3487
3488 bool is_wide = field_type == Primitive::kPrimLong || field_type == Primitive::kPrimDouble;
3489 bool generate_volatile = field_info.IsVolatile()
3490 && is_wide
3491 && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
3492 bool needs_write_barrier =
3493 CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1));
3494 // Temporary registers for the write barrier.
3495 // TODO: consider renaming StoreNeedsWriteBarrier to StoreNeedsGCMark.
3496 if (needs_write_barrier) {
3497 locations->AddTemp(Location::RequiresRegister()); // Possibly used for reference poisoning too.
3498 locations->AddTemp(Location::RequiresRegister());
3499 } else if (generate_volatile) {
3500 // ARM encoding have some additional constraints for ldrexd/strexd:
3501 // - registers need to be consecutive
3502 // - the first register should be even but not R14.
3503 // We don't test for ARM yet, and the assertion makes sure that we
3504 // revisit this if we ever enable ARM encoding.
3505 DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet());
3506
3507 locations->AddTemp(Location::RequiresRegister());
3508 locations->AddTemp(Location::RequiresRegister());
3509 if (field_type == Primitive::kPrimDouble) {
3510 // For doubles we need two more registers to copy the value.
3511 locations->AddTemp(LocationFrom(r2));
3512 locations->AddTemp(LocationFrom(r3));
3513 }
3514 }
3515}
3516
3517void InstructionCodeGeneratorARMVIXL::HandleFieldSet(HInstruction* instruction,
3518 const FieldInfo& field_info,
3519 bool value_can_be_null) {
3520 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
3521
3522 LocationSummary* locations = instruction->GetLocations();
3523 vixl32::Register base = InputRegisterAt(instruction, 0);
3524 Location value = locations->InAt(1);
3525
3526 bool is_volatile = field_info.IsVolatile();
3527 bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
3528 Primitive::Type field_type = field_info.GetFieldType();
3529 uint32_t offset = field_info.GetFieldOffset().Uint32Value();
3530 bool needs_write_barrier =
3531 CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1));
3532
3533 if (is_volatile) {
3534 codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
3535 }
3536
3537 switch (field_type) {
3538 case Primitive::kPrimBoolean:
3539 case Primitive::kPrimByte: {
3540 GetAssembler()->StoreToOffset(kStoreByte, RegisterFrom(value), base, offset);
3541 break;
3542 }
3543
3544 case Primitive::kPrimShort:
3545 case Primitive::kPrimChar: {
3546 GetAssembler()->StoreToOffset(kStoreHalfword, RegisterFrom(value), base, offset);
3547 break;
3548 }
3549
3550 case Primitive::kPrimInt:
3551 case Primitive::kPrimNot: {
3552 if (kPoisonHeapReferences && needs_write_barrier) {
3553 // Note that in the case where `value` is a null reference,
3554 // we do not enter this block, as a null reference does not
3555 // need poisoning.
3556 DCHECK_EQ(field_type, Primitive::kPrimNot);
3557 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
3558 __ Mov(temp, RegisterFrom(value));
3559 GetAssembler()->PoisonHeapReference(temp);
3560 GetAssembler()->StoreToOffset(kStoreWord, temp, base, offset);
3561 } else {
3562 GetAssembler()->StoreToOffset(kStoreWord, RegisterFrom(value), base, offset);
3563 }
3564 break;
3565 }
3566
3567 case Primitive::kPrimLong: {
3568 if (is_volatile && !atomic_ldrd_strd) {
3569 GenerateWideAtomicStore(base,
3570 offset,
3571 LowRegisterFrom(value),
3572 HighRegisterFrom(value),
3573 RegisterFrom(locations->GetTemp(0)),
3574 RegisterFrom(locations->GetTemp(1)),
3575 instruction);
3576 } else {
3577 GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), base, offset);
3578 codegen_->MaybeRecordImplicitNullCheck(instruction);
3579 }
3580 break;
3581 }
3582
3583 case Primitive::kPrimFloat: {
3584 GetAssembler()->StoreSToOffset(SRegisterFrom(value), base, offset);
3585 break;
3586 }
3587
3588 case Primitive::kPrimDouble: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01003589 vixl32::DRegister value_reg = DRegisterFrom(value);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003590 if (is_volatile && !atomic_ldrd_strd) {
3591 vixl32::Register value_reg_lo = RegisterFrom(locations->GetTemp(0));
3592 vixl32::Register value_reg_hi = RegisterFrom(locations->GetTemp(1));
3593
3594 __ Vmov(value_reg_lo, value_reg_hi, value_reg);
3595
3596 GenerateWideAtomicStore(base,
3597 offset,
3598 value_reg_lo,
3599 value_reg_hi,
3600 RegisterFrom(locations->GetTemp(2)),
3601 RegisterFrom(locations->GetTemp(3)),
3602 instruction);
3603 } else {
3604 GetAssembler()->StoreDToOffset(value_reg, base, offset);
3605 codegen_->MaybeRecordImplicitNullCheck(instruction);
3606 }
3607 break;
3608 }
3609
3610 case Primitive::kPrimVoid:
3611 LOG(FATAL) << "Unreachable type " << field_type;
3612 UNREACHABLE();
3613 }
3614
3615 // Longs and doubles are handled in the switch.
3616 if (field_type != Primitive::kPrimLong && field_type != Primitive::kPrimDouble) {
3617 codegen_->MaybeRecordImplicitNullCheck(instruction);
3618 }
3619
3620 if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
3621 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
3622 vixl32::Register card = RegisterFrom(locations->GetTemp(1));
3623 codegen_->MarkGCCard(temp, card, base, RegisterFrom(value), value_can_be_null);
3624 }
3625
3626 if (is_volatile) {
3627 codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
3628 }
3629}
3630
Artem Serov02d37832016-10-25 15:25:33 +01003631void LocationsBuilderARMVIXL::HandleFieldGet(HInstruction* instruction,
3632 const FieldInfo& field_info) {
3633 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
3634
3635 bool object_field_get_with_read_barrier =
3636 kEmitCompilerReadBarrier && (field_info.GetFieldType() == Primitive::kPrimNot);
3637 LocationSummary* locations =
3638 new (GetGraph()->GetArena()) LocationSummary(instruction,
3639 object_field_get_with_read_barrier ?
3640 LocationSummary::kCallOnSlowPath :
3641 LocationSummary::kNoCall);
3642 if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
3643 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
3644 }
3645 locations->SetInAt(0, Location::RequiresRegister());
3646
3647 bool volatile_for_double = field_info.IsVolatile()
3648 && (field_info.GetFieldType() == Primitive::kPrimDouble)
3649 && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
3650 // The output overlaps in case of volatile long: we don't want the
3651 // code generated by GenerateWideAtomicLoad to overwrite the
3652 // object's location. Likewise, in the case of an object field get
3653 // with read barriers enabled, we do not want the load to overwrite
3654 // the object's location, as we need it to emit the read barrier.
3655 bool overlap = (field_info.IsVolatile() && (field_info.GetFieldType() == Primitive::kPrimLong)) ||
3656 object_field_get_with_read_barrier;
3657
3658 if (Primitive::IsFloatingPointType(instruction->GetType())) {
3659 locations->SetOut(Location::RequiresFpuRegister());
3660 } else {
3661 locations->SetOut(Location::RequiresRegister(),
3662 (overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap));
3663 }
3664 if (volatile_for_double) {
3665 // ARM encoding have some additional constraints for ldrexd/strexd:
3666 // - registers need to be consecutive
3667 // - the first register should be even but not R14.
3668 // We don't test for ARM yet, and the assertion makes sure that we
3669 // revisit this if we ever enable ARM encoding.
3670 DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet());
3671 locations->AddTemp(Location::RequiresRegister());
3672 locations->AddTemp(Location::RequiresRegister());
3673 } else if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
3674 // We need a temporary register for the read barrier marking slow
3675 // path in CodeGeneratorARM::GenerateFieldLoadWithBakerReadBarrier.
3676 locations->AddTemp(Location::RequiresRegister());
3677 }
3678}
3679
3680Location LocationsBuilderARMVIXL::ArithmeticZeroOrFpuRegister(HInstruction* input) {
3681 DCHECK(Primitive::IsFloatingPointType(input->GetType())) << input->GetType();
3682 if ((input->IsFloatConstant() && (input->AsFloatConstant()->IsArithmeticZero())) ||
3683 (input->IsDoubleConstant() && (input->AsDoubleConstant()->IsArithmeticZero()))) {
3684 return Location::ConstantLocation(input->AsConstant());
3685 } else {
3686 return Location::RequiresFpuRegister();
3687 }
3688}
3689
Artem Serov02109dd2016-09-23 17:17:54 +01003690Location LocationsBuilderARMVIXL::ArmEncodableConstantOrRegister(HInstruction* constant,
3691 Opcode opcode) {
3692 DCHECK(!Primitive::IsFloatingPointType(constant->GetType()));
3693 if (constant->IsConstant() &&
3694 CanEncodeConstantAsImmediate(constant->AsConstant(), opcode)) {
3695 return Location::ConstantLocation(constant->AsConstant());
3696 }
3697 return Location::RequiresRegister();
3698}
3699
3700bool LocationsBuilderARMVIXL::CanEncodeConstantAsImmediate(HConstant* input_cst,
3701 Opcode opcode) {
3702 uint64_t value = static_cast<uint64_t>(Int64FromConstant(input_cst));
3703 if (Primitive::Is64BitType(input_cst->GetType())) {
3704 Opcode high_opcode = opcode;
3705 SetCc low_set_cc = kCcDontCare;
3706 switch (opcode) {
3707 case SUB:
3708 // Flip the operation to an ADD.
3709 value = -value;
3710 opcode = ADD;
3711 FALLTHROUGH_INTENDED;
3712 case ADD:
3713 if (Low32Bits(value) == 0u) {
3714 return CanEncodeConstantAsImmediate(High32Bits(value), opcode, kCcDontCare);
3715 }
3716 high_opcode = ADC;
3717 low_set_cc = kCcSet;
3718 break;
3719 default:
3720 break;
3721 }
3722 return CanEncodeConstantAsImmediate(Low32Bits(value), opcode, low_set_cc) &&
3723 CanEncodeConstantAsImmediate(High32Bits(value), high_opcode, kCcDontCare);
3724 } else {
3725 return CanEncodeConstantAsImmediate(Low32Bits(value), opcode);
3726 }
3727}
3728
3729// TODO(VIXL): Replace art::arm::SetCc` with `vixl32::FlagsUpdate after flags set optimization
3730// enabled.
3731bool LocationsBuilderARMVIXL::CanEncodeConstantAsImmediate(uint32_t value,
3732 Opcode opcode,
3733 SetCc set_cc) {
3734 ArmVIXLAssembler* assembler = codegen_->GetAssembler();
3735 if (assembler->ShifterOperandCanHold(opcode, value, set_cc)) {
3736 return true;
3737 }
3738 Opcode neg_opcode = kNoOperand;
3739 switch (opcode) {
3740 case AND: neg_opcode = BIC; value = ~value; break;
3741 case ORR: neg_opcode = ORN; value = ~value; break;
3742 case ADD: neg_opcode = SUB; value = -value; break;
3743 case ADC: neg_opcode = SBC; value = ~value; break;
3744 case SUB: neg_opcode = ADD; value = -value; break;
3745 case SBC: neg_opcode = ADC; value = ~value; break;
3746 default:
3747 return false;
3748 }
3749 return assembler->ShifterOperandCanHold(neg_opcode, value, set_cc);
3750}
3751
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003752void InstructionCodeGeneratorARMVIXL::HandleFieldGet(HInstruction* instruction,
3753 const FieldInfo& field_info) {
3754 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
3755
3756 LocationSummary* locations = instruction->GetLocations();
3757 vixl32::Register base = InputRegisterAt(instruction, 0);
3758 Location out = locations->Out();
3759 bool is_volatile = field_info.IsVolatile();
3760 bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
3761 Primitive::Type field_type = field_info.GetFieldType();
3762 uint32_t offset = field_info.GetFieldOffset().Uint32Value();
3763
3764 switch (field_type) {
3765 case Primitive::kPrimBoolean:
3766 GetAssembler()->LoadFromOffset(kLoadUnsignedByte, RegisterFrom(out), base, offset);
3767 break;
3768
3769 case Primitive::kPrimByte:
3770 GetAssembler()->LoadFromOffset(kLoadSignedByte, RegisterFrom(out), base, offset);
3771 break;
3772
3773 case Primitive::kPrimShort:
3774 GetAssembler()->LoadFromOffset(kLoadSignedHalfword, RegisterFrom(out), base, offset);
3775 break;
3776
3777 case Primitive::kPrimChar:
3778 GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, RegisterFrom(out), base, offset);
3779 break;
3780
3781 case Primitive::kPrimInt:
3782 GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(out), base, offset);
3783 break;
3784
3785 case Primitive::kPrimNot: {
3786 // /* HeapReference<Object> */ out = *(base + offset)
3787 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
3788 TODO_VIXL32(FATAL);
3789 } else {
3790 GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(out), base, offset);
3791 // TODO(VIXL): Scope to guarantee the position immediately after the load.
3792 codegen_->MaybeRecordImplicitNullCheck(instruction);
3793 if (is_volatile) {
3794 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
3795 }
3796 // If read barriers are enabled, emit read barriers other than
3797 // Baker's using a slow path (and also unpoison the loaded
3798 // reference, if heap poisoning is enabled).
3799 codegen_->MaybeGenerateReadBarrierSlow(instruction, out, out, locations->InAt(0), offset);
3800 }
3801 break;
3802 }
3803
3804 case Primitive::kPrimLong:
3805 if (is_volatile && !atomic_ldrd_strd) {
3806 GenerateWideAtomicLoad(base, offset, LowRegisterFrom(out), HighRegisterFrom(out));
3807 } else {
3808 GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out), base, offset);
3809 }
3810 break;
3811
3812 case Primitive::kPrimFloat:
3813 GetAssembler()->LoadSFromOffset(SRegisterFrom(out), base, offset);
3814 break;
3815
3816 case Primitive::kPrimDouble: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01003817 vixl32::DRegister out_dreg = DRegisterFrom(out);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003818 if (is_volatile && !atomic_ldrd_strd) {
3819 vixl32::Register lo = RegisterFrom(locations->GetTemp(0));
3820 vixl32::Register hi = RegisterFrom(locations->GetTemp(1));
3821 GenerateWideAtomicLoad(base, offset, lo, hi);
3822 // TODO(VIXL): Do we need to be immediately after the ldrexd instruction? If so we need a
3823 // scope.
3824 codegen_->MaybeRecordImplicitNullCheck(instruction);
3825 __ Vmov(out_dreg, lo, hi);
3826 } else {
3827 GetAssembler()->LoadDFromOffset(out_dreg, base, offset);
3828 // TODO(VIXL): Scope to guarantee the position immediately after the load.
3829 codegen_->MaybeRecordImplicitNullCheck(instruction);
3830 }
3831 break;
3832 }
3833
3834 case Primitive::kPrimVoid:
3835 LOG(FATAL) << "Unreachable type " << field_type;
3836 UNREACHABLE();
3837 }
3838
3839 if (field_type == Primitive::kPrimNot || field_type == Primitive::kPrimDouble) {
3840 // Potential implicit null checks, in the case of reference or
3841 // double fields, are handled in the previous switch statement.
3842 } else {
3843 // Address cases other than reference and double that may require an implicit null check.
3844 codegen_->MaybeRecordImplicitNullCheck(instruction);
3845 }
3846
3847 if (is_volatile) {
3848 if (field_type == Primitive::kPrimNot) {
3849 // Memory barriers, in the case of references, are also handled
3850 // in the previous switch statement.
3851 } else {
3852 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
3853 }
3854 }
3855}
3856
3857void LocationsBuilderARMVIXL::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
3858 HandleFieldSet(instruction, instruction->GetFieldInfo());
3859}
3860
3861void InstructionCodeGeneratorARMVIXL::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
3862 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
3863}
3864
3865void LocationsBuilderARMVIXL::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
3866 HandleFieldGet(instruction, instruction->GetFieldInfo());
3867}
3868
3869void InstructionCodeGeneratorARMVIXL::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
3870 HandleFieldGet(instruction, instruction->GetFieldInfo());
3871}
3872
3873void LocationsBuilderARMVIXL::VisitStaticFieldGet(HStaticFieldGet* instruction) {
3874 HandleFieldGet(instruction, instruction->GetFieldInfo());
3875}
3876
3877void InstructionCodeGeneratorARMVIXL::VisitStaticFieldGet(HStaticFieldGet* instruction) {
3878 HandleFieldGet(instruction, instruction->GetFieldInfo());
3879}
3880
Scott Wakelingc34dba72016-10-03 10:14:44 +01003881void LocationsBuilderARMVIXL::VisitStaticFieldSet(HStaticFieldSet* instruction) {
3882 HandleFieldSet(instruction, instruction->GetFieldInfo());
3883}
3884
3885void InstructionCodeGeneratorARMVIXL::VisitStaticFieldSet(HStaticFieldSet* instruction) {
3886 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
3887}
3888
Artem Serovcfbe9132016-10-14 15:58:56 +01003889void LocationsBuilderARMVIXL::VisitUnresolvedInstanceFieldGet(
3890 HUnresolvedInstanceFieldGet* instruction) {
3891 FieldAccessCallingConventionARMVIXL calling_convention;
3892 codegen_->CreateUnresolvedFieldLocationSummary(
3893 instruction, instruction->GetFieldType(), calling_convention);
3894}
3895
3896void InstructionCodeGeneratorARMVIXL::VisitUnresolvedInstanceFieldGet(
3897 HUnresolvedInstanceFieldGet* instruction) {
3898 FieldAccessCallingConventionARMVIXL calling_convention;
3899 codegen_->GenerateUnresolvedFieldAccess(instruction,
3900 instruction->GetFieldType(),
3901 instruction->GetFieldIndex(),
3902 instruction->GetDexPc(),
3903 calling_convention);
3904}
3905
3906void LocationsBuilderARMVIXL::VisitUnresolvedInstanceFieldSet(
3907 HUnresolvedInstanceFieldSet* instruction) {
3908 FieldAccessCallingConventionARMVIXL calling_convention;
3909 codegen_->CreateUnresolvedFieldLocationSummary(
3910 instruction, instruction->GetFieldType(), calling_convention);
3911}
3912
3913void InstructionCodeGeneratorARMVIXL::VisitUnresolvedInstanceFieldSet(
3914 HUnresolvedInstanceFieldSet* instruction) {
3915 FieldAccessCallingConventionARMVIXL calling_convention;
3916 codegen_->GenerateUnresolvedFieldAccess(instruction,
3917 instruction->GetFieldType(),
3918 instruction->GetFieldIndex(),
3919 instruction->GetDexPc(),
3920 calling_convention);
3921}
3922
3923void LocationsBuilderARMVIXL::VisitUnresolvedStaticFieldGet(
3924 HUnresolvedStaticFieldGet* instruction) {
3925 FieldAccessCallingConventionARMVIXL calling_convention;
3926 codegen_->CreateUnresolvedFieldLocationSummary(
3927 instruction, instruction->GetFieldType(), calling_convention);
3928}
3929
3930void InstructionCodeGeneratorARMVIXL::VisitUnresolvedStaticFieldGet(
3931 HUnresolvedStaticFieldGet* instruction) {
3932 FieldAccessCallingConventionARMVIXL calling_convention;
3933 codegen_->GenerateUnresolvedFieldAccess(instruction,
3934 instruction->GetFieldType(),
3935 instruction->GetFieldIndex(),
3936 instruction->GetDexPc(),
3937 calling_convention);
3938}
3939
3940void LocationsBuilderARMVIXL::VisitUnresolvedStaticFieldSet(
3941 HUnresolvedStaticFieldSet* instruction) {
3942 FieldAccessCallingConventionARMVIXL calling_convention;
3943 codegen_->CreateUnresolvedFieldLocationSummary(
3944 instruction, instruction->GetFieldType(), calling_convention);
3945}
3946
3947void InstructionCodeGeneratorARMVIXL::VisitUnresolvedStaticFieldSet(
3948 HUnresolvedStaticFieldSet* instruction) {
3949 FieldAccessCallingConventionARMVIXL calling_convention;
3950 codegen_->GenerateUnresolvedFieldAccess(instruction,
3951 instruction->GetFieldType(),
3952 instruction->GetFieldIndex(),
3953 instruction->GetDexPc(),
3954 calling_convention);
3955}
3956
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003957void LocationsBuilderARMVIXL::VisitNullCheck(HNullCheck* instruction) {
Artem Serov657022c2016-11-23 14:19:38 +00003958 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003959 locations->SetInAt(0, Location::RequiresRegister());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003960}
3961
3962void CodeGeneratorARMVIXL::GenerateImplicitNullCheck(HNullCheck* instruction) {
3963 if (CanMoveNullCheckToUser(instruction)) {
3964 return;
3965 }
3966
3967 UseScratchRegisterScope temps(GetVIXLAssembler());
3968 AssemblerAccurateScope aas(GetVIXLAssembler(),
3969 kArmInstrMaxSizeInBytes,
3970 CodeBufferCheckScope::kMaximumSize);
3971 __ ldr(temps.Acquire(), MemOperand(InputRegisterAt(instruction, 0)));
3972 RecordPcInfo(instruction, instruction->GetDexPc());
3973}
3974
3975void CodeGeneratorARMVIXL::GenerateExplicitNullCheck(HNullCheck* instruction) {
3976 NullCheckSlowPathARMVIXL* slow_path =
3977 new (GetGraph()->GetArena()) NullCheckSlowPathARMVIXL(instruction);
3978 AddSlowPath(slow_path);
xueliang.zhongf51bc622016-11-04 09:23:32 +00003979 __ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003980}
3981
3982void InstructionCodeGeneratorARMVIXL::VisitNullCheck(HNullCheck* instruction) {
3983 codegen_->GenerateNullCheck(instruction);
3984}
3985
Scott Wakelingc34dba72016-10-03 10:14:44 +01003986static LoadOperandType GetLoadOperandType(Primitive::Type type) {
3987 switch (type) {
3988 case Primitive::kPrimNot:
3989 return kLoadWord;
3990 case Primitive::kPrimBoolean:
3991 return kLoadUnsignedByte;
3992 case Primitive::kPrimByte:
3993 return kLoadSignedByte;
3994 case Primitive::kPrimChar:
3995 return kLoadUnsignedHalfword;
3996 case Primitive::kPrimShort:
3997 return kLoadSignedHalfword;
3998 case Primitive::kPrimInt:
3999 return kLoadWord;
4000 case Primitive::kPrimLong:
4001 return kLoadWordPair;
4002 case Primitive::kPrimFloat:
4003 return kLoadSWord;
4004 case Primitive::kPrimDouble:
4005 return kLoadDWord;
4006 default:
4007 LOG(FATAL) << "Unreachable type " << type;
4008 UNREACHABLE();
4009 }
4010}
4011
4012static StoreOperandType GetStoreOperandType(Primitive::Type type) {
4013 switch (type) {
4014 case Primitive::kPrimNot:
4015 return kStoreWord;
4016 case Primitive::kPrimBoolean:
4017 case Primitive::kPrimByte:
4018 return kStoreByte;
4019 case Primitive::kPrimChar:
4020 case Primitive::kPrimShort:
4021 return kStoreHalfword;
4022 case Primitive::kPrimInt:
4023 return kStoreWord;
4024 case Primitive::kPrimLong:
4025 return kStoreWordPair;
4026 case Primitive::kPrimFloat:
4027 return kStoreSWord;
4028 case Primitive::kPrimDouble:
4029 return kStoreDWord;
4030 default:
4031 LOG(FATAL) << "Unreachable type " << type;
4032 UNREACHABLE();
4033 }
4034}
4035
4036void CodeGeneratorARMVIXL::LoadFromShiftedRegOffset(Primitive::Type type,
4037 Location out_loc,
4038 vixl32::Register base,
4039 vixl32::Register reg_index,
4040 vixl32::Condition cond) {
4041 uint32_t shift_count = Primitive::ComponentSizeShift(type);
4042 MemOperand mem_address(base, reg_index, vixl32::LSL, shift_count);
4043
4044 switch (type) {
4045 case Primitive::kPrimByte:
4046 __ Ldrsb(cond, RegisterFrom(out_loc), mem_address);
4047 break;
4048 case Primitive::kPrimBoolean:
4049 __ Ldrb(cond, RegisterFrom(out_loc), mem_address);
4050 break;
4051 case Primitive::kPrimShort:
4052 __ Ldrsh(cond, RegisterFrom(out_loc), mem_address);
4053 break;
4054 case Primitive::kPrimChar:
4055 __ Ldrh(cond, RegisterFrom(out_loc), mem_address);
4056 break;
4057 case Primitive::kPrimNot:
4058 case Primitive::kPrimInt:
4059 __ Ldr(cond, RegisterFrom(out_loc), mem_address);
4060 break;
4061 // T32 doesn't support LoadFromShiftedRegOffset mem address mode for these types.
4062 case Primitive::kPrimLong:
4063 case Primitive::kPrimFloat:
4064 case Primitive::kPrimDouble:
4065 default:
4066 LOG(FATAL) << "Unreachable type " << type;
4067 UNREACHABLE();
4068 }
4069}
4070
4071void CodeGeneratorARMVIXL::StoreToShiftedRegOffset(Primitive::Type type,
4072 Location loc,
4073 vixl32::Register base,
4074 vixl32::Register reg_index,
4075 vixl32::Condition cond) {
4076 uint32_t shift_count = Primitive::ComponentSizeShift(type);
4077 MemOperand mem_address(base, reg_index, vixl32::LSL, shift_count);
4078
4079 switch (type) {
4080 case Primitive::kPrimByte:
4081 case Primitive::kPrimBoolean:
4082 __ Strb(cond, RegisterFrom(loc), mem_address);
4083 break;
4084 case Primitive::kPrimShort:
4085 case Primitive::kPrimChar:
4086 __ Strh(cond, RegisterFrom(loc), mem_address);
4087 break;
4088 case Primitive::kPrimNot:
4089 case Primitive::kPrimInt:
4090 __ Str(cond, RegisterFrom(loc), mem_address);
4091 break;
4092 // T32 doesn't support StoreToShiftedRegOffset mem address mode for these types.
4093 case Primitive::kPrimLong:
4094 case Primitive::kPrimFloat:
4095 case Primitive::kPrimDouble:
4096 default:
4097 LOG(FATAL) << "Unreachable type " << type;
4098 UNREACHABLE();
4099 }
4100}
4101
4102void LocationsBuilderARMVIXL::VisitArrayGet(HArrayGet* instruction) {
4103 bool object_array_get_with_read_barrier =
4104 kEmitCompilerReadBarrier && (instruction->GetType() == Primitive::kPrimNot);
4105 LocationSummary* locations =
4106 new (GetGraph()->GetArena()) LocationSummary(instruction,
4107 object_array_get_with_read_barrier ?
4108 LocationSummary::kCallOnSlowPath :
4109 LocationSummary::kNoCall);
4110 if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
4111 TODO_VIXL32(FATAL);
4112 }
4113 locations->SetInAt(0, Location::RequiresRegister());
4114 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
4115 if (Primitive::IsFloatingPointType(instruction->GetType())) {
4116 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4117 } else {
4118 // The output overlaps in the case of an object array get with
4119 // read barriers enabled: we do not want the move to overwrite the
4120 // array's location, as we need it to emit the read barrier.
4121 locations->SetOut(
4122 Location::RequiresRegister(),
4123 object_array_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap);
4124 }
4125 // We need a temporary register for the read barrier marking slow
4126 // path in CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier.
4127 // Also need for String compression feature.
4128 if ((object_array_get_with_read_barrier && kUseBakerReadBarrier)
4129 || (mirror::kUseStringCompression && instruction->IsStringCharAt())) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004130 locations->AddTemp(Location::RequiresRegister());
Scott Wakelingc34dba72016-10-03 10:14:44 +01004131 }
4132}
4133
4134void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) {
4135 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
4136 LocationSummary* locations = instruction->GetLocations();
4137 Location obj_loc = locations->InAt(0);
4138 vixl32::Register obj = InputRegisterAt(instruction, 0);
4139 Location index = locations->InAt(1);
4140 Location out_loc = locations->Out();
4141 uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
4142 Primitive::Type type = instruction->GetType();
4143 const bool maybe_compressed_char_at = mirror::kUseStringCompression &&
4144 instruction->IsStringCharAt();
4145 HInstruction* array_instr = instruction->GetArray();
4146 bool has_intermediate_address = array_instr->IsIntermediateAddress();
4147 // The read barrier instrumentation does not support the HIntermediateAddress instruction yet.
4148 DCHECK(!(has_intermediate_address && kEmitCompilerReadBarrier));
4149
4150 switch (type) {
4151 case Primitive::kPrimBoolean:
4152 case Primitive::kPrimByte:
4153 case Primitive::kPrimShort:
4154 case Primitive::kPrimChar:
4155 case Primitive::kPrimInt: {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01004156 vixl32::Register length;
4157 if (maybe_compressed_char_at) {
4158 length = RegisterFrom(locations->GetTemp(0));
4159 uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
4160 GetAssembler()->LoadFromOffset(kLoadWord, length, obj, count_offset);
4161 codegen_->MaybeRecordImplicitNullCheck(instruction);
4162 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01004163 if (index.IsConstant()) {
4164 int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue();
4165 if (maybe_compressed_char_at) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004166 vixl32::Label uncompressed_load, done;
Vladimir Markofdaf0f42016-10-13 19:29:53 +01004167 __ Lsrs(length, length, 1u); // LSRS has a 16-bit encoding, TST (immediate) does not.
4168 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
4169 "Expecting 0=compressed, 1=uncompressed");
4170 __ B(cs, &uncompressed_load);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004171 GetAssembler()->LoadFromOffset(kLoadUnsignedByte,
4172 RegisterFrom(out_loc),
4173 obj,
4174 data_offset + const_index);
4175 __ B(&done);
4176 __ Bind(&uncompressed_load);
4177 GetAssembler()->LoadFromOffset(GetLoadOperandType(Primitive::kPrimChar),
4178 RegisterFrom(out_loc),
4179 obj,
4180 data_offset + (const_index << 1));
4181 __ Bind(&done);
Scott Wakelingc34dba72016-10-03 10:14:44 +01004182 } else {
4183 uint32_t full_offset = data_offset + (const_index << Primitive::ComponentSizeShift(type));
4184
4185 LoadOperandType load_type = GetLoadOperandType(type);
4186 GetAssembler()->LoadFromOffset(load_type, RegisterFrom(out_loc), obj, full_offset);
4187 }
4188 } else {
4189 vixl32::Register temp = temps.Acquire();
4190
4191 if (has_intermediate_address) {
Artem Serov2bbc9532016-10-21 11:51:50 +01004192 // We do not need to compute the intermediate address from the array: the
4193 // input instruction has done it already. See the comment in
4194 // `TryExtractArrayAccessAddress()`.
4195 if (kIsDebugBuild) {
4196 HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
4197 DCHECK_EQ(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64(), data_offset);
4198 }
4199 temp = obj;
Scott Wakelingc34dba72016-10-03 10:14:44 +01004200 } else {
4201 __ Add(temp, obj, data_offset);
4202 }
4203 if (maybe_compressed_char_at) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004204 vixl32::Label uncompressed_load, done;
Vladimir Markofdaf0f42016-10-13 19:29:53 +01004205 __ Lsrs(length, length, 1u); // LSRS has a 16-bit encoding, TST (immediate) does not.
4206 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
4207 "Expecting 0=compressed, 1=uncompressed");
4208 __ B(cs, &uncompressed_load);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004209 __ Ldrb(RegisterFrom(out_loc), MemOperand(temp, RegisterFrom(index), vixl32::LSL, 0));
4210 __ B(&done);
4211 __ Bind(&uncompressed_load);
4212 __ Ldrh(RegisterFrom(out_loc), MemOperand(temp, RegisterFrom(index), vixl32::LSL, 1));
4213 __ Bind(&done);
Scott Wakelingc34dba72016-10-03 10:14:44 +01004214 } else {
4215 codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index));
4216 }
Anton Kirilovdda43962016-11-21 19:55:20 +00004217 temps.Release(temp);
Scott Wakelingc34dba72016-10-03 10:14:44 +01004218 }
4219 break;
4220 }
4221
4222 case Primitive::kPrimNot: {
4223 static_assert(
4224 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
4225 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
4226 // /* HeapReference<Object> */ out =
4227 // *(obj + data_offset + index * sizeof(HeapReference<Object>))
4228 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
4229 TODO_VIXL32(FATAL);
4230 } else {
4231 vixl32::Register out = OutputRegister(instruction);
4232 if (index.IsConstant()) {
4233 size_t offset =
4234 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
4235 GetAssembler()->LoadFromOffset(kLoadWord, out, obj, offset);
4236 codegen_->MaybeRecordImplicitNullCheck(instruction);
4237 // If read barriers are enabled, emit read barriers other than
4238 // Baker's using a slow path (and also unpoison the loaded
4239 // reference, if heap poisoning is enabled).
4240 codegen_->MaybeGenerateReadBarrierSlow(instruction, out_loc, out_loc, obj_loc, offset);
4241 } else {
4242 vixl32::Register temp = temps.Acquire();
4243
4244 if (has_intermediate_address) {
Artem Serov2bbc9532016-10-21 11:51:50 +01004245 // We do not need to compute the intermediate address from the array: the
4246 // input instruction has done it already. See the comment in
4247 // `TryExtractArrayAccessAddress()`.
4248 if (kIsDebugBuild) {
4249 HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
4250 DCHECK_EQ(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64(), data_offset);
4251 }
4252 temp = obj;
Scott Wakelingc34dba72016-10-03 10:14:44 +01004253 } else {
4254 __ Add(temp, obj, data_offset);
4255 }
4256 codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index));
Anton Kirilovdda43962016-11-21 19:55:20 +00004257 temps.Release(temp);
Scott Wakelingc34dba72016-10-03 10:14:44 +01004258
4259 codegen_->MaybeRecordImplicitNullCheck(instruction);
4260 // If read barriers are enabled, emit read barriers other than
4261 // Baker's using a slow path (and also unpoison the loaded
4262 // reference, if heap poisoning is enabled).
4263 codegen_->MaybeGenerateReadBarrierSlow(
4264 instruction, out_loc, out_loc, obj_loc, data_offset, index);
4265 }
4266 }
4267 break;
4268 }
4269
4270 case Primitive::kPrimLong: {
4271 if (index.IsConstant()) {
4272 size_t offset =
4273 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
4274 GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out_loc), obj, offset);
4275 } else {
4276 vixl32::Register temp = temps.Acquire();
4277 __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
4278 GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out_loc), temp, data_offset);
Anton Kirilovdda43962016-11-21 19:55:20 +00004279 temps.Release(temp);
Scott Wakelingc34dba72016-10-03 10:14:44 +01004280 }
4281 break;
4282 }
4283
4284 case Primitive::kPrimFloat: {
4285 vixl32::SRegister out = SRegisterFrom(out_loc);
4286 if (index.IsConstant()) {
4287 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
4288 GetAssembler()->LoadSFromOffset(out, obj, offset);
4289 } else {
4290 vixl32::Register temp = temps.Acquire();
4291 __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_4));
4292 GetAssembler()->LoadSFromOffset(out, temp, data_offset);
Anton Kirilovdda43962016-11-21 19:55:20 +00004293 temps.Release(temp);
Scott Wakelingc34dba72016-10-03 10:14:44 +01004294 }
4295 break;
4296 }
4297
4298 case Primitive::kPrimDouble: {
4299 if (index.IsConstant()) {
4300 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
4301 GetAssembler()->LoadDFromOffset(DRegisterFrom(out_loc), obj, offset);
4302 } else {
4303 vixl32::Register temp = temps.Acquire();
4304 __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
4305 GetAssembler()->LoadDFromOffset(DRegisterFrom(out_loc), temp, data_offset);
Anton Kirilovdda43962016-11-21 19:55:20 +00004306 temps.Release(temp);
Scott Wakelingc34dba72016-10-03 10:14:44 +01004307 }
4308 break;
4309 }
4310
4311 case Primitive::kPrimVoid:
4312 LOG(FATAL) << "Unreachable type " << type;
4313 UNREACHABLE();
4314 }
4315
4316 if (type == Primitive::kPrimNot) {
4317 // Potential implicit null checks, in the case of reference
4318 // arrays, are handled in the previous switch statement.
4319 } else if (!maybe_compressed_char_at) {
4320 codegen_->MaybeRecordImplicitNullCheck(instruction);
4321 }
4322}
4323
4324void LocationsBuilderARMVIXL::VisitArraySet(HArraySet* instruction) {
4325 Primitive::Type value_type = instruction->GetComponentType();
4326
4327 bool needs_write_barrier =
4328 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
4329 bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck();
4330
4331 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(
4332 instruction,
4333 may_need_runtime_call_for_type_check ?
4334 LocationSummary::kCallOnSlowPath :
4335 LocationSummary::kNoCall);
4336
4337 locations->SetInAt(0, Location::RequiresRegister());
4338 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
4339 if (Primitive::IsFloatingPointType(value_type)) {
4340 locations->SetInAt(2, Location::RequiresFpuRegister());
4341 } else {
4342 locations->SetInAt(2, Location::RequiresRegister());
4343 }
4344 if (needs_write_barrier) {
4345 // Temporary registers for the write barrier.
4346 locations->AddTemp(Location::RequiresRegister()); // Possibly used for ref. poisoning too.
4347 locations->AddTemp(Location::RequiresRegister());
4348 }
4349}
4350
4351void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) {
4352 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
4353 LocationSummary* locations = instruction->GetLocations();
4354 vixl32::Register array = InputRegisterAt(instruction, 0);
4355 Location index = locations->InAt(1);
4356 Primitive::Type value_type = instruction->GetComponentType();
4357 bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck();
4358 bool needs_write_barrier =
4359 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
4360 uint32_t data_offset =
4361 mirror::Array::DataOffset(Primitive::ComponentSize(value_type)).Uint32Value();
4362 Location value_loc = locations->InAt(2);
4363 HInstruction* array_instr = instruction->GetArray();
4364 bool has_intermediate_address = array_instr->IsIntermediateAddress();
4365 // The read barrier instrumentation does not support the HIntermediateAddress instruction yet.
4366 DCHECK(!(has_intermediate_address && kEmitCompilerReadBarrier));
4367
4368 switch (value_type) {
4369 case Primitive::kPrimBoolean:
4370 case Primitive::kPrimByte:
4371 case Primitive::kPrimShort:
4372 case Primitive::kPrimChar:
4373 case Primitive::kPrimInt: {
4374 if (index.IsConstant()) {
4375 int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue();
4376 uint32_t full_offset =
4377 data_offset + (const_index << Primitive::ComponentSizeShift(value_type));
4378 StoreOperandType store_type = GetStoreOperandType(value_type);
4379 GetAssembler()->StoreToOffset(store_type, RegisterFrom(value_loc), array, full_offset);
4380 } else {
4381 vixl32::Register temp = temps.Acquire();
4382
4383 if (has_intermediate_address) {
Artem Serov2bbc9532016-10-21 11:51:50 +01004384 // We do not need to compute the intermediate address from the array: the
4385 // input instruction has done it already. See the comment in
4386 // `TryExtractArrayAccessAddress()`.
4387 if (kIsDebugBuild) {
4388 HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
4389 DCHECK(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64() == data_offset);
4390 }
4391 temp = array;
Scott Wakelingc34dba72016-10-03 10:14:44 +01004392 } else {
4393 __ Add(temp, array, data_offset);
4394 }
4395 codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
Anton Kirilovdda43962016-11-21 19:55:20 +00004396 temps.Release(temp);
Scott Wakelingc34dba72016-10-03 10:14:44 +01004397 }
4398 break;
4399 }
4400
4401 case Primitive::kPrimNot: {
4402 vixl32::Register value = RegisterFrom(value_loc);
4403 // TryExtractArrayAccessAddress optimization is never applied for non-primitive ArraySet.
4404 // See the comment in instruction_simplifier_shared.cc.
4405 DCHECK(!has_intermediate_address);
4406
4407 if (instruction->InputAt(2)->IsNullConstant()) {
4408 // Just setting null.
4409 if (index.IsConstant()) {
4410 size_t offset =
4411 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
4412 GetAssembler()->StoreToOffset(kStoreWord, value, array, offset);
4413 } else {
4414 DCHECK(index.IsRegister()) << index;
4415 vixl32::Register temp = temps.Acquire();
4416 __ Add(temp, array, data_offset);
4417 codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
Anton Kirilovdda43962016-11-21 19:55:20 +00004418 temps.Release(temp);
Scott Wakelingc34dba72016-10-03 10:14:44 +01004419 }
4420 codegen_->MaybeRecordImplicitNullCheck(instruction);
4421 DCHECK(!needs_write_barrier);
4422 DCHECK(!may_need_runtime_call_for_type_check);
4423 break;
4424 }
4425
4426 DCHECK(needs_write_barrier);
4427 Location temp1_loc = locations->GetTemp(0);
4428 vixl32::Register temp1 = RegisterFrom(temp1_loc);
4429 Location temp2_loc = locations->GetTemp(1);
4430 vixl32::Register temp2 = RegisterFrom(temp2_loc);
4431 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
4432 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
4433 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
4434 vixl32::Label done;
4435 SlowPathCodeARMVIXL* slow_path = nullptr;
4436
4437 if (may_need_runtime_call_for_type_check) {
4438 slow_path = new (GetGraph()->GetArena()) ArraySetSlowPathARMVIXL(instruction);
4439 codegen_->AddSlowPath(slow_path);
4440 if (instruction->GetValueCanBeNull()) {
4441 vixl32::Label non_zero;
xueliang.zhongf51bc622016-11-04 09:23:32 +00004442 __ CompareAndBranchIfNonZero(value, &non_zero);
Scott Wakelingc34dba72016-10-03 10:14:44 +01004443 if (index.IsConstant()) {
4444 size_t offset =
4445 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
4446 GetAssembler()->StoreToOffset(kStoreWord, value, array, offset);
4447 } else {
4448 DCHECK(index.IsRegister()) << index;
4449 vixl32::Register temp = temps.Acquire();
4450 __ Add(temp, array, data_offset);
4451 codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
Anton Kirilovdda43962016-11-21 19:55:20 +00004452 temps.Release(temp);
Scott Wakelingc34dba72016-10-03 10:14:44 +01004453 }
4454 codegen_->MaybeRecordImplicitNullCheck(instruction);
4455 __ B(&done);
4456 __ Bind(&non_zero);
4457 }
4458
4459 // Note that when read barriers are enabled, the type checks
4460 // are performed without read barriers. This is fine, even in
4461 // the case where a class object is in the from-space after
4462 // the flip, as a comparison involving such a type would not
4463 // produce a false positive; it may of course produce a false
4464 // negative, in which case we would take the ArraySet slow
4465 // path.
4466
4467 // /* HeapReference<Class> */ temp1 = array->klass_
4468 GetAssembler()->LoadFromOffset(kLoadWord, temp1, array, class_offset);
4469 codegen_->MaybeRecordImplicitNullCheck(instruction);
4470 GetAssembler()->MaybeUnpoisonHeapReference(temp1);
4471
4472 // /* HeapReference<Class> */ temp1 = temp1->component_type_
4473 GetAssembler()->LoadFromOffset(kLoadWord, temp1, temp1, component_offset);
4474 // /* HeapReference<Class> */ temp2 = value->klass_
4475 GetAssembler()->LoadFromOffset(kLoadWord, temp2, value, class_offset);
4476 // If heap poisoning is enabled, no need to unpoison `temp1`
4477 // nor `temp2`, as we are comparing two poisoned references.
4478 __ Cmp(temp1, temp2);
4479
4480 if (instruction->StaticTypeOfArrayIsObjectArray()) {
4481 vixl32::Label do_put;
4482 __ B(eq, &do_put);
4483 // If heap poisoning is enabled, the `temp1` reference has
4484 // not been unpoisoned yet; unpoison it now.
4485 GetAssembler()->MaybeUnpoisonHeapReference(temp1);
4486
4487 // /* HeapReference<Class> */ temp1 = temp1->super_class_
4488 GetAssembler()->LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
4489 // If heap poisoning is enabled, no need to unpoison
4490 // `temp1`, as we are comparing against null below.
xueliang.zhongf51bc622016-11-04 09:23:32 +00004491 __ CompareAndBranchIfNonZero(temp1, slow_path->GetEntryLabel());
Scott Wakelingc34dba72016-10-03 10:14:44 +01004492 __ Bind(&do_put);
4493 } else {
4494 __ B(ne, slow_path->GetEntryLabel());
4495 }
4496 }
4497
4498 vixl32::Register source = value;
4499 if (kPoisonHeapReferences) {
4500 // Note that in the case where `value` is a null reference,
4501 // we do not enter this block, as a null reference does not
4502 // need poisoning.
4503 DCHECK_EQ(value_type, Primitive::kPrimNot);
4504 __ Mov(temp1, value);
4505 GetAssembler()->PoisonHeapReference(temp1);
4506 source = temp1;
4507 }
4508
4509 if (index.IsConstant()) {
4510 size_t offset =
4511 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
4512 GetAssembler()->StoreToOffset(kStoreWord, source, array, offset);
4513 } else {
4514 DCHECK(index.IsRegister()) << index;
4515
4516 vixl32::Register temp = temps.Acquire();
4517 __ Add(temp, array, data_offset);
4518 codegen_->StoreToShiftedRegOffset(value_type,
4519 LocationFrom(source),
4520 temp,
4521 RegisterFrom(index));
Anton Kirilovdda43962016-11-21 19:55:20 +00004522 temps.Release(temp);
Scott Wakelingc34dba72016-10-03 10:14:44 +01004523 }
4524
4525 if (!may_need_runtime_call_for_type_check) {
4526 codegen_->MaybeRecordImplicitNullCheck(instruction);
4527 }
4528
4529 codegen_->MarkGCCard(temp1, temp2, array, value, instruction->GetValueCanBeNull());
4530
4531 if (done.IsReferenced()) {
4532 __ Bind(&done);
4533 }
4534
4535 if (slow_path != nullptr) {
4536 __ Bind(slow_path->GetExitLabel());
4537 }
4538
4539 break;
4540 }
4541
4542 case Primitive::kPrimLong: {
4543 Location value = locations->InAt(2);
4544 if (index.IsConstant()) {
4545 size_t offset =
4546 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
4547 GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), array, offset);
4548 } else {
4549 vixl32::Register temp = temps.Acquire();
4550 __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
4551 GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), temp, data_offset);
Anton Kirilovdda43962016-11-21 19:55:20 +00004552 temps.Release(temp);
Scott Wakelingc34dba72016-10-03 10:14:44 +01004553 }
4554 break;
4555 }
4556
4557 case Primitive::kPrimFloat: {
4558 Location value = locations->InAt(2);
4559 DCHECK(value.IsFpuRegister());
4560 if (index.IsConstant()) {
4561 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
4562 GetAssembler()->StoreSToOffset(SRegisterFrom(value), array, offset);
4563 } else {
4564 vixl32::Register temp = temps.Acquire();
4565 __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_4));
4566 GetAssembler()->StoreSToOffset(SRegisterFrom(value), temp, data_offset);
Anton Kirilovdda43962016-11-21 19:55:20 +00004567 temps.Release(temp);
Scott Wakelingc34dba72016-10-03 10:14:44 +01004568 }
4569 break;
4570 }
4571
4572 case Primitive::kPrimDouble: {
4573 Location value = locations->InAt(2);
4574 DCHECK(value.IsFpuRegisterPair());
4575 if (index.IsConstant()) {
4576 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
4577 GetAssembler()->StoreDToOffset(DRegisterFrom(value), array, offset);
4578 } else {
4579 vixl32::Register temp = temps.Acquire();
4580 __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
4581 GetAssembler()->StoreDToOffset(DRegisterFrom(value), temp, data_offset);
Anton Kirilovdda43962016-11-21 19:55:20 +00004582 temps.Release(temp);
Scott Wakelingc34dba72016-10-03 10:14:44 +01004583 }
4584 break;
4585 }
4586
4587 case Primitive::kPrimVoid:
4588 LOG(FATAL) << "Unreachable type " << value_type;
4589 UNREACHABLE();
4590 }
4591
4592 // Objects are handled in the switch.
4593 if (value_type != Primitive::kPrimNot) {
4594 codegen_->MaybeRecordImplicitNullCheck(instruction);
4595 }
4596}
4597
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004598void LocationsBuilderARMVIXL::VisitArrayLength(HArrayLength* instruction) {
4599 LocationSummary* locations =
4600 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
4601 locations->SetInAt(0, Location::RequiresRegister());
4602 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4603}
4604
4605void InstructionCodeGeneratorARMVIXL::VisitArrayLength(HArrayLength* instruction) {
4606 uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction);
4607 vixl32::Register obj = InputRegisterAt(instruction, 0);
4608 vixl32::Register out = OutputRegister(instruction);
4609 GetAssembler()->LoadFromOffset(kLoadWord, out, obj, offset);
4610 codegen_->MaybeRecordImplicitNullCheck(instruction);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004611 // Mask out compression flag from String's array length.
4612 if (mirror::kUseStringCompression && instruction->IsStringLength()) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01004613 __ Lsr(out, out, 1u);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004614 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004615}
4616
Artem Serov2bbc9532016-10-21 11:51:50 +01004617void LocationsBuilderARMVIXL::VisitIntermediateAddress(HIntermediateAddress* instruction) {
4618 // The read barrier instrumentation does not support the HIntermediateAddress instruction yet.
4619 DCHECK(!kEmitCompilerReadBarrier);
4620 LocationSummary* locations =
4621 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
4622
4623 locations->SetInAt(0, Location::RequiresRegister());
4624 locations->SetInAt(1, Location::RegisterOrConstant(instruction->GetOffset()));
4625 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4626}
4627
4628void InstructionCodeGeneratorARMVIXL::VisitIntermediateAddress(HIntermediateAddress* instruction) {
4629 vixl32::Register out = OutputRegister(instruction);
4630 vixl32::Register first = InputRegisterAt(instruction, 0);
4631 Location second = instruction->GetLocations()->InAt(1);
4632
4633 // The read barrier instrumentation does not support the HIntermediateAddress instruction yet.
4634 DCHECK(!kEmitCompilerReadBarrier);
4635
4636 if (second.IsRegister()) {
4637 __ Add(out, first, RegisterFrom(second));
4638 } else {
4639 __ Add(out, first, second.GetConstant()->AsIntConstant()->GetValue());
4640 }
4641}
4642
Scott Wakelingc34dba72016-10-03 10:14:44 +01004643void LocationsBuilderARMVIXL::VisitBoundsCheck(HBoundsCheck* instruction) {
4644 RegisterSet caller_saves = RegisterSet::Empty();
4645 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4646 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0)));
4647 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(1)));
4648 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction, caller_saves);
4649 locations->SetInAt(0, Location::RequiresRegister());
4650 locations->SetInAt(1, Location::RequiresRegister());
4651}
4652
4653void InstructionCodeGeneratorARMVIXL::VisitBoundsCheck(HBoundsCheck* instruction) {
4654 SlowPathCodeARMVIXL* slow_path =
4655 new (GetGraph()->GetArena()) BoundsCheckSlowPathARMVIXL(instruction);
4656 codegen_->AddSlowPath(slow_path);
4657
4658 vixl32::Register index = InputRegisterAt(instruction, 0);
4659 vixl32::Register length = InputRegisterAt(instruction, 1);
4660
4661 __ Cmp(index, length);
4662 __ B(hs, slow_path->GetEntryLabel());
4663}
4664
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004665void CodeGeneratorARMVIXL::MarkGCCard(vixl32::Register temp,
4666 vixl32::Register card,
4667 vixl32::Register object,
4668 vixl32::Register value,
4669 bool can_be_null) {
4670 vixl32::Label is_null;
4671 if (can_be_null) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00004672 __ CompareAndBranchIfZero(value, &is_null);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004673 }
4674 GetAssembler()->LoadFromOffset(
4675 kLoadWord, card, tr, Thread::CardTableOffset<kArmPointerSize>().Int32Value());
Scott Wakelingb77051e2016-11-21 19:46:00 +00004676 __ Lsr(temp, object, Operand::From(gc::accounting::CardTable::kCardShift));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004677 __ Strb(card, MemOperand(card, temp));
4678 if (can_be_null) {
4679 __ Bind(&is_null);
4680 }
4681}
4682
Scott Wakelingfe885462016-09-22 10:24:38 +01004683void LocationsBuilderARMVIXL::VisitParallelMove(HParallelMove* instruction ATTRIBUTE_UNUSED) {
4684 LOG(FATAL) << "Unreachable";
4685}
4686
4687void InstructionCodeGeneratorARMVIXL::VisitParallelMove(HParallelMove* instruction) {
4688 codegen_->GetMoveResolver()->EmitNativeCode(instruction);
4689}
4690
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004691void LocationsBuilderARMVIXL::VisitSuspendCheck(HSuspendCheck* instruction) {
Artem Serov657022c2016-11-23 14:19:38 +00004692 LocationSummary* locations =
4693 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
4694 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004695}
4696
4697void InstructionCodeGeneratorARMVIXL::VisitSuspendCheck(HSuspendCheck* instruction) {
4698 HBasicBlock* block = instruction->GetBlock();
4699 if (block->GetLoopInformation() != nullptr) {
4700 DCHECK(block->GetLoopInformation()->GetSuspendCheck() == instruction);
4701 // The back edge will generate the suspend check.
4702 return;
4703 }
4704 if (block->IsEntryBlock() && instruction->GetNext()->IsGoto()) {
4705 // The goto will generate the suspend check.
4706 return;
4707 }
4708 GenerateSuspendCheck(instruction, nullptr);
4709}
4710
4711void InstructionCodeGeneratorARMVIXL::GenerateSuspendCheck(HSuspendCheck* instruction,
4712 HBasicBlock* successor) {
4713 SuspendCheckSlowPathARMVIXL* slow_path =
4714 down_cast<SuspendCheckSlowPathARMVIXL*>(instruction->GetSlowPath());
4715 if (slow_path == nullptr) {
4716 slow_path = new (GetGraph()->GetArena()) SuspendCheckSlowPathARMVIXL(instruction, successor);
4717 instruction->SetSlowPath(slow_path);
4718 codegen_->AddSlowPath(slow_path);
4719 if (successor != nullptr) {
4720 DCHECK(successor->IsLoopHeader());
4721 codegen_->ClearSpillSlotsFromLoopPhisInStackMap(instruction);
4722 }
4723 } else {
4724 DCHECK_EQ(slow_path->GetSuccessor(), successor);
4725 }
4726
4727 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
4728 vixl32::Register temp = temps.Acquire();
4729 GetAssembler()->LoadFromOffset(
4730 kLoadUnsignedHalfword, temp, tr, Thread::ThreadFlagsOffset<kArmPointerSize>().Int32Value());
4731 if (successor == nullptr) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00004732 __ CompareAndBranchIfNonZero(temp, slow_path->GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004733 __ Bind(slow_path->GetReturnLabel());
4734 } else {
xueliang.zhongf51bc622016-11-04 09:23:32 +00004735 __ CompareAndBranchIfZero(temp, codegen_->GetLabelOf(successor));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004736 __ B(slow_path->GetEntryLabel());
4737 }
4738}
4739
Scott Wakelingfe885462016-09-22 10:24:38 +01004740ArmVIXLAssembler* ParallelMoveResolverARMVIXL::GetAssembler() const {
4741 return codegen_->GetAssembler();
4742}
4743
4744void ParallelMoveResolverARMVIXL::EmitMove(size_t index) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004745 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
Scott Wakelingfe885462016-09-22 10:24:38 +01004746 MoveOperands* move = moves_[index];
4747 Location source = move->GetSource();
4748 Location destination = move->GetDestination();
4749
4750 if (source.IsRegister()) {
4751 if (destination.IsRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004752 __ Mov(RegisterFrom(destination), RegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01004753 } else if (destination.IsFpuRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004754 __ Vmov(SRegisterFrom(destination), RegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01004755 } else {
4756 DCHECK(destination.IsStackSlot());
4757 GetAssembler()->StoreToOffset(kStoreWord,
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004758 RegisterFrom(source),
Scott Wakelingfe885462016-09-22 10:24:38 +01004759 sp,
4760 destination.GetStackIndex());
4761 }
4762 } else if (source.IsStackSlot()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004763 if (destination.IsRegister()) {
4764 GetAssembler()->LoadFromOffset(kLoadWord,
4765 RegisterFrom(destination),
4766 sp,
4767 source.GetStackIndex());
4768 } else if (destination.IsFpuRegister()) {
4769 GetAssembler()->LoadSFromOffset(SRegisterFrom(destination), sp, source.GetStackIndex());
4770 } else {
4771 DCHECK(destination.IsStackSlot());
4772 vixl32::Register temp = temps.Acquire();
4773 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, source.GetStackIndex());
4774 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
4775 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004776 } else if (source.IsFpuRegister()) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01004777 if (destination.IsRegister()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01004778 __ Vmov(RegisterFrom(destination), SRegisterFrom(source));
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01004779 } else if (destination.IsFpuRegister()) {
4780 __ Vmov(SRegisterFrom(destination), SRegisterFrom(source));
4781 } else {
4782 DCHECK(destination.IsStackSlot());
4783 GetAssembler()->StoreSToOffset(SRegisterFrom(source), sp, destination.GetStackIndex());
4784 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004785 } else if (source.IsDoubleStackSlot()) {
Alexandre Rames9c19bd62016-10-24 11:50:32 +01004786 if (destination.IsDoubleStackSlot()) {
4787 vixl32::DRegister temp = temps.AcquireD();
4788 GetAssembler()->LoadDFromOffset(temp, sp, source.GetStackIndex());
4789 GetAssembler()->StoreDToOffset(temp, sp, destination.GetStackIndex());
4790 } else if (destination.IsRegisterPair()) {
4791 DCHECK(ExpectedPairLayout(destination));
4792 GetAssembler()->LoadFromOffset(
4793 kLoadWordPair, LowRegisterFrom(destination), sp, source.GetStackIndex());
4794 } else {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01004795 DCHECK(destination.IsFpuRegisterPair()) << destination;
4796 GetAssembler()->LoadDFromOffset(DRegisterFrom(destination), sp, source.GetStackIndex());
Alexandre Rames9c19bd62016-10-24 11:50:32 +01004797 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004798 } else if (source.IsRegisterPair()) {
4799 if (destination.IsRegisterPair()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004800 __ Mov(LowRegisterFrom(destination), LowRegisterFrom(source));
4801 __ Mov(HighRegisterFrom(destination), HighRegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01004802 } else if (destination.IsFpuRegisterPair()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01004803 __ Vmov(DRegisterFrom(destination), LowRegisterFrom(source), HighRegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01004804 } else {
4805 DCHECK(destination.IsDoubleStackSlot()) << destination;
4806 DCHECK(ExpectedPairLayout(source));
4807 GetAssembler()->StoreToOffset(kStoreWordPair,
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004808 LowRegisterFrom(source),
Scott Wakelingfe885462016-09-22 10:24:38 +01004809 sp,
4810 destination.GetStackIndex());
4811 }
4812 } else if (source.IsFpuRegisterPair()) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01004813 if (destination.IsRegisterPair()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01004814 __ Vmov(LowRegisterFrom(destination), HighRegisterFrom(destination), DRegisterFrom(source));
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01004815 } else if (destination.IsFpuRegisterPair()) {
4816 __ Vmov(DRegisterFrom(destination), DRegisterFrom(source));
4817 } else {
4818 DCHECK(destination.IsDoubleStackSlot()) << destination;
4819 GetAssembler()->StoreDToOffset(DRegisterFrom(source), sp, destination.GetStackIndex());
4820 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004821 } else {
4822 DCHECK(source.IsConstant()) << source;
4823 HConstant* constant = source.GetConstant();
4824 if (constant->IsIntConstant() || constant->IsNullConstant()) {
4825 int32_t value = CodeGenerator::GetInt32ValueOf(constant);
4826 if (destination.IsRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004827 __ Mov(RegisterFrom(destination), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01004828 } else {
4829 DCHECK(destination.IsStackSlot());
Scott Wakelingfe885462016-09-22 10:24:38 +01004830 vixl32::Register temp = temps.Acquire();
4831 __ Mov(temp, value);
4832 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
4833 }
4834 } else if (constant->IsLongConstant()) {
4835 int64_t value = constant->AsLongConstant()->GetValue();
4836 if (destination.IsRegisterPair()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004837 __ Mov(LowRegisterFrom(destination), Low32Bits(value));
4838 __ Mov(HighRegisterFrom(destination), High32Bits(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01004839 } else {
4840 DCHECK(destination.IsDoubleStackSlot()) << destination;
Scott Wakelingfe885462016-09-22 10:24:38 +01004841 vixl32::Register temp = temps.Acquire();
4842 __ Mov(temp, Low32Bits(value));
4843 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
4844 __ Mov(temp, High32Bits(value));
4845 GetAssembler()->StoreToOffset(kStoreWord,
4846 temp,
4847 sp,
4848 destination.GetHighStackIndex(kArmWordSize));
4849 }
4850 } else if (constant->IsDoubleConstant()) {
4851 double value = constant->AsDoubleConstant()->GetValue();
4852 if (destination.IsFpuRegisterPair()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01004853 __ Vmov(DRegisterFrom(destination), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01004854 } else {
4855 DCHECK(destination.IsDoubleStackSlot()) << destination;
4856 uint64_t int_value = bit_cast<uint64_t, double>(value);
Scott Wakelingfe885462016-09-22 10:24:38 +01004857 vixl32::Register temp = temps.Acquire();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004858 __ Mov(temp, Low32Bits(int_value));
Scott Wakelingfe885462016-09-22 10:24:38 +01004859 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004860 __ Mov(temp, High32Bits(int_value));
Scott Wakelingfe885462016-09-22 10:24:38 +01004861 GetAssembler()->StoreToOffset(kStoreWord,
4862 temp,
4863 sp,
4864 destination.GetHighStackIndex(kArmWordSize));
4865 }
4866 } else {
4867 DCHECK(constant->IsFloatConstant()) << constant->DebugName();
4868 float value = constant->AsFloatConstant()->GetValue();
4869 if (destination.IsFpuRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004870 __ Vmov(SRegisterFrom(destination), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01004871 } else {
4872 DCHECK(destination.IsStackSlot());
Scott Wakelingfe885462016-09-22 10:24:38 +01004873 vixl32::Register temp = temps.Acquire();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004874 __ Mov(temp, bit_cast<int32_t, float>(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01004875 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
4876 }
4877 }
4878 }
4879}
4880
Alexandre Rames9c19bd62016-10-24 11:50:32 +01004881void ParallelMoveResolverARMVIXL::Exchange(vixl32::Register reg, int mem) {
4882 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
4883 vixl32::Register temp = temps.Acquire();
4884 __ Mov(temp, reg);
4885 GetAssembler()->LoadFromOffset(kLoadWord, reg, sp, mem);
4886 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, mem);
Scott Wakelingfe885462016-09-22 10:24:38 +01004887}
4888
Alexandre Rames9c19bd62016-10-24 11:50:32 +01004889void ParallelMoveResolverARMVIXL::Exchange(int mem1, int mem2) {
4890 // TODO(VIXL32): Double check the performance of this implementation.
4891 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
4892 vixl32::Register temp = temps.Acquire();
4893 vixl32::SRegister temp_s = temps.AcquireS();
4894
4895 __ Ldr(temp, MemOperand(sp, mem1));
4896 __ Vldr(temp_s, MemOperand(sp, mem2));
4897 __ Str(temp, MemOperand(sp, mem2));
4898 __ Vstr(temp_s, MemOperand(sp, mem1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004899}
4900
Alexandre Rames9c19bd62016-10-24 11:50:32 +01004901void ParallelMoveResolverARMVIXL::EmitSwap(size_t index) {
4902 MoveOperands* move = moves_[index];
4903 Location source = move->GetSource();
4904 Location destination = move->GetDestination();
4905 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
4906
4907 if (source.IsRegister() && destination.IsRegister()) {
4908 vixl32::Register temp = temps.Acquire();
4909 DCHECK(!RegisterFrom(source).Is(temp));
4910 DCHECK(!RegisterFrom(destination).Is(temp));
4911 __ Mov(temp, RegisterFrom(destination));
4912 __ Mov(RegisterFrom(destination), RegisterFrom(source));
4913 __ Mov(RegisterFrom(source), temp);
4914 } else if (source.IsRegister() && destination.IsStackSlot()) {
4915 Exchange(RegisterFrom(source), destination.GetStackIndex());
4916 } else if (source.IsStackSlot() && destination.IsRegister()) {
4917 Exchange(RegisterFrom(destination), source.GetStackIndex());
4918 } else if (source.IsStackSlot() && destination.IsStackSlot()) {
Anton Kirilovdda43962016-11-21 19:55:20 +00004919 Exchange(source.GetStackIndex(), destination.GetStackIndex());
Alexandre Rames9c19bd62016-10-24 11:50:32 +01004920 } else if (source.IsFpuRegister() && destination.IsFpuRegister()) {
Anton Kirilovdda43962016-11-21 19:55:20 +00004921 vixl32::SRegister temp = temps.AcquireS();
4922 __ Vmov(temp, SRegisterFrom(source));
4923 __ Vmov(SRegisterFrom(source), SRegisterFrom(destination));
4924 __ Vmov(SRegisterFrom(destination), temp);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01004925 } else if (source.IsRegisterPair() && destination.IsRegisterPair()) {
4926 vixl32::DRegister temp = temps.AcquireD();
4927 __ Vmov(temp, LowRegisterFrom(source), HighRegisterFrom(source));
4928 __ Mov(LowRegisterFrom(source), LowRegisterFrom(destination));
4929 __ Mov(HighRegisterFrom(source), HighRegisterFrom(destination));
4930 __ Vmov(LowRegisterFrom(destination), HighRegisterFrom(destination), temp);
4931 } else if (source.IsRegisterPair() || destination.IsRegisterPair()) {
4932 vixl32::Register low_reg = LowRegisterFrom(source.IsRegisterPair() ? source : destination);
4933 int mem = source.IsRegisterPair() ? destination.GetStackIndex() : source.GetStackIndex();
4934 DCHECK(ExpectedPairLayout(source.IsRegisterPair() ? source : destination));
4935 vixl32::DRegister temp = temps.AcquireD();
4936 __ Vmov(temp, low_reg, vixl32::Register(low_reg.GetCode() + 1));
4937 GetAssembler()->LoadFromOffset(kLoadWordPair, low_reg, sp, mem);
4938 GetAssembler()->StoreDToOffset(temp, sp, mem);
4939 } else if (source.IsFpuRegisterPair() && destination.IsFpuRegisterPair()) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004940 vixl32::DRegister first = DRegisterFrom(source);
4941 vixl32::DRegister second = DRegisterFrom(destination);
4942 vixl32::DRegister temp = temps.AcquireD();
4943 __ Vmov(temp, first);
4944 __ Vmov(first, second);
4945 __ Vmov(second, temp);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01004946 } else if (source.IsFpuRegisterPair() || destination.IsFpuRegisterPair()) {
Anton Kirilovdda43962016-11-21 19:55:20 +00004947 vixl32::DRegister reg = source.IsFpuRegisterPair()
4948 ? DRegisterFrom(source)
4949 : DRegisterFrom(destination);
4950 int mem = source.IsFpuRegisterPair()
4951 ? destination.GetStackIndex()
4952 : source.GetStackIndex();
4953 vixl32::DRegister temp = temps.AcquireD();
4954 __ Vmov(temp, reg);
4955 GetAssembler()->LoadDFromOffset(reg, sp, mem);
4956 GetAssembler()->StoreDToOffset(temp, sp, mem);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01004957 } else if (source.IsFpuRegister() || destination.IsFpuRegister()) {
Anton Kirilovdda43962016-11-21 19:55:20 +00004958 vixl32::SRegister reg = source.IsFpuRegister()
4959 ? SRegisterFrom(source)
4960 : SRegisterFrom(destination);
4961 int mem = source.IsFpuRegister()
4962 ? destination.GetStackIndex()
4963 : source.GetStackIndex();
4964 vixl32::Register temp = temps.Acquire();
4965 __ Vmov(temp, reg);
4966 GetAssembler()->LoadSFromOffset(reg, sp, mem);
4967 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, mem);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01004968 } else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) {
4969 vixl32::DRegister temp1 = temps.AcquireD();
4970 vixl32::DRegister temp2 = temps.AcquireD();
4971 __ Vldr(temp1, MemOperand(sp, source.GetStackIndex()));
4972 __ Vldr(temp2, MemOperand(sp, destination.GetStackIndex()));
4973 __ Vstr(temp1, MemOperand(sp, destination.GetStackIndex()));
4974 __ Vstr(temp2, MemOperand(sp, source.GetStackIndex()));
4975 } else {
4976 LOG(FATAL) << "Unimplemented" << source << " <-> " << destination;
4977 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004978}
4979
4980void ParallelMoveResolverARMVIXL::SpillScratch(int reg ATTRIBUTE_UNUSED) {
4981 TODO_VIXL32(FATAL);
4982}
4983
4984void ParallelMoveResolverARMVIXL::RestoreScratch(int reg ATTRIBUTE_UNUSED) {
4985 TODO_VIXL32(FATAL);
4986}
4987
Artem Serov02d37832016-10-25 15:25:33 +01004988// Check if the desired_class_load_kind is supported. If it is, return it,
4989// otherwise return a fall-back kind that should be used instead.
4990HLoadClass::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadClassKind(
4991 HLoadClass::LoadKind desired_class_load_kind ATTRIBUTE_UNUSED) {
4992 // TODO(VIXL): Implement optimized code paths.
4993 return HLoadClass::LoadKind::kDexCacheViaMethod;
4994}
4995
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004996void LocationsBuilderARMVIXL::VisitLoadClass(HLoadClass* cls) {
4997 if (cls->NeedsAccessCheck()) {
4998 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4999 CodeGenerator::CreateLoadClassLocationSummary(
5000 cls,
5001 LocationFrom(calling_convention.GetRegisterAt(0)),
5002 LocationFrom(r0),
5003 /* code_generator_supports_read_barrier */ true);
5004 return;
5005 }
Scott Wakelingfe885462016-09-22 10:24:38 +01005006
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005007 // TODO(VIXL): read barrier code.
5008 LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || kEmitCompilerReadBarrier)
5009 ? LocationSummary::kCallOnSlowPath
5010 : LocationSummary::kNoCall;
5011 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
5012 HLoadClass::LoadKind load_kind = cls->GetLoadKind();
5013 if (load_kind == HLoadClass::LoadKind::kReferrersClass ||
5014 load_kind == HLoadClass::LoadKind::kDexCacheViaMethod ||
5015 load_kind == HLoadClass::LoadKind::kDexCachePcRelative) {
5016 locations->SetInAt(0, Location::RequiresRegister());
5017 }
5018 locations->SetOut(Location::RequiresRegister());
5019}
5020
5021void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) {
5022 LocationSummary* locations = cls->GetLocations();
5023 if (cls->NeedsAccessCheck()) {
Andreas Gampea5b09a62016-11-17 15:21:22 -08005024 codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex().index_);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005025 codegen_->InvokeRuntime(kQuickInitializeTypeAndVerifyAccess, cls, cls->GetDexPc());
5026 CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>();
5027 return;
5028 }
5029
5030 Location out_loc = locations->Out();
5031 vixl32::Register out = OutputRegister(cls);
5032
5033 // TODO(VIXL): read barrier code.
5034 bool generate_null_check = false;
5035 switch (cls->GetLoadKind()) {
5036 case HLoadClass::LoadKind::kReferrersClass: {
5037 DCHECK(!cls->CanCallRuntime());
5038 DCHECK(!cls->MustGenerateClinitCheck());
5039 // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
5040 vixl32::Register current_method = InputRegisterAt(cls, 0);
5041 GenerateGcRootFieldLoad(cls,
5042 out_loc,
5043 current_method,
Roland Levillain00468f32016-10-27 18:02:48 +01005044 ArtMethod::DeclaringClassOffset().Int32Value(),
5045 kEmitCompilerReadBarrier);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005046 break;
5047 }
5048 case HLoadClass::LoadKind::kDexCacheViaMethod: {
5049 // /* GcRoot<mirror::Class>[] */ out =
5050 // current_method.ptr_sized_fields_->dex_cache_resolved_types_
5051 vixl32::Register current_method = InputRegisterAt(cls, 0);
5052 const int32_t resolved_types_offset =
5053 ArtMethod::DexCacheResolvedTypesOffset(kArmPointerSize).Int32Value();
5054 GetAssembler()->LoadFromOffset(kLoadWord, out, current_method, resolved_types_offset);
5055 // /* GcRoot<mirror::Class> */ out = out[type_index]
Andreas Gampea5b09a62016-11-17 15:21:22 -08005056 size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_);
Roland Levillain00468f32016-10-27 18:02:48 +01005057 GenerateGcRootFieldLoad(cls, out_loc, out, offset, kEmitCompilerReadBarrier);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005058 generate_null_check = !cls->IsInDexCache();
5059 break;
5060 }
5061 default:
5062 TODO_VIXL32(FATAL);
5063 }
5064
5065 if (generate_null_check || cls->MustGenerateClinitCheck()) {
5066 DCHECK(cls->CanCallRuntime());
5067 LoadClassSlowPathARMVIXL* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARMVIXL(
5068 cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
5069 codegen_->AddSlowPath(slow_path);
5070 if (generate_null_check) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00005071 __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005072 }
5073 if (cls->MustGenerateClinitCheck()) {
5074 GenerateClassInitializationCheck(slow_path, out);
5075 } else {
5076 __ Bind(slow_path->GetExitLabel());
5077 }
5078 }
5079}
5080
Artem Serov02d37832016-10-25 15:25:33 +01005081void LocationsBuilderARMVIXL::VisitClinitCheck(HClinitCheck* check) {
5082 LocationSummary* locations =
5083 new (GetGraph()->GetArena()) LocationSummary(check, LocationSummary::kCallOnSlowPath);
5084 locations->SetInAt(0, Location::RequiresRegister());
5085 if (check->HasUses()) {
5086 locations->SetOut(Location::SameAsFirstInput());
5087 }
5088}
5089
5090void InstructionCodeGeneratorARMVIXL::VisitClinitCheck(HClinitCheck* check) {
5091 // We assume the class is not null.
5092 LoadClassSlowPathARMVIXL* slow_path =
5093 new (GetGraph()->GetArena()) LoadClassSlowPathARMVIXL(check->GetLoadClass(),
5094 check,
5095 check->GetDexPc(),
5096 /* do_clinit */ true);
5097 codegen_->AddSlowPath(slow_path);
5098 GenerateClassInitializationCheck(slow_path, InputRegisterAt(check, 0));
5099}
5100
5101void InstructionCodeGeneratorARMVIXL::GenerateClassInitializationCheck(
5102 LoadClassSlowPathARMVIXL* slow_path, vixl32::Register class_reg) {
5103 UseScratchRegisterScope temps(GetVIXLAssembler());
5104 vixl32::Register temp = temps.Acquire();
5105 GetAssembler()->LoadFromOffset(kLoadWord,
5106 temp,
5107 class_reg,
5108 mirror::Class::StatusOffset().Int32Value());
5109 __ Cmp(temp, mirror::Class::kStatusInitialized);
5110 __ B(lt, slow_path->GetEntryLabel());
5111 // Even if the initialized flag is set, we may be in a situation where caches are not synced
5112 // properly. Therefore, we do a memory fence.
5113 __ Dmb(ISH);
5114 __ Bind(slow_path->GetExitLabel());
5115}
5116
5117// Check if the desired_string_load_kind is supported. If it is, return it,
5118// otherwise return a fall-back kind that should be used instead.
5119HLoadString::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadStringKind(
5120 HLoadString::LoadKind desired_string_load_kind ATTRIBUTE_UNUSED) {
5121 // TODO(VIXL): Implement optimized code paths. For now we always use the simpler fallback code.
5122 return HLoadString::LoadKind::kDexCacheViaMethod;
5123}
5124
5125void LocationsBuilderARMVIXL::VisitLoadString(HLoadString* load) {
5126 LocationSummary::CallKind call_kind = load->NeedsEnvironment()
5127 ? LocationSummary::kCallOnMainOnly
5128 : LocationSummary::kNoCall;
5129 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
5130
5131 // TODO(VIXL): Implement optimized code paths.
5132 // See InstructionCodeGeneratorARMVIXL::VisitLoadString.
5133 HLoadString::LoadKind load_kind = load->GetLoadKind();
5134 if (load_kind == HLoadString::LoadKind::kDexCacheViaMethod) {
5135 locations->SetInAt(0, Location::RequiresRegister());
5136 // TODO(VIXL): Use InvokeRuntimeCallingConventionARMVIXL instead.
5137 locations->SetOut(LocationFrom(r0));
5138 } else {
5139 locations->SetOut(Location::RequiresRegister());
5140 }
5141}
5142
5143void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) {
5144 // TODO(VIXL): Implement optimized code paths.
5145 // We implemented the simplest solution to get first ART tests passing, we deferred the
5146 // optimized path until later, we should implement it using ARM64 implementation as a
5147 // reference. The same related to LocationsBuilderARMVIXL::VisitLoadString.
5148
5149 // TODO: Re-add the compiler code to do string dex cache lookup again.
5150 DCHECK_EQ(load->GetLoadKind(), HLoadString::LoadKind::kDexCacheViaMethod);
5151 InvokeRuntimeCallingConventionARMVIXL calling_convention;
5152 __ Mov(calling_convention.GetRegisterAt(0), load->GetStringIndex());
5153 codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc());
5154 CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
5155}
5156
5157static int32_t GetExceptionTlsOffset() {
5158 return Thread::ExceptionOffset<kArmPointerSize>().Int32Value();
5159}
5160
5161void LocationsBuilderARMVIXL::VisitLoadException(HLoadException* load) {
5162 LocationSummary* locations =
5163 new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kNoCall);
5164 locations->SetOut(Location::RequiresRegister());
5165}
5166
5167void InstructionCodeGeneratorARMVIXL::VisitLoadException(HLoadException* load) {
5168 vixl32::Register out = OutputRegister(load);
5169 GetAssembler()->LoadFromOffset(kLoadWord, out, tr, GetExceptionTlsOffset());
5170}
5171
5172
5173void LocationsBuilderARMVIXL::VisitClearException(HClearException* clear) {
5174 new (GetGraph()->GetArena()) LocationSummary(clear, LocationSummary::kNoCall);
5175}
5176
5177void InstructionCodeGeneratorARMVIXL::VisitClearException(HClearException* clear ATTRIBUTE_UNUSED) {
5178 UseScratchRegisterScope temps(GetVIXLAssembler());
5179 vixl32::Register temp = temps.Acquire();
5180 __ Mov(temp, 0);
5181 GetAssembler()->StoreToOffset(kStoreWord, temp, tr, GetExceptionTlsOffset());
5182}
5183
5184void LocationsBuilderARMVIXL::VisitThrow(HThrow* instruction) {
5185 LocationSummary* locations =
5186 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
5187 InvokeRuntimeCallingConventionARMVIXL calling_convention;
5188 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
5189}
5190
5191void InstructionCodeGeneratorARMVIXL::VisitThrow(HThrow* instruction) {
5192 codegen_->InvokeRuntime(kQuickDeliverException, instruction, instruction->GetDexPc());
5193 CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
5194}
5195
Artem Serov657022c2016-11-23 14:19:38 +00005196// Temp is used for read barrier.
5197static size_t NumberOfInstanceOfTemps(TypeCheckKind type_check_kind) {
5198 if (kEmitCompilerReadBarrier &&
5199 (kUseBakerReadBarrier ||
5200 type_check_kind == TypeCheckKind::kAbstractClassCheck ||
5201 type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
5202 type_check_kind == TypeCheckKind::kArrayObjectCheck)) {
5203 return 1;
5204 }
5205 return 0;
Anton Kirilove28d9ae2016-10-25 18:17:23 +01005206}
5207
Artem Serov657022c2016-11-23 14:19:38 +00005208// Interface case has 3 temps, one for holding the number of interfaces, one for the current
5209// interface pointer, one for loading the current interface.
5210// The other checks have one temp for loading the object's class.
5211static size_t NumberOfCheckCastTemps(TypeCheckKind type_check_kind) {
5212 if (type_check_kind == TypeCheckKind::kInterfaceCheck) {
5213 return 3;
5214 }
5215 return 1 + NumberOfInstanceOfTemps(type_check_kind);
5216}
Artem Serovcfbe9132016-10-14 15:58:56 +01005217
5218void LocationsBuilderARMVIXL::VisitInstanceOf(HInstanceOf* instruction) {
5219 LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
5220 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
5221 bool baker_read_barrier_slow_path = false;
5222 switch (type_check_kind) {
5223 case TypeCheckKind::kExactCheck:
5224 case TypeCheckKind::kAbstractClassCheck:
5225 case TypeCheckKind::kClassHierarchyCheck:
5226 case TypeCheckKind::kArrayObjectCheck:
5227 call_kind =
5228 kEmitCompilerReadBarrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall;
5229 baker_read_barrier_slow_path = kUseBakerReadBarrier;
5230 break;
5231 case TypeCheckKind::kArrayCheck:
5232 case TypeCheckKind::kUnresolvedCheck:
5233 case TypeCheckKind::kInterfaceCheck:
5234 call_kind = LocationSummary::kCallOnSlowPath;
5235 break;
5236 }
5237
5238 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
5239 if (baker_read_barrier_slow_path) {
5240 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
5241 }
5242 locations->SetInAt(0, Location::RequiresRegister());
5243 locations->SetInAt(1, Location::RequiresRegister());
5244 // The "out" register is used as a temporary, so it overlaps with the inputs.
5245 // Note that TypeCheckSlowPathARM uses this register too.
5246 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
Artem Serov657022c2016-11-23 14:19:38 +00005247 locations->AddRegisterTemps(NumberOfInstanceOfTemps(type_check_kind));
Artem Serovcfbe9132016-10-14 15:58:56 +01005248}
5249
5250void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) {
5251 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
5252 LocationSummary* locations = instruction->GetLocations();
5253 Location obj_loc = locations->InAt(0);
5254 vixl32::Register obj = InputRegisterAt(instruction, 0);
5255 vixl32::Register cls = InputRegisterAt(instruction, 1);
5256 Location out_loc = locations->Out();
5257 vixl32::Register out = OutputRegister(instruction);
Artem Serov657022c2016-11-23 14:19:38 +00005258 const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind);
5259 DCHECK_LE(num_temps, 1u);
5260 Location maybe_temp_loc = (num_temps >= 1) ? locations->GetTemp(0) : Location::NoLocation();
Artem Serovcfbe9132016-10-14 15:58:56 +01005261 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
5262 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
5263 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
5264 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
5265 vixl32::Label done, zero;
5266 SlowPathCodeARMVIXL* slow_path = nullptr;
5267
5268 // Return 0 if `obj` is null.
5269 // avoid null check if we know obj is not null.
5270 if (instruction->MustDoNullCheck()) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00005271 __ CompareAndBranchIfZero(obj, &zero, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01005272 }
5273
Artem Serovcfbe9132016-10-14 15:58:56 +01005274 switch (type_check_kind) {
5275 case TypeCheckKind::kExactCheck: {
Mathieu Chartier6beced42016-11-15 15:51:31 -08005276 // /* HeapReference<Class> */ out = obj->klass_
5277 GenerateReferenceLoadTwoRegisters(instruction,
5278 out_loc,
5279 obj_loc,
5280 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00005281 maybe_temp_loc,
5282 kCompilerReadBarrierOption);
Artem Serovcfbe9132016-10-14 15:58:56 +01005283 __ Cmp(out, cls);
5284 // Classes must be equal for the instanceof to succeed.
5285 __ B(ne, &zero);
5286 __ Mov(out, 1);
5287 __ B(&done);
5288 break;
5289 }
5290
5291 case TypeCheckKind::kAbstractClassCheck: {
Mathieu Chartier6beced42016-11-15 15:51:31 -08005292 // /* HeapReference<Class> */ out = obj->klass_
5293 GenerateReferenceLoadTwoRegisters(instruction,
5294 out_loc,
5295 obj_loc,
5296 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00005297 maybe_temp_loc,
5298 kCompilerReadBarrierOption);
Artem Serovcfbe9132016-10-14 15:58:56 +01005299 // If the class is abstract, we eagerly fetch the super class of the
5300 // object to avoid doing a comparison we know will fail.
5301 vixl32::Label loop;
5302 __ Bind(&loop);
5303 // /* HeapReference<Class> */ out = out->super_class_
Artem Serov657022c2016-11-23 14:19:38 +00005304 GenerateReferenceLoadOneRegister(instruction,
5305 out_loc,
5306 super_offset,
5307 maybe_temp_loc,
5308 kCompilerReadBarrierOption);
Artem Serovcfbe9132016-10-14 15:58:56 +01005309 // If `out` is null, we use it for the result, and jump to `done`.
xueliang.zhongf51bc622016-11-04 09:23:32 +00005310 __ CompareAndBranchIfZero(out, &done, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01005311 __ Cmp(out, cls);
5312 __ B(ne, &loop);
5313 __ Mov(out, 1);
5314 if (zero.IsReferenced()) {
5315 __ B(&done);
5316 }
5317 break;
5318 }
5319
5320 case TypeCheckKind::kClassHierarchyCheck: {
Mathieu Chartier6beced42016-11-15 15:51:31 -08005321 // /* HeapReference<Class> */ out = obj->klass_
5322 GenerateReferenceLoadTwoRegisters(instruction,
5323 out_loc,
5324 obj_loc,
5325 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00005326 maybe_temp_loc,
5327 kCompilerReadBarrierOption);
Artem Serovcfbe9132016-10-14 15:58:56 +01005328 // Walk over the class hierarchy to find a match.
5329 vixl32::Label loop, success;
5330 __ Bind(&loop);
5331 __ Cmp(out, cls);
5332 __ B(eq, &success);
5333 // /* HeapReference<Class> */ out = out->super_class_
Artem Serov657022c2016-11-23 14:19:38 +00005334 GenerateReferenceLoadOneRegister(instruction,
5335 out_loc,
5336 super_offset,
5337 maybe_temp_loc,
5338 kCompilerReadBarrierOption);
xueliang.zhongf51bc622016-11-04 09:23:32 +00005339 __ CompareAndBranchIfNonZero(out, &loop);
Artem Serovcfbe9132016-10-14 15:58:56 +01005340 // If `out` is null, we use it for the result, and jump to `done`.
5341 __ B(&done);
5342 __ Bind(&success);
5343 __ Mov(out, 1);
5344 if (zero.IsReferenced()) {
5345 __ B(&done);
5346 }
5347 break;
5348 }
5349
5350 case TypeCheckKind::kArrayObjectCheck: {
Mathieu Chartier6beced42016-11-15 15:51:31 -08005351 // /* HeapReference<Class> */ out = obj->klass_
5352 GenerateReferenceLoadTwoRegisters(instruction,
5353 out_loc,
5354 obj_loc,
5355 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00005356 maybe_temp_loc,
5357 kCompilerReadBarrierOption);
Artem Serovcfbe9132016-10-14 15:58:56 +01005358 // Do an exact check.
5359 vixl32::Label exact_check;
5360 __ Cmp(out, cls);
5361 __ B(eq, &exact_check);
5362 // Otherwise, we need to check that the object's class is a non-primitive array.
5363 // /* HeapReference<Class> */ out = out->component_type_
Artem Serov657022c2016-11-23 14:19:38 +00005364 GenerateReferenceLoadOneRegister(instruction,
5365 out_loc,
5366 component_offset,
5367 maybe_temp_loc,
5368 kCompilerReadBarrierOption);
Artem Serovcfbe9132016-10-14 15:58:56 +01005369 // If `out` is null, we use it for the result, and jump to `done`.
xueliang.zhongf51bc622016-11-04 09:23:32 +00005370 __ CompareAndBranchIfZero(out, &done, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01005371 GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset);
5372 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
xueliang.zhongf51bc622016-11-04 09:23:32 +00005373 __ CompareAndBranchIfNonZero(out, &zero, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01005374 __ Bind(&exact_check);
5375 __ Mov(out, 1);
5376 __ B(&done);
5377 break;
5378 }
5379
5380 case TypeCheckKind::kArrayCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00005381 // No read barrier since the slow path will retry upon failure.
Mathieu Chartier6beced42016-11-15 15:51:31 -08005382 // /* HeapReference<Class> */ out = obj->klass_
5383 GenerateReferenceLoadTwoRegisters(instruction,
5384 out_loc,
5385 obj_loc,
5386 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00005387 maybe_temp_loc,
5388 kWithoutReadBarrier);
Artem Serovcfbe9132016-10-14 15:58:56 +01005389 __ Cmp(out, cls);
5390 DCHECK(locations->OnlyCallsOnSlowPath());
5391 slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARMVIXL(instruction,
5392 /* is_fatal */ false);
5393 codegen_->AddSlowPath(slow_path);
5394 __ B(ne, slow_path->GetEntryLabel());
5395 __ Mov(out, 1);
5396 if (zero.IsReferenced()) {
5397 __ B(&done);
5398 }
5399 break;
5400 }
5401
5402 case TypeCheckKind::kUnresolvedCheck:
5403 case TypeCheckKind::kInterfaceCheck: {
5404 // Note that we indeed only call on slow path, but we always go
5405 // into the slow path for the unresolved and interface check
5406 // cases.
5407 //
5408 // We cannot directly call the InstanceofNonTrivial runtime
5409 // entry point without resorting to a type checking slow path
5410 // here (i.e. by calling InvokeRuntime directly), as it would
5411 // require to assign fixed registers for the inputs of this
5412 // HInstanceOf instruction (following the runtime calling
5413 // convention), which might be cluttered by the potential first
5414 // read barrier emission at the beginning of this method.
5415 //
5416 // TODO: Introduce a new runtime entry point taking the object
5417 // to test (instead of its class) as argument, and let it deal
5418 // with the read barrier issues. This will let us refactor this
5419 // case of the `switch` code as it was previously (with a direct
5420 // call to the runtime not using a type checking slow path).
5421 // This should also be beneficial for the other cases above.
5422 DCHECK(locations->OnlyCallsOnSlowPath());
5423 slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARMVIXL(instruction,
5424 /* is_fatal */ false);
5425 codegen_->AddSlowPath(slow_path);
5426 __ B(slow_path->GetEntryLabel());
5427 if (zero.IsReferenced()) {
5428 __ B(&done);
5429 }
5430 break;
5431 }
5432 }
5433
5434 if (zero.IsReferenced()) {
5435 __ Bind(&zero);
5436 __ Mov(out, 0);
5437 }
5438
5439 if (done.IsReferenced()) {
5440 __ Bind(&done);
5441 }
5442
5443 if (slow_path != nullptr) {
5444 __ Bind(slow_path->GetExitLabel());
5445 }
5446}
5447
Anton Kirilove28d9ae2016-10-25 18:17:23 +01005448void LocationsBuilderARMVIXL::VisitCheckCast(HCheckCast* instruction) {
5449 LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
5450 bool throws_into_catch = instruction->CanThrowIntoCatchBlock();
5451
5452 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
5453 switch (type_check_kind) {
5454 case TypeCheckKind::kExactCheck:
5455 case TypeCheckKind::kAbstractClassCheck:
5456 case TypeCheckKind::kClassHierarchyCheck:
5457 case TypeCheckKind::kArrayObjectCheck:
5458 call_kind = (throws_into_catch || kEmitCompilerReadBarrier) ?
5459 LocationSummary::kCallOnSlowPath :
5460 LocationSummary::kNoCall; // In fact, call on a fatal (non-returning) slow path.
5461 break;
5462 case TypeCheckKind::kArrayCheck:
5463 case TypeCheckKind::kUnresolvedCheck:
5464 case TypeCheckKind::kInterfaceCheck:
5465 call_kind = LocationSummary::kCallOnSlowPath;
5466 break;
5467 }
5468
5469 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
5470 locations->SetInAt(0, Location::RequiresRegister());
5471 locations->SetInAt(1, Location::RequiresRegister());
Artem Serov657022c2016-11-23 14:19:38 +00005472 locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind));
Anton Kirilove28d9ae2016-10-25 18:17:23 +01005473}
5474
5475void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) {
5476 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
5477 LocationSummary* locations = instruction->GetLocations();
5478 Location obj_loc = locations->InAt(0);
5479 vixl32::Register obj = InputRegisterAt(instruction, 0);
5480 vixl32::Register cls = InputRegisterAt(instruction, 1);
5481 Location temp_loc = locations->GetTemp(0);
5482 vixl32::Register temp = RegisterFrom(temp_loc);
Artem Serov657022c2016-11-23 14:19:38 +00005483 const size_t num_temps = NumberOfCheckCastTemps(type_check_kind);
5484 DCHECK_LE(num_temps, 3u);
5485 Location maybe_temp2_loc = (num_temps >= 2) ? locations->GetTemp(1) : Location::NoLocation();
5486 Location maybe_temp3_loc = (num_temps >= 3) ? locations->GetTemp(2) : Location::NoLocation();
5487 const uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
5488 const uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
5489 const uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
5490 const uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
5491 const uint32_t iftable_offset = mirror::Class::IfTableOffset().Uint32Value();
5492 const uint32_t array_length_offset = mirror::Array::LengthOffset().Uint32Value();
5493 const uint32_t object_array_data_offset =
5494 mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value();
Anton Kirilove28d9ae2016-10-25 18:17:23 +01005495
Artem Serov657022c2016-11-23 14:19:38 +00005496 // Always false for read barriers since we may need to go to the entrypoint for non-fatal cases
5497 // from false negatives. The false negatives may come from avoiding read barriers below. Avoiding
5498 // read barriers is done for performance and code size reasons.
5499 bool is_type_check_slow_path_fatal = false;
5500 if (!kEmitCompilerReadBarrier) {
5501 is_type_check_slow_path_fatal =
5502 (type_check_kind == TypeCheckKind::kExactCheck ||
5503 type_check_kind == TypeCheckKind::kAbstractClassCheck ||
5504 type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
5505 type_check_kind == TypeCheckKind::kArrayObjectCheck) &&
5506 !instruction->CanThrowIntoCatchBlock();
5507 }
Anton Kirilove28d9ae2016-10-25 18:17:23 +01005508 SlowPathCodeARMVIXL* type_check_slow_path =
5509 new (GetGraph()->GetArena()) TypeCheckSlowPathARMVIXL(instruction,
5510 is_type_check_slow_path_fatal);
5511 codegen_->AddSlowPath(type_check_slow_path);
5512
5513 vixl32::Label done;
5514 // Avoid null check if we know obj is not null.
5515 if (instruction->MustDoNullCheck()) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00005516 __ CompareAndBranchIfZero(obj, &done, /* far_target */ false);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01005517 }
5518
Anton Kirilove28d9ae2016-10-25 18:17:23 +01005519 switch (type_check_kind) {
5520 case TypeCheckKind::kExactCheck:
5521 case TypeCheckKind::kArrayCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00005522 // /* HeapReference<Class> */ temp = obj->klass_
5523 GenerateReferenceLoadTwoRegisters(instruction,
5524 temp_loc,
5525 obj_loc,
5526 class_offset,
5527 maybe_temp2_loc,
5528 kWithoutReadBarrier);
5529
Anton Kirilove28d9ae2016-10-25 18:17:23 +01005530 __ Cmp(temp, cls);
5531 // Jump to slow path for throwing the exception or doing a
5532 // more involved array check.
5533 __ B(ne, type_check_slow_path->GetEntryLabel());
5534 break;
5535 }
5536
5537 case TypeCheckKind::kAbstractClassCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00005538 // /* HeapReference<Class> */ temp = obj->klass_
5539 GenerateReferenceLoadTwoRegisters(instruction,
5540 temp_loc,
5541 obj_loc,
5542 class_offset,
5543 maybe_temp2_loc,
5544 kWithoutReadBarrier);
5545
Artem Serovcfbe9132016-10-14 15:58:56 +01005546 // If the class is abstract, we eagerly fetch the super class of the
5547 // object to avoid doing a comparison we know will fail.
5548 vixl32::Label loop;
5549 __ Bind(&loop);
5550 // /* HeapReference<Class> */ temp = temp->super_class_
Artem Serov657022c2016-11-23 14:19:38 +00005551 GenerateReferenceLoadOneRegister(instruction,
5552 temp_loc,
5553 super_offset,
5554 maybe_temp2_loc,
5555 kWithoutReadBarrier);
Artem Serovcfbe9132016-10-14 15:58:56 +01005556
5557 // If the class reference currently in `temp` is null, jump to the slow path to throw the
5558 // exception.
xueliang.zhongf51bc622016-11-04 09:23:32 +00005559 __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
Artem Serovcfbe9132016-10-14 15:58:56 +01005560
5561 // Otherwise, compare the classes.
5562 __ Cmp(temp, cls);
5563 __ B(ne, &loop);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01005564 break;
5565 }
5566
5567 case TypeCheckKind::kClassHierarchyCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00005568 // /* HeapReference<Class> */ temp = obj->klass_
5569 GenerateReferenceLoadTwoRegisters(instruction,
5570 temp_loc,
5571 obj_loc,
5572 class_offset,
5573 maybe_temp2_loc,
5574 kWithoutReadBarrier);
5575
Artem Serovcfbe9132016-10-14 15:58:56 +01005576 // Walk over the class hierarchy to find a match.
5577 vixl32::Label loop;
5578 __ Bind(&loop);
5579 __ Cmp(temp, cls);
5580 __ B(eq, &done);
5581
5582 // /* HeapReference<Class> */ temp = temp->super_class_
Artem Serov657022c2016-11-23 14:19:38 +00005583 GenerateReferenceLoadOneRegister(instruction,
5584 temp_loc,
5585 super_offset,
5586 maybe_temp2_loc,
5587 kWithoutReadBarrier);
Artem Serovcfbe9132016-10-14 15:58:56 +01005588
5589 // If the class reference currently in `temp` is null, jump to the slow path to throw the
5590 // exception.
xueliang.zhongf51bc622016-11-04 09:23:32 +00005591 __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
Artem Serovcfbe9132016-10-14 15:58:56 +01005592 // Otherwise, jump to the beginning of the loop.
5593 __ B(&loop);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01005594 break;
5595 }
5596
Artem Serovcfbe9132016-10-14 15:58:56 +01005597 case TypeCheckKind::kArrayObjectCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00005598 // /* HeapReference<Class> */ temp = obj->klass_
5599 GenerateReferenceLoadTwoRegisters(instruction,
5600 temp_loc,
5601 obj_loc,
5602 class_offset,
5603 maybe_temp2_loc,
5604 kWithoutReadBarrier);
5605
Artem Serovcfbe9132016-10-14 15:58:56 +01005606 // Do an exact check.
5607 __ Cmp(temp, cls);
5608 __ B(eq, &done);
5609
5610 // Otherwise, we need to check that the object's class is a non-primitive array.
5611 // /* HeapReference<Class> */ temp = temp->component_type_
Artem Serov657022c2016-11-23 14:19:38 +00005612 GenerateReferenceLoadOneRegister(instruction,
5613 temp_loc,
5614 component_offset,
5615 maybe_temp2_loc,
5616 kWithoutReadBarrier);
Artem Serovcfbe9132016-10-14 15:58:56 +01005617 // If the component type is null, jump to the slow path to throw the exception.
xueliang.zhongf51bc622016-11-04 09:23:32 +00005618 __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
Artem Serovcfbe9132016-10-14 15:58:56 +01005619 // Otherwise,the object is indeed an array, jump to label `check_non_primitive_component_type`
5620 // to further check that this component type is not a primitive type.
5621 GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, temp, temp, primitive_offset);
5622 static_assert(Primitive::kPrimNot == 0, "Expected 0 for art::Primitive::kPrimNot");
xueliang.zhongf51bc622016-11-04 09:23:32 +00005623 __ CompareAndBranchIfNonZero(temp, type_check_slow_path->GetEntryLabel());
Anton Kirilove28d9ae2016-10-25 18:17:23 +01005624 break;
5625 }
5626
5627 case TypeCheckKind::kUnresolvedCheck:
Artem Serov657022c2016-11-23 14:19:38 +00005628 // We always go into the type check slow path for the unresolved check case.
Artem Serovcfbe9132016-10-14 15:58:56 +01005629 // We cannot directly call the CheckCast runtime entry point
5630 // without resorting to a type checking slow path here (i.e. by
5631 // calling InvokeRuntime directly), as it would require to
5632 // assign fixed registers for the inputs of this HInstanceOf
5633 // instruction (following the runtime calling convention), which
5634 // might be cluttered by the potential first read barrier
5635 // emission at the beginning of this method.
Artem Serov657022c2016-11-23 14:19:38 +00005636
Artem Serovcfbe9132016-10-14 15:58:56 +01005637 __ B(type_check_slow_path->GetEntryLabel());
Anton Kirilove28d9ae2016-10-25 18:17:23 +01005638 break;
Artem Serov657022c2016-11-23 14:19:38 +00005639
5640 case TypeCheckKind::kInterfaceCheck: {
5641 // Avoid read barriers to improve performance of the fast path. We can not get false
5642 // positives by doing this.
5643 // /* HeapReference<Class> */ temp = obj->klass_
5644 GenerateReferenceLoadTwoRegisters(instruction,
5645 temp_loc,
5646 obj_loc,
5647 class_offset,
5648 maybe_temp2_loc,
5649 kWithoutReadBarrier);
5650
5651 // /* HeapReference<Class> */ temp = temp->iftable_
5652 GenerateReferenceLoadTwoRegisters(instruction,
5653 temp_loc,
5654 temp_loc,
5655 iftable_offset,
5656 maybe_temp2_loc,
5657 kWithoutReadBarrier);
5658 // Iftable is never null.
5659 __ Ldr(RegisterFrom(maybe_temp2_loc), MemOperand(temp, array_length_offset));
5660 // Loop through the iftable and check if any class matches.
5661 vixl32::Label start_loop;
5662 __ Bind(&start_loop);
5663 __ CompareAndBranchIfZero(RegisterFrom(maybe_temp2_loc),
5664 type_check_slow_path->GetEntryLabel());
5665 __ Ldr(RegisterFrom(maybe_temp3_loc), MemOperand(temp, object_array_data_offset));
5666 GetAssembler()->MaybeUnpoisonHeapReference(RegisterFrom(maybe_temp3_loc));
5667 // Go to next interface.
5668 __ Add(temp, temp, Operand::From(2 * kHeapReferenceSize));
5669 __ Sub(RegisterFrom(maybe_temp2_loc), RegisterFrom(maybe_temp2_loc), 2);
5670 // Compare the classes and continue the loop if they do not match.
5671 __ Cmp(cls, RegisterFrom(maybe_temp3_loc));
5672 __ B(ne, &start_loop);
5673 break;
5674 }
Anton Kirilove28d9ae2016-10-25 18:17:23 +01005675 }
5676 __ Bind(&done);
5677
5678 __ Bind(type_check_slow_path->GetExitLabel());
5679}
5680
Artem Serov551b28f2016-10-18 19:11:30 +01005681void LocationsBuilderARMVIXL::VisitMonitorOperation(HMonitorOperation* instruction) {
5682 LocationSummary* locations =
5683 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
5684 InvokeRuntimeCallingConventionARMVIXL calling_convention;
5685 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
5686}
5687
5688void InstructionCodeGeneratorARMVIXL::VisitMonitorOperation(HMonitorOperation* instruction) {
5689 codegen_->InvokeRuntime(instruction->IsEnter() ? kQuickLockObject : kQuickUnlockObject,
5690 instruction,
5691 instruction->GetDexPc());
5692 if (instruction->IsEnter()) {
5693 CheckEntrypointTypes<kQuickLockObject, void, mirror::Object*>();
5694 } else {
5695 CheckEntrypointTypes<kQuickUnlockObject, void, mirror::Object*>();
5696 }
5697}
5698
Artem Serov02109dd2016-09-23 17:17:54 +01005699void LocationsBuilderARMVIXL::VisitAnd(HAnd* instruction) {
5700 HandleBitwiseOperation(instruction, AND);
5701}
5702
5703void LocationsBuilderARMVIXL::VisitOr(HOr* instruction) {
5704 HandleBitwiseOperation(instruction, ORR);
5705}
5706
5707void LocationsBuilderARMVIXL::VisitXor(HXor* instruction) {
5708 HandleBitwiseOperation(instruction, EOR);
5709}
5710
5711void LocationsBuilderARMVIXL::HandleBitwiseOperation(HBinaryOperation* instruction, Opcode opcode) {
5712 LocationSummary* locations =
5713 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
5714 DCHECK(instruction->GetResultType() == Primitive::kPrimInt
5715 || instruction->GetResultType() == Primitive::kPrimLong);
5716 // Note: GVN reorders commutative operations to have the constant on the right hand side.
5717 locations->SetInAt(0, Location::RequiresRegister());
5718 locations->SetInAt(1, ArmEncodableConstantOrRegister(instruction->InputAt(1), opcode));
5719 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5720}
5721
5722void InstructionCodeGeneratorARMVIXL::VisitAnd(HAnd* instruction) {
5723 HandleBitwiseOperation(instruction);
5724}
5725
5726void InstructionCodeGeneratorARMVIXL::VisitOr(HOr* instruction) {
5727 HandleBitwiseOperation(instruction);
5728}
5729
5730void InstructionCodeGeneratorARMVIXL::VisitXor(HXor* instruction) {
5731 HandleBitwiseOperation(instruction);
5732}
5733
Artem Serov2bbc9532016-10-21 11:51:50 +01005734void LocationsBuilderARMVIXL::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) {
5735 LocationSummary* locations =
5736 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
5737 DCHECK(instruction->GetResultType() == Primitive::kPrimInt
5738 || instruction->GetResultType() == Primitive::kPrimLong);
5739
5740 locations->SetInAt(0, Location::RequiresRegister());
5741 locations->SetInAt(1, Location::RequiresRegister());
5742 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5743}
5744
5745void InstructionCodeGeneratorARMVIXL::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) {
5746 LocationSummary* locations = instruction->GetLocations();
5747 Location first = locations->InAt(0);
5748 Location second = locations->InAt(1);
5749 Location out = locations->Out();
5750
5751 if (instruction->GetResultType() == Primitive::kPrimInt) {
5752 vixl32::Register first_reg = RegisterFrom(first);
5753 vixl32::Register second_reg = RegisterFrom(second);
5754 vixl32::Register out_reg = RegisterFrom(out);
5755
5756 switch (instruction->GetOpKind()) {
5757 case HInstruction::kAnd:
5758 __ Bic(out_reg, first_reg, second_reg);
5759 break;
5760 case HInstruction::kOr:
5761 __ Orn(out_reg, first_reg, second_reg);
5762 break;
5763 // There is no EON on arm.
5764 case HInstruction::kXor:
5765 default:
5766 LOG(FATAL) << "Unexpected instruction " << instruction->DebugName();
5767 UNREACHABLE();
5768 }
5769 return;
5770
5771 } else {
5772 DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong);
5773 vixl32::Register first_low = LowRegisterFrom(first);
5774 vixl32::Register first_high = HighRegisterFrom(first);
5775 vixl32::Register second_low = LowRegisterFrom(second);
5776 vixl32::Register second_high = HighRegisterFrom(second);
5777 vixl32::Register out_low = LowRegisterFrom(out);
5778 vixl32::Register out_high = HighRegisterFrom(out);
5779
5780 switch (instruction->GetOpKind()) {
5781 case HInstruction::kAnd:
5782 __ Bic(out_low, first_low, second_low);
5783 __ Bic(out_high, first_high, second_high);
5784 break;
5785 case HInstruction::kOr:
5786 __ Orn(out_low, first_low, second_low);
5787 __ Orn(out_high, first_high, second_high);
5788 break;
5789 // There is no EON on arm.
5790 case HInstruction::kXor:
5791 default:
5792 LOG(FATAL) << "Unexpected instruction " << instruction->DebugName();
5793 UNREACHABLE();
5794 }
5795 }
5796}
5797
Artem Serov02109dd2016-09-23 17:17:54 +01005798// TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
5799void InstructionCodeGeneratorARMVIXL::GenerateAndConst(vixl32::Register out,
5800 vixl32::Register first,
5801 uint32_t value) {
5802 // Optimize special cases for individual halfs of `and-long` (`and` is simplified earlier).
5803 if (value == 0xffffffffu) {
5804 if (!out.Is(first)) {
5805 __ Mov(out, first);
5806 }
5807 return;
5808 }
5809 if (value == 0u) {
5810 __ Mov(out, 0);
5811 return;
5812 }
5813 if (GetAssembler()->ShifterOperandCanHold(AND, value)) {
5814 __ And(out, first, value);
5815 } else {
5816 DCHECK(GetAssembler()->ShifterOperandCanHold(BIC, ~value));
5817 __ Bic(out, first, ~value);
5818 }
5819}
5820
5821// TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
5822void InstructionCodeGeneratorARMVIXL::GenerateOrrConst(vixl32::Register out,
5823 vixl32::Register first,
5824 uint32_t value) {
5825 // Optimize special cases for individual halfs of `or-long` (`or` is simplified earlier).
5826 if (value == 0u) {
5827 if (!out.Is(first)) {
5828 __ Mov(out, first);
5829 }
5830 return;
5831 }
5832 if (value == 0xffffffffu) {
5833 __ Mvn(out, 0);
5834 return;
5835 }
5836 if (GetAssembler()->ShifterOperandCanHold(ORR, value)) {
5837 __ Orr(out, first, value);
5838 } else {
5839 DCHECK(GetAssembler()->ShifterOperandCanHold(ORN, ~value));
5840 __ Orn(out, first, ~value);
5841 }
5842}
5843
5844// TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
5845void InstructionCodeGeneratorARMVIXL::GenerateEorConst(vixl32::Register out,
5846 vixl32::Register first,
5847 uint32_t value) {
5848 // Optimize special case for individual halfs of `xor-long` (`xor` is simplified earlier).
5849 if (value == 0u) {
5850 if (!out.Is(first)) {
5851 __ Mov(out, first);
5852 }
5853 return;
5854 }
5855 __ Eor(out, first, value);
5856}
5857
Anton Kirilovdda43962016-11-21 19:55:20 +00005858void InstructionCodeGeneratorARMVIXL::GenerateAddLongConst(Location out,
5859 Location first,
5860 uint64_t value) {
5861 vixl32::Register out_low = LowRegisterFrom(out);
5862 vixl32::Register out_high = HighRegisterFrom(out);
5863 vixl32::Register first_low = LowRegisterFrom(first);
5864 vixl32::Register first_high = HighRegisterFrom(first);
5865 uint32_t value_low = Low32Bits(value);
5866 uint32_t value_high = High32Bits(value);
5867 if (value_low == 0u) {
5868 if (!out_low.Is(first_low)) {
5869 __ Mov(out_low, first_low);
5870 }
5871 __ Add(out_high, first_high, value_high);
5872 return;
5873 }
5874 __ Adds(out_low, first_low, value_low);
5875 if (GetAssembler()->ShifterOperandCanHold(ADC, value_high, kCcKeep)) {
5876 __ Adc(out_high, first_high, value_high);
5877 } else if (GetAssembler()->ShifterOperandCanHold(SBC, ~value_high, kCcKeep)) {
5878 __ Sbc(out_high, first_high, ~value_high);
5879 } else {
5880 LOG(FATAL) << "Unexpected constant " << value_high;
5881 UNREACHABLE();
5882 }
5883}
5884
Artem Serov02109dd2016-09-23 17:17:54 +01005885void InstructionCodeGeneratorARMVIXL::HandleBitwiseOperation(HBinaryOperation* instruction) {
5886 LocationSummary* locations = instruction->GetLocations();
5887 Location first = locations->InAt(0);
5888 Location second = locations->InAt(1);
5889 Location out = locations->Out();
5890
5891 if (second.IsConstant()) {
5892 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
5893 uint32_t value_low = Low32Bits(value);
5894 if (instruction->GetResultType() == Primitive::kPrimInt) {
5895 vixl32::Register first_reg = InputRegisterAt(instruction, 0);
5896 vixl32::Register out_reg = OutputRegister(instruction);
5897 if (instruction->IsAnd()) {
5898 GenerateAndConst(out_reg, first_reg, value_low);
5899 } else if (instruction->IsOr()) {
5900 GenerateOrrConst(out_reg, first_reg, value_low);
5901 } else {
5902 DCHECK(instruction->IsXor());
5903 GenerateEorConst(out_reg, first_reg, value_low);
5904 }
5905 } else {
5906 DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong);
5907 uint32_t value_high = High32Bits(value);
5908 vixl32::Register first_low = LowRegisterFrom(first);
5909 vixl32::Register first_high = HighRegisterFrom(first);
5910 vixl32::Register out_low = LowRegisterFrom(out);
5911 vixl32::Register out_high = HighRegisterFrom(out);
5912 if (instruction->IsAnd()) {
5913 GenerateAndConst(out_low, first_low, value_low);
5914 GenerateAndConst(out_high, first_high, value_high);
5915 } else if (instruction->IsOr()) {
5916 GenerateOrrConst(out_low, first_low, value_low);
5917 GenerateOrrConst(out_high, first_high, value_high);
5918 } else {
5919 DCHECK(instruction->IsXor());
5920 GenerateEorConst(out_low, first_low, value_low);
5921 GenerateEorConst(out_high, first_high, value_high);
5922 }
5923 }
5924 return;
5925 }
5926
5927 if (instruction->GetResultType() == Primitive::kPrimInt) {
5928 vixl32::Register first_reg = InputRegisterAt(instruction, 0);
5929 vixl32::Register second_reg = InputRegisterAt(instruction, 1);
5930 vixl32::Register out_reg = OutputRegister(instruction);
5931 if (instruction->IsAnd()) {
5932 __ And(out_reg, first_reg, second_reg);
5933 } else if (instruction->IsOr()) {
5934 __ Orr(out_reg, first_reg, second_reg);
5935 } else {
5936 DCHECK(instruction->IsXor());
5937 __ Eor(out_reg, first_reg, second_reg);
5938 }
5939 } else {
5940 DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong);
5941 vixl32::Register first_low = LowRegisterFrom(first);
5942 vixl32::Register first_high = HighRegisterFrom(first);
5943 vixl32::Register second_low = LowRegisterFrom(second);
5944 vixl32::Register second_high = HighRegisterFrom(second);
5945 vixl32::Register out_low = LowRegisterFrom(out);
5946 vixl32::Register out_high = HighRegisterFrom(out);
5947 if (instruction->IsAnd()) {
5948 __ And(out_low, first_low, second_low);
5949 __ And(out_high, first_high, second_high);
5950 } else if (instruction->IsOr()) {
5951 __ Orr(out_low, first_low, second_low);
5952 __ Orr(out_high, first_high, second_high);
5953 } else {
5954 DCHECK(instruction->IsXor());
5955 __ Eor(out_low, first_low, second_low);
5956 __ Eor(out_high, first_high, second_high);
5957 }
5958 }
5959}
5960
Artem Serovcfbe9132016-10-14 15:58:56 +01005961void InstructionCodeGeneratorARMVIXL::GenerateReferenceLoadOneRegister(
5962 HInstruction* instruction ATTRIBUTE_UNUSED,
5963 Location out,
5964 uint32_t offset,
Artem Serov657022c2016-11-23 14:19:38 +00005965 Location maybe_temp ATTRIBUTE_UNUSED,
5966 ReadBarrierOption read_barrier_option ATTRIBUTE_UNUSED) {
Artem Serovcfbe9132016-10-14 15:58:56 +01005967 vixl32::Register out_reg = RegisterFrom(out);
5968 if (kEmitCompilerReadBarrier) {
5969 TODO_VIXL32(FATAL);
5970 } else {
5971 // Plain load with no read barrier.
5972 // /* HeapReference<Object> */ out = *(out + offset)
5973 GetAssembler()->LoadFromOffset(kLoadWord, out_reg, out_reg, offset);
5974 GetAssembler()->MaybeUnpoisonHeapReference(out_reg);
5975 }
5976}
5977
Anton Kirilove28d9ae2016-10-25 18:17:23 +01005978void InstructionCodeGeneratorARMVIXL::GenerateReferenceLoadTwoRegisters(
5979 HInstruction* instruction ATTRIBUTE_UNUSED,
5980 Location out,
5981 Location obj,
5982 uint32_t offset,
Artem Serov657022c2016-11-23 14:19:38 +00005983 Location maybe_temp ATTRIBUTE_UNUSED,
5984 ReadBarrierOption read_barrier_option ATTRIBUTE_UNUSED) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01005985 vixl32::Register out_reg = RegisterFrom(out);
5986 vixl32::Register obj_reg = RegisterFrom(obj);
5987 if (kEmitCompilerReadBarrier) {
5988 TODO_VIXL32(FATAL);
5989 } else {
5990 // Plain load with no read barrier.
5991 // /* HeapReference<Object> */ out = *(obj + offset)
5992 GetAssembler()->LoadFromOffset(kLoadWord, out_reg, obj_reg, offset);
5993 GetAssembler()->MaybeUnpoisonHeapReference(out_reg);
5994 }
5995}
5996
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005997void InstructionCodeGeneratorARMVIXL::GenerateGcRootFieldLoad(
5998 HInstruction* instruction ATTRIBUTE_UNUSED,
5999 Location root,
6000 vixl32::Register obj,
6001 uint32_t offset,
6002 bool requires_read_barrier) {
6003 vixl32::Register root_reg = RegisterFrom(root);
6004 if (requires_read_barrier) {
6005 TODO_VIXL32(FATAL);
6006 } else {
6007 // Plain GC root load with no read barrier.
6008 // /* GcRoot<mirror::Object> */ root = *(obj + offset)
6009 GetAssembler()->LoadFromOffset(kLoadWord, root_reg, obj, offset);
6010 // Note that GC roots are not affected by heap poisoning, thus we
6011 // do not have to unpoison `root_reg` here.
6012 }
6013}
6014
Roland Levillain6070e882016-11-03 17:51:58 +00006015void CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier(
6016 HInstruction* instruction ATTRIBUTE_UNUSED,
6017 Location ref ATTRIBUTE_UNUSED,
6018 vixl::aarch32::Register obj ATTRIBUTE_UNUSED,
6019 uint32_t offset ATTRIBUTE_UNUSED,
6020 Location temp ATTRIBUTE_UNUSED,
6021 bool needs_null_check ATTRIBUTE_UNUSED) {
6022 TODO_VIXL32(FATAL);
6023}
6024
6025void CodeGeneratorARMVIXL::GenerateReferenceLoadWithBakerReadBarrier(
6026 HInstruction* instruction ATTRIBUTE_UNUSED,
6027 Location ref ATTRIBUTE_UNUSED,
6028 vixl::aarch32::Register obj ATTRIBUTE_UNUSED,
6029 uint32_t offset ATTRIBUTE_UNUSED,
6030 Location index ATTRIBUTE_UNUSED,
6031 ScaleFactor scale_factor ATTRIBUTE_UNUSED,
6032 Location temp ATTRIBUTE_UNUSED,
6033 bool needs_null_check ATTRIBUTE_UNUSED,
6034 bool always_update_field ATTRIBUTE_UNUSED,
6035 vixl::aarch32::Register* temp2 ATTRIBUTE_UNUSED) {
6036 TODO_VIXL32(FATAL);
6037}
6038
Roland Levillain844e6532016-11-03 16:09:47 +00006039void CodeGeneratorARMVIXL::GenerateReadBarrierSlow(HInstruction* instruction ATTRIBUTE_UNUSED,
6040 Location out ATTRIBUTE_UNUSED,
6041 Location ref ATTRIBUTE_UNUSED,
6042 Location obj ATTRIBUTE_UNUSED,
6043 uint32_t offset ATTRIBUTE_UNUSED,
6044 Location index ATTRIBUTE_UNUSED) {
6045 TODO_VIXL32(FATAL);
6046}
6047
Artem Serov02d37832016-10-25 15:25:33 +01006048void CodeGeneratorARMVIXL::MaybeGenerateReadBarrierSlow(HInstruction* instruction ATTRIBUTE_UNUSED,
6049 Location out,
6050 Location ref ATTRIBUTE_UNUSED,
6051 Location obj ATTRIBUTE_UNUSED,
6052 uint32_t offset ATTRIBUTE_UNUSED,
6053 Location index ATTRIBUTE_UNUSED) {
6054 if (kEmitCompilerReadBarrier) {
6055 DCHECK(!kUseBakerReadBarrier);
6056 TODO_VIXL32(FATAL);
6057 } else if (kPoisonHeapReferences) {
6058 GetAssembler()->UnpoisonHeapReference(RegisterFrom(out));
6059 }
6060}
6061
6062// Check if the desired_dispatch_info is supported. If it is, return it,
6063// otherwise return a fall-back info that should be used instead.
6064HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARMVIXL::GetSupportedInvokeStaticOrDirectDispatch(
6065 const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info ATTRIBUTE_UNUSED,
6066 HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
6067 // TODO(VIXL): Implement optimized code paths.
6068 return {
6069 HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod,
6070 HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
6071 0u,
6072 0u
6073 };
6074}
6075
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006076vixl32::Register CodeGeneratorARMVIXL::GetInvokeStaticOrDirectExtraParameter(
6077 HInvokeStaticOrDirect* invoke, vixl32::Register temp) {
6078 DCHECK_EQ(invoke->InputCount(), invoke->GetNumberOfArguments() + 1u);
6079 Location location = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
6080 if (!invoke->GetLocations()->Intrinsified()) {
6081 return RegisterFrom(location);
6082 }
6083 // For intrinsics we allow any location, so it may be on the stack.
6084 if (!location.IsRegister()) {
6085 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, location.GetStackIndex());
6086 return temp;
6087 }
6088 // For register locations, check if the register was saved. If so, get it from the stack.
6089 // Note: There is a chance that the register was saved but not overwritten, so we could
6090 // save one load. However, since this is just an intrinsic slow path we prefer this
6091 // simple and more robust approach rather that trying to determine if that's the case.
6092 SlowPathCode* slow_path = GetCurrentSlowPath();
6093 DCHECK(slow_path != nullptr); // For intrinsified invokes the call is emitted on the slow path.
6094 if (slow_path->IsCoreRegisterSaved(RegisterFrom(location).GetCode())) {
6095 int stack_offset = slow_path->GetStackOffsetOfCoreRegister(RegisterFrom(location).GetCode());
6096 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, stack_offset);
6097 return temp;
6098 }
6099 return RegisterFrom(location);
6100}
6101
6102void CodeGeneratorARMVIXL::GenerateStaticOrDirectCall(
6103 HInvokeStaticOrDirect* invoke, Location temp) {
6104 Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp.
6105 vixl32::Register temp_reg = RegisterFrom(temp);
6106
6107 switch (invoke->GetMethodLoadKind()) {
6108 case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: {
6109 uint32_t offset =
6110 GetThreadOffset<kArmPointerSize>(invoke->GetStringInitEntryPoint()).Int32Value();
6111 // temp = thread->string_init_entrypoint
6112 GetAssembler()->LoadFromOffset(kLoadWord, temp_reg, tr, offset);
6113 break;
6114 }
6115 case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: {
6116 Location current_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
6117 vixl32::Register method_reg;
6118 if (current_method.IsRegister()) {
6119 method_reg = RegisterFrom(current_method);
6120 } else {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006121 DCHECK(invoke->GetLocations()->Intrinsified());
6122 DCHECK(!current_method.IsValid());
6123 method_reg = temp_reg;
6124 GetAssembler()->LoadFromOffset(kLoadWord, temp_reg, sp, kCurrentMethodStackOffset);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006125 }
6126 // /* ArtMethod*[] */ temp = temp.ptr_sized_fields_->dex_cache_resolved_methods_;
6127 GetAssembler()->LoadFromOffset(
6128 kLoadWord,
6129 temp_reg,
6130 method_reg,
6131 ArtMethod::DexCacheResolvedMethodsOffset(kArmPointerSize).Int32Value());
6132 // temp = temp[index_in_cache];
6133 // Note: Don't use invoke->GetTargetMethod() as it may point to a different dex file.
6134 uint32_t index_in_cache = invoke->GetDexMethodIndex();
6135 GetAssembler()->LoadFromOffset(
6136 kLoadWord, temp_reg, temp_reg, CodeGenerator::GetCachePointerOffset(index_in_cache));
6137 break;
6138 }
6139 default:
6140 TODO_VIXL32(FATAL);
6141 }
6142
6143 // TODO(VIXL): Support `CodePtrLocation` values other than `kCallArtMethod`.
6144 if (invoke->GetCodePtrLocation() != HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod) {
6145 TODO_VIXL32(FATAL);
6146 }
6147
6148 // LR = callee_method->entry_point_from_quick_compiled_code_
6149 GetAssembler()->LoadFromOffset(
6150 kLoadWord,
6151 lr,
6152 RegisterFrom(callee_method),
6153 ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value());
6154 // LR()
6155 __ Blx(lr);
6156
6157 DCHECK(!IsLeafMethod());
6158}
6159
6160void CodeGeneratorARMVIXL::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp_location) {
6161 vixl32::Register temp = RegisterFrom(temp_location);
6162 uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
6163 invoke->GetVTableIndex(), kArmPointerSize).Uint32Value();
6164
6165 // Use the calling convention instead of the location of the receiver, as
6166 // intrinsics may have put the receiver in a different register. In the intrinsics
6167 // slow path, the arguments have been moved to the right place, so here we are
6168 // guaranteed that the receiver is the first register of the calling convention.
6169 InvokeDexCallingConventionARMVIXL calling_convention;
6170 vixl32::Register receiver = calling_convention.GetRegisterAt(0);
6171 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
6172 // /* HeapReference<Class> */ temp = receiver->klass_
6173 GetAssembler()->LoadFromOffset(kLoadWord, temp, receiver, class_offset);
6174 MaybeRecordImplicitNullCheck(invoke);
6175 // Instead of simply (possibly) unpoisoning `temp` here, we should
6176 // emit a read barrier for the previous class reference load.
6177 // However this is not required in practice, as this is an
6178 // intermediate/temporary reference and because the current
6179 // concurrent copying collector keeps the from-space memory
6180 // intact/accessible until the end of the marking phase (the
6181 // concurrent copying collector may not in the future).
6182 GetAssembler()->MaybeUnpoisonHeapReference(temp);
6183
6184 // temp = temp->GetMethodAt(method_offset);
6185 uint32_t entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(
6186 kArmPointerSize).Int32Value();
6187 GetAssembler()->LoadFromOffset(kLoadWord, temp, temp, method_offset);
6188 // LR = temp->GetEntryPoint();
6189 GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, entry_point);
6190 // LR();
6191 __ Blx(lr);
6192}
6193
Artem Serov2bbc9532016-10-21 11:51:50 +01006194void LocationsBuilderARMVIXL::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
6195 LocationSummary* locations =
6196 new (GetGraph()->GetArena()) LocationSummary(instr, LocationSummary::kNoCall);
6197 locations->SetInAt(HMultiplyAccumulate::kInputAccumulatorIndex,
6198 Location::RequiresRegister());
6199 locations->SetInAt(HMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister());
6200 locations->SetInAt(HMultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister());
6201 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
6202}
6203
6204void InstructionCodeGeneratorARMVIXL::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
6205 vixl32::Register res = OutputRegister(instr);
6206 vixl32::Register accumulator =
6207 InputRegisterAt(instr, HMultiplyAccumulate::kInputAccumulatorIndex);
6208 vixl32::Register mul_left =
6209 InputRegisterAt(instr, HMultiplyAccumulate::kInputMulLeftIndex);
6210 vixl32::Register mul_right =
6211 InputRegisterAt(instr, HMultiplyAccumulate::kInputMulRightIndex);
6212
6213 if (instr->GetOpKind() == HInstruction::kAdd) {
6214 __ Mla(res, mul_left, mul_right, accumulator);
6215 } else {
6216 __ Mls(res, mul_left, mul_right, accumulator);
6217 }
6218}
6219
Artem Serov551b28f2016-10-18 19:11:30 +01006220void LocationsBuilderARMVIXL::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
6221 // Nothing to do, this should be removed during prepare for register allocator.
6222 LOG(FATAL) << "Unreachable";
6223}
6224
6225void InstructionCodeGeneratorARMVIXL::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
6226 // Nothing to do, this should be removed during prepare for register allocator.
6227 LOG(FATAL) << "Unreachable";
6228}
6229
6230// Simple implementation of packed switch - generate cascaded compare/jumps.
6231void LocationsBuilderARMVIXL::VisitPackedSwitch(HPackedSwitch* switch_instr) {
6232 LocationSummary* locations =
6233 new (GetGraph()->GetArena()) LocationSummary(switch_instr, LocationSummary::kNoCall);
6234 locations->SetInAt(0, Location::RequiresRegister());
6235 if (switch_instr->GetNumEntries() > kPackedSwitchCompareJumpThreshold &&
6236 codegen_->GetAssembler()->GetVIXLAssembler()->IsUsingT32()) {
6237 locations->AddTemp(Location::RequiresRegister()); // We need a temp for the table base.
6238 if (switch_instr->GetStartValue() != 0) {
6239 locations->AddTemp(Location::RequiresRegister()); // We need a temp for the bias.
6240 }
6241 }
6242}
6243
6244// TODO(VIXL): Investigate and reach the parity with old arm codegen.
6245void InstructionCodeGeneratorARMVIXL::VisitPackedSwitch(HPackedSwitch* switch_instr) {
6246 int32_t lower_bound = switch_instr->GetStartValue();
6247 uint32_t num_entries = switch_instr->GetNumEntries();
6248 LocationSummary* locations = switch_instr->GetLocations();
6249 vixl32::Register value_reg = InputRegisterAt(switch_instr, 0);
6250 HBasicBlock* default_block = switch_instr->GetDefaultBlock();
6251
6252 if (num_entries <= kPackedSwitchCompareJumpThreshold ||
6253 !codegen_->GetAssembler()->GetVIXLAssembler()->IsUsingT32()) {
6254 // Create a series of compare/jumps.
6255 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
6256 vixl32::Register temp_reg = temps.Acquire();
6257 // Note: It is fine for the below AddConstantSetFlags() using IP register to temporarily store
6258 // the immediate, because IP is used as the destination register. For the other
6259 // AddConstantSetFlags() and GenerateCompareWithImmediate(), the immediate values are constant,
6260 // and they can be encoded in the instruction without making use of IP register.
6261 __ Adds(temp_reg, value_reg, -lower_bound);
6262
6263 const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors();
6264 // Jump to successors[0] if value == lower_bound.
6265 __ B(eq, codegen_->GetLabelOf(successors[0]));
6266 int32_t last_index = 0;
6267 for (; num_entries - last_index > 2; last_index += 2) {
6268 __ Adds(temp_reg, temp_reg, -2);
6269 // Jump to successors[last_index + 1] if value < case_value[last_index + 2].
6270 __ B(lo, codegen_->GetLabelOf(successors[last_index + 1]));
6271 // Jump to successors[last_index + 2] if value == case_value[last_index + 2].
6272 __ B(eq, codegen_->GetLabelOf(successors[last_index + 2]));
6273 }
6274 if (num_entries - last_index == 2) {
6275 // The last missing case_value.
6276 __ Cmp(temp_reg, 1);
6277 __ B(eq, codegen_->GetLabelOf(successors[last_index + 1]));
6278 }
6279
6280 // And the default for any other value.
6281 if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) {
6282 __ B(codegen_->GetLabelOf(default_block));
6283 }
6284 } else {
6285 // Create a table lookup.
6286 vixl32::Register table_base = RegisterFrom(locations->GetTemp(0));
6287
6288 JumpTableARMVIXL* jump_table = codegen_->CreateJumpTable(switch_instr);
6289
6290 // Remove the bias.
6291 vixl32::Register key_reg;
6292 if (lower_bound != 0) {
6293 key_reg = RegisterFrom(locations->GetTemp(1));
6294 __ Sub(key_reg, value_reg, lower_bound);
6295 } else {
6296 key_reg = value_reg;
6297 }
6298
6299 // Check whether the value is in the table, jump to default block if not.
6300 __ Cmp(key_reg, num_entries - 1);
6301 __ B(hi, codegen_->GetLabelOf(default_block));
6302
6303 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
6304 vixl32::Register jump_offset = temps.Acquire();
6305
6306 // Load jump offset from the table.
6307 __ Adr(table_base, jump_table->GetTableStartLabel());
6308 __ Ldr(jump_offset, MemOperand(table_base, key_reg, vixl32::LSL, 2));
6309
6310 // Jump to target block by branching to table_base(pc related) + offset.
6311 vixl32::Register target_address = table_base;
6312 __ Add(target_address, table_base, jump_offset);
6313 __ Bx(target_address);
Artem Serov09a940d2016-11-11 16:15:11 +00006314
6315 jump_table->EmitTable(codegen_);
Artem Serov551b28f2016-10-18 19:11:30 +01006316 }
6317}
6318
Artem Serov02d37832016-10-25 15:25:33 +01006319// Copy the result of a call into the given target.
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006320void CodeGeneratorARMVIXL::MoveFromReturnRegister(Location trg, Primitive::Type type) {
6321 if (!trg.IsValid()) {
6322 DCHECK_EQ(type, Primitive::kPrimVoid);
6323 return;
6324 }
6325
6326 DCHECK_NE(type, Primitive::kPrimVoid);
6327
6328 Location return_loc = InvokeDexCallingConventionVisitorARM().GetReturnLocation(type);
6329 if (return_loc.Equals(trg)) {
6330 return;
6331 }
6332
6333 // TODO: Consider pairs in the parallel move resolver, then this could be nicely merged
6334 // with the last branch.
6335 if (type == Primitive::kPrimLong) {
6336 TODO_VIXL32(FATAL);
6337 } else if (type == Primitive::kPrimDouble) {
6338 TODO_VIXL32(FATAL);
6339 } else {
6340 // Let the parallel move resolver take care of all of this.
6341 HParallelMove parallel_move(GetGraph()->GetArena());
6342 parallel_move.AddMove(return_loc, trg, type, nullptr);
6343 GetMoveResolver()->EmitNativeCode(&parallel_move);
6344 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006345}
Scott Wakelingfe885462016-09-22 10:24:38 +01006346
xueliang.zhong8d2c4592016-11-23 17:05:25 +00006347void LocationsBuilderARMVIXL::VisitClassTableGet(HClassTableGet* instruction) {
6348 LocationSummary* locations =
6349 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
6350 locations->SetInAt(0, Location::RequiresRegister());
6351 locations->SetOut(Location::RequiresRegister());
Artem Serov551b28f2016-10-18 19:11:30 +01006352}
6353
xueliang.zhong8d2c4592016-11-23 17:05:25 +00006354void InstructionCodeGeneratorARMVIXL::VisitClassTableGet(HClassTableGet* instruction) {
6355 if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) {
6356 uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
6357 instruction->GetIndex(), kArmPointerSize).SizeValue();
6358 GetAssembler()->LoadFromOffset(kLoadWord,
6359 OutputRegister(instruction),
6360 InputRegisterAt(instruction, 0),
6361 method_offset);
6362 } else {
6363 uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement(
6364 instruction->GetIndex(), kArmPointerSize));
6365 GetAssembler()->LoadFromOffset(kLoadWord,
6366 OutputRegister(instruction),
6367 InputRegisterAt(instruction, 0),
6368 mirror::Class::ImtPtrOffset(kArmPointerSize).Uint32Value());
6369 GetAssembler()->LoadFromOffset(kLoadWord,
6370 OutputRegister(instruction),
6371 OutputRegister(instruction),
6372 method_offset);
6373 }
Artem Serov551b28f2016-10-18 19:11:30 +01006374}
6375
Scott Wakelingfe885462016-09-22 10:24:38 +01006376#undef __
6377#undef QUICK_ENTRY_POINT
6378#undef TODO_VIXL32
6379
6380} // namespace arm
6381} // namespace art