blob: 7aea616c7febfc66db34058b1490d33cff34e81a [file] [log] [blame]
Scott Wakelingfe885462016-09-22 10:24:38 +01001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "code_generator_arm_vixl.h"
18
19#include "arch/arm/instruction_set_features_arm.h"
20#include "art_method.h"
21#include "code_generator_utils.h"
22#include "common_arm.h"
23#include "compiled_method.h"
24#include "entrypoints/quick/quick_entrypoints.h"
25#include "gc/accounting/card_table.h"
Anton Kirilov5ec62182016-10-13 20:16:02 +010026#include "intrinsics_arm_vixl.h"
Scott Wakelingfe885462016-09-22 10:24:38 +010027#include "mirror/array-inl.h"
28#include "mirror/class-inl.h"
29#include "thread.h"
30#include "utils/arm/assembler_arm_vixl.h"
31#include "utils/arm/managed_register_arm.h"
32#include "utils/assembler.h"
33#include "utils/stack_checks.h"
34
35namespace art {
36namespace arm {
37
38namespace vixl32 = vixl::aarch32;
39using namespace vixl32; // NOLINT(build/namespaces)
40
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +010041using helpers::DRegisterFrom;
Scott Wakelingfe885462016-09-22 10:24:38 +010042using helpers::DWARFReg;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010043using helpers::HighDRegisterFrom;
44using helpers::HighRegisterFrom;
Scott Wakelingfe885462016-09-22 10:24:38 +010045using helpers::InputOperandAt;
Scott Wakelingc34dba72016-10-03 10:14:44 +010046using helpers::InputRegister;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010047using helpers::InputRegisterAt;
Scott Wakelingfe885462016-09-22 10:24:38 +010048using helpers::InputSRegisterAt;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010049using helpers::InputVRegisterAt;
50using helpers::LocationFrom;
51using helpers::LowRegisterFrom;
52using helpers::LowSRegisterFrom;
53using helpers::OutputRegister;
54using helpers::OutputSRegister;
55using helpers::OutputVRegister;
56using helpers::RegisterFrom;
57using helpers::SRegisterFrom;
Scott Wakelingfe885462016-09-22 10:24:38 +010058
59using RegisterList = vixl32::RegisterList;
60
61static bool ExpectedPairLayout(Location location) {
62 // We expected this for both core and fpu register pairs.
63 return ((location.low() & 1) == 0) && (location.low() + 1 == location.high());
64}
65
Anton Kirilove28d9ae2016-10-25 18:17:23 +010066static constexpr int kCurrentMethodStackOffset = 0;
Scott Wakelingfe885462016-09-22 10:24:38 +010067static constexpr size_t kArmInstrMaxSizeInBytes = 4u;
Artem Serov551b28f2016-10-18 19:11:30 +010068static constexpr uint32_t kPackedSwitchCompareJumpThreshold = 7;
Scott Wakelingfe885462016-09-22 10:24:38 +010069
70#ifdef __
71#error "ARM Codegen VIXL macro-assembler macro already defined."
72#endif
73
Scott Wakelingfe885462016-09-22 10:24:38 +010074// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
75#define __ down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler()-> // NOLINT
76#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArmPointerSize, x).Int32Value()
77
78// Marker that code is yet to be, and must, be implemented.
79#define TODO_VIXL32(level) LOG(level) << __PRETTY_FUNCTION__ << " unimplemented "
80
Scott Wakelinga7812ae2016-10-17 10:03:36 +010081// SaveLiveRegisters and RestoreLiveRegisters from SlowPathCodeARM operate on sets of S registers,
82// for each live D registers they treat two corresponding S registers as live ones.
83//
84// Two following functions (SaveContiguousSRegisterList, RestoreContiguousSRegisterList) build
85// from a list of contiguous S registers a list of contiguous D registers (processing first/last
86// S registers corner cases) and save/restore this new list treating them as D registers.
87// - decreasing code size
88// - avoiding hazards on Cortex-A57, when a pair of S registers for an actual live D register is
89// restored and then used in regular non SlowPath code as D register.
90//
91// For the following example (v means the S register is live):
92// D names: | D0 | D1 | D2 | D4 | ...
93// S names: | S0 | S1 | S2 | S3 | S4 | S5 | S6 | S7 | ...
94// Live? | | v | v | v | v | v | v | | ...
95//
96// S1 and S6 will be saved/restored independently; D registers list (D1, D2) will be processed
97// as D registers.
98//
99// TODO(VIXL): All this code should be unnecessary once the VIXL AArch32 backend provides helpers
100// for lists of floating-point registers.
101static size_t SaveContiguousSRegisterList(size_t first,
102 size_t last,
103 CodeGenerator* codegen,
104 size_t stack_offset) {
105 static_assert(kSRegSizeInBytes == kArmWordSize, "Broken assumption on reg/word sizes.");
106 static_assert(kDRegSizeInBytes == 2 * kArmWordSize, "Broken assumption on reg/word sizes.");
107 DCHECK_LE(first, last);
108 if ((first == last) && (first == 0)) {
109 __ Vstr(vixl32::SRegister(first), MemOperand(sp, stack_offset));
110 return stack_offset + kSRegSizeInBytes;
111 }
112 if (first % 2 == 1) {
113 __ Vstr(vixl32::SRegister(first++), MemOperand(sp, stack_offset));
114 stack_offset += kSRegSizeInBytes;
115 }
116
117 bool save_last = false;
118 if (last % 2 == 0) {
119 save_last = true;
120 --last;
121 }
122
123 if (first < last) {
124 vixl32::DRegister d_reg = vixl32::DRegister(first / 2);
125 DCHECK_EQ((last - first + 1) % 2, 0u);
126 size_t number_of_d_regs = (last - first + 1) / 2;
127
128 if (number_of_d_regs == 1) {
129 __ Vstr(d_reg, MemOperand(sp, stack_offset));
130 } else if (number_of_d_regs > 1) {
131 UseScratchRegisterScope temps(down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler());
132 vixl32::Register base = sp;
133 if (stack_offset != 0) {
134 base = temps.Acquire();
135 __ Add(base, sp, stack_offset);
136 }
137 __ Vstm(F64, base, NO_WRITE_BACK, DRegisterList(d_reg, number_of_d_regs));
138 }
139 stack_offset += number_of_d_regs * kDRegSizeInBytes;
140 }
141
142 if (save_last) {
143 __ Vstr(vixl32::SRegister(last + 1), MemOperand(sp, stack_offset));
144 stack_offset += kSRegSizeInBytes;
145 }
146
147 return stack_offset;
148}
149
150static size_t RestoreContiguousSRegisterList(size_t first,
151 size_t last,
152 CodeGenerator* codegen,
153 size_t stack_offset) {
154 static_assert(kSRegSizeInBytes == kArmWordSize, "Broken assumption on reg/word sizes.");
155 static_assert(kDRegSizeInBytes == 2 * kArmWordSize, "Broken assumption on reg/word sizes.");
156 DCHECK_LE(first, last);
157 if ((first == last) && (first == 0)) {
158 __ Vldr(vixl32::SRegister(first), MemOperand(sp, stack_offset));
159 return stack_offset + kSRegSizeInBytes;
160 }
161 if (first % 2 == 1) {
162 __ Vldr(vixl32::SRegister(first++), MemOperand(sp, stack_offset));
163 stack_offset += kSRegSizeInBytes;
164 }
165
166 bool restore_last = false;
167 if (last % 2 == 0) {
168 restore_last = true;
169 --last;
170 }
171
172 if (first < last) {
173 vixl32::DRegister d_reg = vixl32::DRegister(first / 2);
174 DCHECK_EQ((last - first + 1) % 2, 0u);
175 size_t number_of_d_regs = (last - first + 1) / 2;
176 if (number_of_d_regs == 1) {
177 __ Vldr(d_reg, MemOperand(sp, stack_offset));
178 } else if (number_of_d_regs > 1) {
179 UseScratchRegisterScope temps(down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler());
180 vixl32::Register base = sp;
181 if (stack_offset != 0) {
182 base = temps.Acquire();
183 __ Add(base, sp, stack_offset);
184 }
185 __ Vldm(F64, base, NO_WRITE_BACK, DRegisterList(d_reg, number_of_d_regs));
186 }
187 stack_offset += number_of_d_regs * kDRegSizeInBytes;
188 }
189
190 if (restore_last) {
191 __ Vldr(vixl32::SRegister(last + 1), MemOperand(sp, stack_offset));
192 stack_offset += kSRegSizeInBytes;
193 }
194
195 return stack_offset;
196}
197
198void SlowPathCodeARMVIXL::SaveLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
199 size_t stack_offset = codegen->GetFirstRegisterSlotInSlowPath();
200 size_t orig_offset = stack_offset;
201
202 const uint32_t core_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ true);
203 for (uint32_t i : LowToHighBits(core_spills)) {
204 // If the register holds an object, update the stack mask.
205 if (locations->RegisterContainsObject(i)) {
206 locations->SetStackBit(stack_offset / kVRegSize);
207 }
208 DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
209 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
210 saved_core_stack_offsets_[i] = stack_offset;
211 stack_offset += kArmWordSize;
212 }
213
214 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
215 arm_codegen->GetAssembler()->StoreRegisterList(core_spills, orig_offset);
216
217 uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ false);
218 orig_offset = stack_offset;
219 for (uint32_t i : LowToHighBits(fp_spills)) {
220 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
221 saved_fpu_stack_offsets_[i] = stack_offset;
222 stack_offset += kArmWordSize;
223 }
224
225 stack_offset = orig_offset;
226 while (fp_spills != 0u) {
227 uint32_t begin = CTZ(fp_spills);
228 uint32_t tmp = fp_spills + (1u << begin);
229 fp_spills &= tmp; // Clear the contiguous range of 1s.
230 uint32_t end = (tmp == 0u) ? 32u : CTZ(tmp); // CTZ(0) is undefined.
231 stack_offset = SaveContiguousSRegisterList(begin, end - 1, codegen, stack_offset);
232 }
233 DCHECK_LE(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
234}
235
236void SlowPathCodeARMVIXL::RestoreLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
237 size_t stack_offset = codegen->GetFirstRegisterSlotInSlowPath();
238 size_t orig_offset = stack_offset;
239
240 const uint32_t core_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ true);
241 for (uint32_t i : LowToHighBits(core_spills)) {
242 DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
243 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
244 stack_offset += kArmWordSize;
245 }
246
247 // TODO(VIXL): Check the coherency of stack_offset after this with a test.
248 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
249 arm_codegen->GetAssembler()->LoadRegisterList(core_spills, orig_offset);
250
251 uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ false);
252 while (fp_spills != 0u) {
253 uint32_t begin = CTZ(fp_spills);
254 uint32_t tmp = fp_spills + (1u << begin);
255 fp_spills &= tmp; // Clear the contiguous range of 1s.
256 uint32_t end = (tmp == 0u) ? 32u : CTZ(tmp); // CTZ(0) is undefined.
257 stack_offset = RestoreContiguousSRegisterList(begin, end - 1, codegen, stack_offset);
258 }
259 DCHECK_LE(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
260}
261
262class NullCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
263 public:
264 explicit NullCheckSlowPathARMVIXL(HNullCheck* instruction) : SlowPathCodeARMVIXL(instruction) {}
265
266 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
267 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
268 __ Bind(GetEntryLabel());
269 if (instruction_->CanThrowIntoCatchBlock()) {
270 // Live registers will be restored in the catch block if caught.
271 SaveLiveRegisters(codegen, instruction_->GetLocations());
272 }
273 arm_codegen->InvokeRuntime(kQuickThrowNullPointer,
274 instruction_,
275 instruction_->GetDexPc(),
276 this);
277 CheckEntrypointTypes<kQuickThrowNullPointer, void, void>();
278 }
279
280 bool IsFatal() const OVERRIDE { return true; }
281
282 const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathARMVIXL"; }
283
284 private:
285 DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARMVIXL);
286};
287
Scott Wakelingfe885462016-09-22 10:24:38 +0100288class DivZeroCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
289 public:
290 explicit DivZeroCheckSlowPathARMVIXL(HDivZeroCheck* instruction)
291 : SlowPathCodeARMVIXL(instruction) {}
292
293 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100294 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
Scott Wakelingfe885462016-09-22 10:24:38 +0100295 __ Bind(GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100296 arm_codegen->InvokeRuntime(kQuickThrowDivZero, instruction_, instruction_->GetDexPc(), this);
Scott Wakelingfe885462016-09-22 10:24:38 +0100297 CheckEntrypointTypes<kQuickThrowDivZero, void, void>();
298 }
299
300 bool IsFatal() const OVERRIDE { return true; }
301
302 const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathARMVIXL"; }
303
304 private:
305 DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathARMVIXL);
306};
307
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100308class SuspendCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
309 public:
310 SuspendCheckSlowPathARMVIXL(HSuspendCheck* instruction, HBasicBlock* successor)
311 : SlowPathCodeARMVIXL(instruction), successor_(successor) {}
312
313 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
314 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
315 __ Bind(GetEntryLabel());
316 arm_codegen->InvokeRuntime(kQuickTestSuspend, instruction_, instruction_->GetDexPc(), this);
317 CheckEntrypointTypes<kQuickTestSuspend, void, void>();
318 if (successor_ == nullptr) {
319 __ B(GetReturnLabel());
320 } else {
321 __ B(arm_codegen->GetLabelOf(successor_));
322 }
323 }
324
325 vixl32::Label* GetReturnLabel() {
326 DCHECK(successor_ == nullptr);
327 return &return_label_;
328 }
329
330 HBasicBlock* GetSuccessor() const {
331 return successor_;
332 }
333
334 const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathARMVIXL"; }
335
336 private:
337 // If not null, the block to branch to after the suspend check.
338 HBasicBlock* const successor_;
339
340 // If `successor_` is null, the label to branch to after the suspend check.
341 vixl32::Label return_label_;
342
343 DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathARMVIXL);
344};
345
Scott Wakelingc34dba72016-10-03 10:14:44 +0100346class BoundsCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
347 public:
348 explicit BoundsCheckSlowPathARMVIXL(HBoundsCheck* instruction)
349 : SlowPathCodeARMVIXL(instruction) {}
350
351 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
352 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
353 LocationSummary* locations = instruction_->GetLocations();
354
355 __ Bind(GetEntryLabel());
356 if (instruction_->CanThrowIntoCatchBlock()) {
357 // Live registers will be restored in the catch block if caught.
358 SaveLiveRegisters(codegen, instruction_->GetLocations());
359 }
360 // We're moving two locations to locations that could overlap, so we need a parallel
361 // move resolver.
362 InvokeRuntimeCallingConventionARMVIXL calling_convention;
363 codegen->EmitParallelMoves(
364 locations->InAt(0),
365 LocationFrom(calling_convention.GetRegisterAt(0)),
366 Primitive::kPrimInt,
367 locations->InAt(1),
368 LocationFrom(calling_convention.GetRegisterAt(1)),
369 Primitive::kPrimInt);
370 QuickEntrypointEnum entrypoint = instruction_->AsBoundsCheck()->IsStringCharAt()
371 ? kQuickThrowStringBounds
372 : kQuickThrowArrayBounds;
373 arm_codegen->InvokeRuntime(entrypoint, instruction_, instruction_->GetDexPc(), this);
374 CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>();
375 CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>();
376 }
377
378 bool IsFatal() const OVERRIDE { return true; }
379
380 const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathARMVIXL"; }
381
382 private:
383 DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARMVIXL);
384};
385
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100386class LoadClassSlowPathARMVIXL : public SlowPathCodeARMVIXL {
387 public:
388 LoadClassSlowPathARMVIXL(HLoadClass* cls, HInstruction* at, uint32_t dex_pc, bool do_clinit)
389 : SlowPathCodeARMVIXL(at), cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) {
390 DCHECK(at->IsLoadClass() || at->IsClinitCheck());
391 }
392
393 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
394 LocationSummary* locations = at_->GetLocations();
395
396 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
397 __ Bind(GetEntryLabel());
398 SaveLiveRegisters(codegen, locations);
399
400 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Andreas Gampea5b09a62016-11-17 15:21:22 -0800401 __ Mov(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex().index_);
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100402 QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage
403 : kQuickInitializeType;
404 arm_codegen->InvokeRuntime(entrypoint, at_, dex_pc_, this);
405 if (do_clinit_) {
406 CheckEntrypointTypes<kQuickInitializeStaticStorage, void*, uint32_t>();
407 } else {
408 CheckEntrypointTypes<kQuickInitializeType, void*, uint32_t>();
409 }
410
411 // Move the class to the desired location.
412 Location out = locations->Out();
413 if (out.IsValid()) {
414 DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
415 arm_codegen->Move32(locations->Out(), LocationFrom(r0));
416 }
417 RestoreLiveRegisters(codegen, locations);
418 __ B(GetExitLabel());
419 }
420
421 const char* GetDescription() const OVERRIDE { return "LoadClassSlowPathARMVIXL"; }
422
423 private:
424 // The class this slow path will load.
425 HLoadClass* const cls_;
426
427 // The instruction where this slow path is happening.
428 // (Might be the load class or an initialization check).
429 HInstruction* const at_;
430
431 // The dex PC of `at_`.
432 const uint32_t dex_pc_;
433
434 // Whether to initialize the class.
435 const bool do_clinit_;
436
437 DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathARMVIXL);
438};
439
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100440class TypeCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
441 public:
442 TypeCheckSlowPathARMVIXL(HInstruction* instruction, bool is_fatal)
443 : SlowPathCodeARMVIXL(instruction), is_fatal_(is_fatal) {}
444
445 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
446 LocationSummary* locations = instruction_->GetLocations();
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100447 DCHECK(instruction_->IsCheckCast()
448 || !locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
449
450 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
451 __ Bind(GetEntryLabel());
452
453 if (!is_fatal_) {
Artem Serovcfbe9132016-10-14 15:58:56 +0100454 SaveLiveRegisters(codegen, locations);
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100455 }
456
457 // We're moving two locations to locations that could overlap, so we need a parallel
458 // move resolver.
459 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100460
Mathieu Chartier9fd8c602016-11-14 14:38:53 -0800461 codegen->EmitParallelMoves(locations->InAt(0),
Mathieu Chartierb99f4d62016-11-07 16:17:26 -0800462 LocationFrom(calling_convention.GetRegisterAt(0)),
463 Primitive::kPrimNot,
Mathieu Chartier9fd8c602016-11-14 14:38:53 -0800464 locations->InAt(1),
Mathieu Chartierb99f4d62016-11-07 16:17:26 -0800465 LocationFrom(calling_convention.GetRegisterAt(1)),
466 Primitive::kPrimNot);
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100467 if (instruction_->IsInstanceOf()) {
Artem Serovcfbe9132016-10-14 15:58:56 +0100468 arm_codegen->InvokeRuntime(kQuickInstanceofNonTrivial,
469 instruction_,
470 instruction_->GetDexPc(),
471 this);
Mathieu Chartier9fd8c602016-11-14 14:38:53 -0800472 CheckEntrypointTypes<kQuickInstanceofNonTrivial, size_t, mirror::Object*, mirror::Class*>();
Artem Serovcfbe9132016-10-14 15:58:56 +0100473 arm_codegen->Move32(locations->Out(), LocationFrom(r0));
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100474 } else {
475 DCHECK(instruction_->IsCheckCast());
Mathieu Chartierb99f4d62016-11-07 16:17:26 -0800476 arm_codegen->InvokeRuntime(kQuickCheckInstanceOf,
477 instruction_,
478 instruction_->GetDexPc(),
479 this);
480 CheckEntrypointTypes<kQuickCheckInstanceOf, void, mirror::Object*, mirror::Class*>();
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100481 }
482
483 if (!is_fatal_) {
Artem Serovcfbe9132016-10-14 15:58:56 +0100484 RestoreLiveRegisters(codegen, locations);
485 __ B(GetExitLabel());
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100486 }
487 }
488
489 const char* GetDescription() const OVERRIDE { return "TypeCheckSlowPathARMVIXL"; }
490
491 bool IsFatal() const OVERRIDE { return is_fatal_; }
492
493 private:
494 const bool is_fatal_;
495
496 DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathARMVIXL);
497};
498
Scott Wakelingc34dba72016-10-03 10:14:44 +0100499class DeoptimizationSlowPathARMVIXL : public SlowPathCodeARMVIXL {
500 public:
501 explicit DeoptimizationSlowPathARMVIXL(HDeoptimize* instruction)
502 : SlowPathCodeARMVIXL(instruction) {}
503
504 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
505 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
506 __ Bind(GetEntryLabel());
507 arm_codegen->InvokeRuntime(kQuickDeoptimize, instruction_, instruction_->GetDexPc(), this);
508 CheckEntrypointTypes<kQuickDeoptimize, void, void>();
509 }
510
511 const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathARMVIXL"; }
512
513 private:
514 DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathARMVIXL);
515};
516
517class ArraySetSlowPathARMVIXL : public SlowPathCodeARMVIXL {
518 public:
519 explicit ArraySetSlowPathARMVIXL(HInstruction* instruction) : SlowPathCodeARMVIXL(instruction) {}
520
521 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
522 LocationSummary* locations = instruction_->GetLocations();
523 __ Bind(GetEntryLabel());
524 SaveLiveRegisters(codegen, locations);
525
526 InvokeRuntimeCallingConventionARMVIXL calling_convention;
527 HParallelMove parallel_move(codegen->GetGraph()->GetArena());
528 parallel_move.AddMove(
529 locations->InAt(0),
530 LocationFrom(calling_convention.GetRegisterAt(0)),
531 Primitive::kPrimNot,
532 nullptr);
533 parallel_move.AddMove(
534 locations->InAt(1),
535 LocationFrom(calling_convention.GetRegisterAt(1)),
536 Primitive::kPrimInt,
537 nullptr);
538 parallel_move.AddMove(
539 locations->InAt(2),
540 LocationFrom(calling_convention.GetRegisterAt(2)),
541 Primitive::kPrimNot,
542 nullptr);
543 codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
544
545 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
546 arm_codegen->InvokeRuntime(kQuickAputObject, instruction_, instruction_->GetDexPc(), this);
547 CheckEntrypointTypes<kQuickAputObject, void, mirror::Array*, int32_t, mirror::Object*>();
548 RestoreLiveRegisters(codegen, locations);
549 __ B(GetExitLabel());
550 }
551
552 const char* GetDescription() const OVERRIDE { return "ArraySetSlowPathARMVIXL"; }
553
554 private:
555 DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathARMVIXL);
556};
557
558
Scott Wakelingfe885462016-09-22 10:24:38 +0100559inline vixl32::Condition ARMCondition(IfCondition cond) {
560 switch (cond) {
561 case kCondEQ: return eq;
562 case kCondNE: return ne;
563 case kCondLT: return lt;
564 case kCondLE: return le;
565 case kCondGT: return gt;
566 case kCondGE: return ge;
567 case kCondB: return lo;
568 case kCondBE: return ls;
569 case kCondA: return hi;
570 case kCondAE: return hs;
571 }
572 LOG(FATAL) << "Unreachable";
573 UNREACHABLE();
574}
575
576// Maps signed condition to unsigned condition.
577inline vixl32::Condition ARMUnsignedCondition(IfCondition cond) {
578 switch (cond) {
579 case kCondEQ: return eq;
580 case kCondNE: return ne;
581 // Signed to unsigned.
582 case kCondLT: return lo;
583 case kCondLE: return ls;
584 case kCondGT: return hi;
585 case kCondGE: return hs;
586 // Unsigned remain unchanged.
587 case kCondB: return lo;
588 case kCondBE: return ls;
589 case kCondA: return hi;
590 case kCondAE: return hs;
591 }
592 LOG(FATAL) << "Unreachable";
593 UNREACHABLE();
594}
595
596inline vixl32::Condition ARMFPCondition(IfCondition cond, bool gt_bias) {
597 // The ARM condition codes can express all the necessary branches, see the
598 // "Meaning (floating-point)" column in the table A8-1 of the ARMv7 reference manual.
599 // There is no dex instruction or HIR that would need the missing conditions
600 // "equal or unordered" or "not equal".
601 switch (cond) {
602 case kCondEQ: return eq;
603 case kCondNE: return ne /* unordered */;
604 case kCondLT: return gt_bias ? cc : lt /* unordered */;
605 case kCondLE: return gt_bias ? ls : le /* unordered */;
606 case kCondGT: return gt_bias ? hi /* unordered */ : gt;
607 case kCondGE: return gt_bias ? cs /* unordered */ : ge;
608 default:
609 LOG(FATAL) << "UNREACHABLE";
610 UNREACHABLE();
611 }
612}
613
Scott Wakelingfe885462016-09-22 10:24:38 +0100614void CodeGeneratorARMVIXL::DumpCoreRegister(std::ostream& stream, int reg) const {
615 stream << vixl32::Register(reg);
616}
617
618void CodeGeneratorARMVIXL::DumpFloatingPointRegister(std::ostream& stream, int reg) const {
619 stream << vixl32::SRegister(reg);
620}
621
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100622static uint32_t ComputeSRegisterListMask(const SRegisterList& regs) {
Scott Wakelingfe885462016-09-22 10:24:38 +0100623 uint32_t mask = 0;
624 for (uint32_t i = regs.GetFirstSRegister().GetCode();
625 i <= regs.GetLastSRegister().GetCode();
626 ++i) {
627 mask |= (1 << i);
628 }
629 return mask;
630}
631
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100632size_t CodeGeneratorARMVIXL::RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
633 GetAssembler()->LoadSFromOffset(vixl32::SRegister(reg_id), sp, stack_index);
634 return kArmWordSize;
635}
636
Scott Wakelingfe885462016-09-22 10:24:38 +0100637#undef __
638
639CodeGeneratorARMVIXL::CodeGeneratorARMVIXL(HGraph* graph,
640 const ArmInstructionSetFeatures& isa_features,
641 const CompilerOptions& compiler_options,
642 OptimizingCompilerStats* stats)
643 : CodeGenerator(graph,
644 kNumberOfCoreRegisters,
645 kNumberOfSRegisters,
646 kNumberOfRegisterPairs,
647 kCoreCalleeSaves.GetList(),
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100648 ComputeSRegisterListMask(kFpuCalleeSaves),
Scott Wakelingfe885462016-09-22 10:24:38 +0100649 compiler_options,
650 stats),
651 block_labels_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
Artem Serov551b28f2016-10-18 19:11:30 +0100652 jump_tables_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
Scott Wakelingfe885462016-09-22 10:24:38 +0100653 location_builder_(graph, this),
654 instruction_visitor_(graph, this),
655 move_resolver_(graph->GetArena(), this),
656 assembler_(graph->GetArena()),
657 isa_features_(isa_features) {
658 // Always save the LR register to mimic Quick.
659 AddAllocatedRegister(Location::RegisterLocation(LR));
Alexandre Rames9c19bd62016-10-24 11:50:32 +0100660 // Give d14 and d15 as scratch registers to VIXL.
661 // They are removed from the register allocator in `SetupBlockedRegisters()`.
662 // TODO(VIXL): We need two scratch D registers for `EmitSwap` when swapping two double stack
663 // slots. If that is sufficiently rare, and we have pressure on FP registers, we could instead
664 // spill in `EmitSwap`. But if we actually are guaranteed to have 32 D registers, we could give
665 // d30 and d31 to VIXL to avoid removing registers from the allocator. If that is the case, we may
666 // also want to investigate giving those 14 other D registers to the allocator.
667 GetVIXLAssembler()->GetScratchVRegisterList()->Combine(d14);
668 GetVIXLAssembler()->GetScratchVRegisterList()->Combine(d15);
Scott Wakelingfe885462016-09-22 10:24:38 +0100669}
670
Artem Serov551b28f2016-10-18 19:11:30 +0100671void JumpTableARMVIXL::EmitTable(CodeGeneratorARMVIXL* codegen) {
672 uint32_t num_entries = switch_instr_->GetNumEntries();
673 DCHECK_GE(num_entries, kPackedSwitchCompareJumpThreshold);
674
675 // We are about to use the assembler to place literals directly. Make sure we have enough
676 // underlying code buffer and we have generated the jump table with right size.
677 codegen->GetVIXLAssembler()->GetBuffer().Align();
678 AssemblerAccurateScope aas(codegen->GetVIXLAssembler(),
679 num_entries * sizeof(int32_t),
680 CodeBufferCheckScope::kMaximumSize);
681 // TODO(VIXL): Check that using lower case bind is fine here.
682 codegen->GetVIXLAssembler()->bind(&table_start_);
Artem Serov09a940d2016-11-11 16:15:11 +0000683 for (uint32_t i = 0; i < num_entries; i++) {
684 codegen->GetVIXLAssembler()->place(bb_addresses_[i].get());
685 }
686}
687
688void JumpTableARMVIXL::FixTable(CodeGeneratorARMVIXL* codegen) {
689 uint32_t num_entries = switch_instr_->GetNumEntries();
690 DCHECK_GE(num_entries, kPackedSwitchCompareJumpThreshold);
691
Artem Serov551b28f2016-10-18 19:11:30 +0100692 const ArenaVector<HBasicBlock*>& successors = switch_instr_->GetBlock()->GetSuccessors();
693 for (uint32_t i = 0; i < num_entries; i++) {
694 vixl32::Label* target_label = codegen->GetLabelOf(successors[i]);
695 DCHECK(target_label->IsBound());
696 int32_t jump_offset = target_label->GetLocation() - table_start_.GetLocation();
697 // When doing BX to address we need to have lower bit set to 1 in T32.
698 if (codegen->GetVIXLAssembler()->IsUsingT32()) {
699 jump_offset++;
700 }
701 DCHECK_GT(jump_offset, std::numeric_limits<int32_t>::min());
702 DCHECK_LE(jump_offset, std::numeric_limits<int32_t>::max());
Artem Serov09a940d2016-11-11 16:15:11 +0000703
704 bb_addresses_[i].get()->UpdateValue(jump_offset, &codegen->GetVIXLAssembler()->GetBuffer());
Artem Serov551b28f2016-10-18 19:11:30 +0100705 }
706}
707
Artem Serov09a940d2016-11-11 16:15:11 +0000708void CodeGeneratorARMVIXL::FixJumpTables() {
Artem Serov551b28f2016-10-18 19:11:30 +0100709 for (auto&& jump_table : jump_tables_) {
Artem Serov09a940d2016-11-11 16:15:11 +0000710 jump_table->FixTable(this);
Artem Serov551b28f2016-10-18 19:11:30 +0100711 }
712}
713
Andreas Gampeca620d72016-11-08 08:09:33 -0800714#define __ reinterpret_cast<ArmVIXLAssembler*>(GetAssembler())->GetVIXLAssembler()-> // NOLINT
Scott Wakelingfe885462016-09-22 10:24:38 +0100715
716void CodeGeneratorARMVIXL::Finalize(CodeAllocator* allocator) {
Artem Serov09a940d2016-11-11 16:15:11 +0000717 FixJumpTables();
Scott Wakelingfe885462016-09-22 10:24:38 +0100718 GetAssembler()->FinalizeCode();
719 CodeGenerator::Finalize(allocator);
720}
721
722void CodeGeneratorARMVIXL::SetupBlockedRegisters() const {
Scott Wakelingfe885462016-09-22 10:24:38 +0100723 // Stack register, LR and PC are always reserved.
724 blocked_core_registers_[SP] = true;
725 blocked_core_registers_[LR] = true;
726 blocked_core_registers_[PC] = true;
727
728 // Reserve thread register.
729 blocked_core_registers_[TR] = true;
730
731 // Reserve temp register.
732 blocked_core_registers_[IP] = true;
733
Alexandre Rames9c19bd62016-10-24 11:50:32 +0100734 // Registers s28-s31 (d14-d15) are left to VIXL for scratch registers.
735 // (They are given to the `MacroAssembler` in `CodeGeneratorARMVIXL::CodeGeneratorARMVIXL`.)
736 blocked_fpu_registers_[28] = true;
737 blocked_fpu_registers_[29] = true;
738 blocked_fpu_registers_[30] = true;
739 blocked_fpu_registers_[31] = true;
740
Scott Wakelingfe885462016-09-22 10:24:38 +0100741 if (GetGraph()->IsDebuggable()) {
742 // Stubs do not save callee-save floating point registers. If the graph
743 // is debuggable, we need to deal with these registers differently. For
744 // now, just block them.
745 for (uint32_t i = kFpuCalleeSaves.GetFirstSRegister().GetCode();
746 i <= kFpuCalleeSaves.GetLastSRegister().GetCode();
747 ++i) {
748 blocked_fpu_registers_[i] = true;
749 }
750 }
Scott Wakelingfe885462016-09-22 10:24:38 +0100751}
752
Scott Wakelingfe885462016-09-22 10:24:38 +0100753InstructionCodeGeneratorARMVIXL::InstructionCodeGeneratorARMVIXL(HGraph* graph,
754 CodeGeneratorARMVIXL* codegen)
755 : InstructionCodeGenerator(graph, codegen),
756 assembler_(codegen->GetAssembler()),
757 codegen_(codegen) {}
758
759void CodeGeneratorARMVIXL::ComputeSpillMask() {
760 core_spill_mask_ = allocated_registers_.GetCoreRegisters() & core_callee_save_mask_;
761 DCHECK_NE(core_spill_mask_, 0u) << "At least the return address register must be saved";
762 // There is no easy instruction to restore just the PC on thumb2. We spill and
763 // restore another arbitrary register.
764 core_spill_mask_ |= (1 << kCoreAlwaysSpillRegister.GetCode());
765 fpu_spill_mask_ = allocated_registers_.GetFloatingPointRegisters() & fpu_callee_save_mask_;
766 // We use vpush and vpop for saving and restoring floating point registers, which take
767 // a SRegister and the number of registers to save/restore after that SRegister. We
768 // therefore update the `fpu_spill_mask_` to also contain those registers not allocated,
769 // but in the range.
770 if (fpu_spill_mask_ != 0) {
771 uint32_t least_significant_bit = LeastSignificantBit(fpu_spill_mask_);
772 uint32_t most_significant_bit = MostSignificantBit(fpu_spill_mask_);
773 for (uint32_t i = least_significant_bit + 1 ; i < most_significant_bit; ++i) {
774 fpu_spill_mask_ |= (1 << i);
775 }
776 }
777}
778
779void CodeGeneratorARMVIXL::GenerateFrameEntry() {
780 bool skip_overflow_check =
781 IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kArm);
782 DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks());
783 __ Bind(&frame_entry_label_);
784
785 if (HasEmptyFrame()) {
786 return;
787 }
788
Scott Wakelingfe885462016-09-22 10:24:38 +0100789 if (!skip_overflow_check) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100790 UseScratchRegisterScope temps(GetVIXLAssembler());
791 vixl32::Register temp = temps.Acquire();
Scott Wakelingfe885462016-09-22 10:24:38 +0100792 __ Sub(temp, sp, static_cast<int32_t>(GetStackOverflowReservedBytes(kArm)));
793 // The load must immediately precede RecordPcInfo.
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100794 AssemblerAccurateScope aas(GetVIXLAssembler(),
795 kArmInstrMaxSizeInBytes,
796 CodeBufferCheckScope::kMaximumSize);
797 __ ldr(temp, MemOperand(temp));
798 RecordPcInfo(nullptr, 0);
Scott Wakelingfe885462016-09-22 10:24:38 +0100799 }
800
801 __ Push(RegisterList(core_spill_mask_));
802 GetAssembler()->cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(core_spill_mask_));
803 GetAssembler()->cfi().RelOffsetForMany(DWARFReg(kMethodRegister),
804 0,
805 core_spill_mask_,
806 kArmWordSize);
807 if (fpu_spill_mask_ != 0) {
808 uint32_t first = LeastSignificantBit(fpu_spill_mask_);
809
810 // Check that list is contiguous.
811 DCHECK_EQ(fpu_spill_mask_ >> CTZ(fpu_spill_mask_), ~0u >> (32 - POPCOUNT(fpu_spill_mask_)));
812
813 __ Vpush(SRegisterList(vixl32::SRegister(first), POPCOUNT(fpu_spill_mask_)));
814 GetAssembler()->cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(fpu_spill_mask_));
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100815 GetAssembler()->cfi().RelOffsetForMany(DWARFReg(s0), 0, fpu_spill_mask_, kArmWordSize);
Scott Wakelingfe885462016-09-22 10:24:38 +0100816 }
817 int adjust = GetFrameSize() - FrameEntrySpillSize();
818 __ Sub(sp, sp, adjust);
819 GetAssembler()->cfi().AdjustCFAOffset(adjust);
820 GetAssembler()->StoreToOffset(kStoreWord, kMethodRegister, sp, 0);
821}
822
823void CodeGeneratorARMVIXL::GenerateFrameExit() {
824 if (HasEmptyFrame()) {
825 __ Bx(lr);
826 return;
827 }
828 GetAssembler()->cfi().RememberState();
829 int adjust = GetFrameSize() - FrameEntrySpillSize();
830 __ Add(sp, sp, adjust);
831 GetAssembler()->cfi().AdjustCFAOffset(-adjust);
832 if (fpu_spill_mask_ != 0) {
833 uint32_t first = LeastSignificantBit(fpu_spill_mask_);
834
835 // Check that list is contiguous.
836 DCHECK_EQ(fpu_spill_mask_ >> CTZ(fpu_spill_mask_), ~0u >> (32 - POPCOUNT(fpu_spill_mask_)));
837
838 __ Vpop(SRegisterList(vixl32::SRegister(first), POPCOUNT(fpu_spill_mask_)));
839 GetAssembler()->cfi().AdjustCFAOffset(
840 -static_cast<int>(kArmWordSize) * POPCOUNT(fpu_spill_mask_));
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100841 GetAssembler()->cfi().RestoreMany(DWARFReg(vixl32::SRegister(0)), fpu_spill_mask_);
Scott Wakelingfe885462016-09-22 10:24:38 +0100842 }
843 // Pop LR into PC to return.
844 DCHECK_NE(core_spill_mask_ & (1 << kLrCode), 0U);
845 uint32_t pop_mask = (core_spill_mask_ & (~(1 << kLrCode))) | 1 << kPcCode;
846 __ Pop(RegisterList(pop_mask));
847 GetAssembler()->cfi().RestoreState();
848 GetAssembler()->cfi().DefCFAOffset(GetFrameSize());
849}
850
851void CodeGeneratorARMVIXL::Bind(HBasicBlock* block) {
852 __ Bind(GetLabelOf(block));
853}
854
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100855void CodeGeneratorARMVIXL::Move32(Location destination, Location source) {
856 if (source.Equals(destination)) {
857 return;
858 }
859 if (destination.IsRegister()) {
860 if (source.IsRegister()) {
861 __ Mov(RegisterFrom(destination), RegisterFrom(source));
862 } else if (source.IsFpuRegister()) {
863 __ Vmov(RegisterFrom(destination), SRegisterFrom(source));
864 } else {
865 GetAssembler()->LoadFromOffset(kLoadWord,
866 RegisterFrom(destination),
867 sp,
868 source.GetStackIndex());
869 }
870 } else if (destination.IsFpuRegister()) {
871 if (source.IsRegister()) {
872 __ Vmov(SRegisterFrom(destination), RegisterFrom(source));
873 } else if (source.IsFpuRegister()) {
874 __ Vmov(SRegisterFrom(destination), SRegisterFrom(source));
875 } else {
876 GetAssembler()->LoadSFromOffset(SRegisterFrom(destination), sp, source.GetStackIndex());
877 }
878 } else {
879 DCHECK(destination.IsStackSlot()) << destination;
880 if (source.IsRegister()) {
881 GetAssembler()->StoreToOffset(kStoreWord,
882 RegisterFrom(source),
883 sp,
884 destination.GetStackIndex());
885 } else if (source.IsFpuRegister()) {
886 GetAssembler()->StoreSToOffset(SRegisterFrom(source), sp, destination.GetStackIndex());
887 } else {
888 DCHECK(source.IsStackSlot()) << source;
889 UseScratchRegisterScope temps(GetVIXLAssembler());
890 vixl32::Register temp = temps.Acquire();
891 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, source.GetStackIndex());
892 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
893 }
894 }
895}
896
Artem Serovcfbe9132016-10-14 15:58:56 +0100897void CodeGeneratorARMVIXL::MoveConstant(Location location, int32_t value) {
898 DCHECK(location.IsRegister());
899 __ Mov(RegisterFrom(location), value);
Scott Wakelingfe885462016-09-22 10:24:38 +0100900}
901
902void CodeGeneratorARMVIXL::MoveLocation(Location dst, Location src, Primitive::Type dst_type) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100903 // TODO(VIXL): Maybe refactor to have the 'move' implementation here and use it in
904 // `ParallelMoveResolverARMVIXL::EmitMove`, as is done in the `arm64` backend.
905 HParallelMove move(GetGraph()->GetArena());
906 move.AddMove(src, dst, dst_type, nullptr);
907 GetMoveResolver()->EmitNativeCode(&move);
Scott Wakelingfe885462016-09-22 10:24:38 +0100908}
909
Artem Serovcfbe9132016-10-14 15:58:56 +0100910void CodeGeneratorARMVIXL::AddLocationAsTemp(Location location, LocationSummary* locations) {
911 if (location.IsRegister()) {
912 locations->AddTemp(location);
913 } else if (location.IsRegisterPair()) {
914 locations->AddTemp(LocationFrom(LowRegisterFrom(location)));
915 locations->AddTemp(LocationFrom(HighRegisterFrom(location)));
916 } else {
917 UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location;
918 }
Scott Wakelingfe885462016-09-22 10:24:38 +0100919}
920
921void CodeGeneratorARMVIXL::InvokeRuntime(QuickEntrypointEnum entrypoint,
922 HInstruction* instruction,
923 uint32_t dex_pc,
924 SlowPathCode* slow_path) {
925 ValidateInvokeRuntime(entrypoint, instruction, slow_path);
926 GenerateInvokeRuntime(GetThreadOffset<kArmPointerSize>(entrypoint).Int32Value());
927 if (EntrypointRequiresStackMap(entrypoint)) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100928 // TODO(VIXL): If necessary, use a scope to ensure we record the pc info immediately after the
929 // previous instruction.
Scott Wakelingfe885462016-09-22 10:24:38 +0100930 RecordPcInfo(instruction, dex_pc, slow_path);
931 }
932}
933
934void CodeGeneratorARMVIXL::InvokeRuntimeWithoutRecordingPcInfo(int32_t entry_point_offset,
935 HInstruction* instruction,
936 SlowPathCode* slow_path) {
937 ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction, slow_path);
938 GenerateInvokeRuntime(entry_point_offset);
939}
940
941void CodeGeneratorARMVIXL::GenerateInvokeRuntime(int32_t entry_point_offset) {
942 GetAssembler()->LoadFromOffset(kLoadWord, lr, tr, entry_point_offset);
943 __ Blx(lr);
944}
945
Scott Wakelingfe885462016-09-22 10:24:38 +0100946void InstructionCodeGeneratorARMVIXL::HandleGoto(HInstruction* got, HBasicBlock* successor) {
947 DCHECK(!successor->IsExitBlock());
948 HBasicBlock* block = got->GetBlock();
949 HInstruction* previous = got->GetPrevious();
950 HLoopInformation* info = block->GetLoopInformation();
951
952 if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) {
953 codegen_->ClearSpillSlotsFromLoopPhisInStackMap(info->GetSuspendCheck());
954 GenerateSuspendCheck(info->GetSuspendCheck(), successor);
955 return;
956 }
957 if (block->IsEntryBlock() && (previous != nullptr) && previous->IsSuspendCheck()) {
958 GenerateSuspendCheck(previous->AsSuspendCheck(), nullptr);
959 }
960 if (!codegen_->GoesToNextBlock(block, successor)) {
961 __ B(codegen_->GetLabelOf(successor));
962 }
963}
964
965void LocationsBuilderARMVIXL::VisitGoto(HGoto* got) {
966 got->SetLocations(nullptr);
967}
968
969void InstructionCodeGeneratorARMVIXL::VisitGoto(HGoto* got) {
970 HandleGoto(got, got->GetSuccessor());
971}
972
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100973void LocationsBuilderARMVIXL::VisitTryBoundary(HTryBoundary* try_boundary) {
974 try_boundary->SetLocations(nullptr);
975}
976
977void InstructionCodeGeneratorARMVIXL::VisitTryBoundary(HTryBoundary* try_boundary) {
978 HBasicBlock* successor = try_boundary->GetNormalFlowSuccessor();
979 if (!successor->IsExitBlock()) {
980 HandleGoto(try_boundary, successor);
981 }
982}
983
Scott Wakelingfe885462016-09-22 10:24:38 +0100984void LocationsBuilderARMVIXL::VisitExit(HExit* exit) {
985 exit->SetLocations(nullptr);
986}
987
988void InstructionCodeGeneratorARMVIXL::VisitExit(HExit* exit ATTRIBUTE_UNUSED) {
989}
990
991void InstructionCodeGeneratorARMVIXL::GenerateVcmp(HInstruction* instruction) {
992 Primitive::Type type = instruction->InputAt(0)->GetType();
993 Location lhs_loc = instruction->GetLocations()->InAt(0);
994 Location rhs_loc = instruction->GetLocations()->InAt(1);
995 if (rhs_loc.IsConstant()) {
996 // 0.0 is the only immediate that can be encoded directly in
997 // a VCMP instruction.
998 //
999 // Both the JLS (section 15.20.1) and the JVMS (section 6.5)
1000 // specify that in a floating-point comparison, positive zero
1001 // and negative zero are considered equal, so we can use the
1002 // literal 0.0 for both cases here.
1003 //
1004 // Note however that some methods (Float.equal, Float.compare,
1005 // Float.compareTo, Double.equal, Double.compare,
1006 // Double.compareTo, Math.max, Math.min, StrictMath.max,
1007 // StrictMath.min) consider 0.0 to be (strictly) greater than
1008 // -0.0. So if we ever translate calls to these methods into a
1009 // HCompare instruction, we must handle the -0.0 case with
1010 // care here.
1011 DCHECK(rhs_loc.GetConstant()->IsArithmeticZero());
1012 if (type == Primitive::kPrimFloat) {
1013 __ Vcmp(F32, InputSRegisterAt(instruction, 0), 0.0);
1014 } else {
1015 DCHECK_EQ(type, Primitive::kPrimDouble);
Scott Wakelingc34dba72016-10-03 10:14:44 +01001016 __ Vcmp(F64, DRegisterFrom(lhs_loc), 0.0);
Scott Wakelingfe885462016-09-22 10:24:38 +01001017 }
1018 } else {
1019 if (type == Primitive::kPrimFloat) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001020 __ Vcmp(InputSRegisterAt(instruction, 0), InputSRegisterAt(instruction, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01001021 } else {
1022 DCHECK_EQ(type, Primitive::kPrimDouble);
Scott Wakelingc34dba72016-10-03 10:14:44 +01001023 __ Vcmp(DRegisterFrom(lhs_loc), DRegisterFrom(rhs_loc));
Scott Wakelingfe885462016-09-22 10:24:38 +01001024 }
1025 }
1026}
1027
1028void InstructionCodeGeneratorARMVIXL::GenerateFPJumps(HCondition* cond,
1029 vixl32::Label* true_label,
1030 vixl32::Label* false_label ATTRIBUTE_UNUSED) {
1031 // To branch on the result of the FP compare we transfer FPSCR to APSR (encoded as PC in VMRS).
1032 __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
1033 __ B(ARMFPCondition(cond->GetCondition(), cond->IsGtBias()), true_label);
1034}
1035
1036void InstructionCodeGeneratorARMVIXL::GenerateLongComparesAndJumps(HCondition* cond,
1037 vixl32::Label* true_label,
1038 vixl32::Label* false_label) {
1039 LocationSummary* locations = cond->GetLocations();
1040 Location left = locations->InAt(0);
1041 Location right = locations->InAt(1);
1042 IfCondition if_cond = cond->GetCondition();
1043
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001044 vixl32::Register left_high = HighRegisterFrom(left);
1045 vixl32::Register left_low = LowRegisterFrom(left);
Scott Wakelingfe885462016-09-22 10:24:38 +01001046 IfCondition true_high_cond = if_cond;
1047 IfCondition false_high_cond = cond->GetOppositeCondition();
1048 vixl32::Condition final_condition = ARMUnsignedCondition(if_cond); // unsigned on lower part
1049
1050 // Set the conditions for the test, remembering that == needs to be
1051 // decided using the low words.
1052 // TODO: consider avoiding jumps with temporary and CMP low+SBC high
1053 switch (if_cond) {
1054 case kCondEQ:
1055 case kCondNE:
1056 // Nothing to do.
1057 break;
1058 case kCondLT:
1059 false_high_cond = kCondGT;
1060 break;
1061 case kCondLE:
1062 true_high_cond = kCondLT;
1063 break;
1064 case kCondGT:
1065 false_high_cond = kCondLT;
1066 break;
1067 case kCondGE:
1068 true_high_cond = kCondGT;
1069 break;
1070 case kCondB:
1071 false_high_cond = kCondA;
1072 break;
1073 case kCondBE:
1074 true_high_cond = kCondB;
1075 break;
1076 case kCondA:
1077 false_high_cond = kCondB;
1078 break;
1079 case kCondAE:
1080 true_high_cond = kCondA;
1081 break;
1082 }
1083 if (right.IsConstant()) {
1084 int64_t value = right.GetConstant()->AsLongConstant()->GetValue();
1085 int32_t val_low = Low32Bits(value);
1086 int32_t val_high = High32Bits(value);
1087
1088 __ Cmp(left_high, val_high);
1089 if (if_cond == kCondNE) {
1090 __ B(ARMCondition(true_high_cond), true_label);
1091 } else if (if_cond == kCondEQ) {
1092 __ B(ARMCondition(false_high_cond), false_label);
1093 } else {
1094 __ B(ARMCondition(true_high_cond), true_label);
1095 __ B(ARMCondition(false_high_cond), false_label);
1096 }
1097 // Must be equal high, so compare the lows.
1098 __ Cmp(left_low, val_low);
1099 } else {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001100 vixl32::Register right_high = HighRegisterFrom(right);
1101 vixl32::Register right_low = LowRegisterFrom(right);
Scott Wakelingfe885462016-09-22 10:24:38 +01001102
1103 __ Cmp(left_high, right_high);
1104 if (if_cond == kCondNE) {
1105 __ B(ARMCondition(true_high_cond), true_label);
1106 } else if (if_cond == kCondEQ) {
1107 __ B(ARMCondition(false_high_cond), false_label);
1108 } else {
1109 __ B(ARMCondition(true_high_cond), true_label);
1110 __ B(ARMCondition(false_high_cond), false_label);
1111 }
1112 // Must be equal high, so compare the lows.
1113 __ Cmp(left_low, right_low);
1114 }
1115 // The last comparison might be unsigned.
1116 // TODO: optimize cases where this is always true/false
1117 __ B(final_condition, true_label);
1118}
1119
1120void InstructionCodeGeneratorARMVIXL::GenerateCompareTestAndBranch(HCondition* condition,
1121 vixl32::Label* true_target_in,
1122 vixl32::Label* false_target_in) {
1123 // Generated branching requires both targets to be explicit. If either of the
1124 // targets is nullptr (fallthrough) use and bind `fallthrough` instead.
1125 vixl32::Label fallthrough;
1126 vixl32::Label* true_target = (true_target_in == nullptr) ? &fallthrough : true_target_in;
1127 vixl32::Label* false_target = (false_target_in == nullptr) ? &fallthrough : false_target_in;
1128
1129 Primitive::Type type = condition->InputAt(0)->GetType();
1130 switch (type) {
1131 case Primitive::kPrimLong:
1132 GenerateLongComparesAndJumps(condition, true_target, false_target);
1133 break;
1134 case Primitive::kPrimFloat:
1135 case Primitive::kPrimDouble:
1136 GenerateVcmp(condition);
1137 GenerateFPJumps(condition, true_target, false_target);
1138 break;
1139 default:
1140 LOG(FATAL) << "Unexpected compare type " << type;
1141 }
1142
1143 if (false_target != &fallthrough) {
1144 __ B(false_target);
1145 }
1146
1147 if (true_target_in == nullptr || false_target_in == nullptr) {
1148 __ Bind(&fallthrough);
1149 }
1150}
1151
1152void InstructionCodeGeneratorARMVIXL::GenerateTestAndBranch(HInstruction* instruction,
1153 size_t condition_input_index,
1154 vixl32::Label* true_target,
xueliang.zhongf51bc622016-11-04 09:23:32 +00001155 vixl32::Label* false_target,
1156 bool far_target) {
Scott Wakelingfe885462016-09-22 10:24:38 +01001157 HInstruction* cond = instruction->InputAt(condition_input_index);
1158
1159 if (true_target == nullptr && false_target == nullptr) {
1160 // Nothing to do. The code always falls through.
1161 return;
1162 } else if (cond->IsIntConstant()) {
1163 // Constant condition, statically compared against "true" (integer value 1).
1164 if (cond->AsIntConstant()->IsTrue()) {
1165 if (true_target != nullptr) {
1166 __ B(true_target);
1167 }
1168 } else {
1169 DCHECK(cond->AsIntConstant()->IsFalse()) << cond->AsIntConstant()->GetValue();
1170 if (false_target != nullptr) {
1171 __ B(false_target);
1172 }
1173 }
1174 return;
1175 }
1176
1177 // The following code generates these patterns:
1178 // (1) true_target == nullptr && false_target != nullptr
1179 // - opposite condition true => branch to false_target
1180 // (2) true_target != nullptr && false_target == nullptr
1181 // - condition true => branch to true_target
1182 // (3) true_target != nullptr && false_target != nullptr
1183 // - condition true => branch to true_target
1184 // - branch to false_target
1185 if (IsBooleanValueOrMaterializedCondition(cond)) {
1186 // Condition has been materialized, compare the output to 0.
1187 if (kIsDebugBuild) {
1188 Location cond_val = instruction->GetLocations()->InAt(condition_input_index);
1189 DCHECK(cond_val.IsRegister());
1190 }
1191 if (true_target == nullptr) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00001192 __ CompareAndBranchIfZero(InputRegisterAt(instruction, condition_input_index),
1193 false_target,
1194 far_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01001195 } else {
xueliang.zhongf51bc622016-11-04 09:23:32 +00001196 __ CompareAndBranchIfNonZero(InputRegisterAt(instruction, condition_input_index),
1197 true_target,
1198 far_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01001199 }
1200 } else {
1201 // Condition has not been materialized. Use its inputs as the comparison and
1202 // its condition as the branch condition.
1203 HCondition* condition = cond->AsCondition();
1204
1205 // If this is a long or FP comparison that has been folded into
1206 // the HCondition, generate the comparison directly.
1207 Primitive::Type type = condition->InputAt(0)->GetType();
1208 if (type == Primitive::kPrimLong || Primitive::IsFloatingPointType(type)) {
1209 GenerateCompareTestAndBranch(condition, true_target, false_target);
1210 return;
1211 }
1212
1213 LocationSummary* locations = cond->GetLocations();
1214 DCHECK(locations->InAt(0).IsRegister());
1215 vixl32::Register left = InputRegisterAt(cond, 0);
1216 Location right = locations->InAt(1);
1217 if (right.IsRegister()) {
1218 __ Cmp(left, InputRegisterAt(cond, 1));
1219 } else {
1220 DCHECK(right.IsConstant());
1221 __ Cmp(left, CodeGenerator::GetInt32ValueOf(right.GetConstant()));
1222 }
1223 if (true_target == nullptr) {
1224 __ B(ARMCondition(condition->GetOppositeCondition()), false_target);
1225 } else {
1226 __ B(ARMCondition(condition->GetCondition()), true_target);
1227 }
1228 }
1229
1230 // If neither branch falls through (case 3), the conditional branch to `true_target`
1231 // was already emitted (case 2) and we need to emit a jump to `false_target`.
1232 if (true_target != nullptr && false_target != nullptr) {
1233 __ B(false_target);
1234 }
1235}
1236
1237void LocationsBuilderARMVIXL::VisitIf(HIf* if_instr) {
1238 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr);
1239 if (IsBooleanValueOrMaterializedCondition(if_instr->InputAt(0))) {
1240 locations->SetInAt(0, Location::RequiresRegister());
1241 }
1242}
1243
1244void InstructionCodeGeneratorARMVIXL::VisitIf(HIf* if_instr) {
1245 HBasicBlock* true_successor = if_instr->IfTrueSuccessor();
1246 HBasicBlock* false_successor = if_instr->IfFalseSuccessor();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001247 vixl32::Label* true_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), true_successor) ?
1248 nullptr : codegen_->GetLabelOf(true_successor);
1249 vixl32::Label* false_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), false_successor) ?
1250 nullptr : codegen_->GetLabelOf(false_successor);
Scott Wakelingfe885462016-09-22 10:24:38 +01001251 GenerateTestAndBranch(if_instr, /* condition_input_index */ 0, true_target, false_target);
1252}
1253
Scott Wakelingc34dba72016-10-03 10:14:44 +01001254void LocationsBuilderARMVIXL::VisitDeoptimize(HDeoptimize* deoptimize) {
1255 LocationSummary* locations = new (GetGraph()->GetArena())
1256 LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath);
1257 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
1258 if (IsBooleanValueOrMaterializedCondition(deoptimize->InputAt(0))) {
1259 locations->SetInAt(0, Location::RequiresRegister());
1260 }
1261}
1262
1263void InstructionCodeGeneratorARMVIXL::VisitDeoptimize(HDeoptimize* deoptimize) {
1264 SlowPathCodeARMVIXL* slow_path =
1265 deopt_slow_paths_.NewSlowPath<DeoptimizationSlowPathARMVIXL>(deoptimize);
1266 GenerateTestAndBranch(deoptimize,
1267 /* condition_input_index */ 0,
1268 slow_path->GetEntryLabel(),
1269 /* false_target */ nullptr);
1270}
1271
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001272void LocationsBuilderARMVIXL::VisitSelect(HSelect* select) {
1273 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select);
1274 if (Primitive::IsFloatingPointType(select->GetType())) {
1275 locations->SetInAt(0, Location::RequiresFpuRegister());
1276 locations->SetInAt(1, Location::RequiresFpuRegister());
1277 } else {
1278 locations->SetInAt(0, Location::RequiresRegister());
1279 locations->SetInAt(1, Location::RequiresRegister());
1280 }
1281 if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) {
1282 locations->SetInAt(2, Location::RequiresRegister());
1283 }
1284 locations->SetOut(Location::SameAsFirstInput());
1285}
1286
1287void InstructionCodeGeneratorARMVIXL::VisitSelect(HSelect* select) {
1288 LocationSummary* locations = select->GetLocations();
1289 vixl32::Label false_target;
1290 GenerateTestAndBranch(select,
1291 /* condition_input_index */ 2,
1292 /* true_target */ nullptr,
xueliang.zhongf51bc622016-11-04 09:23:32 +00001293 &false_target,
1294 /* far_target */ false);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001295 codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType());
1296 __ Bind(&false_target);
1297}
1298
Artem Serov551b28f2016-10-18 19:11:30 +01001299void LocationsBuilderARMVIXL::VisitNativeDebugInfo(HNativeDebugInfo* info) {
1300 new (GetGraph()->GetArena()) LocationSummary(info);
1301}
1302
1303void InstructionCodeGeneratorARMVIXL::VisitNativeDebugInfo(HNativeDebugInfo*) {
1304 // MaybeRecordNativeDebugInfo is already called implicitly in CodeGenerator::Compile.
1305}
1306
Scott Wakelingfe885462016-09-22 10:24:38 +01001307void CodeGeneratorARMVIXL::GenerateNop() {
1308 __ Nop();
1309}
1310
1311void LocationsBuilderARMVIXL::HandleCondition(HCondition* cond) {
1312 LocationSummary* locations =
1313 new (GetGraph()->GetArena()) LocationSummary(cond, LocationSummary::kNoCall);
1314 // Handle the long/FP comparisons made in instruction simplification.
1315 switch (cond->InputAt(0)->GetType()) {
1316 case Primitive::kPrimLong:
1317 locations->SetInAt(0, Location::RequiresRegister());
1318 locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
1319 if (!cond->IsEmittedAtUseSite()) {
1320 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
1321 }
1322 break;
1323
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001324 // TODO(VIXL): https://android-review.googlesource.com/#/c/252265/
Scott Wakelingfe885462016-09-22 10:24:38 +01001325 case Primitive::kPrimFloat:
1326 case Primitive::kPrimDouble:
1327 locations->SetInAt(0, Location::RequiresFpuRegister());
1328 locations->SetInAt(1, Location::RequiresFpuRegister());
1329 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
Scott Wakelingfe885462016-09-22 10:24:38 +01001348 vixl32::Register out = OutputRegister(cond);
1349 vixl32::Label true_label, false_label;
1350
1351 switch (cond->InputAt(0)->GetType()) {
1352 default: {
1353 // Integer case.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001354 __ Cmp(InputRegisterAt(cond, 0), InputOperandAt(cond, 1));
1355 AssemblerAccurateScope aas(GetVIXLAssembler(),
1356 kArmInstrMaxSizeInBytes * 3u,
1357 CodeBufferCheckScope::kMaximumSize);
1358 __ ite(ARMCondition(cond->GetCondition()));
1359 __ mov(ARMCondition(cond->GetCondition()), OutputRegister(cond), 1);
1360 __ mov(ARMCondition(cond->GetOppositeCondition()), OutputRegister(cond), 0);
Scott Wakelingfe885462016-09-22 10:24:38 +01001361 return;
1362 }
1363 case Primitive::kPrimLong:
1364 GenerateLongComparesAndJumps(cond, &true_label, &false_label);
1365 break;
1366 case Primitive::kPrimFloat:
1367 case Primitive::kPrimDouble:
1368 GenerateVcmp(cond);
1369 GenerateFPJumps(cond, &true_label, &false_label);
1370 break;
1371 }
1372
1373 // Convert the jumps into the result.
1374 vixl32::Label done_label;
1375
1376 // False case: result = 0.
1377 __ Bind(&false_label);
1378 __ Mov(out, 0);
1379 __ B(&done_label);
1380
1381 // True case: result = 1.
1382 __ Bind(&true_label);
1383 __ Mov(out, 1);
1384 __ Bind(&done_label);
1385}
1386
1387void LocationsBuilderARMVIXL::VisitEqual(HEqual* comp) {
1388 HandleCondition(comp);
1389}
1390
1391void InstructionCodeGeneratorARMVIXL::VisitEqual(HEqual* comp) {
1392 HandleCondition(comp);
1393}
1394
1395void LocationsBuilderARMVIXL::VisitNotEqual(HNotEqual* comp) {
1396 HandleCondition(comp);
1397}
1398
1399void InstructionCodeGeneratorARMVIXL::VisitNotEqual(HNotEqual* comp) {
1400 HandleCondition(comp);
1401}
1402
1403void LocationsBuilderARMVIXL::VisitLessThan(HLessThan* comp) {
1404 HandleCondition(comp);
1405}
1406
1407void InstructionCodeGeneratorARMVIXL::VisitLessThan(HLessThan* comp) {
1408 HandleCondition(comp);
1409}
1410
1411void LocationsBuilderARMVIXL::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
1412 HandleCondition(comp);
1413}
1414
1415void InstructionCodeGeneratorARMVIXL::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
1416 HandleCondition(comp);
1417}
1418
1419void LocationsBuilderARMVIXL::VisitGreaterThan(HGreaterThan* comp) {
1420 HandleCondition(comp);
1421}
1422
1423void InstructionCodeGeneratorARMVIXL::VisitGreaterThan(HGreaterThan* comp) {
1424 HandleCondition(comp);
1425}
1426
1427void LocationsBuilderARMVIXL::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
1428 HandleCondition(comp);
1429}
1430
1431void InstructionCodeGeneratorARMVIXL::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
1432 HandleCondition(comp);
1433}
1434
1435void LocationsBuilderARMVIXL::VisitBelow(HBelow* comp) {
1436 HandleCondition(comp);
1437}
1438
1439void InstructionCodeGeneratorARMVIXL::VisitBelow(HBelow* comp) {
1440 HandleCondition(comp);
1441}
1442
1443void LocationsBuilderARMVIXL::VisitBelowOrEqual(HBelowOrEqual* comp) {
1444 HandleCondition(comp);
1445}
1446
1447void InstructionCodeGeneratorARMVIXL::VisitBelowOrEqual(HBelowOrEqual* comp) {
1448 HandleCondition(comp);
1449}
1450
1451void LocationsBuilderARMVIXL::VisitAbove(HAbove* comp) {
1452 HandleCondition(comp);
1453}
1454
1455void InstructionCodeGeneratorARMVIXL::VisitAbove(HAbove* comp) {
1456 HandleCondition(comp);
1457}
1458
1459void LocationsBuilderARMVIXL::VisitAboveOrEqual(HAboveOrEqual* comp) {
1460 HandleCondition(comp);
1461}
1462
1463void InstructionCodeGeneratorARMVIXL::VisitAboveOrEqual(HAboveOrEqual* comp) {
1464 HandleCondition(comp);
1465}
1466
1467void LocationsBuilderARMVIXL::VisitIntConstant(HIntConstant* constant) {
1468 LocationSummary* locations =
1469 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
1470 locations->SetOut(Location::ConstantLocation(constant));
1471}
1472
1473void InstructionCodeGeneratorARMVIXL::VisitIntConstant(HIntConstant* constant ATTRIBUTE_UNUSED) {
1474 // Will be generated at use site.
1475}
1476
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001477void LocationsBuilderARMVIXL::VisitNullConstant(HNullConstant* constant) {
1478 LocationSummary* locations =
1479 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
1480 locations->SetOut(Location::ConstantLocation(constant));
1481}
1482
1483void InstructionCodeGeneratorARMVIXL::VisitNullConstant(HNullConstant* constant ATTRIBUTE_UNUSED) {
1484 // Will be generated at use site.
1485}
1486
Scott Wakelingfe885462016-09-22 10:24:38 +01001487void LocationsBuilderARMVIXL::VisitLongConstant(HLongConstant* constant) {
1488 LocationSummary* locations =
1489 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
1490 locations->SetOut(Location::ConstantLocation(constant));
1491}
1492
1493void InstructionCodeGeneratorARMVIXL::VisitLongConstant(HLongConstant* constant ATTRIBUTE_UNUSED) {
1494 // Will be generated at use site.
1495}
1496
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01001497void LocationsBuilderARMVIXL::VisitFloatConstant(HFloatConstant* constant) {
1498 LocationSummary* locations =
1499 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
1500 locations->SetOut(Location::ConstantLocation(constant));
1501}
1502
Scott Wakelingc34dba72016-10-03 10:14:44 +01001503void InstructionCodeGeneratorARMVIXL::VisitFloatConstant(
1504 HFloatConstant* constant ATTRIBUTE_UNUSED) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01001505 // Will be generated at use site.
1506}
1507
1508void LocationsBuilderARMVIXL::VisitDoubleConstant(HDoubleConstant* constant) {
1509 LocationSummary* locations =
1510 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
1511 locations->SetOut(Location::ConstantLocation(constant));
1512}
1513
Scott Wakelingc34dba72016-10-03 10:14:44 +01001514void InstructionCodeGeneratorARMVIXL::VisitDoubleConstant(
1515 HDoubleConstant* constant ATTRIBUTE_UNUSED) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01001516 // Will be generated at use site.
1517}
1518
Scott Wakelingfe885462016-09-22 10:24:38 +01001519void LocationsBuilderARMVIXL::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
1520 memory_barrier->SetLocations(nullptr);
1521}
1522
1523void InstructionCodeGeneratorARMVIXL::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
1524 codegen_->GenerateMemoryBarrier(memory_barrier->GetBarrierKind());
1525}
1526
1527void LocationsBuilderARMVIXL::VisitReturnVoid(HReturnVoid* ret) {
1528 ret->SetLocations(nullptr);
1529}
1530
1531void InstructionCodeGeneratorARMVIXL::VisitReturnVoid(HReturnVoid* ret ATTRIBUTE_UNUSED) {
1532 codegen_->GenerateFrameExit();
1533}
1534
1535void LocationsBuilderARMVIXL::VisitReturn(HReturn* ret) {
1536 LocationSummary* locations =
1537 new (GetGraph()->GetArena()) LocationSummary(ret, LocationSummary::kNoCall);
1538 locations->SetInAt(0, parameter_visitor_.GetReturnLocation(ret->InputAt(0)->GetType()));
1539}
1540
1541void InstructionCodeGeneratorARMVIXL::VisitReturn(HReturn* ret ATTRIBUTE_UNUSED) {
1542 codegen_->GenerateFrameExit();
1543}
1544
Artem Serovcfbe9132016-10-14 15:58:56 +01001545void LocationsBuilderARMVIXL::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
1546 // The trampoline uses the same calling convention as dex calling conventions,
1547 // except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain
1548 // the method_idx.
1549 HandleInvoke(invoke);
1550}
1551
1552void InstructionCodeGeneratorARMVIXL::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
1553 codegen_->GenerateInvokeUnresolvedRuntimeCall(invoke);
1554}
1555
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001556void LocationsBuilderARMVIXL::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
1557 // Explicit clinit checks triggered by static invokes must have been pruned by
1558 // art::PrepareForRegisterAllocation.
1559 DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
1560
Anton Kirilov5ec62182016-10-13 20:16:02 +01001561 IntrinsicLocationsBuilderARMVIXL intrinsic(codegen_);
1562 if (intrinsic.TryDispatch(invoke)) {
1563 if (invoke->GetLocations()->CanCall() && invoke->HasPcRelativeDexCache()) {
1564 invoke->GetLocations()->SetInAt(invoke->GetSpecialInputIndex(), Location::Any());
1565 }
1566 return;
1567 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001568
1569 HandleInvoke(invoke);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01001570
1571 // TODO(VIXL): invoke->HasPcRelativeDexCache()
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001572}
1573
Anton Kirilov5ec62182016-10-13 20:16:02 +01001574static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorARMVIXL* codegen) {
1575 if (invoke->GetLocations()->Intrinsified()) {
1576 IntrinsicCodeGeneratorARMVIXL intrinsic(codegen);
1577 intrinsic.Dispatch(invoke);
1578 return true;
1579 }
1580 return false;
1581}
1582
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001583void InstructionCodeGeneratorARMVIXL::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
1584 // Explicit clinit checks triggered by static invokes must have been pruned by
1585 // art::PrepareForRegisterAllocation.
1586 DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
1587
Anton Kirilov5ec62182016-10-13 20:16:02 +01001588 if (TryGenerateIntrinsicCode(invoke, codegen_)) {
1589 return;
1590 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001591
1592 LocationSummary* locations = invoke->GetLocations();
1593 DCHECK(locations->HasTemps());
1594 codegen_->GenerateStaticOrDirectCall(invoke, locations->GetTemp(0));
1595 // TODO(VIXL): If necessary, use a scope to ensure we record the pc info immediately after the
1596 // previous instruction.
1597 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
1598}
1599
1600void LocationsBuilderARMVIXL::HandleInvoke(HInvoke* invoke) {
1601 InvokeDexCallingConventionVisitorARM calling_convention_visitor;
1602 CodeGenerator::CreateCommonInvokeLocationSummary(invoke, &calling_convention_visitor);
1603}
1604
1605void LocationsBuilderARMVIXL::VisitInvokeVirtual(HInvokeVirtual* invoke) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01001606 IntrinsicLocationsBuilderARMVIXL intrinsic(codegen_);
1607 if (intrinsic.TryDispatch(invoke)) {
1608 return;
1609 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001610
1611 HandleInvoke(invoke);
1612}
1613
1614void InstructionCodeGeneratorARMVIXL::VisitInvokeVirtual(HInvokeVirtual* invoke) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01001615 if (TryGenerateIntrinsicCode(invoke, codegen_)) {
1616 return;
1617 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001618
1619 codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0));
1620 DCHECK(!codegen_->IsLeafMethod());
1621 // TODO(VIXL): If necessary, use a scope to ensure we record the pc info immediately after the
1622 // previous instruction.
1623 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
1624}
1625
Artem Serovcfbe9132016-10-14 15:58:56 +01001626void LocationsBuilderARMVIXL::VisitInvokeInterface(HInvokeInterface* invoke) {
1627 HandleInvoke(invoke);
1628 // Add the hidden argument.
1629 invoke->GetLocations()->AddTemp(LocationFrom(r12));
1630}
1631
1632void InstructionCodeGeneratorARMVIXL::VisitInvokeInterface(HInvokeInterface* invoke) {
1633 // TODO: b/18116999, our IMTs can miss an IncompatibleClassChangeError.
1634 LocationSummary* locations = invoke->GetLocations();
1635 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
1636 vixl32::Register hidden_reg = RegisterFrom(locations->GetTemp(1));
1637 Location receiver = locations->InAt(0);
1638 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
1639
1640 DCHECK(!receiver.IsStackSlot());
1641
1642 // /* HeapReference<Class> */ temp = receiver->klass_
1643 GetAssembler()->LoadFromOffset(kLoadWord, temp, RegisterFrom(receiver), class_offset);
1644
1645 codegen_->MaybeRecordImplicitNullCheck(invoke);
1646 // Instead of simply (possibly) unpoisoning `temp` here, we should
1647 // emit a read barrier for the previous class reference load.
1648 // However this is not required in practice, as this is an
1649 // intermediate/temporary reference and because the current
1650 // concurrent copying collector keeps the from-space memory
1651 // intact/accessible until the end of the marking phase (the
1652 // concurrent copying collector may not in the future).
1653 GetAssembler()->MaybeUnpoisonHeapReference(temp);
1654 GetAssembler()->LoadFromOffset(kLoadWord,
1655 temp,
1656 temp,
1657 mirror::Class::ImtPtrOffset(kArmPointerSize).Uint32Value());
1658 uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement(
1659 invoke->GetImtIndex(), kArmPointerSize));
1660 // temp = temp->GetImtEntryAt(method_offset);
1661 GetAssembler()->LoadFromOffset(kLoadWord, temp, temp, method_offset);
1662 uint32_t entry_point =
1663 ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value();
1664 // LR = temp->GetEntryPoint();
1665 GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, entry_point);
1666
1667 // Set the hidden (in r12) argument. It is done here, right before a BLX to prevent other
1668 // instruction from clobbering it as they might use r12 as a scratch register.
1669 DCHECK(hidden_reg.Is(r12));
1670 __ Mov(hidden_reg, invoke->GetDexMethodIndex());
1671
1672 {
1673 AssemblerAccurateScope aas(GetVIXLAssembler(),
1674 kArmInstrMaxSizeInBytes,
1675 CodeBufferCheckScope::kMaximumSize);
1676 // LR();
1677 __ blx(lr);
1678 DCHECK(!codegen_->IsLeafMethod());
1679 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
1680 }
1681}
1682
Artem Serov02109dd2016-09-23 17:17:54 +01001683void LocationsBuilderARMVIXL::VisitNeg(HNeg* neg) {
1684 LocationSummary* locations =
1685 new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall);
1686 switch (neg->GetResultType()) {
1687 case Primitive::kPrimInt: {
1688 locations->SetInAt(0, Location::RequiresRegister());
1689 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
1690 break;
1691 }
1692 case Primitive::kPrimLong: {
1693 locations->SetInAt(0, Location::RequiresRegister());
1694 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
1695 break;
1696 }
1697
1698 case Primitive::kPrimFloat:
1699 case Primitive::kPrimDouble:
1700 locations->SetInAt(0, Location::RequiresFpuRegister());
1701 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
1702 break;
1703
1704 default:
1705 LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
1706 }
1707}
1708
1709void InstructionCodeGeneratorARMVIXL::VisitNeg(HNeg* neg) {
1710 LocationSummary* locations = neg->GetLocations();
1711 Location out = locations->Out();
1712 Location in = locations->InAt(0);
1713 switch (neg->GetResultType()) {
1714 case Primitive::kPrimInt:
1715 __ Rsb(OutputRegister(neg), InputRegisterAt(neg, 0), 0);
1716 break;
1717
1718 case Primitive::kPrimLong:
1719 // out.lo = 0 - in.lo (and update the carry/borrow (C) flag)
1720 __ Rsbs(LowRegisterFrom(out), LowRegisterFrom(in), 0);
1721 // We cannot emit an RSC (Reverse Subtract with Carry)
1722 // instruction here, as it does not exist in the Thumb-2
1723 // instruction set. We use the following approach
1724 // using SBC and SUB instead.
1725 //
1726 // out.hi = -C
1727 __ Sbc(HighRegisterFrom(out), HighRegisterFrom(out), HighRegisterFrom(out));
1728 // out.hi = out.hi - in.hi
1729 __ Sub(HighRegisterFrom(out), HighRegisterFrom(out), HighRegisterFrom(in));
1730 break;
1731
1732 case Primitive::kPrimFloat:
1733 case Primitive::kPrimDouble:
Anton Kirilove28d9ae2016-10-25 18:17:23 +01001734 // TODO(VIXL): Consider introducing an InputVRegister()
1735 // helper function (equivalent to InputRegister()).
Artem Serov02109dd2016-09-23 17:17:54 +01001736 __ Vneg(OutputVRegister(neg), InputVRegisterAt(neg, 0));
1737 break;
1738
1739 default:
1740 LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
1741 }
1742}
1743
Scott Wakelingfe885462016-09-22 10:24:38 +01001744void LocationsBuilderARMVIXL::VisitTypeConversion(HTypeConversion* conversion) {
1745 Primitive::Type result_type = conversion->GetResultType();
1746 Primitive::Type input_type = conversion->GetInputType();
1747 DCHECK_NE(result_type, input_type);
1748
1749 // The float-to-long, double-to-long and long-to-float type conversions
1750 // rely on a call to the runtime.
1751 LocationSummary::CallKind call_kind =
1752 (((input_type == Primitive::kPrimFloat || input_type == Primitive::kPrimDouble)
1753 && result_type == Primitive::kPrimLong)
1754 || (input_type == Primitive::kPrimLong && result_type == Primitive::kPrimFloat))
1755 ? LocationSummary::kCallOnMainOnly
1756 : LocationSummary::kNoCall;
1757 LocationSummary* locations =
1758 new (GetGraph()->GetArena()) LocationSummary(conversion, call_kind);
1759
1760 // The Java language does not allow treating boolean as an integral type but
1761 // our bit representation makes it safe.
1762
1763 switch (result_type) {
1764 case Primitive::kPrimByte:
1765 switch (input_type) {
1766 case Primitive::kPrimLong:
1767 // Type conversion from long to byte is a result of code transformations.
1768 case Primitive::kPrimBoolean:
1769 // Boolean input is a result of code transformations.
1770 case Primitive::kPrimShort:
1771 case Primitive::kPrimInt:
1772 case Primitive::kPrimChar:
1773 // Processing a Dex `int-to-byte' instruction.
1774 locations->SetInAt(0, Location::RequiresRegister());
1775 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
1776 break;
1777
1778 default:
1779 LOG(FATAL) << "Unexpected type conversion from " << input_type
1780 << " to " << result_type;
1781 }
1782 break;
1783
1784 case Primitive::kPrimShort:
1785 switch (input_type) {
1786 case Primitive::kPrimLong:
1787 // Type conversion from long to short is a result of code transformations.
1788 case Primitive::kPrimBoolean:
1789 // Boolean input is a result of code transformations.
1790 case Primitive::kPrimByte:
1791 case Primitive::kPrimInt:
1792 case Primitive::kPrimChar:
1793 // Processing a Dex `int-to-short' 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::kPrimInt:
1805 switch (input_type) {
1806 case Primitive::kPrimLong:
1807 // Processing a Dex `long-to-int' instruction.
1808 locations->SetInAt(0, Location::Any());
1809 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
1810 break;
1811
1812 case Primitive::kPrimFloat:
1813 // Processing a Dex `float-to-int' instruction.
1814 locations->SetInAt(0, Location::RequiresFpuRegister());
1815 locations->SetOut(Location::RequiresRegister());
1816 locations->AddTemp(Location::RequiresFpuRegister());
1817 break;
1818
1819 case Primitive::kPrimDouble:
1820 // Processing a Dex `double-to-int' instruction.
1821 locations->SetInAt(0, Location::RequiresFpuRegister());
1822 locations->SetOut(Location::RequiresRegister());
1823 locations->AddTemp(Location::RequiresFpuRegister());
1824 break;
1825
1826 default:
1827 LOG(FATAL) << "Unexpected type conversion from " << input_type
1828 << " to " << result_type;
1829 }
1830 break;
1831
1832 case Primitive::kPrimLong:
1833 switch (input_type) {
1834 case Primitive::kPrimBoolean:
1835 // Boolean input is a result of code transformations.
1836 case Primitive::kPrimByte:
1837 case Primitive::kPrimShort:
1838 case Primitive::kPrimInt:
1839 case Primitive::kPrimChar:
1840 // Processing a Dex `int-to-long' instruction.
1841 locations->SetInAt(0, Location::RequiresRegister());
1842 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
1843 break;
1844
1845 case Primitive::kPrimFloat: {
1846 // Processing a Dex `float-to-long' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001847 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1848 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
1849 locations->SetOut(LocationFrom(r0, r1));
Scott Wakelingfe885462016-09-22 10:24:38 +01001850 break;
1851 }
1852
1853 case Primitive::kPrimDouble: {
1854 // Processing a Dex `double-to-long' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001855 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1856 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0),
1857 calling_convention.GetFpuRegisterAt(1)));
1858 locations->SetOut(LocationFrom(r0, r1));
Scott Wakelingfe885462016-09-22 10:24:38 +01001859 break;
1860 }
1861
1862 default:
1863 LOG(FATAL) << "Unexpected type conversion from " << input_type
1864 << " to " << result_type;
1865 }
1866 break;
1867
1868 case Primitive::kPrimChar:
1869 switch (input_type) {
1870 case Primitive::kPrimLong:
1871 // Type conversion from long to char is a result of code transformations.
1872 case Primitive::kPrimBoolean:
1873 // Boolean input is a result of code transformations.
1874 case Primitive::kPrimByte:
1875 case Primitive::kPrimShort:
1876 case Primitive::kPrimInt:
1877 // Processing a Dex `int-to-char' instruction.
1878 locations->SetInAt(0, Location::RequiresRegister());
1879 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
1880 break;
1881
1882 default:
1883 LOG(FATAL) << "Unexpected type conversion from " << input_type
1884 << " to " << result_type;
1885 }
1886 break;
1887
1888 case Primitive::kPrimFloat:
1889 switch (input_type) {
1890 case Primitive::kPrimBoolean:
1891 // Boolean input is a result of code transformations.
1892 case Primitive::kPrimByte:
1893 case Primitive::kPrimShort:
1894 case Primitive::kPrimInt:
1895 case Primitive::kPrimChar:
1896 // Processing a Dex `int-to-float' instruction.
1897 locations->SetInAt(0, Location::RequiresRegister());
1898 locations->SetOut(Location::RequiresFpuRegister());
1899 break;
1900
1901 case Primitive::kPrimLong: {
1902 // Processing a Dex `long-to-float' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001903 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1904 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0),
1905 calling_convention.GetRegisterAt(1)));
1906 locations->SetOut(LocationFrom(calling_convention.GetFpuRegisterAt(0)));
Scott Wakelingfe885462016-09-22 10:24:38 +01001907 break;
1908 }
1909
1910 case Primitive::kPrimDouble:
1911 // Processing a Dex `double-to-float' instruction.
1912 locations->SetInAt(0, Location::RequiresFpuRegister());
1913 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
1914 break;
1915
1916 default:
1917 LOG(FATAL) << "Unexpected type conversion from " << input_type
1918 << " to " << result_type;
1919 };
1920 break;
1921
1922 case Primitive::kPrimDouble:
1923 switch (input_type) {
1924 case Primitive::kPrimBoolean:
1925 // Boolean input is a result of code transformations.
1926 case Primitive::kPrimByte:
1927 case Primitive::kPrimShort:
1928 case Primitive::kPrimInt:
1929 case Primitive::kPrimChar:
1930 // Processing a Dex `int-to-double' instruction.
1931 locations->SetInAt(0, Location::RequiresRegister());
1932 locations->SetOut(Location::RequiresFpuRegister());
1933 break;
1934
1935 case Primitive::kPrimLong:
1936 // Processing a Dex `long-to-double' instruction.
1937 locations->SetInAt(0, Location::RequiresRegister());
1938 locations->SetOut(Location::RequiresFpuRegister());
1939 locations->AddTemp(Location::RequiresFpuRegister());
1940 locations->AddTemp(Location::RequiresFpuRegister());
1941 break;
1942
1943 case Primitive::kPrimFloat:
1944 // Processing a Dex `float-to-double' instruction.
1945 locations->SetInAt(0, Location::RequiresFpuRegister());
1946 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
1947 break;
1948
1949 default:
1950 LOG(FATAL) << "Unexpected type conversion from " << input_type
1951 << " to " << result_type;
1952 };
1953 break;
1954
1955 default:
1956 LOG(FATAL) << "Unexpected type conversion from " << input_type
1957 << " to " << result_type;
1958 }
1959}
1960
1961void InstructionCodeGeneratorARMVIXL::VisitTypeConversion(HTypeConversion* conversion) {
1962 LocationSummary* locations = conversion->GetLocations();
1963 Location out = locations->Out();
1964 Location in = locations->InAt(0);
1965 Primitive::Type result_type = conversion->GetResultType();
1966 Primitive::Type input_type = conversion->GetInputType();
1967 DCHECK_NE(result_type, input_type);
1968 switch (result_type) {
1969 case Primitive::kPrimByte:
1970 switch (input_type) {
1971 case Primitive::kPrimLong:
1972 // Type conversion from long to byte is a result of code transformations.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001973 __ Sbfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 8);
Scott Wakelingfe885462016-09-22 10:24:38 +01001974 break;
1975 case Primitive::kPrimBoolean:
1976 // Boolean input is a result of code transformations.
1977 case Primitive::kPrimShort:
1978 case Primitive::kPrimInt:
1979 case Primitive::kPrimChar:
1980 // Processing a Dex `int-to-byte' instruction.
1981 __ Sbfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 8);
1982 break;
1983
1984 default:
1985 LOG(FATAL) << "Unexpected type conversion from " << input_type
1986 << " to " << result_type;
1987 }
1988 break;
1989
1990 case Primitive::kPrimShort:
1991 switch (input_type) {
1992 case Primitive::kPrimLong:
1993 // Type conversion from long to short is a result of code transformations.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001994 __ Sbfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 16);
Scott Wakelingfe885462016-09-22 10:24:38 +01001995 break;
1996 case Primitive::kPrimBoolean:
1997 // Boolean input is a result of code transformations.
1998 case Primitive::kPrimByte:
1999 case Primitive::kPrimInt:
2000 case Primitive::kPrimChar:
2001 // Processing a Dex `int-to-short' instruction.
2002 __ Sbfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 16);
2003 break;
2004
2005 default:
2006 LOG(FATAL) << "Unexpected type conversion from " << input_type
2007 << " to " << result_type;
2008 }
2009 break;
2010
2011 case Primitive::kPrimInt:
2012 switch (input_type) {
2013 case Primitive::kPrimLong:
2014 // Processing a Dex `long-to-int' instruction.
2015 DCHECK(out.IsRegister());
2016 if (in.IsRegisterPair()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002017 __ Mov(OutputRegister(conversion), LowRegisterFrom(in));
Scott Wakelingfe885462016-09-22 10:24:38 +01002018 } else if (in.IsDoubleStackSlot()) {
2019 GetAssembler()->LoadFromOffset(kLoadWord,
2020 OutputRegister(conversion),
2021 sp,
2022 in.GetStackIndex());
2023 } else {
2024 DCHECK(in.IsConstant());
2025 DCHECK(in.GetConstant()->IsLongConstant());
2026 int64_t value = in.GetConstant()->AsLongConstant()->GetValue();
2027 __ Mov(OutputRegister(conversion), static_cast<int32_t>(value));
2028 }
2029 break;
2030
2031 case Primitive::kPrimFloat: {
2032 // Processing a Dex `float-to-int' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002033 vixl32::SRegister temp = LowSRegisterFrom(locations->GetTemp(0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01002034 __ Vcvt(S32, F32, temp, InputSRegisterAt(conversion, 0));
Scott Wakelingfe885462016-09-22 10:24:38 +01002035 __ Vmov(OutputRegister(conversion), temp);
2036 break;
2037 }
2038
2039 case Primitive::kPrimDouble: {
2040 // Processing a Dex `double-to-int' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002041 vixl32::SRegister temp_s = LowSRegisterFrom(locations->GetTemp(0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01002042 __ Vcvt(S32, F64, temp_s, DRegisterFrom(in));
Scott Wakelingfe885462016-09-22 10:24:38 +01002043 __ Vmov(OutputRegister(conversion), temp_s);
2044 break;
2045 }
2046
2047 default:
2048 LOG(FATAL) << "Unexpected type conversion from " << input_type
2049 << " to " << result_type;
2050 }
2051 break;
2052
2053 case Primitive::kPrimLong:
2054 switch (input_type) {
2055 case Primitive::kPrimBoolean:
2056 // Boolean input is a result of code transformations.
2057 case Primitive::kPrimByte:
2058 case Primitive::kPrimShort:
2059 case Primitive::kPrimInt:
2060 case Primitive::kPrimChar:
2061 // Processing a Dex `int-to-long' instruction.
2062 DCHECK(out.IsRegisterPair());
2063 DCHECK(in.IsRegister());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002064 __ Mov(LowRegisterFrom(out), InputRegisterAt(conversion, 0));
Scott Wakelingfe885462016-09-22 10:24:38 +01002065 // Sign extension.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002066 __ Asr(HighRegisterFrom(out), LowRegisterFrom(out), 31);
Scott Wakelingfe885462016-09-22 10:24:38 +01002067 break;
2068
2069 case Primitive::kPrimFloat:
2070 // Processing a Dex `float-to-long' instruction.
2071 codegen_->InvokeRuntime(kQuickF2l, conversion, conversion->GetDexPc());
2072 CheckEntrypointTypes<kQuickF2l, int64_t, float>();
2073 break;
2074
2075 case Primitive::kPrimDouble:
2076 // Processing a Dex `double-to-long' instruction.
2077 codegen_->InvokeRuntime(kQuickD2l, conversion, conversion->GetDexPc());
2078 CheckEntrypointTypes<kQuickD2l, int64_t, double>();
2079 break;
2080
2081 default:
2082 LOG(FATAL) << "Unexpected type conversion from " << input_type
2083 << " to " << result_type;
2084 }
2085 break;
2086
2087 case Primitive::kPrimChar:
2088 switch (input_type) {
2089 case Primitive::kPrimLong:
2090 // Type conversion from long to char is a result of code transformations.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002091 __ Ubfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 16);
Scott Wakelingfe885462016-09-22 10:24:38 +01002092 break;
2093 case Primitive::kPrimBoolean:
2094 // Boolean input is a result of code transformations.
2095 case Primitive::kPrimByte:
2096 case Primitive::kPrimShort:
2097 case Primitive::kPrimInt:
2098 // Processing a Dex `int-to-char' instruction.
2099 __ Ubfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 16);
2100 break;
2101
2102 default:
2103 LOG(FATAL) << "Unexpected type conversion from " << input_type
2104 << " to " << result_type;
2105 }
2106 break;
2107
2108 case Primitive::kPrimFloat:
2109 switch (input_type) {
2110 case Primitive::kPrimBoolean:
2111 // Boolean input is a result of code transformations.
2112 case Primitive::kPrimByte:
2113 case Primitive::kPrimShort:
2114 case Primitive::kPrimInt:
2115 case Primitive::kPrimChar: {
2116 // Processing a Dex `int-to-float' instruction.
2117 __ Vmov(OutputSRegister(conversion), InputRegisterAt(conversion, 0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01002118 __ Vcvt(F32, S32, OutputSRegister(conversion), OutputSRegister(conversion));
Scott Wakelingfe885462016-09-22 10:24:38 +01002119 break;
2120 }
2121
2122 case Primitive::kPrimLong:
2123 // Processing a Dex `long-to-float' instruction.
2124 codegen_->InvokeRuntime(kQuickL2f, conversion, conversion->GetDexPc());
2125 CheckEntrypointTypes<kQuickL2f, float, int64_t>();
2126 break;
2127
2128 case Primitive::kPrimDouble:
2129 // Processing a Dex `double-to-float' instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01002130 __ Vcvt(F32, F64, OutputSRegister(conversion), DRegisterFrom(in));
Scott Wakelingfe885462016-09-22 10:24:38 +01002131 break;
2132
2133 default:
2134 LOG(FATAL) << "Unexpected type conversion from " << input_type
2135 << " to " << result_type;
2136 };
2137 break;
2138
2139 case Primitive::kPrimDouble:
2140 switch (input_type) {
2141 case Primitive::kPrimBoolean:
2142 // Boolean input is a result of code transformations.
2143 case Primitive::kPrimByte:
2144 case Primitive::kPrimShort:
2145 case Primitive::kPrimInt:
2146 case Primitive::kPrimChar: {
2147 // Processing a Dex `int-to-double' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002148 __ Vmov(LowSRegisterFrom(out), InputRegisterAt(conversion, 0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01002149 __ Vcvt(F64, S32, DRegisterFrom(out), LowSRegisterFrom(out));
Scott Wakelingfe885462016-09-22 10:24:38 +01002150 break;
2151 }
2152
2153 case Primitive::kPrimLong: {
2154 // Processing a Dex `long-to-double' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002155 vixl32::Register low = LowRegisterFrom(in);
2156 vixl32::Register high = HighRegisterFrom(in);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002157 vixl32::SRegister out_s = LowSRegisterFrom(out);
Scott Wakelingc34dba72016-10-03 10:14:44 +01002158 vixl32::DRegister out_d = DRegisterFrom(out);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002159 vixl32::SRegister temp_s = LowSRegisterFrom(locations->GetTemp(0));
Scott Wakelingc34dba72016-10-03 10:14:44 +01002160 vixl32::DRegister temp_d = DRegisterFrom(locations->GetTemp(0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01002161 vixl32::DRegister constant_d = DRegisterFrom(locations->GetTemp(1));
Scott Wakelingfe885462016-09-22 10:24:38 +01002162
2163 // temp_d = int-to-double(high)
2164 __ Vmov(temp_s, high);
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01002165 __ Vcvt(F64, S32, temp_d, temp_s);
Scott Wakelingfe885462016-09-22 10:24:38 +01002166 // constant_d = k2Pow32EncodingForDouble
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002167 __ Vmov(constant_d, bit_cast<double, int64_t>(k2Pow32EncodingForDouble));
Scott Wakelingfe885462016-09-22 10:24:38 +01002168 // out_d = unsigned-to-double(low)
2169 __ Vmov(out_s, low);
2170 __ Vcvt(F64, U32, out_d, out_s);
2171 // out_d += temp_d * constant_d
2172 __ Vmla(F64, out_d, temp_d, constant_d);
2173 break;
2174 }
2175
2176 case Primitive::kPrimFloat:
2177 // Processing a Dex `float-to-double' instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01002178 __ Vcvt(F64, F32, DRegisterFrom(out), InputSRegisterAt(conversion, 0));
Scott Wakelingfe885462016-09-22 10:24:38 +01002179 break;
2180
2181 default:
2182 LOG(FATAL) << "Unexpected type conversion from " << input_type
2183 << " to " << result_type;
2184 };
2185 break;
2186
2187 default:
2188 LOG(FATAL) << "Unexpected type conversion from " << input_type
2189 << " to " << result_type;
2190 }
2191}
2192
2193void LocationsBuilderARMVIXL::VisitAdd(HAdd* add) {
2194 LocationSummary* locations =
2195 new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall);
2196 switch (add->GetResultType()) {
2197 case Primitive::kPrimInt: {
2198 locations->SetInAt(0, Location::RequiresRegister());
2199 locations->SetInAt(1, Location::RegisterOrConstant(add->InputAt(1)));
2200 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2201 break;
2202 }
2203
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002204 // TODO(VIXL): https://android-review.googlesource.com/#/c/254144/
Scott Wakelingfe885462016-09-22 10:24:38 +01002205 case Primitive::kPrimLong: {
2206 locations->SetInAt(0, Location::RequiresRegister());
2207 locations->SetInAt(1, Location::RequiresRegister());
2208 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2209 break;
2210 }
2211
2212 case Primitive::kPrimFloat:
2213 case Primitive::kPrimDouble: {
2214 locations->SetInAt(0, Location::RequiresFpuRegister());
2215 locations->SetInAt(1, Location::RequiresFpuRegister());
2216 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2217 break;
2218 }
2219
2220 default:
2221 LOG(FATAL) << "Unexpected add type " << add->GetResultType();
2222 }
2223}
2224
2225void InstructionCodeGeneratorARMVIXL::VisitAdd(HAdd* add) {
2226 LocationSummary* locations = add->GetLocations();
2227 Location out = locations->Out();
2228 Location first = locations->InAt(0);
2229 Location second = locations->InAt(1);
2230
2231 switch (add->GetResultType()) {
2232 case Primitive::kPrimInt: {
2233 __ Add(OutputRegister(add), InputRegisterAt(add, 0), InputOperandAt(add, 1));
2234 }
2235 break;
2236
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002237 // TODO(VIXL): https://android-review.googlesource.com/#/c/254144/
Scott Wakelingfe885462016-09-22 10:24:38 +01002238 case Primitive::kPrimLong: {
2239 DCHECK(second.IsRegisterPair());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002240 __ Adds(LowRegisterFrom(out), LowRegisterFrom(first), LowRegisterFrom(second));
2241 __ Adc(HighRegisterFrom(out), HighRegisterFrom(first), HighRegisterFrom(second));
Scott Wakelingfe885462016-09-22 10:24:38 +01002242 break;
2243 }
2244
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002245 case Primitive::kPrimFloat:
Scott Wakelingfe885462016-09-22 10:24:38 +01002246 case Primitive::kPrimDouble:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002247 __ Vadd(OutputVRegister(add), InputVRegisterAt(add, 0), InputVRegisterAt(add, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01002248 break;
2249
2250 default:
2251 LOG(FATAL) << "Unexpected add type " << add->GetResultType();
2252 }
2253}
2254
2255void LocationsBuilderARMVIXL::VisitSub(HSub* sub) {
2256 LocationSummary* locations =
2257 new (GetGraph()->GetArena()) LocationSummary(sub, LocationSummary::kNoCall);
2258 switch (sub->GetResultType()) {
2259 case Primitive::kPrimInt: {
2260 locations->SetInAt(0, Location::RequiresRegister());
2261 locations->SetInAt(1, Location::RegisterOrConstant(sub->InputAt(1)));
2262 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2263 break;
2264 }
2265
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002266 // TODO(VIXL): https://android-review.googlesource.com/#/c/254144/
Scott Wakelingfe885462016-09-22 10:24:38 +01002267 case Primitive::kPrimLong: {
2268 locations->SetInAt(0, Location::RequiresRegister());
2269 locations->SetInAt(1, Location::RequiresRegister());
2270 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2271 break;
2272 }
2273 case Primitive::kPrimFloat:
2274 case Primitive::kPrimDouble: {
2275 locations->SetInAt(0, Location::RequiresFpuRegister());
2276 locations->SetInAt(1, Location::RequiresFpuRegister());
2277 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2278 break;
2279 }
2280 default:
2281 LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
2282 }
2283}
2284
2285void InstructionCodeGeneratorARMVIXL::VisitSub(HSub* sub) {
2286 LocationSummary* locations = sub->GetLocations();
2287 Location out = locations->Out();
2288 Location first = locations->InAt(0);
2289 Location second = locations->InAt(1);
2290 switch (sub->GetResultType()) {
2291 case Primitive::kPrimInt: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002292 __ Sub(OutputRegister(sub), InputRegisterAt(sub, 0), InputOperandAt(sub, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01002293 break;
2294 }
2295
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002296 // TODO(VIXL): https://android-review.googlesource.com/#/c/254144/
Scott Wakelingfe885462016-09-22 10:24:38 +01002297 case Primitive::kPrimLong: {
2298 DCHECK(second.IsRegisterPair());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002299 __ Subs(LowRegisterFrom(out), LowRegisterFrom(first), LowRegisterFrom(second));
2300 __ Sbc(HighRegisterFrom(out), HighRegisterFrom(first), HighRegisterFrom(second));
Scott Wakelingfe885462016-09-22 10:24:38 +01002301 break;
2302 }
2303
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002304 case Primitive::kPrimFloat:
2305 case Primitive::kPrimDouble:
2306 __ Vsub(OutputVRegister(sub), InputVRegisterAt(sub, 0), InputVRegisterAt(sub, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01002307 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01002308
2309 default:
2310 LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
2311 }
2312}
2313
2314void LocationsBuilderARMVIXL::VisitMul(HMul* mul) {
2315 LocationSummary* locations =
2316 new (GetGraph()->GetArena()) LocationSummary(mul, LocationSummary::kNoCall);
2317 switch (mul->GetResultType()) {
2318 case Primitive::kPrimInt:
2319 case Primitive::kPrimLong: {
2320 locations->SetInAt(0, Location::RequiresRegister());
2321 locations->SetInAt(1, Location::RequiresRegister());
2322 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2323 break;
2324 }
2325
2326 case Primitive::kPrimFloat:
2327 case Primitive::kPrimDouble: {
2328 locations->SetInAt(0, Location::RequiresFpuRegister());
2329 locations->SetInAt(1, Location::RequiresFpuRegister());
2330 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2331 break;
2332 }
2333
2334 default:
2335 LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
2336 }
2337}
2338
2339void InstructionCodeGeneratorARMVIXL::VisitMul(HMul* mul) {
2340 LocationSummary* locations = mul->GetLocations();
2341 Location out = locations->Out();
2342 Location first = locations->InAt(0);
2343 Location second = locations->InAt(1);
2344 switch (mul->GetResultType()) {
2345 case Primitive::kPrimInt: {
2346 __ Mul(OutputRegister(mul), InputRegisterAt(mul, 0), InputRegisterAt(mul, 1));
2347 break;
2348 }
2349 case Primitive::kPrimLong: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002350 vixl32::Register out_hi = HighRegisterFrom(out);
2351 vixl32::Register out_lo = LowRegisterFrom(out);
2352 vixl32::Register in1_hi = HighRegisterFrom(first);
2353 vixl32::Register in1_lo = LowRegisterFrom(first);
2354 vixl32::Register in2_hi = HighRegisterFrom(second);
2355 vixl32::Register in2_lo = LowRegisterFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01002356
2357 // Extra checks to protect caused by the existence of R1_R2.
2358 // The algorithm is wrong if out.hi is either in1.lo or in2.lo:
2359 // (e.g. in1=r0_r1, in2=r2_r3 and out=r1_r2);
2360 DCHECK_NE(out_hi.GetCode(), in1_lo.GetCode());
2361 DCHECK_NE(out_hi.GetCode(), in2_lo.GetCode());
2362
2363 // input: in1 - 64 bits, in2 - 64 bits
2364 // output: out
2365 // formula: out.hi : out.lo = (in1.lo * in2.hi + in1.hi * in2.lo)* 2^32 + in1.lo * in2.lo
2366 // parts: out.hi = in1.lo * in2.hi + in1.hi * in2.lo + (in1.lo * in2.lo)[63:32]
2367 // parts: out.lo = (in1.lo * in2.lo)[31:0]
2368
2369 UseScratchRegisterScope temps(GetVIXLAssembler());
2370 vixl32::Register temp = temps.Acquire();
2371 // temp <- in1.lo * in2.hi
2372 __ Mul(temp, in1_lo, in2_hi);
2373 // out.hi <- in1.lo * in2.hi + in1.hi * in2.lo
2374 __ Mla(out_hi, in1_hi, in2_lo, temp);
2375 // out.lo <- (in1.lo * in2.lo)[31:0];
2376 __ Umull(out_lo, temp, in1_lo, in2_lo);
2377 // out.hi <- in2.hi * in1.lo + in2.lo * in1.hi + (in1.lo * in2.lo)[63:32]
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002378 __ Add(out_hi, out_hi, temp);
Scott Wakelingfe885462016-09-22 10:24:38 +01002379 break;
2380 }
2381
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002382 case Primitive::kPrimFloat:
2383 case Primitive::kPrimDouble:
2384 __ Vmul(OutputVRegister(mul), InputVRegisterAt(mul, 0), InputVRegisterAt(mul, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01002385 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01002386
2387 default:
2388 LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
2389 }
2390}
2391
Scott Wakelingfe885462016-09-22 10:24:38 +01002392void InstructionCodeGeneratorARMVIXL::DivRemOneOrMinusOne(HBinaryOperation* instruction) {
2393 DCHECK(instruction->IsDiv() || instruction->IsRem());
2394 DCHECK(instruction->GetResultType() == Primitive::kPrimInt);
2395
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002396 Location second = instruction->GetLocations()->InAt(1);
Scott Wakelingfe885462016-09-22 10:24:38 +01002397 DCHECK(second.IsConstant());
2398
2399 vixl32::Register out = OutputRegister(instruction);
2400 vixl32::Register dividend = InputRegisterAt(instruction, 0);
2401 int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
2402 DCHECK(imm == 1 || imm == -1);
2403
2404 if (instruction->IsRem()) {
2405 __ Mov(out, 0);
2406 } else {
2407 if (imm == 1) {
2408 __ Mov(out, dividend);
2409 } else {
2410 __ Rsb(out, dividend, 0);
2411 }
2412 }
2413}
2414
2415void InstructionCodeGeneratorARMVIXL::DivRemByPowerOfTwo(HBinaryOperation* instruction) {
2416 DCHECK(instruction->IsDiv() || instruction->IsRem());
2417 DCHECK(instruction->GetResultType() == Primitive::kPrimInt);
2418
2419 LocationSummary* locations = instruction->GetLocations();
2420 Location second = locations->InAt(1);
2421 DCHECK(second.IsConstant());
2422
2423 vixl32::Register out = OutputRegister(instruction);
2424 vixl32::Register dividend = InputRegisterAt(instruction, 0);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002425 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
Scott Wakelingfe885462016-09-22 10:24:38 +01002426 int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
2427 uint32_t abs_imm = static_cast<uint32_t>(AbsOrMin(imm));
2428 int ctz_imm = CTZ(abs_imm);
2429
2430 if (ctz_imm == 1) {
2431 __ Lsr(temp, dividend, 32 - ctz_imm);
2432 } else {
2433 __ Asr(temp, dividend, 31);
2434 __ Lsr(temp, temp, 32 - ctz_imm);
2435 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002436 __ Add(out, temp, dividend);
Scott Wakelingfe885462016-09-22 10:24:38 +01002437
2438 if (instruction->IsDiv()) {
2439 __ Asr(out, out, ctz_imm);
2440 if (imm < 0) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002441 __ Rsb(out, out, 0);
Scott Wakelingfe885462016-09-22 10:24:38 +01002442 }
2443 } else {
2444 __ Ubfx(out, out, 0, ctz_imm);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002445 __ Sub(out, out, temp);
Scott Wakelingfe885462016-09-22 10:24:38 +01002446 }
2447}
2448
2449void InstructionCodeGeneratorARMVIXL::GenerateDivRemWithAnyConstant(HBinaryOperation* instruction) {
2450 DCHECK(instruction->IsDiv() || instruction->IsRem());
2451 DCHECK(instruction->GetResultType() == Primitive::kPrimInt);
2452
2453 LocationSummary* locations = instruction->GetLocations();
2454 Location second = locations->InAt(1);
2455 DCHECK(second.IsConstant());
2456
2457 vixl32::Register out = OutputRegister(instruction);
2458 vixl32::Register dividend = InputRegisterAt(instruction, 0);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002459 vixl32::Register temp1 = RegisterFrom(locations->GetTemp(0));
2460 vixl32::Register temp2 = RegisterFrom(locations->GetTemp(1));
Scott Wakelingfe885462016-09-22 10:24:38 +01002461 int64_t imm = second.GetConstant()->AsIntConstant()->GetValue();
2462
2463 int64_t magic;
2464 int shift;
2465 CalculateMagicAndShiftForDivRem(imm, false /* is_long */, &magic, &shift);
2466
2467 __ Mov(temp1, magic);
2468 __ Smull(temp2, temp1, dividend, temp1);
2469
2470 if (imm > 0 && magic < 0) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002471 __ Add(temp1, temp1, dividend);
Scott Wakelingfe885462016-09-22 10:24:38 +01002472 } else if (imm < 0 && magic > 0) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002473 __ Sub(temp1, temp1, dividend);
Scott Wakelingfe885462016-09-22 10:24:38 +01002474 }
2475
2476 if (shift != 0) {
2477 __ Asr(temp1, temp1, shift);
2478 }
2479
2480 if (instruction->IsDiv()) {
2481 __ Sub(out, temp1, Operand(temp1, vixl32::Shift(ASR), 31));
2482 } else {
2483 __ Sub(temp1, temp1, Operand(temp1, vixl32::Shift(ASR), 31));
2484 // TODO: Strength reduction for mls.
2485 __ Mov(temp2, imm);
2486 __ Mls(out, temp1, temp2, dividend);
2487 }
2488}
2489
2490void InstructionCodeGeneratorARMVIXL::GenerateDivRemConstantIntegral(
2491 HBinaryOperation* instruction) {
2492 DCHECK(instruction->IsDiv() || instruction->IsRem());
2493 DCHECK(instruction->GetResultType() == Primitive::kPrimInt);
2494
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002495 Location second = instruction->GetLocations()->InAt(1);
Scott Wakelingfe885462016-09-22 10:24:38 +01002496 DCHECK(second.IsConstant());
2497
2498 int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
2499 if (imm == 0) {
2500 // Do not generate anything. DivZeroCheck would prevent any code to be executed.
2501 } else if (imm == 1 || imm == -1) {
2502 DivRemOneOrMinusOne(instruction);
2503 } else if (IsPowerOfTwo(AbsOrMin(imm))) {
2504 DivRemByPowerOfTwo(instruction);
2505 } else {
2506 DCHECK(imm <= -2 || imm >= 2);
2507 GenerateDivRemWithAnyConstant(instruction);
2508 }
2509}
2510
2511void LocationsBuilderARMVIXL::VisitDiv(HDiv* div) {
2512 LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
2513 if (div->GetResultType() == Primitive::kPrimLong) {
2514 // pLdiv runtime call.
2515 call_kind = LocationSummary::kCallOnMainOnly;
2516 } else if (div->GetResultType() == Primitive::kPrimInt && div->InputAt(1)->IsConstant()) {
2517 // sdiv will be replaced by other instruction sequence.
2518 } else if (div->GetResultType() == Primitive::kPrimInt &&
2519 !codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
2520 // pIdivmod runtime call.
2521 call_kind = LocationSummary::kCallOnMainOnly;
2522 }
2523
2524 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(div, call_kind);
2525
2526 switch (div->GetResultType()) {
2527 case Primitive::kPrimInt: {
2528 if (div->InputAt(1)->IsConstant()) {
2529 locations->SetInAt(0, Location::RequiresRegister());
2530 locations->SetInAt(1, Location::ConstantLocation(div->InputAt(1)->AsConstant()));
2531 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2532 int32_t value = div->InputAt(1)->AsIntConstant()->GetValue();
2533 if (value == 1 || value == 0 || value == -1) {
2534 // No temp register required.
2535 } else {
2536 locations->AddTemp(Location::RequiresRegister());
2537 if (!IsPowerOfTwo(AbsOrMin(value))) {
2538 locations->AddTemp(Location::RequiresRegister());
2539 }
2540 }
2541 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
2542 locations->SetInAt(0, Location::RequiresRegister());
2543 locations->SetInAt(1, Location::RequiresRegister());
2544 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2545 } else {
Artem Serov551b28f2016-10-18 19:11:30 +01002546 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2547 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
2548 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
2549 // Note: divrem will compute both the quotient and the remainder as the pair R0 and R1, but
2550 // we only need the former.
2551 locations->SetOut(LocationFrom(r0));
Scott Wakelingfe885462016-09-22 10:24:38 +01002552 }
2553 break;
2554 }
2555 case Primitive::kPrimLong: {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01002556 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2557 locations->SetInAt(0, LocationFrom(
2558 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
2559 locations->SetInAt(1, LocationFrom(
2560 calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3)));
2561 locations->SetOut(LocationFrom(r0, r1));
Scott Wakelingfe885462016-09-22 10:24:38 +01002562 break;
2563 }
2564 case Primitive::kPrimFloat:
2565 case Primitive::kPrimDouble: {
2566 locations->SetInAt(0, Location::RequiresFpuRegister());
2567 locations->SetInAt(1, Location::RequiresFpuRegister());
2568 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2569 break;
2570 }
2571
2572 default:
2573 LOG(FATAL) << "Unexpected div type " << div->GetResultType();
2574 }
2575}
2576
2577void InstructionCodeGeneratorARMVIXL::VisitDiv(HDiv* div) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01002578 Location lhs = div->GetLocations()->InAt(0);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002579 Location rhs = div->GetLocations()->InAt(1);
Scott Wakelingfe885462016-09-22 10:24:38 +01002580
2581 switch (div->GetResultType()) {
2582 case Primitive::kPrimInt: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002583 if (rhs.IsConstant()) {
Scott Wakelingfe885462016-09-22 10:24:38 +01002584 GenerateDivRemConstantIntegral(div);
2585 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
2586 __ Sdiv(OutputRegister(div), InputRegisterAt(div, 0), InputRegisterAt(div, 1));
2587 } else {
Artem Serov551b28f2016-10-18 19:11:30 +01002588 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2589 DCHECK(calling_convention.GetRegisterAt(0).Is(RegisterFrom(lhs)));
2590 DCHECK(calling_convention.GetRegisterAt(1).Is(RegisterFrom(rhs)));
2591 DCHECK(r0.Is(OutputRegister(div)));
2592
2593 codegen_->InvokeRuntime(kQuickIdivmod, div, div->GetDexPc());
2594 CheckEntrypointTypes<kQuickIdivmod, int32_t, int32_t, int32_t>();
Scott Wakelingfe885462016-09-22 10:24:38 +01002595 }
2596 break;
2597 }
2598
2599 case Primitive::kPrimLong: {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01002600 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2601 DCHECK(calling_convention.GetRegisterAt(0).Is(LowRegisterFrom(lhs)));
2602 DCHECK(calling_convention.GetRegisterAt(1).Is(HighRegisterFrom(lhs)));
2603 DCHECK(calling_convention.GetRegisterAt(2).Is(LowRegisterFrom(rhs)));
2604 DCHECK(calling_convention.GetRegisterAt(3).Is(HighRegisterFrom(rhs)));
2605 DCHECK(LowRegisterFrom(div->GetLocations()->Out()).Is(r0));
2606 DCHECK(HighRegisterFrom(div->GetLocations()->Out()).Is(r1));
2607
2608 codegen_->InvokeRuntime(kQuickLdiv, div, div->GetDexPc());
2609 CheckEntrypointTypes<kQuickLdiv, int64_t, int64_t, int64_t>();
Scott Wakelingfe885462016-09-22 10:24:38 +01002610 break;
2611 }
2612
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002613 case Primitive::kPrimFloat:
2614 case Primitive::kPrimDouble:
2615 __ Vdiv(OutputVRegister(div), InputVRegisterAt(div, 0), InputVRegisterAt(div, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01002616 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01002617
2618 default:
2619 LOG(FATAL) << "Unexpected div type " << div->GetResultType();
2620 }
2621}
2622
Artem Serov551b28f2016-10-18 19:11:30 +01002623void LocationsBuilderARMVIXL::VisitRem(HRem* rem) {
2624 Primitive::Type type = rem->GetResultType();
2625
2626 // Most remainders are implemented in the runtime.
2627 LocationSummary::CallKind call_kind = LocationSummary::kCallOnMainOnly;
2628 if (rem->GetResultType() == Primitive::kPrimInt && rem->InputAt(1)->IsConstant()) {
2629 // sdiv will be replaced by other instruction sequence.
2630 call_kind = LocationSummary::kNoCall;
2631 } else if ((rem->GetResultType() == Primitive::kPrimInt)
2632 && codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
2633 // Have hardware divide instruction for int, do it with three instructions.
2634 call_kind = LocationSummary::kNoCall;
2635 }
2636
2637 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(rem, call_kind);
2638
2639 switch (type) {
2640 case Primitive::kPrimInt: {
2641 if (rem->InputAt(1)->IsConstant()) {
2642 locations->SetInAt(0, Location::RequiresRegister());
2643 locations->SetInAt(1, Location::ConstantLocation(rem->InputAt(1)->AsConstant()));
2644 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2645 int32_t value = rem->InputAt(1)->AsIntConstant()->GetValue();
2646 if (value == 1 || value == 0 || value == -1) {
2647 // No temp register required.
2648 } else {
2649 locations->AddTemp(Location::RequiresRegister());
2650 if (!IsPowerOfTwo(AbsOrMin(value))) {
2651 locations->AddTemp(Location::RequiresRegister());
2652 }
2653 }
2654 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
2655 locations->SetInAt(0, Location::RequiresRegister());
2656 locations->SetInAt(1, Location::RequiresRegister());
2657 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2658 locations->AddTemp(Location::RequiresRegister());
2659 } else {
2660 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2661 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
2662 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
2663 // Note: divrem will compute both the quotient and the remainder as the pair R0 and R1, but
2664 // we only need the latter.
2665 locations->SetOut(LocationFrom(r1));
2666 }
2667 break;
2668 }
2669 case Primitive::kPrimLong: {
2670 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2671 locations->SetInAt(0, LocationFrom(
2672 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
2673 locations->SetInAt(1, LocationFrom(
2674 calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3)));
2675 // The runtime helper puts the output in R2,R3.
2676 locations->SetOut(LocationFrom(r2, r3));
2677 break;
2678 }
2679 case Primitive::kPrimFloat: {
2680 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2681 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
2682 locations->SetInAt(1, LocationFrom(calling_convention.GetFpuRegisterAt(1)));
2683 locations->SetOut(LocationFrom(s0));
2684 break;
2685 }
2686
2687 case Primitive::kPrimDouble: {
2688 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2689 locations->SetInAt(0, LocationFrom(
2690 calling_convention.GetFpuRegisterAt(0), calling_convention.GetFpuRegisterAt(1)));
2691 locations->SetInAt(1, LocationFrom(
2692 calling_convention.GetFpuRegisterAt(2), calling_convention.GetFpuRegisterAt(3)));
2693 locations->SetOut(LocationFrom(s0, s1));
2694 break;
2695 }
2696
2697 default:
2698 LOG(FATAL) << "Unexpected rem type " << type;
2699 }
2700}
2701
2702void InstructionCodeGeneratorARMVIXL::VisitRem(HRem* rem) {
2703 LocationSummary* locations = rem->GetLocations();
2704 Location second = locations->InAt(1);
2705
2706 Primitive::Type type = rem->GetResultType();
2707 switch (type) {
2708 case Primitive::kPrimInt: {
2709 vixl32::Register reg1 = InputRegisterAt(rem, 0);
2710 vixl32::Register out_reg = OutputRegister(rem);
2711 if (second.IsConstant()) {
2712 GenerateDivRemConstantIntegral(rem);
2713 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
2714 vixl32::Register reg2 = RegisterFrom(second);
2715 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
2716
2717 // temp = reg1 / reg2 (integer division)
2718 // dest = reg1 - temp * reg2
2719 __ Sdiv(temp, reg1, reg2);
2720 __ Mls(out_reg, temp, reg2, reg1);
2721 } else {
2722 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2723 DCHECK(reg1.Is(calling_convention.GetRegisterAt(0)));
2724 DCHECK(RegisterFrom(second).Is(calling_convention.GetRegisterAt(1)));
2725 DCHECK(out_reg.Is(r1));
2726
2727 codegen_->InvokeRuntime(kQuickIdivmod, rem, rem->GetDexPc());
2728 CheckEntrypointTypes<kQuickIdivmod, int32_t, int32_t, int32_t>();
2729 }
2730 break;
2731 }
2732
2733 case Primitive::kPrimLong: {
2734 codegen_->InvokeRuntime(kQuickLmod, rem, rem->GetDexPc());
2735 CheckEntrypointTypes<kQuickLmod, int64_t, int64_t, int64_t>();
2736 break;
2737 }
2738
2739 case Primitive::kPrimFloat: {
2740 codegen_->InvokeRuntime(kQuickFmodf, rem, rem->GetDexPc());
2741 CheckEntrypointTypes<kQuickFmodf, float, float, float>();
2742 break;
2743 }
2744
2745 case Primitive::kPrimDouble: {
2746 codegen_->InvokeRuntime(kQuickFmod, rem, rem->GetDexPc());
2747 CheckEntrypointTypes<kQuickFmod, double, double, double>();
2748 break;
2749 }
2750
2751 default:
2752 LOG(FATAL) << "Unexpected rem type " << type;
2753 }
2754}
2755
2756
Scott Wakelingfe885462016-09-22 10:24:38 +01002757void LocationsBuilderARMVIXL::VisitDivZeroCheck(HDivZeroCheck* instruction) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002758 // TODO(VIXL): https://android-review.googlesource.com/#/c/275337/
Scott Wakelingfe885462016-09-22 10:24:38 +01002759 LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
2760 ? LocationSummary::kCallOnSlowPath
2761 : LocationSummary::kNoCall;
2762 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
2763 locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0)));
2764 if (instruction->HasUses()) {
2765 locations->SetOut(Location::SameAsFirstInput());
2766 }
2767}
2768
2769void InstructionCodeGeneratorARMVIXL::VisitDivZeroCheck(HDivZeroCheck* instruction) {
2770 DivZeroCheckSlowPathARMVIXL* slow_path =
2771 new (GetGraph()->GetArena()) DivZeroCheckSlowPathARMVIXL(instruction);
2772 codegen_->AddSlowPath(slow_path);
2773
2774 LocationSummary* locations = instruction->GetLocations();
2775 Location value = locations->InAt(0);
2776
2777 switch (instruction->GetType()) {
2778 case Primitive::kPrimBoolean:
2779 case Primitive::kPrimByte:
2780 case Primitive::kPrimChar:
2781 case Primitive::kPrimShort:
2782 case Primitive::kPrimInt: {
2783 if (value.IsRegister()) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00002784 __ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
Scott Wakelingfe885462016-09-22 10:24:38 +01002785 } else {
2786 DCHECK(value.IsConstant()) << value;
2787 if (value.GetConstant()->AsIntConstant()->GetValue() == 0) {
2788 __ B(slow_path->GetEntryLabel());
2789 }
2790 }
2791 break;
2792 }
2793 case Primitive::kPrimLong: {
2794 if (value.IsRegisterPair()) {
2795 UseScratchRegisterScope temps(GetVIXLAssembler());
2796 vixl32::Register temp = temps.Acquire();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002797 __ Orrs(temp, LowRegisterFrom(value), HighRegisterFrom(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01002798 __ B(eq, slow_path->GetEntryLabel());
2799 } else {
2800 DCHECK(value.IsConstant()) << value;
2801 if (value.GetConstant()->AsLongConstant()->GetValue() == 0) {
2802 __ B(slow_path->GetEntryLabel());
2803 }
2804 }
2805 break;
2806 }
2807 default:
2808 LOG(FATAL) << "Unexpected type for HDivZeroCheck " << instruction->GetType();
2809 }
2810}
2811
Artem Serov02109dd2016-09-23 17:17:54 +01002812void InstructionCodeGeneratorARMVIXL::HandleIntegerRotate(HRor* ror) {
2813 LocationSummary* locations = ror->GetLocations();
2814 vixl32::Register in = InputRegisterAt(ror, 0);
2815 Location rhs = locations->InAt(1);
2816 vixl32::Register out = OutputRegister(ror);
2817
2818 if (rhs.IsConstant()) {
2819 // Arm32 and Thumb2 assemblers require a rotation on the interval [1,31],
2820 // so map all rotations to a +ve. equivalent in that range.
2821 // (e.g. left *or* right by -2 bits == 30 bits in the same direction.)
2822 uint32_t rot = CodeGenerator::GetInt32ValueOf(rhs.GetConstant()) & 0x1F;
2823 if (rot) {
2824 // Rotate, mapping left rotations to right equivalents if necessary.
2825 // (e.g. left by 2 bits == right by 30.)
2826 __ Ror(out, in, rot);
2827 } else if (!out.Is(in)) {
2828 __ Mov(out, in);
2829 }
2830 } else {
2831 __ Ror(out, in, RegisterFrom(rhs));
2832 }
2833}
2834
2835// Gain some speed by mapping all Long rotates onto equivalent pairs of Integer
2836// rotates by swapping input regs (effectively rotating by the first 32-bits of
2837// a larger rotation) or flipping direction (thus treating larger right/left
2838// rotations as sub-word sized rotations in the other direction) as appropriate.
2839void InstructionCodeGeneratorARMVIXL::HandleLongRotate(HRor* ror) {
2840 LocationSummary* locations = ror->GetLocations();
2841 vixl32::Register in_reg_lo = LowRegisterFrom(locations->InAt(0));
2842 vixl32::Register in_reg_hi = HighRegisterFrom(locations->InAt(0));
2843 Location rhs = locations->InAt(1);
2844 vixl32::Register out_reg_lo = LowRegisterFrom(locations->Out());
2845 vixl32::Register out_reg_hi = HighRegisterFrom(locations->Out());
2846
2847 if (rhs.IsConstant()) {
2848 uint64_t rot = CodeGenerator::GetInt64ValueOf(rhs.GetConstant());
2849 // Map all rotations to +ve. equivalents on the interval [0,63].
2850 rot &= kMaxLongShiftDistance;
2851 // For rotates over a word in size, 'pre-rotate' by 32-bits to keep rotate
2852 // logic below to a simple pair of binary orr.
2853 // (e.g. 34 bits == in_reg swap + 2 bits right.)
2854 if (rot >= kArmBitsPerWord) {
2855 rot -= kArmBitsPerWord;
2856 std::swap(in_reg_hi, in_reg_lo);
2857 }
2858 // Rotate, or mov to out for zero or word size rotations.
2859 if (rot != 0u) {
2860 __ Lsr(out_reg_hi, in_reg_hi, rot);
2861 __ Orr(out_reg_hi, out_reg_hi, Operand(in_reg_lo, ShiftType::LSL, kArmBitsPerWord - rot));
2862 __ Lsr(out_reg_lo, in_reg_lo, rot);
2863 __ Orr(out_reg_lo, out_reg_lo, Operand(in_reg_hi, ShiftType::LSL, kArmBitsPerWord - rot));
2864 } else {
2865 __ Mov(out_reg_lo, in_reg_lo);
2866 __ Mov(out_reg_hi, in_reg_hi);
2867 }
2868 } else {
2869 vixl32::Register shift_right = RegisterFrom(locations->GetTemp(0));
2870 vixl32::Register shift_left = RegisterFrom(locations->GetTemp(1));
2871 vixl32::Label end;
2872 vixl32::Label shift_by_32_plus_shift_right;
2873
2874 __ And(shift_right, RegisterFrom(rhs), 0x1F);
2875 __ Lsrs(shift_left, RegisterFrom(rhs), 6);
2876 // TODO(VIXL): Check that flags are kept after "vixl32::LeaveFlags" enabled.
2877 __ Rsb(shift_left, shift_right, kArmBitsPerWord);
2878 __ B(cc, &shift_by_32_plus_shift_right);
2879
2880 // out_reg_hi = (reg_hi << shift_left) | (reg_lo >> shift_right).
2881 // out_reg_lo = (reg_lo << shift_left) | (reg_hi >> shift_right).
2882 __ Lsl(out_reg_hi, in_reg_hi, shift_left);
2883 __ Lsr(out_reg_lo, in_reg_lo, shift_right);
2884 __ Add(out_reg_hi, out_reg_hi, out_reg_lo);
2885 __ Lsl(out_reg_lo, in_reg_lo, shift_left);
2886 __ Lsr(shift_left, in_reg_hi, shift_right);
2887 __ Add(out_reg_lo, out_reg_lo, shift_left);
2888 __ B(&end);
2889
2890 __ Bind(&shift_by_32_plus_shift_right); // Shift by 32+shift_right.
2891 // out_reg_hi = (reg_hi >> shift_right) | (reg_lo << shift_left).
2892 // out_reg_lo = (reg_lo >> shift_right) | (reg_hi << shift_left).
2893 __ Lsr(out_reg_hi, in_reg_hi, shift_right);
2894 __ Lsl(out_reg_lo, in_reg_lo, shift_left);
2895 __ Add(out_reg_hi, out_reg_hi, out_reg_lo);
2896 __ Lsr(out_reg_lo, in_reg_lo, shift_right);
2897 __ Lsl(shift_right, in_reg_hi, shift_left);
2898 __ Add(out_reg_lo, out_reg_lo, shift_right);
2899
2900 __ Bind(&end);
2901 }
2902}
2903
2904void LocationsBuilderARMVIXL::VisitRor(HRor* ror) {
2905 LocationSummary* locations =
2906 new (GetGraph()->GetArena()) LocationSummary(ror, LocationSummary::kNoCall);
2907 switch (ror->GetResultType()) {
2908 case Primitive::kPrimInt: {
2909 locations->SetInAt(0, Location::RequiresRegister());
2910 locations->SetInAt(1, Location::RegisterOrConstant(ror->InputAt(1)));
2911 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2912 break;
2913 }
2914 case Primitive::kPrimLong: {
2915 locations->SetInAt(0, Location::RequiresRegister());
2916 if (ror->InputAt(1)->IsConstant()) {
2917 locations->SetInAt(1, Location::ConstantLocation(ror->InputAt(1)->AsConstant()));
2918 } else {
2919 locations->SetInAt(1, Location::RequiresRegister());
2920 locations->AddTemp(Location::RequiresRegister());
2921 locations->AddTemp(Location::RequiresRegister());
2922 }
2923 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
2924 break;
2925 }
2926 default:
2927 LOG(FATAL) << "Unexpected operation type " << ror->GetResultType();
2928 }
2929}
2930
2931void InstructionCodeGeneratorARMVIXL::VisitRor(HRor* ror) {
2932 Primitive::Type type = ror->GetResultType();
2933 switch (type) {
2934 case Primitive::kPrimInt: {
2935 HandleIntegerRotate(ror);
2936 break;
2937 }
2938 case Primitive::kPrimLong: {
2939 HandleLongRotate(ror);
2940 break;
2941 }
2942 default:
2943 LOG(FATAL) << "Unexpected operation type " << type;
2944 UNREACHABLE();
2945 }
2946}
2947
Artem Serov02d37832016-10-25 15:25:33 +01002948void LocationsBuilderARMVIXL::HandleShift(HBinaryOperation* op) {
2949 DCHECK(op->IsShl() || op->IsShr() || op->IsUShr());
2950
2951 LocationSummary* locations =
2952 new (GetGraph()->GetArena()) LocationSummary(op, LocationSummary::kNoCall);
2953
2954 switch (op->GetResultType()) {
2955 case Primitive::kPrimInt: {
2956 locations->SetInAt(0, Location::RequiresRegister());
2957 if (op->InputAt(1)->IsConstant()) {
2958 locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant()));
2959 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2960 } else {
2961 locations->SetInAt(1, Location::RequiresRegister());
2962 // Make the output overlap, as it will be used to hold the masked
2963 // second input.
2964 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
2965 }
2966 break;
2967 }
2968 case Primitive::kPrimLong: {
2969 locations->SetInAt(0, Location::RequiresRegister());
2970 if (op->InputAt(1)->IsConstant()) {
2971 locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant()));
2972 // For simplicity, use kOutputOverlap even though we only require that low registers
2973 // don't clash with high registers which the register allocator currently guarantees.
2974 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
2975 } else {
2976 locations->SetInAt(1, Location::RequiresRegister());
2977 locations->AddTemp(Location::RequiresRegister());
2978 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
2979 }
2980 break;
2981 }
2982 default:
2983 LOG(FATAL) << "Unexpected operation type " << op->GetResultType();
2984 }
2985}
2986
2987void InstructionCodeGeneratorARMVIXL::HandleShift(HBinaryOperation* op) {
2988 DCHECK(op->IsShl() || op->IsShr() || op->IsUShr());
2989
2990 LocationSummary* locations = op->GetLocations();
2991 Location out = locations->Out();
2992 Location first = locations->InAt(0);
2993 Location second = locations->InAt(1);
2994
2995 Primitive::Type type = op->GetResultType();
2996 switch (type) {
2997 case Primitive::kPrimInt: {
2998 vixl32::Register out_reg = OutputRegister(op);
2999 vixl32::Register first_reg = InputRegisterAt(op, 0);
3000 if (second.IsRegister()) {
3001 vixl32::Register second_reg = RegisterFrom(second);
3002 // ARM doesn't mask the shift count so we need to do it ourselves.
3003 __ And(out_reg, second_reg, kMaxIntShiftDistance);
3004 if (op->IsShl()) {
3005 __ Lsl(out_reg, first_reg, out_reg);
3006 } else if (op->IsShr()) {
3007 __ Asr(out_reg, first_reg, out_reg);
3008 } else {
3009 __ Lsr(out_reg, first_reg, out_reg);
3010 }
3011 } else {
3012 int32_t cst = second.GetConstant()->AsIntConstant()->GetValue();
3013 uint32_t shift_value = cst & kMaxIntShiftDistance;
3014 if (shift_value == 0) { // ARM does not support shifting with 0 immediate.
3015 __ Mov(out_reg, first_reg);
3016 } else if (op->IsShl()) {
3017 __ Lsl(out_reg, first_reg, shift_value);
3018 } else if (op->IsShr()) {
3019 __ Asr(out_reg, first_reg, shift_value);
3020 } else {
3021 __ Lsr(out_reg, first_reg, shift_value);
3022 }
3023 }
3024 break;
3025 }
3026 case Primitive::kPrimLong: {
3027 vixl32::Register o_h = HighRegisterFrom(out);
3028 vixl32::Register o_l = LowRegisterFrom(out);
3029
3030 vixl32::Register high = HighRegisterFrom(first);
3031 vixl32::Register low = LowRegisterFrom(first);
3032
3033 if (second.IsRegister()) {
3034 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
3035
3036 vixl32::Register second_reg = RegisterFrom(second);
3037
3038 if (op->IsShl()) {
3039 __ And(o_l, second_reg, kMaxLongShiftDistance);
3040 // Shift the high part
3041 __ Lsl(o_h, high, o_l);
3042 // Shift the low part and `or` what overflew on the high part
3043 __ Rsb(temp, o_l, kArmBitsPerWord);
3044 __ Lsr(temp, low, temp);
3045 __ Orr(o_h, o_h, temp);
3046 // If the shift is > 32 bits, override the high part
3047 __ Subs(temp, o_l, kArmBitsPerWord);
3048 {
3049 AssemblerAccurateScope guard(GetVIXLAssembler(),
3050 3 * kArmInstrMaxSizeInBytes,
3051 CodeBufferCheckScope::kMaximumSize);
3052 __ it(pl);
3053 __ lsl(pl, o_h, low, temp);
3054 }
3055 // Shift the low part
3056 __ Lsl(o_l, low, o_l);
3057 } else if (op->IsShr()) {
3058 __ And(o_h, second_reg, kMaxLongShiftDistance);
3059 // Shift the low part
3060 __ Lsr(o_l, low, o_h);
3061 // Shift the high part and `or` what underflew on the low part
3062 __ Rsb(temp, o_h, kArmBitsPerWord);
3063 __ Lsl(temp, high, temp);
3064 __ Orr(o_l, o_l, temp);
3065 // If the shift is > 32 bits, override the low part
3066 __ Subs(temp, o_h, kArmBitsPerWord);
3067 {
3068 AssemblerAccurateScope guard(GetVIXLAssembler(),
3069 3 * kArmInstrMaxSizeInBytes,
3070 CodeBufferCheckScope::kMaximumSize);
3071 __ it(pl);
3072 __ asr(pl, o_l, high, temp);
3073 }
3074 // Shift the high part
3075 __ Asr(o_h, high, o_h);
3076 } else {
3077 __ And(o_h, second_reg, kMaxLongShiftDistance);
3078 // same as Shr except we use `Lsr`s and not `Asr`s
3079 __ Lsr(o_l, low, o_h);
3080 __ Rsb(temp, o_h, kArmBitsPerWord);
3081 __ Lsl(temp, high, temp);
3082 __ Orr(o_l, o_l, temp);
3083 __ Subs(temp, o_h, kArmBitsPerWord);
3084 {
3085 AssemblerAccurateScope guard(GetVIXLAssembler(),
3086 3 * kArmInstrMaxSizeInBytes,
3087 CodeBufferCheckScope::kMaximumSize);
3088 __ it(pl);
3089 __ lsr(pl, o_l, high, temp);
3090 }
3091 __ Lsr(o_h, high, o_h);
3092 }
3093 } else {
3094 // Register allocator doesn't create partial overlap.
3095 DCHECK(!o_l.Is(high));
3096 DCHECK(!o_h.Is(low));
3097 int32_t cst = second.GetConstant()->AsIntConstant()->GetValue();
3098 uint32_t shift_value = cst & kMaxLongShiftDistance;
3099 if (shift_value > 32) {
3100 if (op->IsShl()) {
3101 __ Lsl(o_h, low, shift_value - 32);
3102 __ Mov(o_l, 0);
3103 } else if (op->IsShr()) {
3104 __ Asr(o_l, high, shift_value - 32);
3105 __ Asr(o_h, high, 31);
3106 } else {
3107 __ Lsr(o_l, high, shift_value - 32);
3108 __ Mov(o_h, 0);
3109 }
3110 } else if (shift_value == 32) {
3111 if (op->IsShl()) {
3112 __ Mov(o_h, low);
3113 __ Mov(o_l, 0);
3114 } else if (op->IsShr()) {
3115 __ Mov(o_l, high);
3116 __ Asr(o_h, high, 31);
3117 } else {
3118 __ Mov(o_l, high);
3119 __ Mov(o_h, 0);
3120 }
3121 } else if (shift_value == 1) {
3122 if (op->IsShl()) {
3123 __ Lsls(o_l, low, 1);
3124 __ Adc(o_h, high, high);
3125 } else if (op->IsShr()) {
3126 __ Asrs(o_h, high, 1);
3127 __ Rrx(o_l, low);
3128 } else {
3129 __ Lsrs(o_h, high, 1);
3130 __ Rrx(o_l, low);
3131 }
3132 } else {
3133 DCHECK(2 <= shift_value && shift_value < 32) << shift_value;
3134 if (op->IsShl()) {
3135 __ Lsl(o_h, high, shift_value);
3136 __ Orr(o_h, o_h, Operand(low, ShiftType::LSR, 32 - shift_value));
3137 __ Lsl(o_l, low, shift_value);
3138 } else if (op->IsShr()) {
3139 __ Lsr(o_l, low, shift_value);
3140 __ Orr(o_l, o_l, Operand(high, ShiftType::LSL, 32 - shift_value));
3141 __ Asr(o_h, high, shift_value);
3142 } else {
3143 __ Lsr(o_l, low, shift_value);
3144 __ Orr(o_l, o_l, Operand(high, ShiftType::LSL, 32 - shift_value));
3145 __ Lsr(o_h, high, shift_value);
3146 }
3147 }
3148 }
3149 break;
3150 }
3151 default:
3152 LOG(FATAL) << "Unexpected operation type " << type;
3153 UNREACHABLE();
3154 }
3155}
3156
3157void LocationsBuilderARMVIXL::VisitShl(HShl* shl) {
3158 HandleShift(shl);
3159}
3160
3161void InstructionCodeGeneratorARMVIXL::VisitShl(HShl* shl) {
3162 HandleShift(shl);
3163}
3164
3165void LocationsBuilderARMVIXL::VisitShr(HShr* shr) {
3166 HandleShift(shr);
3167}
3168
3169void InstructionCodeGeneratorARMVIXL::VisitShr(HShr* shr) {
3170 HandleShift(shr);
3171}
3172
3173void LocationsBuilderARMVIXL::VisitUShr(HUShr* ushr) {
3174 HandleShift(ushr);
3175}
3176
3177void InstructionCodeGeneratorARMVIXL::VisitUShr(HUShr* ushr) {
3178 HandleShift(ushr);
3179}
3180
3181void LocationsBuilderARMVIXL::VisitNewInstance(HNewInstance* instruction) {
3182 LocationSummary* locations =
3183 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
3184 if (instruction->IsStringAlloc()) {
3185 locations->AddTemp(LocationFrom(kMethodRegister));
3186 } else {
3187 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3188 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
3189 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
3190 }
3191 locations->SetOut(LocationFrom(r0));
3192}
3193
3194void InstructionCodeGeneratorARMVIXL::VisitNewInstance(HNewInstance* instruction) {
3195 // Note: if heap poisoning is enabled, the entry point takes cares
3196 // of poisoning the reference.
3197 if (instruction->IsStringAlloc()) {
3198 // String is allocated through StringFactory. Call NewEmptyString entry point.
3199 vixl32::Register temp = RegisterFrom(instruction->GetLocations()->GetTemp(0));
3200 MemberOffset code_offset = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize);
3201 GetAssembler()->LoadFromOffset(kLoadWord, temp, tr, QUICK_ENTRY_POINT(pNewEmptyString));
3202 GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, code_offset.Int32Value());
3203 AssemblerAccurateScope aas(GetVIXLAssembler(),
3204 kArmInstrMaxSizeInBytes,
3205 CodeBufferCheckScope::kMaximumSize);
3206 __ blx(lr);
3207 codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
3208 } else {
3209 codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
3210 CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, ArtMethod*>();
3211 }
3212}
3213
3214void LocationsBuilderARMVIXL::VisitNewArray(HNewArray* instruction) {
3215 LocationSummary* locations =
3216 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
3217 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3218 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(0)));
3219 locations->SetOut(LocationFrom(r0));
3220 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(1)));
3221 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(2)));
3222}
3223
3224void InstructionCodeGeneratorARMVIXL::VisitNewArray(HNewArray* instruction) {
3225 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Andreas Gampea5b09a62016-11-17 15:21:22 -08003226 __ Mov(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex().index_);
Artem Serov02d37832016-10-25 15:25:33 +01003227 // Note: if heap poisoning is enabled, the entry point takes cares
3228 // of poisoning the reference.
3229 codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
3230 CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>();
3231}
3232
3233void LocationsBuilderARMVIXL::VisitParameterValue(HParameterValue* instruction) {
3234 LocationSummary* locations =
3235 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
3236 Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
3237 if (location.IsStackSlot()) {
3238 location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
3239 } else if (location.IsDoubleStackSlot()) {
3240 location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
3241 }
3242 locations->SetOut(location);
3243}
3244
3245void InstructionCodeGeneratorARMVIXL::VisitParameterValue(
3246 HParameterValue* instruction ATTRIBUTE_UNUSED) {
3247 // Nothing to do, the parameter is already at its location.
3248}
3249
3250void LocationsBuilderARMVIXL::VisitCurrentMethod(HCurrentMethod* instruction) {
3251 LocationSummary* locations =
3252 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
3253 locations->SetOut(LocationFrom(kMethodRegister));
3254}
3255
3256void InstructionCodeGeneratorARMVIXL::VisitCurrentMethod(
3257 HCurrentMethod* instruction ATTRIBUTE_UNUSED) {
3258 // Nothing to do, the method is already at its location.
3259}
3260
3261void LocationsBuilderARMVIXL::VisitNot(HNot* not_) {
3262 LocationSummary* locations =
3263 new (GetGraph()->GetArena()) LocationSummary(not_, LocationSummary::kNoCall);
3264 locations->SetInAt(0, Location::RequiresRegister());
3265 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3266}
3267
3268void InstructionCodeGeneratorARMVIXL::VisitNot(HNot* not_) {
3269 LocationSummary* locations = not_->GetLocations();
3270 Location out = locations->Out();
3271 Location in = locations->InAt(0);
3272 switch (not_->GetResultType()) {
3273 case Primitive::kPrimInt:
3274 __ Mvn(OutputRegister(not_), InputRegisterAt(not_, 0));
3275 break;
3276
3277 case Primitive::kPrimLong:
3278 __ Mvn(LowRegisterFrom(out), LowRegisterFrom(in));
3279 __ Mvn(HighRegisterFrom(out), HighRegisterFrom(in));
3280 break;
3281
3282 default:
3283 LOG(FATAL) << "Unimplemented type for not operation " << not_->GetResultType();
3284 }
3285}
3286
Scott Wakelingc34dba72016-10-03 10:14:44 +01003287void LocationsBuilderARMVIXL::VisitBooleanNot(HBooleanNot* bool_not) {
3288 LocationSummary* locations =
3289 new (GetGraph()->GetArena()) LocationSummary(bool_not, LocationSummary::kNoCall);
3290 locations->SetInAt(0, Location::RequiresRegister());
3291 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3292}
3293
3294void InstructionCodeGeneratorARMVIXL::VisitBooleanNot(HBooleanNot* bool_not) {
3295 __ Eor(OutputRegister(bool_not), InputRegister(bool_not), 1);
3296}
3297
Artem Serov02d37832016-10-25 15:25:33 +01003298void LocationsBuilderARMVIXL::VisitCompare(HCompare* compare) {
3299 LocationSummary* locations =
3300 new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall);
3301 switch (compare->InputAt(0)->GetType()) {
3302 case Primitive::kPrimBoolean:
3303 case Primitive::kPrimByte:
3304 case Primitive::kPrimShort:
3305 case Primitive::kPrimChar:
3306 case Primitive::kPrimInt:
3307 case Primitive::kPrimLong: {
3308 locations->SetInAt(0, Location::RequiresRegister());
3309 locations->SetInAt(1, Location::RequiresRegister());
3310 // Output overlaps because it is written before doing the low comparison.
3311 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
3312 break;
3313 }
3314 case Primitive::kPrimFloat:
3315 case Primitive::kPrimDouble: {
3316 locations->SetInAt(0, Location::RequiresFpuRegister());
3317 locations->SetInAt(1, ArithmeticZeroOrFpuRegister(compare->InputAt(1)));
3318 locations->SetOut(Location::RequiresRegister());
3319 break;
3320 }
3321 default:
3322 LOG(FATAL) << "Unexpected type for compare operation " << compare->InputAt(0)->GetType();
3323 }
3324}
3325
3326void InstructionCodeGeneratorARMVIXL::VisitCompare(HCompare* compare) {
3327 LocationSummary* locations = compare->GetLocations();
3328 vixl32::Register out = OutputRegister(compare);
3329 Location left = locations->InAt(0);
3330 Location right = locations->InAt(1);
3331
3332 vixl32::Label less, greater, done;
3333 Primitive::Type type = compare->InputAt(0)->GetType();
3334 vixl32::Condition less_cond = vixl32::Condition(kNone);
3335 switch (type) {
3336 case Primitive::kPrimBoolean:
3337 case Primitive::kPrimByte:
3338 case Primitive::kPrimShort:
3339 case Primitive::kPrimChar:
3340 case Primitive::kPrimInt: {
3341 // Emit move to `out` before the `Cmp`, as `Mov` might affect the status flags.
3342 __ Mov(out, 0);
3343 __ Cmp(RegisterFrom(left), RegisterFrom(right)); // Signed compare.
3344 less_cond = lt;
3345 break;
3346 }
3347 case Primitive::kPrimLong: {
3348 __ Cmp(HighRegisterFrom(left), HighRegisterFrom(right)); // Signed compare.
3349 __ B(lt, &less);
3350 __ B(gt, &greater);
3351 // Emit move to `out` before the last `Cmp`, as `Mov` might affect the status flags.
3352 __ Mov(out, 0);
3353 __ Cmp(LowRegisterFrom(left), LowRegisterFrom(right)); // Unsigned compare.
3354 less_cond = lo;
3355 break;
3356 }
3357 case Primitive::kPrimFloat:
3358 case Primitive::kPrimDouble: {
3359 __ Mov(out, 0);
3360 GenerateVcmp(compare);
3361 // To branch on the FP compare result we transfer FPSCR to APSR (encoded as PC in VMRS).
3362 __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
3363 less_cond = ARMFPCondition(kCondLT, compare->IsGtBias());
3364 break;
3365 }
3366 default:
3367 LOG(FATAL) << "Unexpected compare type " << type;
3368 UNREACHABLE();
3369 }
3370
3371 __ B(eq, &done);
3372 __ B(less_cond, &less);
3373
3374 __ Bind(&greater);
3375 __ Mov(out, 1);
3376 __ B(&done);
3377
3378 __ Bind(&less);
3379 __ Mov(out, -1);
3380
3381 __ Bind(&done);
3382}
3383
3384void LocationsBuilderARMVIXL::VisitPhi(HPhi* instruction) {
3385 LocationSummary* locations =
3386 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
3387 for (size_t i = 0, e = locations->GetInputCount(); i < e; ++i) {
3388 locations->SetInAt(i, Location::Any());
3389 }
3390 locations->SetOut(Location::Any());
3391}
3392
3393void InstructionCodeGeneratorARMVIXL::VisitPhi(HPhi* instruction ATTRIBUTE_UNUSED) {
3394 LOG(FATAL) << "Unreachable";
3395}
3396
3397void CodeGeneratorARMVIXL::GenerateMemoryBarrier(MemBarrierKind kind) {
3398 // TODO (ported from quick): revisit ARM barrier kinds.
3399 DmbOptions flavor = DmbOptions::ISH; // Quiet C++ warnings.
3400 switch (kind) {
3401 case MemBarrierKind::kAnyStore:
3402 case MemBarrierKind::kLoadAny:
3403 case MemBarrierKind::kAnyAny: {
3404 flavor = DmbOptions::ISH;
3405 break;
3406 }
3407 case MemBarrierKind::kStoreStore: {
3408 flavor = DmbOptions::ISHST;
3409 break;
3410 }
3411 default:
3412 LOG(FATAL) << "Unexpected memory barrier " << kind;
3413 }
3414 __ Dmb(flavor);
3415}
3416
3417void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicLoad(vixl32::Register addr,
3418 uint32_t offset,
3419 vixl32::Register out_lo,
3420 vixl32::Register out_hi) {
3421 UseScratchRegisterScope temps(GetVIXLAssembler());
3422 if (offset != 0) {
3423 vixl32::Register temp = temps.Acquire();
3424 __ Add(temp, addr, offset);
3425 addr = temp;
3426 }
3427 __ Ldrexd(out_lo, out_hi, addr);
3428}
3429
3430void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicStore(vixl32::Register addr,
3431 uint32_t offset,
3432 vixl32::Register value_lo,
3433 vixl32::Register value_hi,
3434 vixl32::Register temp1,
3435 vixl32::Register temp2,
3436 HInstruction* instruction) {
3437 UseScratchRegisterScope temps(GetVIXLAssembler());
3438 vixl32::Label fail;
3439 if (offset != 0) {
3440 vixl32::Register temp = temps.Acquire();
3441 __ Add(temp, addr, offset);
3442 addr = temp;
3443 }
3444 __ Bind(&fail);
3445 // We need a load followed by store. (The address used in a STREX instruction must
3446 // be the same as the address in the most recently executed LDREX instruction.)
3447 __ Ldrexd(temp1, temp2, addr);
3448 codegen_->MaybeRecordImplicitNullCheck(instruction);
3449 __ Strexd(temp1, value_lo, value_hi, addr);
xueliang.zhongf51bc622016-11-04 09:23:32 +00003450 __ CompareAndBranchIfNonZero(temp1, &fail);
Artem Serov02d37832016-10-25 15:25:33 +01003451}
Artem Serov02109dd2016-09-23 17:17:54 +01003452
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003453void LocationsBuilderARMVIXL::HandleFieldSet(
3454 HInstruction* instruction, const FieldInfo& field_info) {
3455 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
3456
3457 LocationSummary* locations =
3458 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
3459 locations->SetInAt(0, Location::RequiresRegister());
3460
3461 Primitive::Type field_type = field_info.GetFieldType();
3462 if (Primitive::IsFloatingPointType(field_type)) {
3463 locations->SetInAt(1, Location::RequiresFpuRegister());
3464 } else {
3465 locations->SetInAt(1, Location::RequiresRegister());
3466 }
3467
3468 bool is_wide = field_type == Primitive::kPrimLong || field_type == Primitive::kPrimDouble;
3469 bool generate_volatile = field_info.IsVolatile()
3470 && is_wide
3471 && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
3472 bool needs_write_barrier =
3473 CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1));
3474 // Temporary registers for the write barrier.
3475 // TODO: consider renaming StoreNeedsWriteBarrier to StoreNeedsGCMark.
3476 if (needs_write_barrier) {
3477 locations->AddTemp(Location::RequiresRegister()); // Possibly used for reference poisoning too.
3478 locations->AddTemp(Location::RequiresRegister());
3479 } else if (generate_volatile) {
3480 // ARM encoding have some additional constraints for ldrexd/strexd:
3481 // - registers need to be consecutive
3482 // - the first register should be even but not R14.
3483 // We don't test for ARM yet, and the assertion makes sure that we
3484 // revisit this if we ever enable ARM encoding.
3485 DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet());
3486
3487 locations->AddTemp(Location::RequiresRegister());
3488 locations->AddTemp(Location::RequiresRegister());
3489 if (field_type == Primitive::kPrimDouble) {
3490 // For doubles we need two more registers to copy the value.
3491 locations->AddTemp(LocationFrom(r2));
3492 locations->AddTemp(LocationFrom(r3));
3493 }
3494 }
3495}
3496
3497void InstructionCodeGeneratorARMVIXL::HandleFieldSet(HInstruction* instruction,
3498 const FieldInfo& field_info,
3499 bool value_can_be_null) {
3500 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
3501
3502 LocationSummary* locations = instruction->GetLocations();
3503 vixl32::Register base = InputRegisterAt(instruction, 0);
3504 Location value = locations->InAt(1);
3505
3506 bool is_volatile = field_info.IsVolatile();
3507 bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
3508 Primitive::Type field_type = field_info.GetFieldType();
3509 uint32_t offset = field_info.GetFieldOffset().Uint32Value();
3510 bool needs_write_barrier =
3511 CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1));
3512
3513 if (is_volatile) {
3514 codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
3515 }
3516
3517 switch (field_type) {
3518 case Primitive::kPrimBoolean:
3519 case Primitive::kPrimByte: {
3520 GetAssembler()->StoreToOffset(kStoreByte, RegisterFrom(value), base, offset);
3521 break;
3522 }
3523
3524 case Primitive::kPrimShort:
3525 case Primitive::kPrimChar: {
3526 GetAssembler()->StoreToOffset(kStoreHalfword, RegisterFrom(value), base, offset);
3527 break;
3528 }
3529
3530 case Primitive::kPrimInt:
3531 case Primitive::kPrimNot: {
3532 if (kPoisonHeapReferences && needs_write_barrier) {
3533 // Note that in the case where `value` is a null reference,
3534 // we do not enter this block, as a null reference does not
3535 // need poisoning.
3536 DCHECK_EQ(field_type, Primitive::kPrimNot);
3537 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
3538 __ Mov(temp, RegisterFrom(value));
3539 GetAssembler()->PoisonHeapReference(temp);
3540 GetAssembler()->StoreToOffset(kStoreWord, temp, base, offset);
3541 } else {
3542 GetAssembler()->StoreToOffset(kStoreWord, RegisterFrom(value), base, offset);
3543 }
3544 break;
3545 }
3546
3547 case Primitive::kPrimLong: {
3548 if (is_volatile && !atomic_ldrd_strd) {
3549 GenerateWideAtomicStore(base,
3550 offset,
3551 LowRegisterFrom(value),
3552 HighRegisterFrom(value),
3553 RegisterFrom(locations->GetTemp(0)),
3554 RegisterFrom(locations->GetTemp(1)),
3555 instruction);
3556 } else {
3557 GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), base, offset);
3558 codegen_->MaybeRecordImplicitNullCheck(instruction);
3559 }
3560 break;
3561 }
3562
3563 case Primitive::kPrimFloat: {
3564 GetAssembler()->StoreSToOffset(SRegisterFrom(value), base, offset);
3565 break;
3566 }
3567
3568 case Primitive::kPrimDouble: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01003569 vixl32::DRegister value_reg = DRegisterFrom(value);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003570 if (is_volatile && !atomic_ldrd_strd) {
3571 vixl32::Register value_reg_lo = RegisterFrom(locations->GetTemp(0));
3572 vixl32::Register value_reg_hi = RegisterFrom(locations->GetTemp(1));
3573
3574 __ Vmov(value_reg_lo, value_reg_hi, value_reg);
3575
3576 GenerateWideAtomicStore(base,
3577 offset,
3578 value_reg_lo,
3579 value_reg_hi,
3580 RegisterFrom(locations->GetTemp(2)),
3581 RegisterFrom(locations->GetTemp(3)),
3582 instruction);
3583 } else {
3584 GetAssembler()->StoreDToOffset(value_reg, base, offset);
3585 codegen_->MaybeRecordImplicitNullCheck(instruction);
3586 }
3587 break;
3588 }
3589
3590 case Primitive::kPrimVoid:
3591 LOG(FATAL) << "Unreachable type " << field_type;
3592 UNREACHABLE();
3593 }
3594
3595 // Longs and doubles are handled in the switch.
3596 if (field_type != Primitive::kPrimLong && field_type != Primitive::kPrimDouble) {
3597 codegen_->MaybeRecordImplicitNullCheck(instruction);
3598 }
3599
3600 if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
3601 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
3602 vixl32::Register card = RegisterFrom(locations->GetTemp(1));
3603 codegen_->MarkGCCard(temp, card, base, RegisterFrom(value), value_can_be_null);
3604 }
3605
3606 if (is_volatile) {
3607 codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
3608 }
3609}
3610
Artem Serov02d37832016-10-25 15:25:33 +01003611void LocationsBuilderARMVIXL::HandleFieldGet(HInstruction* instruction,
3612 const FieldInfo& field_info) {
3613 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
3614
3615 bool object_field_get_with_read_barrier =
3616 kEmitCompilerReadBarrier && (field_info.GetFieldType() == Primitive::kPrimNot);
3617 LocationSummary* locations =
3618 new (GetGraph()->GetArena()) LocationSummary(instruction,
3619 object_field_get_with_read_barrier ?
3620 LocationSummary::kCallOnSlowPath :
3621 LocationSummary::kNoCall);
3622 if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
3623 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
3624 }
3625 locations->SetInAt(0, Location::RequiresRegister());
3626
3627 bool volatile_for_double = field_info.IsVolatile()
3628 && (field_info.GetFieldType() == Primitive::kPrimDouble)
3629 && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
3630 // The output overlaps in case of volatile long: we don't want the
3631 // code generated by GenerateWideAtomicLoad to overwrite the
3632 // object's location. Likewise, in the case of an object field get
3633 // with read barriers enabled, we do not want the load to overwrite
3634 // the object's location, as we need it to emit the read barrier.
3635 bool overlap = (field_info.IsVolatile() && (field_info.GetFieldType() == Primitive::kPrimLong)) ||
3636 object_field_get_with_read_barrier;
3637
3638 if (Primitive::IsFloatingPointType(instruction->GetType())) {
3639 locations->SetOut(Location::RequiresFpuRegister());
3640 } else {
3641 locations->SetOut(Location::RequiresRegister(),
3642 (overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap));
3643 }
3644 if (volatile_for_double) {
3645 // ARM encoding have some additional constraints for ldrexd/strexd:
3646 // - registers need to be consecutive
3647 // - the first register should be even but not R14.
3648 // We don't test for ARM yet, and the assertion makes sure that we
3649 // revisit this if we ever enable ARM encoding.
3650 DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet());
3651 locations->AddTemp(Location::RequiresRegister());
3652 locations->AddTemp(Location::RequiresRegister());
3653 } else if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
3654 // We need a temporary register for the read barrier marking slow
3655 // path in CodeGeneratorARM::GenerateFieldLoadWithBakerReadBarrier.
3656 locations->AddTemp(Location::RequiresRegister());
3657 }
3658}
3659
3660Location LocationsBuilderARMVIXL::ArithmeticZeroOrFpuRegister(HInstruction* input) {
3661 DCHECK(Primitive::IsFloatingPointType(input->GetType())) << input->GetType();
3662 if ((input->IsFloatConstant() && (input->AsFloatConstant()->IsArithmeticZero())) ||
3663 (input->IsDoubleConstant() && (input->AsDoubleConstant()->IsArithmeticZero()))) {
3664 return Location::ConstantLocation(input->AsConstant());
3665 } else {
3666 return Location::RequiresFpuRegister();
3667 }
3668}
3669
Artem Serov02109dd2016-09-23 17:17:54 +01003670Location LocationsBuilderARMVIXL::ArmEncodableConstantOrRegister(HInstruction* constant,
3671 Opcode opcode) {
3672 DCHECK(!Primitive::IsFloatingPointType(constant->GetType()));
3673 if (constant->IsConstant() &&
3674 CanEncodeConstantAsImmediate(constant->AsConstant(), opcode)) {
3675 return Location::ConstantLocation(constant->AsConstant());
3676 }
3677 return Location::RequiresRegister();
3678}
3679
3680bool LocationsBuilderARMVIXL::CanEncodeConstantAsImmediate(HConstant* input_cst,
3681 Opcode opcode) {
3682 uint64_t value = static_cast<uint64_t>(Int64FromConstant(input_cst));
3683 if (Primitive::Is64BitType(input_cst->GetType())) {
3684 Opcode high_opcode = opcode;
3685 SetCc low_set_cc = kCcDontCare;
3686 switch (opcode) {
3687 case SUB:
3688 // Flip the operation to an ADD.
3689 value = -value;
3690 opcode = ADD;
3691 FALLTHROUGH_INTENDED;
3692 case ADD:
3693 if (Low32Bits(value) == 0u) {
3694 return CanEncodeConstantAsImmediate(High32Bits(value), opcode, kCcDontCare);
3695 }
3696 high_opcode = ADC;
3697 low_set_cc = kCcSet;
3698 break;
3699 default:
3700 break;
3701 }
3702 return CanEncodeConstantAsImmediate(Low32Bits(value), opcode, low_set_cc) &&
3703 CanEncodeConstantAsImmediate(High32Bits(value), high_opcode, kCcDontCare);
3704 } else {
3705 return CanEncodeConstantAsImmediate(Low32Bits(value), opcode);
3706 }
3707}
3708
3709// TODO(VIXL): Replace art::arm::SetCc` with `vixl32::FlagsUpdate after flags set optimization
3710// enabled.
3711bool LocationsBuilderARMVIXL::CanEncodeConstantAsImmediate(uint32_t value,
3712 Opcode opcode,
3713 SetCc set_cc) {
3714 ArmVIXLAssembler* assembler = codegen_->GetAssembler();
3715 if (assembler->ShifterOperandCanHold(opcode, value, set_cc)) {
3716 return true;
3717 }
3718 Opcode neg_opcode = kNoOperand;
3719 switch (opcode) {
3720 case AND: neg_opcode = BIC; value = ~value; break;
3721 case ORR: neg_opcode = ORN; value = ~value; break;
3722 case ADD: neg_opcode = SUB; value = -value; break;
3723 case ADC: neg_opcode = SBC; value = ~value; break;
3724 case SUB: neg_opcode = ADD; value = -value; break;
3725 case SBC: neg_opcode = ADC; value = ~value; break;
3726 default:
3727 return false;
3728 }
3729 return assembler->ShifterOperandCanHold(neg_opcode, value, set_cc);
3730}
3731
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003732void InstructionCodeGeneratorARMVIXL::HandleFieldGet(HInstruction* instruction,
3733 const FieldInfo& field_info) {
3734 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
3735
3736 LocationSummary* locations = instruction->GetLocations();
3737 vixl32::Register base = InputRegisterAt(instruction, 0);
3738 Location out = locations->Out();
3739 bool is_volatile = field_info.IsVolatile();
3740 bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
3741 Primitive::Type field_type = field_info.GetFieldType();
3742 uint32_t offset = field_info.GetFieldOffset().Uint32Value();
3743
3744 switch (field_type) {
3745 case Primitive::kPrimBoolean:
3746 GetAssembler()->LoadFromOffset(kLoadUnsignedByte, RegisterFrom(out), base, offset);
3747 break;
3748
3749 case Primitive::kPrimByte:
3750 GetAssembler()->LoadFromOffset(kLoadSignedByte, RegisterFrom(out), base, offset);
3751 break;
3752
3753 case Primitive::kPrimShort:
3754 GetAssembler()->LoadFromOffset(kLoadSignedHalfword, RegisterFrom(out), base, offset);
3755 break;
3756
3757 case Primitive::kPrimChar:
3758 GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, RegisterFrom(out), base, offset);
3759 break;
3760
3761 case Primitive::kPrimInt:
3762 GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(out), base, offset);
3763 break;
3764
3765 case Primitive::kPrimNot: {
3766 // /* HeapReference<Object> */ out = *(base + offset)
3767 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
3768 TODO_VIXL32(FATAL);
3769 } else {
3770 GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(out), base, offset);
3771 // TODO(VIXL): Scope to guarantee the position immediately after the load.
3772 codegen_->MaybeRecordImplicitNullCheck(instruction);
3773 if (is_volatile) {
3774 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
3775 }
3776 // If read barriers are enabled, emit read barriers other than
3777 // Baker's using a slow path (and also unpoison the loaded
3778 // reference, if heap poisoning is enabled).
3779 codegen_->MaybeGenerateReadBarrierSlow(instruction, out, out, locations->InAt(0), offset);
3780 }
3781 break;
3782 }
3783
3784 case Primitive::kPrimLong:
3785 if (is_volatile && !atomic_ldrd_strd) {
3786 GenerateWideAtomicLoad(base, offset, LowRegisterFrom(out), HighRegisterFrom(out));
3787 } else {
3788 GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out), base, offset);
3789 }
3790 break;
3791
3792 case Primitive::kPrimFloat:
3793 GetAssembler()->LoadSFromOffset(SRegisterFrom(out), base, offset);
3794 break;
3795
3796 case Primitive::kPrimDouble: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01003797 vixl32::DRegister out_dreg = DRegisterFrom(out);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003798 if (is_volatile && !atomic_ldrd_strd) {
3799 vixl32::Register lo = RegisterFrom(locations->GetTemp(0));
3800 vixl32::Register hi = RegisterFrom(locations->GetTemp(1));
3801 GenerateWideAtomicLoad(base, offset, lo, hi);
3802 // TODO(VIXL): Do we need to be immediately after the ldrexd instruction? If so we need a
3803 // scope.
3804 codegen_->MaybeRecordImplicitNullCheck(instruction);
3805 __ Vmov(out_dreg, lo, hi);
3806 } else {
3807 GetAssembler()->LoadDFromOffset(out_dreg, base, offset);
3808 // TODO(VIXL): Scope to guarantee the position immediately after the load.
3809 codegen_->MaybeRecordImplicitNullCheck(instruction);
3810 }
3811 break;
3812 }
3813
3814 case Primitive::kPrimVoid:
3815 LOG(FATAL) << "Unreachable type " << field_type;
3816 UNREACHABLE();
3817 }
3818
3819 if (field_type == Primitive::kPrimNot || field_type == Primitive::kPrimDouble) {
3820 // Potential implicit null checks, in the case of reference or
3821 // double fields, are handled in the previous switch statement.
3822 } else {
3823 // Address cases other than reference and double that may require an implicit null check.
3824 codegen_->MaybeRecordImplicitNullCheck(instruction);
3825 }
3826
3827 if (is_volatile) {
3828 if (field_type == Primitive::kPrimNot) {
3829 // Memory barriers, in the case of references, are also handled
3830 // in the previous switch statement.
3831 } else {
3832 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
3833 }
3834 }
3835}
3836
3837void LocationsBuilderARMVIXL::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
3838 HandleFieldSet(instruction, instruction->GetFieldInfo());
3839}
3840
3841void InstructionCodeGeneratorARMVIXL::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
3842 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
3843}
3844
3845void LocationsBuilderARMVIXL::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
3846 HandleFieldGet(instruction, instruction->GetFieldInfo());
3847}
3848
3849void InstructionCodeGeneratorARMVIXL::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
3850 HandleFieldGet(instruction, instruction->GetFieldInfo());
3851}
3852
3853void LocationsBuilderARMVIXL::VisitStaticFieldGet(HStaticFieldGet* instruction) {
3854 HandleFieldGet(instruction, instruction->GetFieldInfo());
3855}
3856
3857void InstructionCodeGeneratorARMVIXL::VisitStaticFieldGet(HStaticFieldGet* instruction) {
3858 HandleFieldGet(instruction, instruction->GetFieldInfo());
3859}
3860
Scott Wakelingc34dba72016-10-03 10:14:44 +01003861void LocationsBuilderARMVIXL::VisitStaticFieldSet(HStaticFieldSet* instruction) {
3862 HandleFieldSet(instruction, instruction->GetFieldInfo());
3863}
3864
3865void InstructionCodeGeneratorARMVIXL::VisitStaticFieldSet(HStaticFieldSet* instruction) {
3866 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
3867}
3868
Artem Serovcfbe9132016-10-14 15:58:56 +01003869void LocationsBuilderARMVIXL::VisitUnresolvedInstanceFieldGet(
3870 HUnresolvedInstanceFieldGet* instruction) {
3871 FieldAccessCallingConventionARMVIXL calling_convention;
3872 codegen_->CreateUnresolvedFieldLocationSummary(
3873 instruction, instruction->GetFieldType(), calling_convention);
3874}
3875
3876void InstructionCodeGeneratorARMVIXL::VisitUnresolvedInstanceFieldGet(
3877 HUnresolvedInstanceFieldGet* instruction) {
3878 FieldAccessCallingConventionARMVIXL calling_convention;
3879 codegen_->GenerateUnresolvedFieldAccess(instruction,
3880 instruction->GetFieldType(),
3881 instruction->GetFieldIndex(),
3882 instruction->GetDexPc(),
3883 calling_convention);
3884}
3885
3886void LocationsBuilderARMVIXL::VisitUnresolvedInstanceFieldSet(
3887 HUnresolvedInstanceFieldSet* instruction) {
3888 FieldAccessCallingConventionARMVIXL calling_convention;
3889 codegen_->CreateUnresolvedFieldLocationSummary(
3890 instruction, instruction->GetFieldType(), calling_convention);
3891}
3892
3893void InstructionCodeGeneratorARMVIXL::VisitUnresolvedInstanceFieldSet(
3894 HUnresolvedInstanceFieldSet* instruction) {
3895 FieldAccessCallingConventionARMVIXL calling_convention;
3896 codegen_->GenerateUnresolvedFieldAccess(instruction,
3897 instruction->GetFieldType(),
3898 instruction->GetFieldIndex(),
3899 instruction->GetDexPc(),
3900 calling_convention);
3901}
3902
3903void LocationsBuilderARMVIXL::VisitUnresolvedStaticFieldGet(
3904 HUnresolvedStaticFieldGet* instruction) {
3905 FieldAccessCallingConventionARMVIXL calling_convention;
3906 codegen_->CreateUnresolvedFieldLocationSummary(
3907 instruction, instruction->GetFieldType(), calling_convention);
3908}
3909
3910void InstructionCodeGeneratorARMVIXL::VisitUnresolvedStaticFieldGet(
3911 HUnresolvedStaticFieldGet* instruction) {
3912 FieldAccessCallingConventionARMVIXL calling_convention;
3913 codegen_->GenerateUnresolvedFieldAccess(instruction,
3914 instruction->GetFieldType(),
3915 instruction->GetFieldIndex(),
3916 instruction->GetDexPc(),
3917 calling_convention);
3918}
3919
3920void LocationsBuilderARMVIXL::VisitUnresolvedStaticFieldSet(
3921 HUnresolvedStaticFieldSet* instruction) {
3922 FieldAccessCallingConventionARMVIXL calling_convention;
3923 codegen_->CreateUnresolvedFieldLocationSummary(
3924 instruction, instruction->GetFieldType(), calling_convention);
3925}
3926
3927void InstructionCodeGeneratorARMVIXL::VisitUnresolvedStaticFieldSet(
3928 HUnresolvedStaticFieldSet* instruction) {
3929 FieldAccessCallingConventionARMVIXL calling_convention;
3930 codegen_->GenerateUnresolvedFieldAccess(instruction,
3931 instruction->GetFieldType(),
3932 instruction->GetFieldIndex(),
3933 instruction->GetDexPc(),
3934 calling_convention);
3935}
3936
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003937void LocationsBuilderARMVIXL::VisitNullCheck(HNullCheck* instruction) {
3938 // TODO(VIXL): https://android-review.googlesource.com/#/c/275337/
3939 LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
3940 ? LocationSummary::kCallOnSlowPath
3941 : LocationSummary::kNoCall;
3942 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
3943 locations->SetInAt(0, Location::RequiresRegister());
3944 if (instruction->HasUses()) {
3945 locations->SetOut(Location::SameAsFirstInput());
3946 }
3947}
3948
3949void CodeGeneratorARMVIXL::GenerateImplicitNullCheck(HNullCheck* instruction) {
3950 if (CanMoveNullCheckToUser(instruction)) {
3951 return;
3952 }
3953
3954 UseScratchRegisterScope temps(GetVIXLAssembler());
3955 AssemblerAccurateScope aas(GetVIXLAssembler(),
3956 kArmInstrMaxSizeInBytes,
3957 CodeBufferCheckScope::kMaximumSize);
3958 __ ldr(temps.Acquire(), MemOperand(InputRegisterAt(instruction, 0)));
3959 RecordPcInfo(instruction, instruction->GetDexPc());
3960}
3961
3962void CodeGeneratorARMVIXL::GenerateExplicitNullCheck(HNullCheck* instruction) {
3963 NullCheckSlowPathARMVIXL* slow_path =
3964 new (GetGraph()->GetArena()) NullCheckSlowPathARMVIXL(instruction);
3965 AddSlowPath(slow_path);
xueliang.zhongf51bc622016-11-04 09:23:32 +00003966 __ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003967}
3968
3969void InstructionCodeGeneratorARMVIXL::VisitNullCheck(HNullCheck* instruction) {
3970 codegen_->GenerateNullCheck(instruction);
3971}
3972
Scott Wakelingc34dba72016-10-03 10:14:44 +01003973static LoadOperandType GetLoadOperandType(Primitive::Type type) {
3974 switch (type) {
3975 case Primitive::kPrimNot:
3976 return kLoadWord;
3977 case Primitive::kPrimBoolean:
3978 return kLoadUnsignedByte;
3979 case Primitive::kPrimByte:
3980 return kLoadSignedByte;
3981 case Primitive::kPrimChar:
3982 return kLoadUnsignedHalfword;
3983 case Primitive::kPrimShort:
3984 return kLoadSignedHalfword;
3985 case Primitive::kPrimInt:
3986 return kLoadWord;
3987 case Primitive::kPrimLong:
3988 return kLoadWordPair;
3989 case Primitive::kPrimFloat:
3990 return kLoadSWord;
3991 case Primitive::kPrimDouble:
3992 return kLoadDWord;
3993 default:
3994 LOG(FATAL) << "Unreachable type " << type;
3995 UNREACHABLE();
3996 }
3997}
3998
3999static StoreOperandType GetStoreOperandType(Primitive::Type type) {
4000 switch (type) {
4001 case Primitive::kPrimNot:
4002 return kStoreWord;
4003 case Primitive::kPrimBoolean:
4004 case Primitive::kPrimByte:
4005 return kStoreByte;
4006 case Primitive::kPrimChar:
4007 case Primitive::kPrimShort:
4008 return kStoreHalfword;
4009 case Primitive::kPrimInt:
4010 return kStoreWord;
4011 case Primitive::kPrimLong:
4012 return kStoreWordPair;
4013 case Primitive::kPrimFloat:
4014 return kStoreSWord;
4015 case Primitive::kPrimDouble:
4016 return kStoreDWord;
4017 default:
4018 LOG(FATAL) << "Unreachable type " << type;
4019 UNREACHABLE();
4020 }
4021}
4022
4023void CodeGeneratorARMVIXL::LoadFromShiftedRegOffset(Primitive::Type type,
4024 Location out_loc,
4025 vixl32::Register base,
4026 vixl32::Register reg_index,
4027 vixl32::Condition cond) {
4028 uint32_t shift_count = Primitive::ComponentSizeShift(type);
4029 MemOperand mem_address(base, reg_index, vixl32::LSL, shift_count);
4030
4031 switch (type) {
4032 case Primitive::kPrimByte:
4033 __ Ldrsb(cond, RegisterFrom(out_loc), mem_address);
4034 break;
4035 case Primitive::kPrimBoolean:
4036 __ Ldrb(cond, RegisterFrom(out_loc), mem_address);
4037 break;
4038 case Primitive::kPrimShort:
4039 __ Ldrsh(cond, RegisterFrom(out_loc), mem_address);
4040 break;
4041 case Primitive::kPrimChar:
4042 __ Ldrh(cond, RegisterFrom(out_loc), mem_address);
4043 break;
4044 case Primitive::kPrimNot:
4045 case Primitive::kPrimInt:
4046 __ Ldr(cond, RegisterFrom(out_loc), mem_address);
4047 break;
4048 // T32 doesn't support LoadFromShiftedRegOffset mem address mode for these types.
4049 case Primitive::kPrimLong:
4050 case Primitive::kPrimFloat:
4051 case Primitive::kPrimDouble:
4052 default:
4053 LOG(FATAL) << "Unreachable type " << type;
4054 UNREACHABLE();
4055 }
4056}
4057
4058void CodeGeneratorARMVIXL::StoreToShiftedRegOffset(Primitive::Type type,
4059 Location loc,
4060 vixl32::Register base,
4061 vixl32::Register reg_index,
4062 vixl32::Condition cond) {
4063 uint32_t shift_count = Primitive::ComponentSizeShift(type);
4064 MemOperand mem_address(base, reg_index, vixl32::LSL, shift_count);
4065
4066 switch (type) {
4067 case Primitive::kPrimByte:
4068 case Primitive::kPrimBoolean:
4069 __ Strb(cond, RegisterFrom(loc), mem_address);
4070 break;
4071 case Primitive::kPrimShort:
4072 case Primitive::kPrimChar:
4073 __ Strh(cond, RegisterFrom(loc), mem_address);
4074 break;
4075 case Primitive::kPrimNot:
4076 case Primitive::kPrimInt:
4077 __ Str(cond, RegisterFrom(loc), mem_address);
4078 break;
4079 // T32 doesn't support StoreToShiftedRegOffset mem address mode for these types.
4080 case Primitive::kPrimLong:
4081 case Primitive::kPrimFloat:
4082 case Primitive::kPrimDouble:
4083 default:
4084 LOG(FATAL) << "Unreachable type " << type;
4085 UNREACHABLE();
4086 }
4087}
4088
4089void LocationsBuilderARMVIXL::VisitArrayGet(HArrayGet* instruction) {
4090 bool object_array_get_with_read_barrier =
4091 kEmitCompilerReadBarrier && (instruction->GetType() == Primitive::kPrimNot);
4092 LocationSummary* locations =
4093 new (GetGraph()->GetArena()) LocationSummary(instruction,
4094 object_array_get_with_read_barrier ?
4095 LocationSummary::kCallOnSlowPath :
4096 LocationSummary::kNoCall);
4097 if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
4098 TODO_VIXL32(FATAL);
4099 }
4100 locations->SetInAt(0, Location::RequiresRegister());
4101 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
4102 if (Primitive::IsFloatingPointType(instruction->GetType())) {
4103 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4104 } else {
4105 // The output overlaps in the case of an object array get with
4106 // read barriers enabled: we do not want the move to overwrite the
4107 // array's location, as we need it to emit the read barrier.
4108 locations->SetOut(
4109 Location::RequiresRegister(),
4110 object_array_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap);
4111 }
4112 // We need a temporary register for the read barrier marking slow
4113 // path in CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier.
4114 // Also need for String compression feature.
4115 if ((object_array_get_with_read_barrier && kUseBakerReadBarrier)
4116 || (mirror::kUseStringCompression && instruction->IsStringCharAt())) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004117 locations->AddTemp(Location::RequiresRegister());
Scott Wakelingc34dba72016-10-03 10:14:44 +01004118 }
4119}
4120
4121void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) {
4122 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
4123 LocationSummary* locations = instruction->GetLocations();
4124 Location obj_loc = locations->InAt(0);
4125 vixl32::Register obj = InputRegisterAt(instruction, 0);
4126 Location index = locations->InAt(1);
4127 Location out_loc = locations->Out();
4128 uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
4129 Primitive::Type type = instruction->GetType();
4130 const bool maybe_compressed_char_at = mirror::kUseStringCompression &&
4131 instruction->IsStringCharAt();
4132 HInstruction* array_instr = instruction->GetArray();
4133 bool has_intermediate_address = array_instr->IsIntermediateAddress();
4134 // The read barrier instrumentation does not support the HIntermediateAddress instruction yet.
4135 DCHECK(!(has_intermediate_address && kEmitCompilerReadBarrier));
4136
4137 switch (type) {
4138 case Primitive::kPrimBoolean:
4139 case Primitive::kPrimByte:
4140 case Primitive::kPrimShort:
4141 case Primitive::kPrimChar:
4142 case Primitive::kPrimInt: {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01004143 vixl32::Register length;
4144 if (maybe_compressed_char_at) {
4145 length = RegisterFrom(locations->GetTemp(0));
4146 uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
4147 GetAssembler()->LoadFromOffset(kLoadWord, length, obj, count_offset);
4148 codegen_->MaybeRecordImplicitNullCheck(instruction);
4149 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01004150 if (index.IsConstant()) {
4151 int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue();
4152 if (maybe_compressed_char_at) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004153 vixl32::Label uncompressed_load, done;
Vladimir Markofdaf0f42016-10-13 19:29:53 +01004154 __ Lsrs(length, length, 1u); // LSRS has a 16-bit encoding, TST (immediate) does not.
4155 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
4156 "Expecting 0=compressed, 1=uncompressed");
4157 __ B(cs, &uncompressed_load);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004158 GetAssembler()->LoadFromOffset(kLoadUnsignedByte,
4159 RegisterFrom(out_loc),
4160 obj,
4161 data_offset + const_index);
4162 __ B(&done);
4163 __ Bind(&uncompressed_load);
4164 GetAssembler()->LoadFromOffset(GetLoadOperandType(Primitive::kPrimChar),
4165 RegisterFrom(out_loc),
4166 obj,
4167 data_offset + (const_index << 1));
4168 __ Bind(&done);
Scott Wakelingc34dba72016-10-03 10:14:44 +01004169 } else {
4170 uint32_t full_offset = data_offset + (const_index << Primitive::ComponentSizeShift(type));
4171
4172 LoadOperandType load_type = GetLoadOperandType(type);
4173 GetAssembler()->LoadFromOffset(load_type, RegisterFrom(out_loc), obj, full_offset);
4174 }
4175 } else {
4176 vixl32::Register temp = temps.Acquire();
4177
4178 if (has_intermediate_address) {
Artem Serov2bbc9532016-10-21 11:51:50 +01004179 // We do not need to compute the intermediate address from the array: the
4180 // input instruction has done it already. See the comment in
4181 // `TryExtractArrayAccessAddress()`.
4182 if (kIsDebugBuild) {
4183 HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
4184 DCHECK_EQ(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64(), data_offset);
4185 }
4186 temp = obj;
Scott Wakelingc34dba72016-10-03 10:14:44 +01004187 } else {
4188 __ Add(temp, obj, data_offset);
4189 }
4190 if (maybe_compressed_char_at) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004191 vixl32::Label uncompressed_load, done;
Vladimir Markofdaf0f42016-10-13 19:29:53 +01004192 __ Lsrs(length, length, 1u); // LSRS has a 16-bit encoding, TST (immediate) does not.
4193 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
4194 "Expecting 0=compressed, 1=uncompressed");
4195 __ B(cs, &uncompressed_load);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004196 __ Ldrb(RegisterFrom(out_loc), MemOperand(temp, RegisterFrom(index), vixl32::LSL, 0));
4197 __ B(&done);
4198 __ Bind(&uncompressed_load);
4199 __ Ldrh(RegisterFrom(out_loc), MemOperand(temp, RegisterFrom(index), vixl32::LSL, 1));
4200 __ Bind(&done);
Scott Wakelingc34dba72016-10-03 10:14:44 +01004201 } else {
4202 codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index));
4203 }
4204 }
4205 break;
4206 }
4207
4208 case Primitive::kPrimNot: {
4209 static_assert(
4210 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
4211 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
4212 // /* HeapReference<Object> */ out =
4213 // *(obj + data_offset + index * sizeof(HeapReference<Object>))
4214 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
4215 TODO_VIXL32(FATAL);
4216 } else {
4217 vixl32::Register out = OutputRegister(instruction);
4218 if (index.IsConstant()) {
4219 size_t offset =
4220 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
4221 GetAssembler()->LoadFromOffset(kLoadWord, out, obj, offset);
4222 codegen_->MaybeRecordImplicitNullCheck(instruction);
4223 // If read barriers are enabled, emit read barriers other than
4224 // Baker's using a slow path (and also unpoison the loaded
4225 // reference, if heap poisoning is enabled).
4226 codegen_->MaybeGenerateReadBarrierSlow(instruction, out_loc, out_loc, obj_loc, offset);
4227 } else {
4228 vixl32::Register temp = temps.Acquire();
4229
4230 if (has_intermediate_address) {
Artem Serov2bbc9532016-10-21 11:51:50 +01004231 // We do not need to compute the intermediate address from the array: the
4232 // input instruction has done it already. See the comment in
4233 // `TryExtractArrayAccessAddress()`.
4234 if (kIsDebugBuild) {
4235 HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
4236 DCHECK_EQ(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64(), data_offset);
4237 }
4238 temp = obj;
Scott Wakelingc34dba72016-10-03 10:14:44 +01004239 } else {
4240 __ Add(temp, obj, data_offset);
4241 }
4242 codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index));
4243
4244 codegen_->MaybeRecordImplicitNullCheck(instruction);
4245 // If read barriers are enabled, emit read barriers other than
4246 // Baker's using a slow path (and also unpoison the loaded
4247 // reference, if heap poisoning is enabled).
4248 codegen_->MaybeGenerateReadBarrierSlow(
4249 instruction, out_loc, out_loc, obj_loc, data_offset, index);
4250 }
4251 }
4252 break;
4253 }
4254
4255 case Primitive::kPrimLong: {
4256 if (index.IsConstant()) {
4257 size_t offset =
4258 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
4259 GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out_loc), obj, offset);
4260 } else {
4261 vixl32::Register temp = temps.Acquire();
4262 __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
4263 GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out_loc), temp, data_offset);
4264 }
4265 break;
4266 }
4267
4268 case Primitive::kPrimFloat: {
4269 vixl32::SRegister out = SRegisterFrom(out_loc);
4270 if (index.IsConstant()) {
4271 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
4272 GetAssembler()->LoadSFromOffset(out, obj, offset);
4273 } else {
4274 vixl32::Register temp = temps.Acquire();
4275 __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_4));
4276 GetAssembler()->LoadSFromOffset(out, temp, data_offset);
4277 }
4278 break;
4279 }
4280
4281 case Primitive::kPrimDouble: {
4282 if (index.IsConstant()) {
4283 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
4284 GetAssembler()->LoadDFromOffset(DRegisterFrom(out_loc), obj, offset);
4285 } else {
4286 vixl32::Register temp = temps.Acquire();
4287 __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
4288 GetAssembler()->LoadDFromOffset(DRegisterFrom(out_loc), temp, data_offset);
4289 }
4290 break;
4291 }
4292
4293 case Primitive::kPrimVoid:
4294 LOG(FATAL) << "Unreachable type " << type;
4295 UNREACHABLE();
4296 }
4297
4298 if (type == Primitive::kPrimNot) {
4299 // Potential implicit null checks, in the case of reference
4300 // arrays, are handled in the previous switch statement.
4301 } else if (!maybe_compressed_char_at) {
4302 codegen_->MaybeRecordImplicitNullCheck(instruction);
4303 }
4304}
4305
4306void LocationsBuilderARMVIXL::VisitArraySet(HArraySet* instruction) {
4307 Primitive::Type value_type = instruction->GetComponentType();
4308
4309 bool needs_write_barrier =
4310 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
4311 bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck();
4312
4313 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(
4314 instruction,
4315 may_need_runtime_call_for_type_check ?
4316 LocationSummary::kCallOnSlowPath :
4317 LocationSummary::kNoCall);
4318
4319 locations->SetInAt(0, Location::RequiresRegister());
4320 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
4321 if (Primitive::IsFloatingPointType(value_type)) {
4322 locations->SetInAt(2, Location::RequiresFpuRegister());
4323 } else {
4324 locations->SetInAt(2, Location::RequiresRegister());
4325 }
4326 if (needs_write_barrier) {
4327 // Temporary registers for the write barrier.
4328 locations->AddTemp(Location::RequiresRegister()); // Possibly used for ref. poisoning too.
4329 locations->AddTemp(Location::RequiresRegister());
4330 }
4331}
4332
4333void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) {
4334 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
4335 LocationSummary* locations = instruction->GetLocations();
4336 vixl32::Register array = InputRegisterAt(instruction, 0);
4337 Location index = locations->InAt(1);
4338 Primitive::Type value_type = instruction->GetComponentType();
4339 bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck();
4340 bool needs_write_barrier =
4341 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
4342 uint32_t data_offset =
4343 mirror::Array::DataOffset(Primitive::ComponentSize(value_type)).Uint32Value();
4344 Location value_loc = locations->InAt(2);
4345 HInstruction* array_instr = instruction->GetArray();
4346 bool has_intermediate_address = array_instr->IsIntermediateAddress();
4347 // The read barrier instrumentation does not support the HIntermediateAddress instruction yet.
4348 DCHECK(!(has_intermediate_address && kEmitCompilerReadBarrier));
4349
4350 switch (value_type) {
4351 case Primitive::kPrimBoolean:
4352 case Primitive::kPrimByte:
4353 case Primitive::kPrimShort:
4354 case Primitive::kPrimChar:
4355 case Primitive::kPrimInt: {
4356 if (index.IsConstant()) {
4357 int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue();
4358 uint32_t full_offset =
4359 data_offset + (const_index << Primitive::ComponentSizeShift(value_type));
4360 StoreOperandType store_type = GetStoreOperandType(value_type);
4361 GetAssembler()->StoreToOffset(store_type, RegisterFrom(value_loc), array, full_offset);
4362 } else {
4363 vixl32::Register temp = temps.Acquire();
4364
4365 if (has_intermediate_address) {
Artem Serov2bbc9532016-10-21 11:51:50 +01004366 // We do not need to compute the intermediate address from the array: the
4367 // input instruction has done it already. See the comment in
4368 // `TryExtractArrayAccessAddress()`.
4369 if (kIsDebugBuild) {
4370 HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
4371 DCHECK(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64() == data_offset);
4372 }
4373 temp = array;
Scott Wakelingc34dba72016-10-03 10:14:44 +01004374 } else {
4375 __ Add(temp, array, data_offset);
4376 }
4377 codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
4378 }
4379 break;
4380 }
4381
4382 case Primitive::kPrimNot: {
4383 vixl32::Register value = RegisterFrom(value_loc);
4384 // TryExtractArrayAccessAddress optimization is never applied for non-primitive ArraySet.
4385 // See the comment in instruction_simplifier_shared.cc.
4386 DCHECK(!has_intermediate_address);
4387
4388 if (instruction->InputAt(2)->IsNullConstant()) {
4389 // Just setting null.
4390 if (index.IsConstant()) {
4391 size_t offset =
4392 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
4393 GetAssembler()->StoreToOffset(kStoreWord, value, array, offset);
4394 } else {
4395 DCHECK(index.IsRegister()) << index;
4396 vixl32::Register temp = temps.Acquire();
4397 __ Add(temp, array, data_offset);
4398 codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
4399 }
4400 codegen_->MaybeRecordImplicitNullCheck(instruction);
4401 DCHECK(!needs_write_barrier);
4402 DCHECK(!may_need_runtime_call_for_type_check);
4403 break;
4404 }
4405
4406 DCHECK(needs_write_barrier);
4407 Location temp1_loc = locations->GetTemp(0);
4408 vixl32::Register temp1 = RegisterFrom(temp1_loc);
4409 Location temp2_loc = locations->GetTemp(1);
4410 vixl32::Register temp2 = RegisterFrom(temp2_loc);
4411 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
4412 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
4413 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
4414 vixl32::Label done;
4415 SlowPathCodeARMVIXL* slow_path = nullptr;
4416
4417 if (may_need_runtime_call_for_type_check) {
4418 slow_path = new (GetGraph()->GetArena()) ArraySetSlowPathARMVIXL(instruction);
4419 codegen_->AddSlowPath(slow_path);
4420 if (instruction->GetValueCanBeNull()) {
4421 vixl32::Label non_zero;
xueliang.zhongf51bc622016-11-04 09:23:32 +00004422 __ CompareAndBranchIfNonZero(value, &non_zero);
Scott Wakelingc34dba72016-10-03 10:14:44 +01004423 if (index.IsConstant()) {
4424 size_t offset =
4425 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
4426 GetAssembler()->StoreToOffset(kStoreWord, value, array, offset);
4427 } else {
4428 DCHECK(index.IsRegister()) << index;
4429 vixl32::Register temp = temps.Acquire();
4430 __ Add(temp, array, data_offset);
4431 codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
4432 }
4433 codegen_->MaybeRecordImplicitNullCheck(instruction);
4434 __ B(&done);
4435 __ Bind(&non_zero);
4436 }
4437
4438 // Note that when read barriers are enabled, the type checks
4439 // are performed without read barriers. This is fine, even in
4440 // the case where a class object is in the from-space after
4441 // the flip, as a comparison involving such a type would not
4442 // produce a false positive; it may of course produce a false
4443 // negative, in which case we would take the ArraySet slow
4444 // path.
4445
4446 // /* HeapReference<Class> */ temp1 = array->klass_
4447 GetAssembler()->LoadFromOffset(kLoadWord, temp1, array, class_offset);
4448 codegen_->MaybeRecordImplicitNullCheck(instruction);
4449 GetAssembler()->MaybeUnpoisonHeapReference(temp1);
4450
4451 // /* HeapReference<Class> */ temp1 = temp1->component_type_
4452 GetAssembler()->LoadFromOffset(kLoadWord, temp1, temp1, component_offset);
4453 // /* HeapReference<Class> */ temp2 = value->klass_
4454 GetAssembler()->LoadFromOffset(kLoadWord, temp2, value, class_offset);
4455 // If heap poisoning is enabled, no need to unpoison `temp1`
4456 // nor `temp2`, as we are comparing two poisoned references.
4457 __ Cmp(temp1, temp2);
4458
4459 if (instruction->StaticTypeOfArrayIsObjectArray()) {
4460 vixl32::Label do_put;
4461 __ B(eq, &do_put);
4462 // If heap poisoning is enabled, the `temp1` reference has
4463 // not been unpoisoned yet; unpoison it now.
4464 GetAssembler()->MaybeUnpoisonHeapReference(temp1);
4465
4466 // /* HeapReference<Class> */ temp1 = temp1->super_class_
4467 GetAssembler()->LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
4468 // If heap poisoning is enabled, no need to unpoison
4469 // `temp1`, as we are comparing against null below.
xueliang.zhongf51bc622016-11-04 09:23:32 +00004470 __ CompareAndBranchIfNonZero(temp1, slow_path->GetEntryLabel());
Scott Wakelingc34dba72016-10-03 10:14:44 +01004471 __ Bind(&do_put);
4472 } else {
4473 __ B(ne, slow_path->GetEntryLabel());
4474 }
4475 }
4476
4477 vixl32::Register source = value;
4478 if (kPoisonHeapReferences) {
4479 // Note that in the case where `value` is a null reference,
4480 // we do not enter this block, as a null reference does not
4481 // need poisoning.
4482 DCHECK_EQ(value_type, Primitive::kPrimNot);
4483 __ Mov(temp1, value);
4484 GetAssembler()->PoisonHeapReference(temp1);
4485 source = temp1;
4486 }
4487
4488 if (index.IsConstant()) {
4489 size_t offset =
4490 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
4491 GetAssembler()->StoreToOffset(kStoreWord, source, array, offset);
4492 } else {
4493 DCHECK(index.IsRegister()) << index;
4494
4495 vixl32::Register temp = temps.Acquire();
4496 __ Add(temp, array, data_offset);
4497 codegen_->StoreToShiftedRegOffset(value_type,
4498 LocationFrom(source),
4499 temp,
4500 RegisterFrom(index));
4501 }
4502
4503 if (!may_need_runtime_call_for_type_check) {
4504 codegen_->MaybeRecordImplicitNullCheck(instruction);
4505 }
4506
4507 codegen_->MarkGCCard(temp1, temp2, array, value, instruction->GetValueCanBeNull());
4508
4509 if (done.IsReferenced()) {
4510 __ Bind(&done);
4511 }
4512
4513 if (slow_path != nullptr) {
4514 __ Bind(slow_path->GetExitLabel());
4515 }
4516
4517 break;
4518 }
4519
4520 case Primitive::kPrimLong: {
4521 Location value = locations->InAt(2);
4522 if (index.IsConstant()) {
4523 size_t offset =
4524 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
4525 GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), array, offset);
4526 } else {
4527 vixl32::Register temp = temps.Acquire();
4528 __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
4529 GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), temp, data_offset);
4530 }
4531 break;
4532 }
4533
4534 case Primitive::kPrimFloat: {
4535 Location value = locations->InAt(2);
4536 DCHECK(value.IsFpuRegister());
4537 if (index.IsConstant()) {
4538 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
4539 GetAssembler()->StoreSToOffset(SRegisterFrom(value), array, offset);
4540 } else {
4541 vixl32::Register temp = temps.Acquire();
4542 __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_4));
4543 GetAssembler()->StoreSToOffset(SRegisterFrom(value), temp, data_offset);
4544 }
4545 break;
4546 }
4547
4548 case Primitive::kPrimDouble: {
4549 Location value = locations->InAt(2);
4550 DCHECK(value.IsFpuRegisterPair());
4551 if (index.IsConstant()) {
4552 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
4553 GetAssembler()->StoreDToOffset(DRegisterFrom(value), array, offset);
4554 } else {
4555 vixl32::Register temp = temps.Acquire();
4556 __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
4557 GetAssembler()->StoreDToOffset(DRegisterFrom(value), temp, data_offset);
4558 }
4559 break;
4560 }
4561
4562 case Primitive::kPrimVoid:
4563 LOG(FATAL) << "Unreachable type " << value_type;
4564 UNREACHABLE();
4565 }
4566
4567 // Objects are handled in the switch.
4568 if (value_type != Primitive::kPrimNot) {
4569 codegen_->MaybeRecordImplicitNullCheck(instruction);
4570 }
4571}
4572
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004573void LocationsBuilderARMVIXL::VisitArrayLength(HArrayLength* instruction) {
4574 LocationSummary* locations =
4575 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
4576 locations->SetInAt(0, Location::RequiresRegister());
4577 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4578}
4579
4580void InstructionCodeGeneratorARMVIXL::VisitArrayLength(HArrayLength* instruction) {
4581 uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction);
4582 vixl32::Register obj = InputRegisterAt(instruction, 0);
4583 vixl32::Register out = OutputRegister(instruction);
4584 GetAssembler()->LoadFromOffset(kLoadWord, out, obj, offset);
4585 codegen_->MaybeRecordImplicitNullCheck(instruction);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004586 // Mask out compression flag from String's array length.
4587 if (mirror::kUseStringCompression && instruction->IsStringLength()) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01004588 __ Lsr(out, out, 1u);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004589 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004590}
4591
Artem Serov2bbc9532016-10-21 11:51:50 +01004592void LocationsBuilderARMVIXL::VisitIntermediateAddress(HIntermediateAddress* instruction) {
4593 // The read barrier instrumentation does not support the HIntermediateAddress instruction yet.
4594 DCHECK(!kEmitCompilerReadBarrier);
4595 LocationSummary* locations =
4596 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
4597
4598 locations->SetInAt(0, Location::RequiresRegister());
4599 locations->SetInAt(1, Location::RegisterOrConstant(instruction->GetOffset()));
4600 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4601}
4602
4603void InstructionCodeGeneratorARMVIXL::VisitIntermediateAddress(HIntermediateAddress* instruction) {
4604 vixl32::Register out = OutputRegister(instruction);
4605 vixl32::Register first = InputRegisterAt(instruction, 0);
4606 Location second = instruction->GetLocations()->InAt(1);
4607
4608 // The read barrier instrumentation does not support the HIntermediateAddress instruction yet.
4609 DCHECK(!kEmitCompilerReadBarrier);
4610
4611 if (second.IsRegister()) {
4612 __ Add(out, first, RegisterFrom(second));
4613 } else {
4614 __ Add(out, first, second.GetConstant()->AsIntConstant()->GetValue());
4615 }
4616}
4617
Scott Wakelingc34dba72016-10-03 10:14:44 +01004618void LocationsBuilderARMVIXL::VisitBoundsCheck(HBoundsCheck* instruction) {
4619 RegisterSet caller_saves = RegisterSet::Empty();
4620 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4621 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0)));
4622 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(1)));
4623 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction, caller_saves);
4624 locations->SetInAt(0, Location::RequiresRegister());
4625 locations->SetInAt(1, Location::RequiresRegister());
4626}
4627
4628void InstructionCodeGeneratorARMVIXL::VisitBoundsCheck(HBoundsCheck* instruction) {
4629 SlowPathCodeARMVIXL* slow_path =
4630 new (GetGraph()->GetArena()) BoundsCheckSlowPathARMVIXL(instruction);
4631 codegen_->AddSlowPath(slow_path);
4632
4633 vixl32::Register index = InputRegisterAt(instruction, 0);
4634 vixl32::Register length = InputRegisterAt(instruction, 1);
4635
4636 __ Cmp(index, length);
4637 __ B(hs, slow_path->GetEntryLabel());
4638}
4639
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004640void CodeGeneratorARMVIXL::MarkGCCard(vixl32::Register temp,
4641 vixl32::Register card,
4642 vixl32::Register object,
4643 vixl32::Register value,
4644 bool can_be_null) {
4645 vixl32::Label is_null;
4646 if (can_be_null) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00004647 __ CompareAndBranchIfZero(value, &is_null);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004648 }
4649 GetAssembler()->LoadFromOffset(
4650 kLoadWord, card, tr, Thread::CardTableOffset<kArmPointerSize>().Int32Value());
4651 __ Lsr(temp, object, gc::accounting::CardTable::kCardShift);
4652 __ Strb(card, MemOperand(card, temp));
4653 if (can_be_null) {
4654 __ Bind(&is_null);
4655 }
4656}
4657
Scott Wakelingfe885462016-09-22 10:24:38 +01004658void LocationsBuilderARMVIXL::VisitParallelMove(HParallelMove* instruction ATTRIBUTE_UNUSED) {
4659 LOG(FATAL) << "Unreachable";
4660}
4661
4662void InstructionCodeGeneratorARMVIXL::VisitParallelMove(HParallelMove* instruction) {
4663 codegen_->GetMoveResolver()->EmitNativeCode(instruction);
4664}
4665
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004666void LocationsBuilderARMVIXL::VisitSuspendCheck(HSuspendCheck* instruction) {
4667 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
4668 // TODO(VIXL): https://android-review.googlesource.com/#/c/275337/ and related.
4669}
4670
4671void InstructionCodeGeneratorARMVIXL::VisitSuspendCheck(HSuspendCheck* instruction) {
4672 HBasicBlock* block = instruction->GetBlock();
4673 if (block->GetLoopInformation() != nullptr) {
4674 DCHECK(block->GetLoopInformation()->GetSuspendCheck() == instruction);
4675 // The back edge will generate the suspend check.
4676 return;
4677 }
4678 if (block->IsEntryBlock() && instruction->GetNext()->IsGoto()) {
4679 // The goto will generate the suspend check.
4680 return;
4681 }
4682 GenerateSuspendCheck(instruction, nullptr);
4683}
4684
4685void InstructionCodeGeneratorARMVIXL::GenerateSuspendCheck(HSuspendCheck* instruction,
4686 HBasicBlock* successor) {
4687 SuspendCheckSlowPathARMVIXL* slow_path =
4688 down_cast<SuspendCheckSlowPathARMVIXL*>(instruction->GetSlowPath());
4689 if (slow_path == nullptr) {
4690 slow_path = new (GetGraph()->GetArena()) SuspendCheckSlowPathARMVIXL(instruction, successor);
4691 instruction->SetSlowPath(slow_path);
4692 codegen_->AddSlowPath(slow_path);
4693 if (successor != nullptr) {
4694 DCHECK(successor->IsLoopHeader());
4695 codegen_->ClearSpillSlotsFromLoopPhisInStackMap(instruction);
4696 }
4697 } else {
4698 DCHECK_EQ(slow_path->GetSuccessor(), successor);
4699 }
4700
4701 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
4702 vixl32::Register temp = temps.Acquire();
4703 GetAssembler()->LoadFromOffset(
4704 kLoadUnsignedHalfword, temp, tr, Thread::ThreadFlagsOffset<kArmPointerSize>().Int32Value());
4705 if (successor == nullptr) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00004706 __ CompareAndBranchIfNonZero(temp, slow_path->GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004707 __ Bind(slow_path->GetReturnLabel());
4708 } else {
xueliang.zhongf51bc622016-11-04 09:23:32 +00004709 __ CompareAndBranchIfZero(temp, codegen_->GetLabelOf(successor));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004710 __ B(slow_path->GetEntryLabel());
4711 }
4712}
4713
Scott Wakelingfe885462016-09-22 10:24:38 +01004714ArmVIXLAssembler* ParallelMoveResolverARMVIXL::GetAssembler() const {
4715 return codegen_->GetAssembler();
4716}
4717
4718void ParallelMoveResolverARMVIXL::EmitMove(size_t index) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004719 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
Scott Wakelingfe885462016-09-22 10:24:38 +01004720 MoveOperands* move = moves_[index];
4721 Location source = move->GetSource();
4722 Location destination = move->GetDestination();
4723
4724 if (source.IsRegister()) {
4725 if (destination.IsRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004726 __ Mov(RegisterFrom(destination), RegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01004727 } else if (destination.IsFpuRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004728 __ Vmov(SRegisterFrom(destination), RegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01004729 } else {
4730 DCHECK(destination.IsStackSlot());
4731 GetAssembler()->StoreToOffset(kStoreWord,
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004732 RegisterFrom(source),
Scott Wakelingfe885462016-09-22 10:24:38 +01004733 sp,
4734 destination.GetStackIndex());
4735 }
4736 } else if (source.IsStackSlot()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004737 if (destination.IsRegister()) {
4738 GetAssembler()->LoadFromOffset(kLoadWord,
4739 RegisterFrom(destination),
4740 sp,
4741 source.GetStackIndex());
4742 } else if (destination.IsFpuRegister()) {
4743 GetAssembler()->LoadSFromOffset(SRegisterFrom(destination), sp, source.GetStackIndex());
4744 } else {
4745 DCHECK(destination.IsStackSlot());
4746 vixl32::Register temp = temps.Acquire();
4747 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, source.GetStackIndex());
4748 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
4749 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004750 } else if (source.IsFpuRegister()) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01004751 if (destination.IsRegister()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01004752 __ Vmov(RegisterFrom(destination), SRegisterFrom(source));
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01004753 } else if (destination.IsFpuRegister()) {
4754 __ Vmov(SRegisterFrom(destination), SRegisterFrom(source));
4755 } else {
4756 DCHECK(destination.IsStackSlot());
4757 GetAssembler()->StoreSToOffset(SRegisterFrom(source), sp, destination.GetStackIndex());
4758 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004759 } else if (source.IsDoubleStackSlot()) {
Alexandre Rames9c19bd62016-10-24 11:50:32 +01004760 if (destination.IsDoubleStackSlot()) {
4761 vixl32::DRegister temp = temps.AcquireD();
4762 GetAssembler()->LoadDFromOffset(temp, sp, source.GetStackIndex());
4763 GetAssembler()->StoreDToOffset(temp, sp, destination.GetStackIndex());
4764 } else if (destination.IsRegisterPair()) {
4765 DCHECK(ExpectedPairLayout(destination));
4766 GetAssembler()->LoadFromOffset(
4767 kLoadWordPair, LowRegisterFrom(destination), sp, source.GetStackIndex());
4768 } else {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01004769 DCHECK(destination.IsFpuRegisterPair()) << destination;
4770 GetAssembler()->LoadDFromOffset(DRegisterFrom(destination), sp, source.GetStackIndex());
Alexandre Rames9c19bd62016-10-24 11:50:32 +01004771 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004772 } else if (source.IsRegisterPair()) {
4773 if (destination.IsRegisterPair()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004774 __ Mov(LowRegisterFrom(destination), LowRegisterFrom(source));
4775 __ Mov(HighRegisterFrom(destination), HighRegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01004776 } else if (destination.IsFpuRegisterPair()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01004777 __ Vmov(DRegisterFrom(destination), LowRegisterFrom(source), HighRegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01004778 } else {
4779 DCHECK(destination.IsDoubleStackSlot()) << destination;
4780 DCHECK(ExpectedPairLayout(source));
4781 GetAssembler()->StoreToOffset(kStoreWordPair,
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004782 LowRegisterFrom(source),
Scott Wakelingfe885462016-09-22 10:24:38 +01004783 sp,
4784 destination.GetStackIndex());
4785 }
4786 } else if (source.IsFpuRegisterPair()) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01004787 if (destination.IsRegisterPair()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01004788 __ Vmov(LowRegisterFrom(destination), HighRegisterFrom(destination), DRegisterFrom(source));
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01004789 } else if (destination.IsFpuRegisterPair()) {
4790 __ Vmov(DRegisterFrom(destination), DRegisterFrom(source));
4791 } else {
4792 DCHECK(destination.IsDoubleStackSlot()) << destination;
4793 GetAssembler()->StoreDToOffset(DRegisterFrom(source), sp, destination.GetStackIndex());
4794 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004795 } else {
4796 DCHECK(source.IsConstant()) << source;
4797 HConstant* constant = source.GetConstant();
4798 if (constant->IsIntConstant() || constant->IsNullConstant()) {
4799 int32_t value = CodeGenerator::GetInt32ValueOf(constant);
4800 if (destination.IsRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004801 __ Mov(RegisterFrom(destination), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01004802 } else {
4803 DCHECK(destination.IsStackSlot());
Scott Wakelingfe885462016-09-22 10:24:38 +01004804 vixl32::Register temp = temps.Acquire();
4805 __ Mov(temp, value);
4806 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
4807 }
4808 } else if (constant->IsLongConstant()) {
4809 int64_t value = constant->AsLongConstant()->GetValue();
4810 if (destination.IsRegisterPair()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004811 __ Mov(LowRegisterFrom(destination), Low32Bits(value));
4812 __ Mov(HighRegisterFrom(destination), High32Bits(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01004813 } else {
4814 DCHECK(destination.IsDoubleStackSlot()) << destination;
Scott Wakelingfe885462016-09-22 10:24:38 +01004815 vixl32::Register temp = temps.Acquire();
4816 __ Mov(temp, Low32Bits(value));
4817 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
4818 __ Mov(temp, High32Bits(value));
4819 GetAssembler()->StoreToOffset(kStoreWord,
4820 temp,
4821 sp,
4822 destination.GetHighStackIndex(kArmWordSize));
4823 }
4824 } else if (constant->IsDoubleConstant()) {
4825 double value = constant->AsDoubleConstant()->GetValue();
4826 if (destination.IsFpuRegisterPair()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01004827 __ Vmov(DRegisterFrom(destination), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01004828 } else {
4829 DCHECK(destination.IsDoubleStackSlot()) << destination;
4830 uint64_t int_value = bit_cast<uint64_t, double>(value);
Scott Wakelingfe885462016-09-22 10:24:38 +01004831 vixl32::Register temp = temps.Acquire();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004832 __ Mov(temp, Low32Bits(int_value));
Scott Wakelingfe885462016-09-22 10:24:38 +01004833 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004834 __ Mov(temp, High32Bits(int_value));
Scott Wakelingfe885462016-09-22 10:24:38 +01004835 GetAssembler()->StoreToOffset(kStoreWord,
4836 temp,
4837 sp,
4838 destination.GetHighStackIndex(kArmWordSize));
4839 }
4840 } else {
4841 DCHECK(constant->IsFloatConstant()) << constant->DebugName();
4842 float value = constant->AsFloatConstant()->GetValue();
4843 if (destination.IsFpuRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004844 __ Vmov(SRegisterFrom(destination), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01004845 } else {
4846 DCHECK(destination.IsStackSlot());
Scott Wakelingfe885462016-09-22 10:24:38 +01004847 vixl32::Register temp = temps.Acquire();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004848 __ Mov(temp, bit_cast<int32_t, float>(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01004849 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
4850 }
4851 }
4852 }
4853}
4854
Alexandre Rames9c19bd62016-10-24 11:50:32 +01004855void ParallelMoveResolverARMVIXL::Exchange(vixl32::Register reg, int mem) {
4856 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
4857 vixl32::Register temp = temps.Acquire();
4858 __ Mov(temp, reg);
4859 GetAssembler()->LoadFromOffset(kLoadWord, reg, sp, mem);
4860 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, mem);
Scott Wakelingfe885462016-09-22 10:24:38 +01004861}
4862
Alexandre Rames9c19bd62016-10-24 11:50:32 +01004863void ParallelMoveResolverARMVIXL::Exchange(int mem1, int mem2) {
4864 // TODO(VIXL32): Double check the performance of this implementation.
4865 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
4866 vixl32::Register temp = temps.Acquire();
4867 vixl32::SRegister temp_s = temps.AcquireS();
4868
4869 __ Ldr(temp, MemOperand(sp, mem1));
4870 __ Vldr(temp_s, MemOperand(sp, mem2));
4871 __ Str(temp, MemOperand(sp, mem2));
4872 __ Vstr(temp_s, MemOperand(sp, mem1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004873}
4874
Alexandre Rames9c19bd62016-10-24 11:50:32 +01004875void ParallelMoveResolverARMVIXL::EmitSwap(size_t index) {
4876 MoveOperands* move = moves_[index];
4877 Location source = move->GetSource();
4878 Location destination = move->GetDestination();
4879 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
4880
4881 if (source.IsRegister() && destination.IsRegister()) {
4882 vixl32::Register temp = temps.Acquire();
4883 DCHECK(!RegisterFrom(source).Is(temp));
4884 DCHECK(!RegisterFrom(destination).Is(temp));
4885 __ Mov(temp, RegisterFrom(destination));
4886 __ Mov(RegisterFrom(destination), RegisterFrom(source));
4887 __ Mov(RegisterFrom(source), temp);
4888 } else if (source.IsRegister() && destination.IsStackSlot()) {
4889 Exchange(RegisterFrom(source), destination.GetStackIndex());
4890 } else if (source.IsStackSlot() && destination.IsRegister()) {
4891 Exchange(RegisterFrom(destination), source.GetStackIndex());
4892 } else if (source.IsStackSlot() && destination.IsStackSlot()) {
4893 TODO_VIXL32(FATAL);
4894 } else if (source.IsFpuRegister() && destination.IsFpuRegister()) {
4895 TODO_VIXL32(FATAL);
4896 } else if (source.IsRegisterPair() && destination.IsRegisterPair()) {
4897 vixl32::DRegister temp = temps.AcquireD();
4898 __ Vmov(temp, LowRegisterFrom(source), HighRegisterFrom(source));
4899 __ Mov(LowRegisterFrom(source), LowRegisterFrom(destination));
4900 __ Mov(HighRegisterFrom(source), HighRegisterFrom(destination));
4901 __ Vmov(LowRegisterFrom(destination), HighRegisterFrom(destination), temp);
4902 } else if (source.IsRegisterPair() || destination.IsRegisterPair()) {
4903 vixl32::Register low_reg = LowRegisterFrom(source.IsRegisterPair() ? source : destination);
4904 int mem = source.IsRegisterPair() ? destination.GetStackIndex() : source.GetStackIndex();
4905 DCHECK(ExpectedPairLayout(source.IsRegisterPair() ? source : destination));
4906 vixl32::DRegister temp = temps.AcquireD();
4907 __ Vmov(temp, low_reg, vixl32::Register(low_reg.GetCode() + 1));
4908 GetAssembler()->LoadFromOffset(kLoadWordPair, low_reg, sp, mem);
4909 GetAssembler()->StoreDToOffset(temp, sp, mem);
4910 } else if (source.IsFpuRegisterPair() && destination.IsFpuRegisterPair()) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004911 vixl32::DRegister first = DRegisterFrom(source);
4912 vixl32::DRegister second = DRegisterFrom(destination);
4913 vixl32::DRegister temp = temps.AcquireD();
4914 __ Vmov(temp, first);
4915 __ Vmov(first, second);
4916 __ Vmov(second, temp);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01004917 } else if (source.IsFpuRegisterPair() || destination.IsFpuRegisterPair()) {
4918 TODO_VIXL32(FATAL);
4919 } else if (source.IsFpuRegister() || destination.IsFpuRegister()) {
4920 TODO_VIXL32(FATAL);
4921 } else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) {
4922 vixl32::DRegister temp1 = temps.AcquireD();
4923 vixl32::DRegister temp2 = temps.AcquireD();
4924 __ Vldr(temp1, MemOperand(sp, source.GetStackIndex()));
4925 __ Vldr(temp2, MemOperand(sp, destination.GetStackIndex()));
4926 __ Vstr(temp1, MemOperand(sp, destination.GetStackIndex()));
4927 __ Vstr(temp2, MemOperand(sp, source.GetStackIndex()));
4928 } else {
4929 LOG(FATAL) << "Unimplemented" << source << " <-> " << destination;
4930 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004931}
4932
4933void ParallelMoveResolverARMVIXL::SpillScratch(int reg ATTRIBUTE_UNUSED) {
4934 TODO_VIXL32(FATAL);
4935}
4936
4937void ParallelMoveResolverARMVIXL::RestoreScratch(int reg ATTRIBUTE_UNUSED) {
4938 TODO_VIXL32(FATAL);
4939}
4940
Artem Serov02d37832016-10-25 15:25:33 +01004941// Check if the desired_class_load_kind is supported. If it is, return it,
4942// otherwise return a fall-back kind that should be used instead.
4943HLoadClass::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadClassKind(
4944 HLoadClass::LoadKind desired_class_load_kind ATTRIBUTE_UNUSED) {
4945 // TODO(VIXL): Implement optimized code paths.
4946 return HLoadClass::LoadKind::kDexCacheViaMethod;
4947}
4948
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004949void LocationsBuilderARMVIXL::VisitLoadClass(HLoadClass* cls) {
4950 if (cls->NeedsAccessCheck()) {
4951 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4952 CodeGenerator::CreateLoadClassLocationSummary(
4953 cls,
4954 LocationFrom(calling_convention.GetRegisterAt(0)),
4955 LocationFrom(r0),
4956 /* code_generator_supports_read_barrier */ true);
4957 return;
4958 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004959
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004960 // TODO(VIXL): read barrier code.
4961 LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || kEmitCompilerReadBarrier)
4962 ? LocationSummary::kCallOnSlowPath
4963 : LocationSummary::kNoCall;
4964 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
4965 HLoadClass::LoadKind load_kind = cls->GetLoadKind();
4966 if (load_kind == HLoadClass::LoadKind::kReferrersClass ||
4967 load_kind == HLoadClass::LoadKind::kDexCacheViaMethod ||
4968 load_kind == HLoadClass::LoadKind::kDexCachePcRelative) {
4969 locations->SetInAt(0, Location::RequiresRegister());
4970 }
4971 locations->SetOut(Location::RequiresRegister());
4972}
4973
4974void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) {
4975 LocationSummary* locations = cls->GetLocations();
4976 if (cls->NeedsAccessCheck()) {
Andreas Gampea5b09a62016-11-17 15:21:22 -08004977 codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex().index_);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004978 codegen_->InvokeRuntime(kQuickInitializeTypeAndVerifyAccess, cls, cls->GetDexPc());
4979 CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>();
4980 return;
4981 }
4982
4983 Location out_loc = locations->Out();
4984 vixl32::Register out = OutputRegister(cls);
4985
4986 // TODO(VIXL): read barrier code.
4987 bool generate_null_check = false;
4988 switch (cls->GetLoadKind()) {
4989 case HLoadClass::LoadKind::kReferrersClass: {
4990 DCHECK(!cls->CanCallRuntime());
4991 DCHECK(!cls->MustGenerateClinitCheck());
4992 // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
4993 vixl32::Register current_method = InputRegisterAt(cls, 0);
4994 GenerateGcRootFieldLoad(cls,
4995 out_loc,
4996 current_method,
Roland Levillain00468f32016-10-27 18:02:48 +01004997 ArtMethod::DeclaringClassOffset().Int32Value(),
4998 kEmitCompilerReadBarrier);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004999 break;
5000 }
5001 case HLoadClass::LoadKind::kDexCacheViaMethod: {
5002 // /* GcRoot<mirror::Class>[] */ out =
5003 // current_method.ptr_sized_fields_->dex_cache_resolved_types_
5004 vixl32::Register current_method = InputRegisterAt(cls, 0);
5005 const int32_t resolved_types_offset =
5006 ArtMethod::DexCacheResolvedTypesOffset(kArmPointerSize).Int32Value();
5007 GetAssembler()->LoadFromOffset(kLoadWord, out, current_method, resolved_types_offset);
5008 // /* GcRoot<mirror::Class> */ out = out[type_index]
Andreas Gampea5b09a62016-11-17 15:21:22 -08005009 size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_);
Roland Levillain00468f32016-10-27 18:02:48 +01005010 GenerateGcRootFieldLoad(cls, out_loc, out, offset, kEmitCompilerReadBarrier);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005011 generate_null_check = !cls->IsInDexCache();
5012 break;
5013 }
5014 default:
5015 TODO_VIXL32(FATAL);
5016 }
5017
5018 if (generate_null_check || cls->MustGenerateClinitCheck()) {
5019 DCHECK(cls->CanCallRuntime());
5020 LoadClassSlowPathARMVIXL* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARMVIXL(
5021 cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
5022 codegen_->AddSlowPath(slow_path);
5023 if (generate_null_check) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00005024 __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005025 }
5026 if (cls->MustGenerateClinitCheck()) {
5027 GenerateClassInitializationCheck(slow_path, out);
5028 } else {
5029 __ Bind(slow_path->GetExitLabel());
5030 }
5031 }
5032}
5033
Artem Serov02d37832016-10-25 15:25:33 +01005034void LocationsBuilderARMVIXL::VisitClinitCheck(HClinitCheck* check) {
5035 LocationSummary* locations =
5036 new (GetGraph()->GetArena()) LocationSummary(check, LocationSummary::kCallOnSlowPath);
5037 locations->SetInAt(0, Location::RequiresRegister());
5038 if (check->HasUses()) {
5039 locations->SetOut(Location::SameAsFirstInput());
5040 }
5041}
5042
5043void InstructionCodeGeneratorARMVIXL::VisitClinitCheck(HClinitCheck* check) {
5044 // We assume the class is not null.
5045 LoadClassSlowPathARMVIXL* slow_path =
5046 new (GetGraph()->GetArena()) LoadClassSlowPathARMVIXL(check->GetLoadClass(),
5047 check,
5048 check->GetDexPc(),
5049 /* do_clinit */ true);
5050 codegen_->AddSlowPath(slow_path);
5051 GenerateClassInitializationCheck(slow_path, InputRegisterAt(check, 0));
5052}
5053
5054void InstructionCodeGeneratorARMVIXL::GenerateClassInitializationCheck(
5055 LoadClassSlowPathARMVIXL* slow_path, vixl32::Register class_reg) {
5056 UseScratchRegisterScope temps(GetVIXLAssembler());
5057 vixl32::Register temp = temps.Acquire();
5058 GetAssembler()->LoadFromOffset(kLoadWord,
5059 temp,
5060 class_reg,
5061 mirror::Class::StatusOffset().Int32Value());
5062 __ Cmp(temp, mirror::Class::kStatusInitialized);
5063 __ B(lt, slow_path->GetEntryLabel());
5064 // Even if the initialized flag is set, we may be in a situation where caches are not synced
5065 // properly. Therefore, we do a memory fence.
5066 __ Dmb(ISH);
5067 __ Bind(slow_path->GetExitLabel());
5068}
5069
5070// Check if the desired_string_load_kind is supported. If it is, return it,
5071// otherwise return a fall-back kind that should be used instead.
5072HLoadString::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadStringKind(
5073 HLoadString::LoadKind desired_string_load_kind ATTRIBUTE_UNUSED) {
5074 // TODO(VIXL): Implement optimized code paths. For now we always use the simpler fallback code.
5075 return HLoadString::LoadKind::kDexCacheViaMethod;
5076}
5077
5078void LocationsBuilderARMVIXL::VisitLoadString(HLoadString* load) {
5079 LocationSummary::CallKind call_kind = load->NeedsEnvironment()
5080 ? LocationSummary::kCallOnMainOnly
5081 : LocationSummary::kNoCall;
5082 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
5083
5084 // TODO(VIXL): Implement optimized code paths.
5085 // See InstructionCodeGeneratorARMVIXL::VisitLoadString.
5086 HLoadString::LoadKind load_kind = load->GetLoadKind();
5087 if (load_kind == HLoadString::LoadKind::kDexCacheViaMethod) {
5088 locations->SetInAt(0, Location::RequiresRegister());
5089 // TODO(VIXL): Use InvokeRuntimeCallingConventionARMVIXL instead.
5090 locations->SetOut(LocationFrom(r0));
5091 } else {
5092 locations->SetOut(Location::RequiresRegister());
5093 }
5094}
5095
5096void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) {
5097 // TODO(VIXL): Implement optimized code paths.
5098 // We implemented the simplest solution to get first ART tests passing, we deferred the
5099 // optimized path until later, we should implement it using ARM64 implementation as a
5100 // reference. The same related to LocationsBuilderARMVIXL::VisitLoadString.
5101
5102 // TODO: Re-add the compiler code to do string dex cache lookup again.
5103 DCHECK_EQ(load->GetLoadKind(), HLoadString::LoadKind::kDexCacheViaMethod);
5104 InvokeRuntimeCallingConventionARMVIXL calling_convention;
5105 __ Mov(calling_convention.GetRegisterAt(0), load->GetStringIndex());
5106 codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc());
5107 CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
5108}
5109
5110static int32_t GetExceptionTlsOffset() {
5111 return Thread::ExceptionOffset<kArmPointerSize>().Int32Value();
5112}
5113
5114void LocationsBuilderARMVIXL::VisitLoadException(HLoadException* load) {
5115 LocationSummary* locations =
5116 new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kNoCall);
5117 locations->SetOut(Location::RequiresRegister());
5118}
5119
5120void InstructionCodeGeneratorARMVIXL::VisitLoadException(HLoadException* load) {
5121 vixl32::Register out = OutputRegister(load);
5122 GetAssembler()->LoadFromOffset(kLoadWord, out, tr, GetExceptionTlsOffset());
5123}
5124
5125
5126void LocationsBuilderARMVIXL::VisitClearException(HClearException* clear) {
5127 new (GetGraph()->GetArena()) LocationSummary(clear, LocationSummary::kNoCall);
5128}
5129
5130void InstructionCodeGeneratorARMVIXL::VisitClearException(HClearException* clear ATTRIBUTE_UNUSED) {
5131 UseScratchRegisterScope temps(GetVIXLAssembler());
5132 vixl32::Register temp = temps.Acquire();
5133 __ Mov(temp, 0);
5134 GetAssembler()->StoreToOffset(kStoreWord, temp, tr, GetExceptionTlsOffset());
5135}
5136
5137void LocationsBuilderARMVIXL::VisitThrow(HThrow* instruction) {
5138 LocationSummary* locations =
5139 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
5140 InvokeRuntimeCallingConventionARMVIXL calling_convention;
5141 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
5142}
5143
5144void InstructionCodeGeneratorARMVIXL::VisitThrow(HThrow* instruction) {
5145 codegen_->InvokeRuntime(kQuickDeliverException, instruction, instruction->GetDexPc());
5146 CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
5147}
5148
Anton Kirilove28d9ae2016-10-25 18:17:23 +01005149static bool TypeCheckNeedsATemporary(TypeCheckKind type_check_kind) {
5150 return kEmitCompilerReadBarrier &&
5151 (kUseBakerReadBarrier ||
5152 type_check_kind == TypeCheckKind::kAbstractClassCheck ||
5153 type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
5154 type_check_kind == TypeCheckKind::kArrayObjectCheck);
5155}
5156
Artem Serovcfbe9132016-10-14 15:58:56 +01005157
5158void LocationsBuilderARMVIXL::VisitInstanceOf(HInstanceOf* instruction) {
5159 LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
5160 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
5161 bool baker_read_barrier_slow_path = false;
5162 switch (type_check_kind) {
5163 case TypeCheckKind::kExactCheck:
5164 case TypeCheckKind::kAbstractClassCheck:
5165 case TypeCheckKind::kClassHierarchyCheck:
5166 case TypeCheckKind::kArrayObjectCheck:
5167 call_kind =
5168 kEmitCompilerReadBarrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall;
5169 baker_read_barrier_slow_path = kUseBakerReadBarrier;
5170 break;
5171 case TypeCheckKind::kArrayCheck:
5172 case TypeCheckKind::kUnresolvedCheck:
5173 case TypeCheckKind::kInterfaceCheck:
5174 call_kind = LocationSummary::kCallOnSlowPath;
5175 break;
5176 }
5177
5178 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
5179 if (baker_read_barrier_slow_path) {
5180 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
5181 }
5182 locations->SetInAt(0, Location::RequiresRegister());
5183 locations->SetInAt(1, Location::RequiresRegister());
5184 // The "out" register is used as a temporary, so it overlaps with the inputs.
5185 // Note that TypeCheckSlowPathARM uses this register too.
5186 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
5187 // When read barriers are enabled, we need a temporary register for
5188 // some cases.
5189 if (TypeCheckNeedsATemporary(type_check_kind)) {
5190 locations->AddTemp(Location::RequiresRegister());
5191 }
5192}
5193
5194void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) {
5195 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
5196 LocationSummary* locations = instruction->GetLocations();
5197 Location obj_loc = locations->InAt(0);
5198 vixl32::Register obj = InputRegisterAt(instruction, 0);
5199 vixl32::Register cls = InputRegisterAt(instruction, 1);
5200 Location out_loc = locations->Out();
5201 vixl32::Register out = OutputRegister(instruction);
5202 Location maybe_temp_loc = TypeCheckNeedsATemporary(type_check_kind) ?
5203 locations->GetTemp(0) :
5204 Location::NoLocation();
5205 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
5206 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
5207 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
5208 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
5209 vixl32::Label done, zero;
5210 SlowPathCodeARMVIXL* slow_path = nullptr;
5211
5212 // Return 0 if `obj` is null.
5213 // avoid null check if we know obj is not null.
5214 if (instruction->MustDoNullCheck()) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00005215 __ CompareAndBranchIfZero(obj, &zero, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01005216 }
5217
Artem Serovcfbe9132016-10-14 15:58:56 +01005218 switch (type_check_kind) {
5219 case TypeCheckKind::kExactCheck: {
Mathieu Chartier6beced42016-11-15 15:51:31 -08005220 // /* HeapReference<Class> */ out = obj->klass_
5221 GenerateReferenceLoadTwoRegisters(instruction,
5222 out_loc,
5223 obj_loc,
5224 class_offset,
5225 maybe_temp_loc);
Artem Serovcfbe9132016-10-14 15:58:56 +01005226 __ Cmp(out, cls);
5227 // Classes must be equal for the instanceof to succeed.
5228 __ B(ne, &zero);
5229 __ Mov(out, 1);
5230 __ B(&done);
5231 break;
5232 }
5233
5234 case TypeCheckKind::kAbstractClassCheck: {
Mathieu Chartier6beced42016-11-15 15:51:31 -08005235 // /* HeapReference<Class> */ out = obj->klass_
5236 GenerateReferenceLoadTwoRegisters(instruction,
5237 out_loc,
5238 obj_loc,
5239 class_offset,
5240 maybe_temp_loc);
Artem Serovcfbe9132016-10-14 15:58:56 +01005241 // If the class is abstract, we eagerly fetch the super class of the
5242 // object to avoid doing a comparison we know will fail.
5243 vixl32::Label loop;
5244 __ Bind(&loop);
5245 // /* HeapReference<Class> */ out = out->super_class_
5246 GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, maybe_temp_loc);
5247 // If `out` is null, we use it for the result, and jump to `done`.
xueliang.zhongf51bc622016-11-04 09:23:32 +00005248 __ CompareAndBranchIfZero(out, &done, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01005249 __ Cmp(out, cls);
5250 __ B(ne, &loop);
5251 __ Mov(out, 1);
5252 if (zero.IsReferenced()) {
5253 __ B(&done);
5254 }
5255 break;
5256 }
5257
5258 case TypeCheckKind::kClassHierarchyCheck: {
Mathieu Chartier6beced42016-11-15 15:51:31 -08005259 // /* HeapReference<Class> */ out = obj->klass_
5260 GenerateReferenceLoadTwoRegisters(instruction,
5261 out_loc,
5262 obj_loc,
5263 class_offset,
5264 maybe_temp_loc);
Artem Serovcfbe9132016-10-14 15:58:56 +01005265 // Walk over the class hierarchy to find a match.
5266 vixl32::Label loop, success;
5267 __ Bind(&loop);
5268 __ Cmp(out, cls);
5269 __ B(eq, &success);
5270 // /* HeapReference<Class> */ out = out->super_class_
5271 GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, maybe_temp_loc);
xueliang.zhongf51bc622016-11-04 09:23:32 +00005272 __ CompareAndBranchIfNonZero(out, &loop);
Artem Serovcfbe9132016-10-14 15:58:56 +01005273 // If `out` is null, we use it for the result, and jump to `done`.
5274 __ B(&done);
5275 __ Bind(&success);
5276 __ Mov(out, 1);
5277 if (zero.IsReferenced()) {
5278 __ B(&done);
5279 }
5280 break;
5281 }
5282
5283 case TypeCheckKind::kArrayObjectCheck: {
Mathieu Chartier6beced42016-11-15 15:51:31 -08005284 // /* HeapReference<Class> */ out = obj->klass_
5285 GenerateReferenceLoadTwoRegisters(instruction,
5286 out_loc,
5287 obj_loc,
5288 class_offset,
5289 maybe_temp_loc);
Artem Serovcfbe9132016-10-14 15:58:56 +01005290 // Do an exact check.
5291 vixl32::Label exact_check;
5292 __ Cmp(out, cls);
5293 __ B(eq, &exact_check);
5294 // Otherwise, we need to check that the object's class is a non-primitive array.
5295 // /* HeapReference<Class> */ out = out->component_type_
5296 GenerateReferenceLoadOneRegister(instruction, out_loc, component_offset, maybe_temp_loc);
5297 // If `out` is null, we use it for the result, and jump to `done`.
xueliang.zhongf51bc622016-11-04 09:23:32 +00005298 __ CompareAndBranchIfZero(out, &done, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01005299 GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset);
5300 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
xueliang.zhongf51bc622016-11-04 09:23:32 +00005301 __ CompareAndBranchIfNonZero(out, &zero, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01005302 __ Bind(&exact_check);
5303 __ Mov(out, 1);
5304 __ B(&done);
5305 break;
5306 }
5307
5308 case TypeCheckKind::kArrayCheck: {
Mathieu Chartier6beced42016-11-15 15:51:31 -08005309 // /* HeapReference<Class> */ out = obj->klass_
5310 GenerateReferenceLoadTwoRegisters(instruction,
5311 out_loc,
5312 obj_loc,
5313 class_offset,
5314 maybe_temp_loc);
Artem Serovcfbe9132016-10-14 15:58:56 +01005315 __ Cmp(out, cls);
5316 DCHECK(locations->OnlyCallsOnSlowPath());
5317 slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARMVIXL(instruction,
5318 /* is_fatal */ false);
5319 codegen_->AddSlowPath(slow_path);
5320 __ B(ne, slow_path->GetEntryLabel());
5321 __ Mov(out, 1);
5322 if (zero.IsReferenced()) {
5323 __ B(&done);
5324 }
5325 break;
5326 }
5327
5328 case TypeCheckKind::kUnresolvedCheck:
5329 case TypeCheckKind::kInterfaceCheck: {
5330 // Note that we indeed only call on slow path, but we always go
5331 // into the slow path for the unresolved and interface check
5332 // cases.
5333 //
5334 // We cannot directly call the InstanceofNonTrivial runtime
5335 // entry point without resorting to a type checking slow path
5336 // here (i.e. by calling InvokeRuntime directly), as it would
5337 // require to assign fixed registers for the inputs of this
5338 // HInstanceOf instruction (following the runtime calling
5339 // convention), which might be cluttered by the potential first
5340 // read barrier emission at the beginning of this method.
5341 //
5342 // TODO: Introduce a new runtime entry point taking the object
5343 // to test (instead of its class) as argument, and let it deal
5344 // with the read barrier issues. This will let us refactor this
5345 // case of the `switch` code as it was previously (with a direct
5346 // call to the runtime not using a type checking slow path).
5347 // This should also be beneficial for the other cases above.
5348 DCHECK(locations->OnlyCallsOnSlowPath());
5349 slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARMVIXL(instruction,
5350 /* is_fatal */ false);
5351 codegen_->AddSlowPath(slow_path);
5352 __ B(slow_path->GetEntryLabel());
5353 if (zero.IsReferenced()) {
5354 __ B(&done);
5355 }
5356 break;
5357 }
5358 }
5359
5360 if (zero.IsReferenced()) {
5361 __ Bind(&zero);
5362 __ Mov(out, 0);
5363 }
5364
5365 if (done.IsReferenced()) {
5366 __ Bind(&done);
5367 }
5368
5369 if (slow_path != nullptr) {
5370 __ Bind(slow_path->GetExitLabel());
5371 }
5372}
5373
Anton Kirilove28d9ae2016-10-25 18:17:23 +01005374void LocationsBuilderARMVIXL::VisitCheckCast(HCheckCast* instruction) {
5375 LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
5376 bool throws_into_catch = instruction->CanThrowIntoCatchBlock();
5377
5378 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
5379 switch (type_check_kind) {
5380 case TypeCheckKind::kExactCheck:
5381 case TypeCheckKind::kAbstractClassCheck:
5382 case TypeCheckKind::kClassHierarchyCheck:
5383 case TypeCheckKind::kArrayObjectCheck:
5384 call_kind = (throws_into_catch || kEmitCompilerReadBarrier) ?
5385 LocationSummary::kCallOnSlowPath :
5386 LocationSummary::kNoCall; // In fact, call on a fatal (non-returning) slow path.
5387 break;
5388 case TypeCheckKind::kArrayCheck:
5389 case TypeCheckKind::kUnresolvedCheck:
5390 case TypeCheckKind::kInterfaceCheck:
5391 call_kind = LocationSummary::kCallOnSlowPath;
5392 break;
5393 }
5394
5395 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
5396 locations->SetInAt(0, Location::RequiresRegister());
5397 locations->SetInAt(1, Location::RequiresRegister());
5398 // Note that TypeCheckSlowPathARM uses this "temp" register too.
5399 locations->AddTemp(Location::RequiresRegister());
5400 // When read barriers are enabled, we need an additional temporary
5401 // register for some cases.
5402 if (TypeCheckNeedsATemporary(type_check_kind)) {
5403 locations->AddTemp(Location::RequiresRegister());
5404 }
5405}
5406
5407void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) {
5408 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
5409 LocationSummary* locations = instruction->GetLocations();
5410 Location obj_loc = locations->InAt(0);
5411 vixl32::Register obj = InputRegisterAt(instruction, 0);
5412 vixl32::Register cls = InputRegisterAt(instruction, 1);
5413 Location temp_loc = locations->GetTemp(0);
5414 vixl32::Register temp = RegisterFrom(temp_loc);
5415 Location maybe_temp2_loc = TypeCheckNeedsATemporary(type_check_kind) ?
5416 locations->GetTemp(1) :
5417 Location::NoLocation();
5418 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
Artem Serovcfbe9132016-10-14 15:58:56 +01005419 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
5420 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
5421 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
Anton Kirilove28d9ae2016-10-25 18:17:23 +01005422
5423 bool is_type_check_slow_path_fatal =
5424 (type_check_kind == TypeCheckKind::kExactCheck ||
5425 type_check_kind == TypeCheckKind::kAbstractClassCheck ||
5426 type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
5427 type_check_kind == TypeCheckKind::kArrayObjectCheck) &&
5428 !instruction->CanThrowIntoCatchBlock();
5429 SlowPathCodeARMVIXL* type_check_slow_path =
5430 new (GetGraph()->GetArena()) TypeCheckSlowPathARMVIXL(instruction,
5431 is_type_check_slow_path_fatal);
5432 codegen_->AddSlowPath(type_check_slow_path);
5433
5434 vixl32::Label done;
5435 // Avoid null check if we know obj is not null.
5436 if (instruction->MustDoNullCheck()) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00005437 __ CompareAndBranchIfZero(obj, &done, /* far_target */ false);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01005438 }
5439
5440 // /* HeapReference<Class> */ temp = obj->klass_
5441 GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc);
5442
5443 switch (type_check_kind) {
5444 case TypeCheckKind::kExactCheck:
5445 case TypeCheckKind::kArrayCheck: {
5446 __ Cmp(temp, cls);
5447 // Jump to slow path for throwing the exception or doing a
5448 // more involved array check.
5449 __ B(ne, type_check_slow_path->GetEntryLabel());
5450 break;
5451 }
5452
5453 case TypeCheckKind::kAbstractClassCheck: {
Artem Serovcfbe9132016-10-14 15:58:56 +01005454 // If the class is abstract, we eagerly fetch the super class of the
5455 // object to avoid doing a comparison we know will fail.
5456 vixl32::Label loop;
5457 __ Bind(&loop);
5458 // /* HeapReference<Class> */ temp = temp->super_class_
5459 GenerateReferenceLoadOneRegister(instruction, temp_loc, super_offset, maybe_temp2_loc);
5460
5461 // If the class reference currently in `temp` is null, jump to the slow path to throw the
5462 // exception.
xueliang.zhongf51bc622016-11-04 09:23:32 +00005463 __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
Artem Serovcfbe9132016-10-14 15:58:56 +01005464
5465 // Otherwise, compare the classes.
5466 __ Cmp(temp, cls);
5467 __ B(ne, &loop);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01005468 break;
5469 }
5470
5471 case TypeCheckKind::kClassHierarchyCheck: {
Artem Serovcfbe9132016-10-14 15:58:56 +01005472 // Walk over the class hierarchy to find a match.
5473 vixl32::Label loop;
5474 __ Bind(&loop);
5475 __ Cmp(temp, cls);
5476 __ B(eq, &done);
5477
5478 // /* HeapReference<Class> */ temp = temp->super_class_
5479 GenerateReferenceLoadOneRegister(instruction, temp_loc, super_offset, maybe_temp2_loc);
5480
5481 // If the class reference currently in `temp` is null, jump to the slow path to throw the
5482 // exception.
xueliang.zhongf51bc622016-11-04 09:23:32 +00005483 __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
Artem Serovcfbe9132016-10-14 15:58:56 +01005484 // Otherwise, jump to the beginning of the loop.
5485 __ B(&loop);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01005486 break;
5487 }
5488
Artem Serovcfbe9132016-10-14 15:58:56 +01005489 case TypeCheckKind::kArrayObjectCheck: {
5490 // Do an exact check.
5491 __ Cmp(temp, cls);
5492 __ B(eq, &done);
5493
5494 // Otherwise, we need to check that the object's class is a non-primitive array.
5495 // /* HeapReference<Class> */ temp = temp->component_type_
5496 GenerateReferenceLoadOneRegister(instruction, temp_loc, component_offset, maybe_temp2_loc);
5497 // If the component type is null, jump to the slow path to throw the exception.
xueliang.zhongf51bc622016-11-04 09:23:32 +00005498 __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
Artem Serovcfbe9132016-10-14 15:58:56 +01005499 // Otherwise,the object is indeed an array, jump to label `check_non_primitive_component_type`
5500 // to further check that this component type is not a primitive type.
5501 GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, temp, temp, primitive_offset);
5502 static_assert(Primitive::kPrimNot == 0, "Expected 0 for art::Primitive::kPrimNot");
xueliang.zhongf51bc622016-11-04 09:23:32 +00005503 __ CompareAndBranchIfNonZero(temp, type_check_slow_path->GetEntryLabel());
Anton Kirilove28d9ae2016-10-25 18:17:23 +01005504 break;
5505 }
5506
5507 case TypeCheckKind::kUnresolvedCheck:
5508 case TypeCheckKind::kInterfaceCheck:
Artem Serovcfbe9132016-10-14 15:58:56 +01005509 // We always go into the type check slow path for the unresolved
5510 // and interface check cases.
5511 //
5512 // We cannot directly call the CheckCast runtime entry point
5513 // without resorting to a type checking slow path here (i.e. by
5514 // calling InvokeRuntime directly), as it would require to
5515 // assign fixed registers for the inputs of this HInstanceOf
5516 // instruction (following the runtime calling convention), which
5517 // might be cluttered by the potential first read barrier
5518 // emission at the beginning of this method.
5519 __ B(type_check_slow_path->GetEntryLabel());
Anton Kirilove28d9ae2016-10-25 18:17:23 +01005520 break;
5521 }
5522 __ Bind(&done);
5523
5524 __ Bind(type_check_slow_path->GetExitLabel());
5525}
5526
Artem Serov551b28f2016-10-18 19:11:30 +01005527void LocationsBuilderARMVIXL::VisitMonitorOperation(HMonitorOperation* instruction) {
5528 LocationSummary* locations =
5529 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
5530 InvokeRuntimeCallingConventionARMVIXL calling_convention;
5531 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
5532}
5533
5534void InstructionCodeGeneratorARMVIXL::VisitMonitorOperation(HMonitorOperation* instruction) {
5535 codegen_->InvokeRuntime(instruction->IsEnter() ? kQuickLockObject : kQuickUnlockObject,
5536 instruction,
5537 instruction->GetDexPc());
5538 if (instruction->IsEnter()) {
5539 CheckEntrypointTypes<kQuickLockObject, void, mirror::Object*>();
5540 } else {
5541 CheckEntrypointTypes<kQuickUnlockObject, void, mirror::Object*>();
5542 }
5543}
5544
Artem Serov02109dd2016-09-23 17:17:54 +01005545void LocationsBuilderARMVIXL::VisitAnd(HAnd* instruction) {
5546 HandleBitwiseOperation(instruction, AND);
5547}
5548
5549void LocationsBuilderARMVIXL::VisitOr(HOr* instruction) {
5550 HandleBitwiseOperation(instruction, ORR);
5551}
5552
5553void LocationsBuilderARMVIXL::VisitXor(HXor* instruction) {
5554 HandleBitwiseOperation(instruction, EOR);
5555}
5556
5557void LocationsBuilderARMVIXL::HandleBitwiseOperation(HBinaryOperation* instruction, Opcode opcode) {
5558 LocationSummary* locations =
5559 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
5560 DCHECK(instruction->GetResultType() == Primitive::kPrimInt
5561 || instruction->GetResultType() == Primitive::kPrimLong);
5562 // Note: GVN reorders commutative operations to have the constant on the right hand side.
5563 locations->SetInAt(0, Location::RequiresRegister());
5564 locations->SetInAt(1, ArmEncodableConstantOrRegister(instruction->InputAt(1), opcode));
5565 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5566}
5567
5568void InstructionCodeGeneratorARMVIXL::VisitAnd(HAnd* instruction) {
5569 HandleBitwiseOperation(instruction);
5570}
5571
5572void InstructionCodeGeneratorARMVIXL::VisitOr(HOr* instruction) {
5573 HandleBitwiseOperation(instruction);
5574}
5575
5576void InstructionCodeGeneratorARMVIXL::VisitXor(HXor* instruction) {
5577 HandleBitwiseOperation(instruction);
5578}
5579
Artem Serov2bbc9532016-10-21 11:51:50 +01005580void LocationsBuilderARMVIXL::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) {
5581 LocationSummary* locations =
5582 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
5583 DCHECK(instruction->GetResultType() == Primitive::kPrimInt
5584 || instruction->GetResultType() == Primitive::kPrimLong);
5585
5586 locations->SetInAt(0, Location::RequiresRegister());
5587 locations->SetInAt(1, Location::RequiresRegister());
5588 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5589}
5590
5591void InstructionCodeGeneratorARMVIXL::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) {
5592 LocationSummary* locations = instruction->GetLocations();
5593 Location first = locations->InAt(0);
5594 Location second = locations->InAt(1);
5595 Location out = locations->Out();
5596
5597 if (instruction->GetResultType() == Primitive::kPrimInt) {
5598 vixl32::Register first_reg = RegisterFrom(first);
5599 vixl32::Register second_reg = RegisterFrom(second);
5600 vixl32::Register out_reg = RegisterFrom(out);
5601
5602 switch (instruction->GetOpKind()) {
5603 case HInstruction::kAnd:
5604 __ Bic(out_reg, first_reg, second_reg);
5605 break;
5606 case HInstruction::kOr:
5607 __ Orn(out_reg, first_reg, second_reg);
5608 break;
5609 // There is no EON on arm.
5610 case HInstruction::kXor:
5611 default:
5612 LOG(FATAL) << "Unexpected instruction " << instruction->DebugName();
5613 UNREACHABLE();
5614 }
5615 return;
5616
5617 } else {
5618 DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong);
5619 vixl32::Register first_low = LowRegisterFrom(first);
5620 vixl32::Register first_high = HighRegisterFrom(first);
5621 vixl32::Register second_low = LowRegisterFrom(second);
5622 vixl32::Register second_high = HighRegisterFrom(second);
5623 vixl32::Register out_low = LowRegisterFrom(out);
5624 vixl32::Register out_high = HighRegisterFrom(out);
5625
5626 switch (instruction->GetOpKind()) {
5627 case HInstruction::kAnd:
5628 __ Bic(out_low, first_low, second_low);
5629 __ Bic(out_high, first_high, second_high);
5630 break;
5631 case HInstruction::kOr:
5632 __ Orn(out_low, first_low, second_low);
5633 __ Orn(out_high, first_high, second_high);
5634 break;
5635 // There is no EON on arm.
5636 case HInstruction::kXor:
5637 default:
5638 LOG(FATAL) << "Unexpected instruction " << instruction->DebugName();
5639 UNREACHABLE();
5640 }
5641 }
5642}
5643
Artem Serov02109dd2016-09-23 17:17:54 +01005644// TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
5645void InstructionCodeGeneratorARMVIXL::GenerateAndConst(vixl32::Register out,
5646 vixl32::Register first,
5647 uint32_t value) {
5648 // Optimize special cases for individual halfs of `and-long` (`and` is simplified earlier).
5649 if (value == 0xffffffffu) {
5650 if (!out.Is(first)) {
5651 __ Mov(out, first);
5652 }
5653 return;
5654 }
5655 if (value == 0u) {
5656 __ Mov(out, 0);
5657 return;
5658 }
5659 if (GetAssembler()->ShifterOperandCanHold(AND, value)) {
5660 __ And(out, first, value);
5661 } else {
5662 DCHECK(GetAssembler()->ShifterOperandCanHold(BIC, ~value));
5663 __ Bic(out, first, ~value);
5664 }
5665}
5666
5667// TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
5668void InstructionCodeGeneratorARMVIXL::GenerateOrrConst(vixl32::Register out,
5669 vixl32::Register first,
5670 uint32_t value) {
5671 // Optimize special cases for individual halfs of `or-long` (`or` is simplified earlier).
5672 if (value == 0u) {
5673 if (!out.Is(first)) {
5674 __ Mov(out, first);
5675 }
5676 return;
5677 }
5678 if (value == 0xffffffffu) {
5679 __ Mvn(out, 0);
5680 return;
5681 }
5682 if (GetAssembler()->ShifterOperandCanHold(ORR, value)) {
5683 __ Orr(out, first, value);
5684 } else {
5685 DCHECK(GetAssembler()->ShifterOperandCanHold(ORN, ~value));
5686 __ Orn(out, first, ~value);
5687 }
5688}
5689
5690// TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
5691void InstructionCodeGeneratorARMVIXL::GenerateEorConst(vixl32::Register out,
5692 vixl32::Register first,
5693 uint32_t value) {
5694 // Optimize special case for individual halfs of `xor-long` (`xor` is simplified earlier).
5695 if (value == 0u) {
5696 if (!out.Is(first)) {
5697 __ Mov(out, first);
5698 }
5699 return;
5700 }
5701 __ Eor(out, first, value);
5702}
5703
5704void InstructionCodeGeneratorARMVIXL::HandleBitwiseOperation(HBinaryOperation* instruction) {
5705 LocationSummary* locations = instruction->GetLocations();
5706 Location first = locations->InAt(0);
5707 Location second = locations->InAt(1);
5708 Location out = locations->Out();
5709
5710 if (second.IsConstant()) {
5711 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
5712 uint32_t value_low = Low32Bits(value);
5713 if (instruction->GetResultType() == Primitive::kPrimInt) {
5714 vixl32::Register first_reg = InputRegisterAt(instruction, 0);
5715 vixl32::Register out_reg = OutputRegister(instruction);
5716 if (instruction->IsAnd()) {
5717 GenerateAndConst(out_reg, first_reg, value_low);
5718 } else if (instruction->IsOr()) {
5719 GenerateOrrConst(out_reg, first_reg, value_low);
5720 } else {
5721 DCHECK(instruction->IsXor());
5722 GenerateEorConst(out_reg, first_reg, value_low);
5723 }
5724 } else {
5725 DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong);
5726 uint32_t value_high = High32Bits(value);
5727 vixl32::Register first_low = LowRegisterFrom(first);
5728 vixl32::Register first_high = HighRegisterFrom(first);
5729 vixl32::Register out_low = LowRegisterFrom(out);
5730 vixl32::Register out_high = HighRegisterFrom(out);
5731 if (instruction->IsAnd()) {
5732 GenerateAndConst(out_low, first_low, value_low);
5733 GenerateAndConst(out_high, first_high, value_high);
5734 } else if (instruction->IsOr()) {
5735 GenerateOrrConst(out_low, first_low, value_low);
5736 GenerateOrrConst(out_high, first_high, value_high);
5737 } else {
5738 DCHECK(instruction->IsXor());
5739 GenerateEorConst(out_low, first_low, value_low);
5740 GenerateEorConst(out_high, first_high, value_high);
5741 }
5742 }
5743 return;
5744 }
5745
5746 if (instruction->GetResultType() == Primitive::kPrimInt) {
5747 vixl32::Register first_reg = InputRegisterAt(instruction, 0);
5748 vixl32::Register second_reg = InputRegisterAt(instruction, 1);
5749 vixl32::Register out_reg = OutputRegister(instruction);
5750 if (instruction->IsAnd()) {
5751 __ And(out_reg, first_reg, second_reg);
5752 } else if (instruction->IsOr()) {
5753 __ Orr(out_reg, first_reg, second_reg);
5754 } else {
5755 DCHECK(instruction->IsXor());
5756 __ Eor(out_reg, first_reg, second_reg);
5757 }
5758 } else {
5759 DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong);
5760 vixl32::Register first_low = LowRegisterFrom(first);
5761 vixl32::Register first_high = HighRegisterFrom(first);
5762 vixl32::Register second_low = LowRegisterFrom(second);
5763 vixl32::Register second_high = HighRegisterFrom(second);
5764 vixl32::Register out_low = LowRegisterFrom(out);
5765 vixl32::Register out_high = HighRegisterFrom(out);
5766 if (instruction->IsAnd()) {
5767 __ And(out_low, first_low, second_low);
5768 __ And(out_high, first_high, second_high);
5769 } else if (instruction->IsOr()) {
5770 __ Orr(out_low, first_low, second_low);
5771 __ Orr(out_high, first_high, second_high);
5772 } else {
5773 DCHECK(instruction->IsXor());
5774 __ Eor(out_low, first_low, second_low);
5775 __ Eor(out_high, first_high, second_high);
5776 }
5777 }
5778}
5779
Artem Serovcfbe9132016-10-14 15:58:56 +01005780void InstructionCodeGeneratorARMVIXL::GenerateReferenceLoadOneRegister(
5781 HInstruction* instruction ATTRIBUTE_UNUSED,
5782 Location out,
5783 uint32_t offset,
5784 Location maybe_temp ATTRIBUTE_UNUSED) {
5785 vixl32::Register out_reg = RegisterFrom(out);
5786 if (kEmitCompilerReadBarrier) {
5787 TODO_VIXL32(FATAL);
5788 } else {
5789 // Plain load with no read barrier.
5790 // /* HeapReference<Object> */ out = *(out + offset)
5791 GetAssembler()->LoadFromOffset(kLoadWord, out_reg, out_reg, offset);
5792 GetAssembler()->MaybeUnpoisonHeapReference(out_reg);
5793 }
5794}
5795
Anton Kirilove28d9ae2016-10-25 18:17:23 +01005796void InstructionCodeGeneratorARMVIXL::GenerateReferenceLoadTwoRegisters(
5797 HInstruction* instruction ATTRIBUTE_UNUSED,
5798 Location out,
5799 Location obj,
5800 uint32_t offset,
5801 Location maybe_temp ATTRIBUTE_UNUSED) {
5802 vixl32::Register out_reg = RegisterFrom(out);
5803 vixl32::Register obj_reg = RegisterFrom(obj);
5804 if (kEmitCompilerReadBarrier) {
5805 TODO_VIXL32(FATAL);
5806 } else {
5807 // Plain load with no read barrier.
5808 // /* HeapReference<Object> */ out = *(obj + offset)
5809 GetAssembler()->LoadFromOffset(kLoadWord, out_reg, obj_reg, offset);
5810 GetAssembler()->MaybeUnpoisonHeapReference(out_reg);
5811 }
5812}
5813
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005814void InstructionCodeGeneratorARMVIXL::GenerateGcRootFieldLoad(
5815 HInstruction* instruction ATTRIBUTE_UNUSED,
5816 Location root,
5817 vixl32::Register obj,
5818 uint32_t offset,
5819 bool requires_read_barrier) {
5820 vixl32::Register root_reg = RegisterFrom(root);
5821 if (requires_read_barrier) {
5822 TODO_VIXL32(FATAL);
5823 } else {
5824 // Plain GC root load with no read barrier.
5825 // /* GcRoot<mirror::Object> */ root = *(obj + offset)
5826 GetAssembler()->LoadFromOffset(kLoadWord, root_reg, obj, offset);
5827 // Note that GC roots are not affected by heap poisoning, thus we
5828 // do not have to unpoison `root_reg` here.
5829 }
5830}
5831
Roland Levillain6070e882016-11-03 17:51:58 +00005832void CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier(
5833 HInstruction* instruction ATTRIBUTE_UNUSED,
5834 Location ref ATTRIBUTE_UNUSED,
5835 vixl::aarch32::Register obj ATTRIBUTE_UNUSED,
5836 uint32_t offset ATTRIBUTE_UNUSED,
5837 Location temp ATTRIBUTE_UNUSED,
5838 bool needs_null_check ATTRIBUTE_UNUSED) {
5839 TODO_VIXL32(FATAL);
5840}
5841
5842void CodeGeneratorARMVIXL::GenerateReferenceLoadWithBakerReadBarrier(
5843 HInstruction* instruction ATTRIBUTE_UNUSED,
5844 Location ref ATTRIBUTE_UNUSED,
5845 vixl::aarch32::Register obj ATTRIBUTE_UNUSED,
5846 uint32_t offset ATTRIBUTE_UNUSED,
5847 Location index ATTRIBUTE_UNUSED,
5848 ScaleFactor scale_factor ATTRIBUTE_UNUSED,
5849 Location temp ATTRIBUTE_UNUSED,
5850 bool needs_null_check ATTRIBUTE_UNUSED,
5851 bool always_update_field ATTRIBUTE_UNUSED,
5852 vixl::aarch32::Register* temp2 ATTRIBUTE_UNUSED) {
5853 TODO_VIXL32(FATAL);
5854}
5855
Roland Levillain844e6532016-11-03 16:09:47 +00005856void CodeGeneratorARMVIXL::GenerateReadBarrierSlow(HInstruction* instruction ATTRIBUTE_UNUSED,
5857 Location out ATTRIBUTE_UNUSED,
5858 Location ref ATTRIBUTE_UNUSED,
5859 Location obj ATTRIBUTE_UNUSED,
5860 uint32_t offset ATTRIBUTE_UNUSED,
5861 Location index ATTRIBUTE_UNUSED) {
5862 TODO_VIXL32(FATAL);
5863}
5864
Artem Serov02d37832016-10-25 15:25:33 +01005865void CodeGeneratorARMVIXL::MaybeGenerateReadBarrierSlow(HInstruction* instruction ATTRIBUTE_UNUSED,
5866 Location out,
5867 Location ref ATTRIBUTE_UNUSED,
5868 Location obj ATTRIBUTE_UNUSED,
5869 uint32_t offset ATTRIBUTE_UNUSED,
5870 Location index ATTRIBUTE_UNUSED) {
5871 if (kEmitCompilerReadBarrier) {
5872 DCHECK(!kUseBakerReadBarrier);
5873 TODO_VIXL32(FATAL);
5874 } else if (kPoisonHeapReferences) {
5875 GetAssembler()->UnpoisonHeapReference(RegisterFrom(out));
5876 }
5877}
5878
5879// Check if the desired_dispatch_info is supported. If it is, return it,
5880// otherwise return a fall-back info that should be used instead.
5881HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARMVIXL::GetSupportedInvokeStaticOrDirectDispatch(
5882 const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info ATTRIBUTE_UNUSED,
5883 HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
5884 // TODO(VIXL): Implement optimized code paths.
5885 return {
5886 HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod,
5887 HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
5888 0u,
5889 0u
5890 };
5891}
5892
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005893vixl32::Register CodeGeneratorARMVIXL::GetInvokeStaticOrDirectExtraParameter(
5894 HInvokeStaticOrDirect* invoke, vixl32::Register temp) {
5895 DCHECK_EQ(invoke->InputCount(), invoke->GetNumberOfArguments() + 1u);
5896 Location location = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
5897 if (!invoke->GetLocations()->Intrinsified()) {
5898 return RegisterFrom(location);
5899 }
5900 // For intrinsics we allow any location, so it may be on the stack.
5901 if (!location.IsRegister()) {
5902 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, location.GetStackIndex());
5903 return temp;
5904 }
5905 // For register locations, check if the register was saved. If so, get it from the stack.
5906 // Note: There is a chance that the register was saved but not overwritten, so we could
5907 // save one load. However, since this is just an intrinsic slow path we prefer this
5908 // simple and more robust approach rather that trying to determine if that's the case.
5909 SlowPathCode* slow_path = GetCurrentSlowPath();
5910 DCHECK(slow_path != nullptr); // For intrinsified invokes the call is emitted on the slow path.
5911 if (slow_path->IsCoreRegisterSaved(RegisterFrom(location).GetCode())) {
5912 int stack_offset = slow_path->GetStackOffsetOfCoreRegister(RegisterFrom(location).GetCode());
5913 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, stack_offset);
5914 return temp;
5915 }
5916 return RegisterFrom(location);
5917}
5918
5919void CodeGeneratorARMVIXL::GenerateStaticOrDirectCall(
5920 HInvokeStaticOrDirect* invoke, Location temp) {
5921 Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp.
5922 vixl32::Register temp_reg = RegisterFrom(temp);
5923
5924 switch (invoke->GetMethodLoadKind()) {
5925 case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: {
5926 uint32_t offset =
5927 GetThreadOffset<kArmPointerSize>(invoke->GetStringInitEntryPoint()).Int32Value();
5928 // temp = thread->string_init_entrypoint
5929 GetAssembler()->LoadFromOffset(kLoadWord, temp_reg, tr, offset);
5930 break;
5931 }
5932 case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: {
5933 Location current_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
5934 vixl32::Register method_reg;
5935 if (current_method.IsRegister()) {
5936 method_reg = RegisterFrom(current_method);
5937 } else {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01005938 DCHECK(invoke->GetLocations()->Intrinsified());
5939 DCHECK(!current_method.IsValid());
5940 method_reg = temp_reg;
5941 GetAssembler()->LoadFromOffset(kLoadWord, temp_reg, sp, kCurrentMethodStackOffset);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005942 }
5943 // /* ArtMethod*[] */ temp = temp.ptr_sized_fields_->dex_cache_resolved_methods_;
5944 GetAssembler()->LoadFromOffset(
5945 kLoadWord,
5946 temp_reg,
5947 method_reg,
5948 ArtMethod::DexCacheResolvedMethodsOffset(kArmPointerSize).Int32Value());
5949 // temp = temp[index_in_cache];
5950 // Note: Don't use invoke->GetTargetMethod() as it may point to a different dex file.
5951 uint32_t index_in_cache = invoke->GetDexMethodIndex();
5952 GetAssembler()->LoadFromOffset(
5953 kLoadWord, temp_reg, temp_reg, CodeGenerator::GetCachePointerOffset(index_in_cache));
5954 break;
5955 }
5956 default:
5957 TODO_VIXL32(FATAL);
5958 }
5959
5960 // TODO(VIXL): Support `CodePtrLocation` values other than `kCallArtMethod`.
5961 if (invoke->GetCodePtrLocation() != HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod) {
5962 TODO_VIXL32(FATAL);
5963 }
5964
5965 // LR = callee_method->entry_point_from_quick_compiled_code_
5966 GetAssembler()->LoadFromOffset(
5967 kLoadWord,
5968 lr,
5969 RegisterFrom(callee_method),
5970 ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value());
5971 // LR()
5972 __ Blx(lr);
5973
5974 DCHECK(!IsLeafMethod());
5975}
5976
5977void CodeGeneratorARMVIXL::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp_location) {
5978 vixl32::Register temp = RegisterFrom(temp_location);
5979 uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
5980 invoke->GetVTableIndex(), kArmPointerSize).Uint32Value();
5981
5982 // Use the calling convention instead of the location of the receiver, as
5983 // intrinsics may have put the receiver in a different register. In the intrinsics
5984 // slow path, the arguments have been moved to the right place, so here we are
5985 // guaranteed that the receiver is the first register of the calling convention.
5986 InvokeDexCallingConventionARMVIXL calling_convention;
5987 vixl32::Register receiver = calling_convention.GetRegisterAt(0);
5988 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
5989 // /* HeapReference<Class> */ temp = receiver->klass_
5990 GetAssembler()->LoadFromOffset(kLoadWord, temp, receiver, class_offset);
5991 MaybeRecordImplicitNullCheck(invoke);
5992 // Instead of simply (possibly) unpoisoning `temp` here, we should
5993 // emit a read barrier for the previous class reference load.
5994 // However this is not required in practice, as this is an
5995 // intermediate/temporary reference and because the current
5996 // concurrent copying collector keeps the from-space memory
5997 // intact/accessible until the end of the marking phase (the
5998 // concurrent copying collector may not in the future).
5999 GetAssembler()->MaybeUnpoisonHeapReference(temp);
6000
6001 // temp = temp->GetMethodAt(method_offset);
6002 uint32_t entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(
6003 kArmPointerSize).Int32Value();
6004 GetAssembler()->LoadFromOffset(kLoadWord, temp, temp, method_offset);
6005 // LR = temp->GetEntryPoint();
6006 GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, entry_point);
6007 // LR();
6008 __ Blx(lr);
6009}
6010
Artem Serov2bbc9532016-10-21 11:51:50 +01006011void LocationsBuilderARMVIXL::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
6012 LocationSummary* locations =
6013 new (GetGraph()->GetArena()) LocationSummary(instr, LocationSummary::kNoCall);
6014 locations->SetInAt(HMultiplyAccumulate::kInputAccumulatorIndex,
6015 Location::RequiresRegister());
6016 locations->SetInAt(HMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister());
6017 locations->SetInAt(HMultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister());
6018 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
6019}
6020
6021void InstructionCodeGeneratorARMVIXL::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
6022 vixl32::Register res = OutputRegister(instr);
6023 vixl32::Register accumulator =
6024 InputRegisterAt(instr, HMultiplyAccumulate::kInputAccumulatorIndex);
6025 vixl32::Register mul_left =
6026 InputRegisterAt(instr, HMultiplyAccumulate::kInputMulLeftIndex);
6027 vixl32::Register mul_right =
6028 InputRegisterAt(instr, HMultiplyAccumulate::kInputMulRightIndex);
6029
6030 if (instr->GetOpKind() == HInstruction::kAdd) {
6031 __ Mla(res, mul_left, mul_right, accumulator);
6032 } else {
6033 __ Mls(res, mul_left, mul_right, accumulator);
6034 }
6035}
6036
Artem Serov551b28f2016-10-18 19:11:30 +01006037void LocationsBuilderARMVIXL::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
6038 // Nothing to do, this should be removed during prepare for register allocator.
6039 LOG(FATAL) << "Unreachable";
6040}
6041
6042void InstructionCodeGeneratorARMVIXL::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
6043 // Nothing to do, this should be removed during prepare for register allocator.
6044 LOG(FATAL) << "Unreachable";
6045}
6046
6047// Simple implementation of packed switch - generate cascaded compare/jumps.
6048void LocationsBuilderARMVIXL::VisitPackedSwitch(HPackedSwitch* switch_instr) {
6049 LocationSummary* locations =
6050 new (GetGraph()->GetArena()) LocationSummary(switch_instr, LocationSummary::kNoCall);
6051 locations->SetInAt(0, Location::RequiresRegister());
6052 if (switch_instr->GetNumEntries() > kPackedSwitchCompareJumpThreshold &&
6053 codegen_->GetAssembler()->GetVIXLAssembler()->IsUsingT32()) {
6054 locations->AddTemp(Location::RequiresRegister()); // We need a temp for the table base.
6055 if (switch_instr->GetStartValue() != 0) {
6056 locations->AddTemp(Location::RequiresRegister()); // We need a temp for the bias.
6057 }
6058 }
6059}
6060
6061// TODO(VIXL): Investigate and reach the parity with old arm codegen.
6062void InstructionCodeGeneratorARMVIXL::VisitPackedSwitch(HPackedSwitch* switch_instr) {
6063 int32_t lower_bound = switch_instr->GetStartValue();
6064 uint32_t num_entries = switch_instr->GetNumEntries();
6065 LocationSummary* locations = switch_instr->GetLocations();
6066 vixl32::Register value_reg = InputRegisterAt(switch_instr, 0);
6067 HBasicBlock* default_block = switch_instr->GetDefaultBlock();
6068
6069 if (num_entries <= kPackedSwitchCompareJumpThreshold ||
6070 !codegen_->GetAssembler()->GetVIXLAssembler()->IsUsingT32()) {
6071 // Create a series of compare/jumps.
6072 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
6073 vixl32::Register temp_reg = temps.Acquire();
6074 // Note: It is fine for the below AddConstantSetFlags() using IP register to temporarily store
6075 // the immediate, because IP is used as the destination register. For the other
6076 // AddConstantSetFlags() and GenerateCompareWithImmediate(), the immediate values are constant,
6077 // and they can be encoded in the instruction without making use of IP register.
6078 __ Adds(temp_reg, value_reg, -lower_bound);
6079
6080 const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors();
6081 // Jump to successors[0] if value == lower_bound.
6082 __ B(eq, codegen_->GetLabelOf(successors[0]));
6083 int32_t last_index = 0;
6084 for (; num_entries - last_index > 2; last_index += 2) {
6085 __ Adds(temp_reg, temp_reg, -2);
6086 // Jump to successors[last_index + 1] if value < case_value[last_index + 2].
6087 __ B(lo, codegen_->GetLabelOf(successors[last_index + 1]));
6088 // Jump to successors[last_index + 2] if value == case_value[last_index + 2].
6089 __ B(eq, codegen_->GetLabelOf(successors[last_index + 2]));
6090 }
6091 if (num_entries - last_index == 2) {
6092 // The last missing case_value.
6093 __ Cmp(temp_reg, 1);
6094 __ B(eq, codegen_->GetLabelOf(successors[last_index + 1]));
6095 }
6096
6097 // And the default for any other value.
6098 if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) {
6099 __ B(codegen_->GetLabelOf(default_block));
6100 }
6101 } else {
6102 // Create a table lookup.
6103 vixl32::Register table_base = RegisterFrom(locations->GetTemp(0));
6104
6105 JumpTableARMVIXL* jump_table = codegen_->CreateJumpTable(switch_instr);
6106
6107 // Remove the bias.
6108 vixl32::Register key_reg;
6109 if (lower_bound != 0) {
6110 key_reg = RegisterFrom(locations->GetTemp(1));
6111 __ Sub(key_reg, value_reg, lower_bound);
6112 } else {
6113 key_reg = value_reg;
6114 }
6115
6116 // Check whether the value is in the table, jump to default block if not.
6117 __ Cmp(key_reg, num_entries - 1);
6118 __ B(hi, codegen_->GetLabelOf(default_block));
6119
6120 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
6121 vixl32::Register jump_offset = temps.Acquire();
6122
6123 // Load jump offset from the table.
6124 __ Adr(table_base, jump_table->GetTableStartLabel());
6125 __ Ldr(jump_offset, MemOperand(table_base, key_reg, vixl32::LSL, 2));
6126
6127 // Jump to target block by branching to table_base(pc related) + offset.
6128 vixl32::Register target_address = table_base;
6129 __ Add(target_address, table_base, jump_offset);
6130 __ Bx(target_address);
Artem Serov09a940d2016-11-11 16:15:11 +00006131
6132 jump_table->EmitTable(codegen_);
Artem Serov551b28f2016-10-18 19:11:30 +01006133 }
6134}
6135
Artem Serov02d37832016-10-25 15:25:33 +01006136// Copy the result of a call into the given target.
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006137void CodeGeneratorARMVIXL::MoveFromReturnRegister(Location trg, Primitive::Type type) {
6138 if (!trg.IsValid()) {
6139 DCHECK_EQ(type, Primitive::kPrimVoid);
6140 return;
6141 }
6142
6143 DCHECK_NE(type, Primitive::kPrimVoid);
6144
6145 Location return_loc = InvokeDexCallingConventionVisitorARM().GetReturnLocation(type);
6146 if (return_loc.Equals(trg)) {
6147 return;
6148 }
6149
6150 // TODO: Consider pairs in the parallel move resolver, then this could be nicely merged
6151 // with the last branch.
6152 if (type == Primitive::kPrimLong) {
6153 TODO_VIXL32(FATAL);
6154 } else if (type == Primitive::kPrimDouble) {
6155 TODO_VIXL32(FATAL);
6156 } else {
6157 // Let the parallel move resolver take care of all of this.
6158 HParallelMove parallel_move(GetGraph()->GetArena());
6159 parallel_move.AddMove(return_loc, trg, type, nullptr);
6160 GetMoveResolver()->EmitNativeCode(&parallel_move);
6161 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006162}
Scott Wakelingfe885462016-09-22 10:24:38 +01006163
Artem Serov551b28f2016-10-18 19:11:30 +01006164void LocationsBuilderARMVIXL::VisitClassTableGet(
6165 HClassTableGet* instruction ATTRIBUTE_UNUSED) {
6166 TODO_VIXL32(FATAL);
6167}
6168
6169void InstructionCodeGeneratorARMVIXL::VisitClassTableGet(
6170 HClassTableGet* instruction ATTRIBUTE_UNUSED) {
6171 TODO_VIXL32(FATAL);
6172}
6173
6174
Scott Wakelingfe885462016-09-22 10:24:38 +01006175#undef __
6176#undef QUICK_ENTRY_POINT
6177#undef TODO_VIXL32
6178
6179} // namespace arm
6180} // namespace art