blob: 2c6df38daa47187594493c7658c99943a6c76918 [file] [log] [blame]
Scott Wakelingfe885462016-09-22 10:24:38 +01001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "code_generator_arm_vixl.h"
18
19#include "arch/arm/instruction_set_features_arm.h"
20#include "art_method.h"
21#include "code_generator_utils.h"
22#include "common_arm.h"
23#include "compiled_method.h"
24#include "entrypoints/quick/quick_entrypoints.h"
25#include "gc/accounting/card_table.h"
Anton Kirilov5ec62182016-10-13 20:16:02 +010026#include "intrinsics_arm_vixl.h"
Scott Wakelingfe885462016-09-22 10:24:38 +010027#include "mirror/array-inl.h"
28#include "mirror/class-inl.h"
29#include "thread.h"
30#include "utils/arm/assembler_arm_vixl.h"
31#include "utils/arm/managed_register_arm.h"
32#include "utils/assembler.h"
33#include "utils/stack_checks.h"
34
35namespace art {
36namespace arm {
37
38namespace vixl32 = vixl::aarch32;
39using namespace vixl32; // NOLINT(build/namespaces)
40
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +010041using helpers::DRegisterFrom;
Scott Wakelingfe885462016-09-22 10:24:38 +010042using helpers::DWARFReg;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010043using helpers::HighDRegisterFrom;
44using helpers::HighRegisterFrom;
Scott Wakelingfe885462016-09-22 10:24:38 +010045using helpers::InputOperandAt;
Scott Wakelingc34dba72016-10-03 10:14:44 +010046using helpers::InputRegister;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010047using helpers::InputRegisterAt;
Scott Wakelingfe885462016-09-22 10:24:38 +010048using helpers::InputSRegisterAt;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010049using helpers::InputVRegisterAt;
Scott Wakelingb77051e2016-11-21 19:46:00 +000050using helpers::Int32ConstantFrom;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010051using helpers::LocationFrom;
52using helpers::LowRegisterFrom;
53using helpers::LowSRegisterFrom;
54using helpers::OutputRegister;
55using helpers::OutputSRegister;
56using helpers::OutputVRegister;
57using helpers::RegisterFrom;
58using helpers::SRegisterFrom;
Scott Wakelingfe885462016-09-22 10:24:38 +010059
60using RegisterList = vixl32::RegisterList;
61
62static bool ExpectedPairLayout(Location location) {
63 // We expected this for both core and fpu register pairs.
64 return ((location.low() & 1) == 0) && (location.low() + 1 == location.high());
65}
Artem Serovd4cc5b22016-11-04 11:19:09 +000066// Use a local definition to prevent copying mistakes.
67static constexpr size_t kArmWordSize = static_cast<size_t>(kArmPointerSize);
68static constexpr size_t kArmBitsPerWord = kArmWordSize * kBitsPerByte;
Anton Kirilove28d9ae2016-10-25 18:17:23 +010069static constexpr int kCurrentMethodStackOffset = 0;
Artem Serov551b28f2016-10-18 19:11:30 +010070static constexpr uint32_t kPackedSwitchCompareJumpThreshold = 7;
Scott Wakelingfe885462016-09-22 10:24:38 +010071
72#ifdef __
73#error "ARM Codegen VIXL macro-assembler macro already defined."
74#endif
75
Scott Wakelingfe885462016-09-22 10:24:38 +010076// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
77#define __ down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler()-> // NOLINT
78#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArmPointerSize, x).Int32Value()
79
80// Marker that code is yet to be, and must, be implemented.
81#define TODO_VIXL32(level) LOG(level) << __PRETTY_FUNCTION__ << " unimplemented "
82
Scott Wakelinga7812ae2016-10-17 10:03:36 +010083// SaveLiveRegisters and RestoreLiveRegisters from SlowPathCodeARM operate on sets of S registers,
84// for each live D registers they treat two corresponding S registers as live ones.
85//
86// Two following functions (SaveContiguousSRegisterList, RestoreContiguousSRegisterList) build
87// from a list of contiguous S registers a list of contiguous D registers (processing first/last
88// S registers corner cases) and save/restore this new list treating them as D registers.
89// - decreasing code size
90// - avoiding hazards on Cortex-A57, when a pair of S registers for an actual live D register is
91// restored and then used in regular non SlowPath code as D register.
92//
93// For the following example (v means the S register is live):
94// D names: | D0 | D1 | D2 | D4 | ...
95// S names: | S0 | S1 | S2 | S3 | S4 | S5 | S6 | S7 | ...
96// Live? | | v | v | v | v | v | v | | ...
97//
98// S1 and S6 will be saved/restored independently; D registers list (D1, D2) will be processed
99// as D registers.
100//
101// TODO(VIXL): All this code should be unnecessary once the VIXL AArch32 backend provides helpers
102// for lists of floating-point registers.
103static size_t SaveContiguousSRegisterList(size_t first,
104 size_t last,
105 CodeGenerator* codegen,
106 size_t stack_offset) {
107 static_assert(kSRegSizeInBytes == kArmWordSize, "Broken assumption on reg/word sizes.");
108 static_assert(kDRegSizeInBytes == 2 * kArmWordSize, "Broken assumption on reg/word sizes.");
109 DCHECK_LE(first, last);
110 if ((first == last) && (first == 0)) {
111 __ Vstr(vixl32::SRegister(first), MemOperand(sp, stack_offset));
112 return stack_offset + kSRegSizeInBytes;
113 }
114 if (first % 2 == 1) {
115 __ Vstr(vixl32::SRegister(first++), MemOperand(sp, stack_offset));
116 stack_offset += kSRegSizeInBytes;
117 }
118
119 bool save_last = false;
120 if (last % 2 == 0) {
121 save_last = true;
122 --last;
123 }
124
125 if (first < last) {
126 vixl32::DRegister d_reg = vixl32::DRegister(first / 2);
127 DCHECK_EQ((last - first + 1) % 2, 0u);
128 size_t number_of_d_regs = (last - first + 1) / 2;
129
130 if (number_of_d_regs == 1) {
131 __ Vstr(d_reg, MemOperand(sp, stack_offset));
132 } else if (number_of_d_regs > 1) {
133 UseScratchRegisterScope temps(down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler());
134 vixl32::Register base = sp;
135 if (stack_offset != 0) {
136 base = temps.Acquire();
Scott Wakelingb77051e2016-11-21 19:46:00 +0000137 __ Add(base, sp, Operand::From(stack_offset));
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100138 }
139 __ Vstm(F64, base, NO_WRITE_BACK, DRegisterList(d_reg, number_of_d_regs));
140 }
141 stack_offset += number_of_d_regs * kDRegSizeInBytes;
142 }
143
144 if (save_last) {
145 __ Vstr(vixl32::SRegister(last + 1), MemOperand(sp, stack_offset));
146 stack_offset += kSRegSizeInBytes;
147 }
148
149 return stack_offset;
150}
151
152static size_t RestoreContiguousSRegisterList(size_t first,
153 size_t last,
154 CodeGenerator* codegen,
155 size_t stack_offset) {
156 static_assert(kSRegSizeInBytes == kArmWordSize, "Broken assumption on reg/word sizes.");
157 static_assert(kDRegSizeInBytes == 2 * kArmWordSize, "Broken assumption on reg/word sizes.");
158 DCHECK_LE(first, last);
159 if ((first == last) && (first == 0)) {
160 __ Vldr(vixl32::SRegister(first), MemOperand(sp, stack_offset));
161 return stack_offset + kSRegSizeInBytes;
162 }
163 if (first % 2 == 1) {
164 __ Vldr(vixl32::SRegister(first++), MemOperand(sp, stack_offset));
165 stack_offset += kSRegSizeInBytes;
166 }
167
168 bool restore_last = false;
169 if (last % 2 == 0) {
170 restore_last = true;
171 --last;
172 }
173
174 if (first < last) {
175 vixl32::DRegister d_reg = vixl32::DRegister(first / 2);
176 DCHECK_EQ((last - first + 1) % 2, 0u);
177 size_t number_of_d_regs = (last - first + 1) / 2;
178 if (number_of_d_regs == 1) {
179 __ Vldr(d_reg, MemOperand(sp, stack_offset));
180 } else if (number_of_d_regs > 1) {
181 UseScratchRegisterScope temps(down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler());
182 vixl32::Register base = sp;
183 if (stack_offset != 0) {
184 base = temps.Acquire();
Scott Wakelingb77051e2016-11-21 19:46:00 +0000185 __ Add(base, sp, Operand::From(stack_offset));
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100186 }
187 __ Vldm(F64, base, NO_WRITE_BACK, DRegisterList(d_reg, number_of_d_regs));
188 }
189 stack_offset += number_of_d_regs * kDRegSizeInBytes;
190 }
191
192 if (restore_last) {
193 __ Vldr(vixl32::SRegister(last + 1), MemOperand(sp, stack_offset));
194 stack_offset += kSRegSizeInBytes;
195 }
196
197 return stack_offset;
198}
199
200void SlowPathCodeARMVIXL::SaveLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
201 size_t stack_offset = codegen->GetFirstRegisterSlotInSlowPath();
202 size_t orig_offset = stack_offset;
203
204 const uint32_t core_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ true);
205 for (uint32_t i : LowToHighBits(core_spills)) {
206 // If the register holds an object, update the stack mask.
207 if (locations->RegisterContainsObject(i)) {
208 locations->SetStackBit(stack_offset / kVRegSize);
209 }
210 DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
211 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
212 saved_core_stack_offsets_[i] = stack_offset;
213 stack_offset += kArmWordSize;
214 }
215
216 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
217 arm_codegen->GetAssembler()->StoreRegisterList(core_spills, orig_offset);
218
219 uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ false);
220 orig_offset = stack_offset;
221 for (uint32_t i : LowToHighBits(fp_spills)) {
222 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
223 saved_fpu_stack_offsets_[i] = stack_offset;
224 stack_offset += kArmWordSize;
225 }
226
227 stack_offset = orig_offset;
228 while (fp_spills != 0u) {
229 uint32_t begin = CTZ(fp_spills);
230 uint32_t tmp = fp_spills + (1u << begin);
231 fp_spills &= tmp; // Clear the contiguous range of 1s.
232 uint32_t end = (tmp == 0u) ? 32u : CTZ(tmp); // CTZ(0) is undefined.
233 stack_offset = SaveContiguousSRegisterList(begin, end - 1, codegen, stack_offset);
234 }
235 DCHECK_LE(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
236}
237
238void SlowPathCodeARMVIXL::RestoreLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
239 size_t stack_offset = codegen->GetFirstRegisterSlotInSlowPath();
240 size_t orig_offset = stack_offset;
241
242 const uint32_t core_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ true);
243 for (uint32_t i : LowToHighBits(core_spills)) {
244 DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
245 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
246 stack_offset += kArmWordSize;
247 }
248
249 // TODO(VIXL): Check the coherency of stack_offset after this with a test.
250 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
251 arm_codegen->GetAssembler()->LoadRegisterList(core_spills, orig_offset);
252
253 uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ false);
254 while (fp_spills != 0u) {
255 uint32_t begin = CTZ(fp_spills);
256 uint32_t tmp = fp_spills + (1u << begin);
257 fp_spills &= tmp; // Clear the contiguous range of 1s.
258 uint32_t end = (tmp == 0u) ? 32u : CTZ(tmp); // CTZ(0) is undefined.
259 stack_offset = RestoreContiguousSRegisterList(begin, end - 1, codegen, stack_offset);
260 }
261 DCHECK_LE(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
262}
263
264class NullCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
265 public:
266 explicit NullCheckSlowPathARMVIXL(HNullCheck* instruction) : SlowPathCodeARMVIXL(instruction) {}
267
268 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
269 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
270 __ Bind(GetEntryLabel());
271 if (instruction_->CanThrowIntoCatchBlock()) {
272 // Live registers will be restored in the catch block if caught.
273 SaveLiveRegisters(codegen, instruction_->GetLocations());
274 }
275 arm_codegen->InvokeRuntime(kQuickThrowNullPointer,
276 instruction_,
277 instruction_->GetDexPc(),
278 this);
279 CheckEntrypointTypes<kQuickThrowNullPointer, void, void>();
280 }
281
282 bool IsFatal() const OVERRIDE { return true; }
283
284 const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathARMVIXL"; }
285
286 private:
287 DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARMVIXL);
288};
289
Scott Wakelingfe885462016-09-22 10:24:38 +0100290class DivZeroCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
291 public:
292 explicit DivZeroCheckSlowPathARMVIXL(HDivZeroCheck* instruction)
293 : SlowPathCodeARMVIXL(instruction) {}
294
295 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100296 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
Scott Wakelingfe885462016-09-22 10:24:38 +0100297 __ Bind(GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100298 arm_codegen->InvokeRuntime(kQuickThrowDivZero, instruction_, instruction_->GetDexPc(), this);
Scott Wakelingfe885462016-09-22 10:24:38 +0100299 CheckEntrypointTypes<kQuickThrowDivZero, void, void>();
300 }
301
302 bool IsFatal() const OVERRIDE { return true; }
303
304 const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathARMVIXL"; }
305
306 private:
307 DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathARMVIXL);
308};
309
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100310class SuspendCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
311 public:
312 SuspendCheckSlowPathARMVIXL(HSuspendCheck* instruction, HBasicBlock* successor)
313 : SlowPathCodeARMVIXL(instruction), successor_(successor) {}
314
315 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
316 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
317 __ Bind(GetEntryLabel());
318 arm_codegen->InvokeRuntime(kQuickTestSuspend, instruction_, instruction_->GetDexPc(), this);
319 CheckEntrypointTypes<kQuickTestSuspend, void, void>();
320 if (successor_ == nullptr) {
321 __ B(GetReturnLabel());
322 } else {
323 __ B(arm_codegen->GetLabelOf(successor_));
324 }
325 }
326
327 vixl32::Label* GetReturnLabel() {
328 DCHECK(successor_ == nullptr);
329 return &return_label_;
330 }
331
332 HBasicBlock* GetSuccessor() const {
333 return successor_;
334 }
335
336 const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathARMVIXL"; }
337
338 private:
339 // If not null, the block to branch to after the suspend check.
340 HBasicBlock* const successor_;
341
342 // If `successor_` is null, the label to branch to after the suspend check.
343 vixl32::Label return_label_;
344
345 DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathARMVIXL);
346};
347
Scott Wakelingc34dba72016-10-03 10:14:44 +0100348class BoundsCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
349 public:
350 explicit BoundsCheckSlowPathARMVIXL(HBoundsCheck* instruction)
351 : SlowPathCodeARMVIXL(instruction) {}
352
353 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
354 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
355 LocationSummary* locations = instruction_->GetLocations();
356
357 __ Bind(GetEntryLabel());
358 if (instruction_->CanThrowIntoCatchBlock()) {
359 // Live registers will be restored in the catch block if caught.
360 SaveLiveRegisters(codegen, instruction_->GetLocations());
361 }
362 // We're moving two locations to locations that could overlap, so we need a parallel
363 // move resolver.
364 InvokeRuntimeCallingConventionARMVIXL calling_convention;
365 codegen->EmitParallelMoves(
366 locations->InAt(0),
367 LocationFrom(calling_convention.GetRegisterAt(0)),
368 Primitive::kPrimInt,
369 locations->InAt(1),
370 LocationFrom(calling_convention.GetRegisterAt(1)),
371 Primitive::kPrimInt);
372 QuickEntrypointEnum entrypoint = instruction_->AsBoundsCheck()->IsStringCharAt()
373 ? kQuickThrowStringBounds
374 : kQuickThrowArrayBounds;
375 arm_codegen->InvokeRuntime(entrypoint, instruction_, instruction_->GetDexPc(), this);
376 CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>();
377 CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>();
378 }
379
380 bool IsFatal() const OVERRIDE { return true; }
381
382 const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathARMVIXL"; }
383
384 private:
385 DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARMVIXL);
386};
387
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100388class LoadClassSlowPathARMVIXL : public SlowPathCodeARMVIXL {
389 public:
390 LoadClassSlowPathARMVIXL(HLoadClass* cls, HInstruction* at, uint32_t dex_pc, bool do_clinit)
391 : SlowPathCodeARMVIXL(at), cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) {
392 DCHECK(at->IsLoadClass() || at->IsClinitCheck());
393 }
394
395 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
396 LocationSummary* locations = at_->GetLocations();
397
398 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
399 __ Bind(GetEntryLabel());
400 SaveLiveRegisters(codegen, locations);
401
402 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Andreas Gampea5b09a62016-11-17 15:21:22 -0800403 __ Mov(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex().index_);
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100404 QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage
405 : kQuickInitializeType;
406 arm_codegen->InvokeRuntime(entrypoint, at_, dex_pc_, this);
407 if (do_clinit_) {
408 CheckEntrypointTypes<kQuickInitializeStaticStorage, void*, uint32_t>();
409 } else {
410 CheckEntrypointTypes<kQuickInitializeType, void*, uint32_t>();
411 }
412
413 // Move the class to the desired location.
414 Location out = locations->Out();
415 if (out.IsValid()) {
416 DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
417 arm_codegen->Move32(locations->Out(), LocationFrom(r0));
418 }
419 RestoreLiveRegisters(codegen, locations);
420 __ B(GetExitLabel());
421 }
422
423 const char* GetDescription() const OVERRIDE { return "LoadClassSlowPathARMVIXL"; }
424
425 private:
426 // The class this slow path will load.
427 HLoadClass* const cls_;
428
429 // The instruction where this slow path is happening.
430 // (Might be the load class or an initialization check).
431 HInstruction* const at_;
432
433 // The dex PC of `at_`.
434 const uint32_t dex_pc_;
435
436 // Whether to initialize the class.
437 const bool do_clinit_;
438
439 DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathARMVIXL);
440};
441
Artem Serovd4cc5b22016-11-04 11:19:09 +0000442class LoadStringSlowPathARMVIXL : public SlowPathCodeARMVIXL {
443 public:
444 explicit LoadStringSlowPathARMVIXL(HLoadString* instruction)
445 : SlowPathCodeARMVIXL(instruction) {}
446
447 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
448 LocationSummary* locations = instruction_->GetLocations();
449 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
450 HLoadString* load = instruction_->AsLoadString();
451 const uint32_t string_index = load->GetStringIndex().index_;
452 vixl32::Register out = OutputRegister(load);
453 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
454 constexpr bool call_saves_everything_except_r0 = (!kUseReadBarrier || kUseBakerReadBarrier);
455
456 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
457 __ Bind(GetEntryLabel());
458 SaveLiveRegisters(codegen, locations);
459
460 InvokeRuntimeCallingConventionARMVIXL calling_convention;
461 // In the unlucky case that the `temp` is R0, we preserve the address in `out` across
462 // the kSaveEverything call (or use `out` for the address after non-kSaveEverything call).
463 bool temp_is_r0 = (temp.Is(calling_convention.GetRegisterAt(0)));
464 vixl32::Register entry_address = temp_is_r0 ? out : temp;
465 DCHECK(!entry_address.Is(calling_convention.GetRegisterAt(0)));
466 if (call_saves_everything_except_r0 && temp_is_r0) {
467 __ Mov(entry_address, temp);
468 }
469
470 __ Mov(calling_convention.GetRegisterAt(0), string_index);
471 arm_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), this);
472 CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
473
474 // Store the resolved String to the .bss entry.
475 if (call_saves_everything_except_r0) {
476 // The string entry address was preserved in `entry_address` thanks to kSaveEverything.
477 __ Str(r0, MemOperand(entry_address));
478 } else {
479 // For non-Baker read barrier, we need to re-calculate the address of the string entry.
480 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
481 arm_codegen->NewPcRelativeStringPatch(load->GetDexFile(), string_index);
482 arm_codegen->EmitMovwMovtPlaceholder(labels, out);
483 __ Str(r0, MemOperand(entry_address));
484 }
485
486 arm_codegen->Move32(locations->Out(), LocationFrom(r0));
487 RestoreLiveRegisters(codegen, locations);
488
489 __ B(GetExitLabel());
490 }
491
492 const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathARMVIXL"; }
493
494 private:
495 DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathARMVIXL);
496};
497
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100498class TypeCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
499 public:
500 TypeCheckSlowPathARMVIXL(HInstruction* instruction, bool is_fatal)
501 : SlowPathCodeARMVIXL(instruction), is_fatal_(is_fatal) {}
502
503 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
504 LocationSummary* locations = instruction_->GetLocations();
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100505 DCHECK(instruction_->IsCheckCast()
506 || !locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
507
508 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
509 __ Bind(GetEntryLabel());
510
511 if (!is_fatal_) {
Artem Serovcfbe9132016-10-14 15:58:56 +0100512 SaveLiveRegisters(codegen, locations);
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100513 }
514
515 // We're moving two locations to locations that could overlap, so we need a parallel
516 // move resolver.
517 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100518
Mathieu Chartier9fd8c602016-11-14 14:38:53 -0800519 codegen->EmitParallelMoves(locations->InAt(0),
Mathieu Chartierb99f4d62016-11-07 16:17:26 -0800520 LocationFrom(calling_convention.GetRegisterAt(0)),
521 Primitive::kPrimNot,
Mathieu Chartier9fd8c602016-11-14 14:38:53 -0800522 locations->InAt(1),
Mathieu Chartierb99f4d62016-11-07 16:17:26 -0800523 LocationFrom(calling_convention.GetRegisterAt(1)),
524 Primitive::kPrimNot);
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100525 if (instruction_->IsInstanceOf()) {
Artem Serovcfbe9132016-10-14 15:58:56 +0100526 arm_codegen->InvokeRuntime(kQuickInstanceofNonTrivial,
527 instruction_,
528 instruction_->GetDexPc(),
529 this);
Mathieu Chartier9fd8c602016-11-14 14:38:53 -0800530 CheckEntrypointTypes<kQuickInstanceofNonTrivial, size_t, mirror::Object*, mirror::Class*>();
Artem Serovcfbe9132016-10-14 15:58:56 +0100531 arm_codegen->Move32(locations->Out(), LocationFrom(r0));
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100532 } else {
533 DCHECK(instruction_->IsCheckCast());
Mathieu Chartierb99f4d62016-11-07 16:17:26 -0800534 arm_codegen->InvokeRuntime(kQuickCheckInstanceOf,
535 instruction_,
536 instruction_->GetDexPc(),
537 this);
538 CheckEntrypointTypes<kQuickCheckInstanceOf, void, mirror::Object*, mirror::Class*>();
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100539 }
540
541 if (!is_fatal_) {
Artem Serovcfbe9132016-10-14 15:58:56 +0100542 RestoreLiveRegisters(codegen, locations);
543 __ B(GetExitLabel());
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100544 }
545 }
546
547 const char* GetDescription() const OVERRIDE { return "TypeCheckSlowPathARMVIXL"; }
548
549 bool IsFatal() const OVERRIDE { return is_fatal_; }
550
551 private:
552 const bool is_fatal_;
553
554 DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathARMVIXL);
555};
556
Scott Wakelingc34dba72016-10-03 10:14:44 +0100557class DeoptimizationSlowPathARMVIXL : public SlowPathCodeARMVIXL {
558 public:
559 explicit DeoptimizationSlowPathARMVIXL(HDeoptimize* instruction)
560 : SlowPathCodeARMVIXL(instruction) {}
561
562 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
563 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
564 __ Bind(GetEntryLabel());
565 arm_codegen->InvokeRuntime(kQuickDeoptimize, instruction_, instruction_->GetDexPc(), this);
566 CheckEntrypointTypes<kQuickDeoptimize, void, void>();
567 }
568
569 const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathARMVIXL"; }
570
571 private:
572 DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathARMVIXL);
573};
574
575class ArraySetSlowPathARMVIXL : public SlowPathCodeARMVIXL {
576 public:
577 explicit ArraySetSlowPathARMVIXL(HInstruction* instruction) : SlowPathCodeARMVIXL(instruction) {}
578
579 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
580 LocationSummary* locations = instruction_->GetLocations();
581 __ Bind(GetEntryLabel());
582 SaveLiveRegisters(codegen, locations);
583
584 InvokeRuntimeCallingConventionARMVIXL calling_convention;
585 HParallelMove parallel_move(codegen->GetGraph()->GetArena());
586 parallel_move.AddMove(
587 locations->InAt(0),
588 LocationFrom(calling_convention.GetRegisterAt(0)),
589 Primitive::kPrimNot,
590 nullptr);
591 parallel_move.AddMove(
592 locations->InAt(1),
593 LocationFrom(calling_convention.GetRegisterAt(1)),
594 Primitive::kPrimInt,
595 nullptr);
596 parallel_move.AddMove(
597 locations->InAt(2),
598 LocationFrom(calling_convention.GetRegisterAt(2)),
599 Primitive::kPrimNot,
600 nullptr);
601 codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
602
603 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
604 arm_codegen->InvokeRuntime(kQuickAputObject, instruction_, instruction_->GetDexPc(), this);
605 CheckEntrypointTypes<kQuickAputObject, void, mirror::Array*, int32_t, mirror::Object*>();
606 RestoreLiveRegisters(codegen, locations);
607 __ B(GetExitLabel());
608 }
609
610 const char* GetDescription() const OVERRIDE { return "ArraySetSlowPathARMVIXL"; }
611
612 private:
613 DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathARMVIXL);
614};
615
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000616// Slow path marking an object reference `ref` during a read
617// barrier. The field `obj.field` in the object `obj` holding this
618// reference does not get updated by this slow path after marking (see
619// ReadBarrierMarkAndUpdateFieldSlowPathARM below for that).
620//
621// This means that after the execution of this slow path, `ref` will
622// always be up-to-date, but `obj.field` may not; i.e., after the
623// flip, `ref` will be a to-space reference, but `obj.field` will
624// probably still be a from-space reference (unless it gets updated by
625// another thread, or if another thread installed another object
626// reference (different from `ref`) in `obj.field`).
627class ReadBarrierMarkSlowPathARMVIXL : public SlowPathCodeARMVIXL {
628 public:
629 ReadBarrierMarkSlowPathARMVIXL(HInstruction* instruction,
630 Location ref,
631 Location entrypoint = Location::NoLocation())
632 : SlowPathCodeARMVIXL(instruction), ref_(ref), entrypoint_(entrypoint) {
633 DCHECK(kEmitCompilerReadBarrier);
634 }
635
636 const char* GetDescription() const OVERRIDE { return "ReadBarrierMarkSlowPathARMVIXL"; }
637
638 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
639 LocationSummary* locations = instruction_->GetLocations();
640 vixl32::Register ref_reg = RegisterFrom(ref_);
641 DCHECK(locations->CanCall());
642 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_reg.GetCode())) << ref_reg;
643 DCHECK(instruction_->IsInstanceFieldGet() ||
644 instruction_->IsStaticFieldGet() ||
645 instruction_->IsArrayGet() ||
646 instruction_->IsArraySet() ||
647 instruction_->IsLoadClass() ||
648 instruction_->IsLoadString() ||
649 instruction_->IsInstanceOf() ||
650 instruction_->IsCheckCast() ||
651 (instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()) ||
652 (instruction_->IsInvokeStaticOrDirect() && instruction_->GetLocations()->Intrinsified()))
653 << "Unexpected instruction in read barrier marking slow path: "
654 << instruction_->DebugName();
655 // The read barrier instrumentation of object ArrayGet
656 // instructions does not support the HIntermediateAddress
657 // instruction.
658 DCHECK(!(instruction_->IsArrayGet() &&
659 instruction_->AsArrayGet()->GetArray()->IsIntermediateAddress()));
660
661 __ Bind(GetEntryLabel());
662 // No need to save live registers; it's taken care of by the
663 // entrypoint. Also, there is no need to update the stack mask,
664 // as this runtime call will not trigger a garbage collection.
665 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
666 DCHECK(!ref_reg.Is(sp));
667 DCHECK(!ref_reg.Is(lr));
668 DCHECK(!ref_reg.Is(pc));
669 // IP is used internally by the ReadBarrierMarkRegX entry point
670 // as a temporary, it cannot be the entry point's input/output.
671 DCHECK(!ref_reg.Is(ip));
672 DCHECK(ref_reg.IsRegister()) << ref_reg;
673 // "Compact" slow path, saving two moves.
674 //
675 // Instead of using the standard runtime calling convention (input
676 // and output in R0):
677 //
678 // R0 <- ref
679 // R0 <- ReadBarrierMark(R0)
680 // ref <- R0
681 //
682 // we just use rX (the register containing `ref`) as input and output
683 // of a dedicated entrypoint:
684 //
685 // rX <- ReadBarrierMarkRegX(rX)
686 //
687 if (entrypoint_.IsValid()) {
688 arm_codegen->ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction_, this);
689 __ Blx(RegisterFrom(entrypoint_));
690 } else {
691 int32_t entry_point_offset =
692 CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArmPointerSize>(ref_reg.GetCode());
693 // This runtime call does not require a stack map.
694 arm_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
695 }
696 __ B(GetExitLabel());
697 }
698
699 private:
700 // The location (register) of the marked object reference.
701 const Location ref_;
702
703 // The location of the entrypoint if already loaded.
704 const Location entrypoint_;
705
706 DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkSlowPathARMVIXL);
707};
708
709// Slow path marking an object reference `ref` during a read barrier,
710// and if needed, atomically updating the field `obj.field` in the
711// object `obj` holding this reference after marking (contrary to
712// ReadBarrierMarkSlowPathARM above, which never tries to update
713// `obj.field`).
714//
715// This means that after the execution of this slow path, both `ref`
716// and `obj.field` will be up-to-date; i.e., after the flip, both will
717// hold the same to-space reference (unless another thread installed
718// another object reference (different from `ref`) in `obj.field`).
719class ReadBarrierMarkAndUpdateFieldSlowPathARMVIXL : public SlowPathCodeARMVIXL {
720 public:
721 ReadBarrierMarkAndUpdateFieldSlowPathARMVIXL(HInstruction* instruction,
722 Location ref,
723 vixl32::Register obj,
724 Location field_offset,
725 vixl32::Register temp1,
726 vixl32::Register temp2)
727 : SlowPathCodeARMVIXL(instruction),
728 ref_(ref),
729 obj_(obj),
730 field_offset_(field_offset),
731 temp1_(temp1),
732 temp2_(temp2) {
733 DCHECK(kEmitCompilerReadBarrier);
734 }
735
736 const char* GetDescription() const OVERRIDE {
737 return "ReadBarrierMarkAndUpdateFieldSlowPathARMVIXL";
738 }
739
740 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
741 LocationSummary* locations = instruction_->GetLocations();
742 vixl32::Register ref_reg = RegisterFrom(ref_);
743 DCHECK(locations->CanCall());
744 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_reg.GetCode())) << ref_reg;
745 // This slow path is only used by the UnsafeCASObject intrinsic.
746 DCHECK((instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()))
747 << "Unexpected instruction in read barrier marking and field updating slow path: "
748 << instruction_->DebugName();
749 DCHECK(instruction_->GetLocations()->Intrinsified());
750 DCHECK_EQ(instruction_->AsInvoke()->GetIntrinsic(), Intrinsics::kUnsafeCASObject);
751 DCHECK(field_offset_.IsRegisterPair()) << field_offset_;
752
753 __ Bind(GetEntryLabel());
754
755 // Save the old reference.
756 // Note that we cannot use IP to save the old reference, as IP is
757 // used internally by the ReadBarrierMarkRegX entry point, and we
758 // need the old reference after the call to that entry point.
759 DCHECK(!temp1_.Is(ip));
760 __ Mov(temp1_, ref_reg);
761
762 // No need to save live registers; it's taken care of by the
763 // entrypoint. Also, there is no need to update the stack mask,
764 // as this runtime call will not trigger a garbage collection.
765 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
766 DCHECK(!ref_reg.Is(sp));
767 DCHECK(!ref_reg.Is(lr));
768 DCHECK(!ref_reg.Is(pc));
769 // IP is used internally by the ReadBarrierMarkRegX entry point
770 // as a temporary, it cannot be the entry point's input/output.
771 DCHECK(!ref_reg.Is(ip));
772 DCHECK(ref_reg.IsRegister()) << ref_reg;
773 // "Compact" slow path, saving two moves.
774 //
775 // Instead of using the standard runtime calling convention (input
776 // and output in R0):
777 //
778 // R0 <- ref
779 // R0 <- ReadBarrierMark(R0)
780 // ref <- R0
781 //
782 // we just use rX (the register containing `ref`) as input and output
783 // of a dedicated entrypoint:
784 //
785 // rX <- ReadBarrierMarkRegX(rX)
786 //
787 int32_t entry_point_offset =
788 CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArmPointerSize>(ref_reg.GetCode());
789 // This runtime call does not require a stack map.
790 arm_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
791
792 // If the new reference is different from the old reference,
793 // update the field in the holder (`*(obj_ + field_offset_)`).
794 //
795 // Note that this field could also hold a different object, if
796 // another thread had concurrently changed it. In that case, the
797 // LDREX/SUBS/ITNE sequence of instructions in the compare-and-set
798 // (CAS) operation below would abort the CAS, leaving the field
799 // as-is.
800 vixl32::Label done;
801 __ Cmp(temp1_, ref_reg);
802 __ B(eq, &done);
803
804 // Update the the holder's field atomically. This may fail if
805 // mutator updates before us, but it's OK. This is achieved
806 // using a strong compare-and-set (CAS) operation with relaxed
807 // memory synchronization ordering, where the expected value is
808 // the old reference and the desired value is the new reference.
809
810 UseScratchRegisterScope temps(arm_codegen->GetVIXLAssembler());
811 // Convenience aliases.
812 vixl32::Register base = obj_;
813 // The UnsafeCASObject intrinsic uses a register pair as field
814 // offset ("long offset"), of which only the low part contains
815 // data.
816 vixl32::Register offset = LowRegisterFrom(field_offset_);
817 vixl32::Register expected = temp1_;
818 vixl32::Register value = ref_reg;
819 vixl32::Register tmp_ptr = temps.Acquire(); // Pointer to actual memory.
820 vixl32::Register tmp = temp2_; // Value in memory.
821
822 __ Add(tmp_ptr, base, offset);
823
824 if (kPoisonHeapReferences) {
825 arm_codegen->GetAssembler()->PoisonHeapReference(expected);
826 if (value.Is(expected)) {
827 // Do not poison `value`, as it is the same register as
828 // `expected`, which has just been poisoned.
829 } else {
830 arm_codegen->GetAssembler()->PoisonHeapReference(value);
831 }
832 }
833
834 // do {
835 // tmp = [r_ptr] - expected;
836 // } while (tmp == 0 && failure([r_ptr] <- r_new_value));
837
838 vixl32::Label loop_head, exit_loop;
839 __ Bind(&loop_head);
840
841 __ Ldrex(tmp, MemOperand(tmp_ptr));
842
843 __ Subs(tmp, tmp, expected);
844
845 {
846 AssemblerAccurateScope aas(arm_codegen->GetVIXLAssembler(),
847 2 * kMaxInstructionSizeInBytes,
848 CodeBufferCheckScope::kMaximumSize);
849
850 __ it(ne);
851 __ clrex(ne);
852 }
853
854 __ B(ne, &exit_loop);
855
856 __ Strex(tmp, value, MemOperand(tmp_ptr));
857 __ Cmp(tmp, 1);
858 __ B(eq, &loop_head);
859
860 __ Bind(&exit_loop);
861
862 if (kPoisonHeapReferences) {
863 arm_codegen->GetAssembler()->UnpoisonHeapReference(expected);
864 if (value.Is(expected)) {
865 // Do not unpoison `value`, as it is the same register as
866 // `expected`, which has just been unpoisoned.
867 } else {
868 arm_codegen->GetAssembler()->UnpoisonHeapReference(value);
869 }
870 }
871
872 __ Bind(&done);
873 __ B(GetExitLabel());
874 }
875
876 private:
877 // The location (register) of the marked object reference.
878 const Location ref_;
879 // The register containing the object holding the marked object reference field.
880 const vixl32::Register obj_;
881 // The location of the offset of the marked reference field within `obj_`.
882 Location field_offset_;
883
884 const vixl32::Register temp1_;
885 const vixl32::Register temp2_;
886
887 DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkAndUpdateFieldSlowPathARMVIXL);
888};
889
890// Slow path generating a read barrier for a heap reference.
891class ReadBarrierForHeapReferenceSlowPathARMVIXL : public SlowPathCodeARMVIXL {
892 public:
893 ReadBarrierForHeapReferenceSlowPathARMVIXL(HInstruction* instruction,
894 Location out,
895 Location ref,
896 Location obj,
897 uint32_t offset,
898 Location index)
899 : SlowPathCodeARMVIXL(instruction),
900 out_(out),
901 ref_(ref),
902 obj_(obj),
903 offset_(offset),
904 index_(index) {
905 DCHECK(kEmitCompilerReadBarrier);
906 // If `obj` is equal to `out` or `ref`, it means the initial object
907 // has been overwritten by (or after) the heap object reference load
908 // to be instrumented, e.g.:
909 //
910 // __ LoadFromOffset(kLoadWord, out, out, offset);
911 // codegen_->GenerateReadBarrierSlow(instruction, out_loc, out_loc, out_loc, offset);
912 //
913 // In that case, we have lost the information about the original
914 // object, and the emitted read barrier cannot work properly.
915 DCHECK(!obj.Equals(out)) << "obj=" << obj << " out=" << out;
916 DCHECK(!obj.Equals(ref)) << "obj=" << obj << " ref=" << ref;
917 }
918
919 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
920 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
921 LocationSummary* locations = instruction_->GetLocations();
922 vixl32::Register reg_out = RegisterFrom(out_);
923 DCHECK(locations->CanCall());
924 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out.GetCode()));
925 DCHECK(instruction_->IsInstanceFieldGet() ||
926 instruction_->IsStaticFieldGet() ||
927 instruction_->IsArrayGet() ||
928 instruction_->IsInstanceOf() ||
929 instruction_->IsCheckCast() ||
930 (instruction_->IsInvokeVirtual()) && instruction_->GetLocations()->Intrinsified())
931 << "Unexpected instruction in read barrier for heap reference slow path: "
932 << instruction_->DebugName();
933 // The read barrier instrumentation of object ArrayGet
934 // instructions does not support the HIntermediateAddress
935 // instruction.
936 DCHECK(!(instruction_->IsArrayGet() &&
937 instruction_->AsArrayGet()->GetArray()->IsIntermediateAddress()));
938
939 __ Bind(GetEntryLabel());
940 SaveLiveRegisters(codegen, locations);
941
942 // We may have to change the index's value, but as `index_` is a
943 // constant member (like other "inputs" of this slow path),
944 // introduce a copy of it, `index`.
945 Location index = index_;
946 if (index_.IsValid()) {
947 // Handle `index_` for HArrayGet and UnsafeGetObject/UnsafeGetObjectVolatile intrinsics.
948 if (instruction_->IsArrayGet()) {
949 // Compute the actual memory offset and store it in `index`.
950 vixl32::Register index_reg = RegisterFrom(index_);
951 DCHECK(locations->GetLiveRegisters()->ContainsCoreRegister(index_reg.GetCode()));
952 if (codegen->IsCoreCalleeSaveRegister(index_reg.GetCode())) {
953 // We are about to change the value of `index_reg` (see the
954 // calls to art::arm::Thumb2Assembler::Lsl and
955 // art::arm::Thumb2Assembler::AddConstant below), but it has
956 // not been saved by the previous call to
957 // art::SlowPathCode::SaveLiveRegisters, as it is a
958 // callee-save register --
959 // art::SlowPathCode::SaveLiveRegisters does not consider
960 // callee-save registers, as it has been designed with the
961 // assumption that callee-save registers are supposed to be
962 // handled by the called function. So, as a callee-save
963 // register, `index_reg` _would_ eventually be saved onto
964 // the stack, but it would be too late: we would have
965 // changed its value earlier. Therefore, we manually save
966 // it here into another freely available register,
967 // `free_reg`, chosen of course among the caller-save
968 // registers (as a callee-save `free_reg` register would
969 // exhibit the same problem).
970 //
971 // Note we could have requested a temporary register from
972 // the register allocator instead; but we prefer not to, as
973 // this is a slow path, and we know we can find a
974 // caller-save register that is available.
975 vixl32::Register free_reg = FindAvailableCallerSaveRegister(codegen);
976 __ Mov(free_reg, index_reg);
977 index_reg = free_reg;
978 index = LocationFrom(index_reg);
979 } else {
980 // The initial register stored in `index_` has already been
981 // saved in the call to art::SlowPathCode::SaveLiveRegisters
982 // (as it is not a callee-save register), so we can freely
983 // use it.
984 }
985 // Shifting the index value contained in `index_reg` by the scale
986 // factor (2) cannot overflow in practice, as the runtime is
987 // unable to allocate object arrays with a size larger than
988 // 2^26 - 1 (that is, 2^28 - 4 bytes).
989 __ Lsl(index_reg, index_reg, TIMES_4);
990 static_assert(
991 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
992 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
993 __ Add(index_reg, index_reg, offset_);
994 } else {
995 // In the case of the UnsafeGetObject/UnsafeGetObjectVolatile
996 // intrinsics, `index_` is not shifted by a scale factor of 2
997 // (as in the case of ArrayGet), as it is actually an offset
998 // to an object field within an object.
999 DCHECK(instruction_->IsInvoke()) << instruction_->DebugName();
1000 DCHECK(instruction_->GetLocations()->Intrinsified());
1001 DCHECK((instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObject) ||
1002 (instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile))
1003 << instruction_->AsInvoke()->GetIntrinsic();
1004 DCHECK_EQ(offset_, 0U);
1005 DCHECK(index_.IsRegisterPair());
1006 // UnsafeGet's offset location is a register pair, the low
1007 // part contains the correct offset.
1008 index = index_.ToLow();
1009 }
1010 }
1011
1012 // We're moving two or three locations to locations that could
1013 // overlap, so we need a parallel move resolver.
1014 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1015 HParallelMove parallel_move(codegen->GetGraph()->GetArena());
1016 parallel_move.AddMove(ref_,
1017 LocationFrom(calling_convention.GetRegisterAt(0)),
1018 Primitive::kPrimNot,
1019 nullptr);
1020 parallel_move.AddMove(obj_,
1021 LocationFrom(calling_convention.GetRegisterAt(1)),
1022 Primitive::kPrimNot,
1023 nullptr);
1024 if (index.IsValid()) {
1025 parallel_move.AddMove(index,
1026 LocationFrom(calling_convention.GetRegisterAt(2)),
1027 Primitive::kPrimInt,
1028 nullptr);
1029 codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
1030 } else {
1031 codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
1032 __ Mov(calling_convention.GetRegisterAt(2), offset_);
1033 }
1034 arm_codegen->InvokeRuntime(kQuickReadBarrierSlow, instruction_, instruction_->GetDexPc(), this);
1035 CheckEntrypointTypes<
1036 kQuickReadBarrierSlow, mirror::Object*, mirror::Object*, mirror::Object*, uint32_t>();
1037 arm_codegen->Move32(out_, LocationFrom(r0));
1038
1039 RestoreLiveRegisters(codegen, locations);
1040 __ B(GetExitLabel());
1041 }
1042
1043 const char* GetDescription() const OVERRIDE {
1044 return "ReadBarrierForHeapReferenceSlowPathARMVIXL";
1045 }
1046
1047 private:
1048 vixl32::Register FindAvailableCallerSaveRegister(CodeGenerator* codegen) {
1049 uint32_t ref = RegisterFrom(ref_).GetCode();
1050 uint32_t obj = RegisterFrom(obj_).GetCode();
1051 for (uint32_t i = 0, e = codegen->GetNumberOfCoreRegisters(); i < e; ++i) {
1052 if (i != ref && i != obj && !codegen->IsCoreCalleeSaveRegister(i)) {
1053 return vixl32::Register(i);
1054 }
1055 }
1056 // We shall never fail to find a free caller-save register, as
1057 // there are more than two core caller-save registers on ARM
1058 // (meaning it is possible to find one which is different from
1059 // `ref` and `obj`).
1060 DCHECK_GT(codegen->GetNumberOfCoreCallerSaveRegisters(), 2u);
1061 LOG(FATAL) << "Could not find a free caller-save register";
1062 UNREACHABLE();
1063 }
1064
1065 const Location out_;
1066 const Location ref_;
1067 const Location obj_;
1068 const uint32_t offset_;
1069 // An additional location containing an index to an array.
1070 // Only used for HArrayGet and the UnsafeGetObject &
1071 // UnsafeGetObjectVolatile intrinsics.
1072 const Location index_;
1073
1074 DISALLOW_COPY_AND_ASSIGN(ReadBarrierForHeapReferenceSlowPathARMVIXL);
1075};
1076
1077// Slow path generating a read barrier for a GC root.
1078class ReadBarrierForRootSlowPathARMVIXL : public SlowPathCodeARMVIXL {
1079 public:
1080 ReadBarrierForRootSlowPathARMVIXL(HInstruction* instruction, Location out, Location root)
1081 : SlowPathCodeARMVIXL(instruction), out_(out), root_(root) {
1082 DCHECK(kEmitCompilerReadBarrier);
1083 }
1084
1085 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
1086 LocationSummary* locations = instruction_->GetLocations();
1087 vixl32::Register reg_out = RegisterFrom(out_);
1088 DCHECK(locations->CanCall());
1089 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out.GetCode()));
1090 DCHECK(instruction_->IsLoadClass() || instruction_->IsLoadString())
1091 << "Unexpected instruction in read barrier for GC root slow path: "
1092 << instruction_->DebugName();
1093
1094 __ Bind(GetEntryLabel());
1095 SaveLiveRegisters(codegen, locations);
1096
1097 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1098 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
1099 arm_codegen->Move32(LocationFrom(calling_convention.GetRegisterAt(0)), root_);
1100 arm_codegen->InvokeRuntime(kQuickReadBarrierForRootSlow,
1101 instruction_,
1102 instruction_->GetDexPc(),
1103 this);
1104 CheckEntrypointTypes<kQuickReadBarrierForRootSlow, mirror::Object*, GcRoot<mirror::Object>*>();
1105 arm_codegen->Move32(out_, LocationFrom(r0));
1106
1107 RestoreLiveRegisters(codegen, locations);
1108 __ B(GetExitLabel());
1109 }
1110
1111 const char* GetDescription() const OVERRIDE { return "ReadBarrierForRootSlowPathARMVIXL"; }
1112
1113 private:
1114 const Location out_;
1115 const Location root_;
1116
1117 DISALLOW_COPY_AND_ASSIGN(ReadBarrierForRootSlowPathARMVIXL);
1118};
Scott Wakelingc34dba72016-10-03 10:14:44 +01001119
Scott Wakelingfe885462016-09-22 10:24:38 +01001120inline vixl32::Condition ARMCondition(IfCondition cond) {
1121 switch (cond) {
1122 case kCondEQ: return eq;
1123 case kCondNE: return ne;
1124 case kCondLT: return lt;
1125 case kCondLE: return le;
1126 case kCondGT: return gt;
1127 case kCondGE: return ge;
1128 case kCondB: return lo;
1129 case kCondBE: return ls;
1130 case kCondA: return hi;
1131 case kCondAE: return hs;
1132 }
1133 LOG(FATAL) << "Unreachable";
1134 UNREACHABLE();
1135}
1136
1137// Maps signed condition to unsigned condition.
1138inline vixl32::Condition ARMUnsignedCondition(IfCondition cond) {
1139 switch (cond) {
1140 case kCondEQ: return eq;
1141 case kCondNE: return ne;
1142 // Signed to unsigned.
1143 case kCondLT: return lo;
1144 case kCondLE: return ls;
1145 case kCondGT: return hi;
1146 case kCondGE: return hs;
1147 // Unsigned remain unchanged.
1148 case kCondB: return lo;
1149 case kCondBE: return ls;
1150 case kCondA: return hi;
1151 case kCondAE: return hs;
1152 }
1153 LOG(FATAL) << "Unreachable";
1154 UNREACHABLE();
1155}
1156
1157inline vixl32::Condition ARMFPCondition(IfCondition cond, bool gt_bias) {
1158 // The ARM condition codes can express all the necessary branches, see the
1159 // "Meaning (floating-point)" column in the table A8-1 of the ARMv7 reference manual.
1160 // There is no dex instruction or HIR that would need the missing conditions
1161 // "equal or unordered" or "not equal".
1162 switch (cond) {
1163 case kCondEQ: return eq;
1164 case kCondNE: return ne /* unordered */;
1165 case kCondLT: return gt_bias ? cc : lt /* unordered */;
1166 case kCondLE: return gt_bias ? ls : le /* unordered */;
1167 case kCondGT: return gt_bias ? hi /* unordered */ : gt;
1168 case kCondGE: return gt_bias ? cs /* unordered */ : ge;
1169 default:
1170 LOG(FATAL) << "UNREACHABLE";
1171 UNREACHABLE();
1172 }
1173}
1174
Scott Wakelingfe885462016-09-22 10:24:38 +01001175void CodeGeneratorARMVIXL::DumpCoreRegister(std::ostream& stream, int reg) const {
1176 stream << vixl32::Register(reg);
1177}
1178
1179void CodeGeneratorARMVIXL::DumpFloatingPointRegister(std::ostream& stream, int reg) const {
1180 stream << vixl32::SRegister(reg);
1181}
1182
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001183static uint32_t ComputeSRegisterListMask(const SRegisterList& regs) {
Scott Wakelingfe885462016-09-22 10:24:38 +01001184 uint32_t mask = 0;
1185 for (uint32_t i = regs.GetFirstSRegister().GetCode();
1186 i <= regs.GetLastSRegister().GetCode();
1187 ++i) {
1188 mask |= (1 << i);
1189 }
1190 return mask;
1191}
1192
Artem Serovd4cc5b22016-11-04 11:19:09 +00001193// Saves the register in the stack. Returns the size taken on stack.
1194size_t CodeGeneratorARMVIXL::SaveCoreRegister(size_t stack_index ATTRIBUTE_UNUSED,
1195 uint32_t reg_id ATTRIBUTE_UNUSED) {
1196 TODO_VIXL32(FATAL);
1197 return 0;
1198}
1199
1200// Restores the register from the stack. Returns the size taken on stack.
1201size_t CodeGeneratorARMVIXL::RestoreCoreRegister(size_t stack_index ATTRIBUTE_UNUSED,
1202 uint32_t reg_id ATTRIBUTE_UNUSED) {
1203 TODO_VIXL32(FATAL);
1204 return 0;
1205}
1206
1207size_t CodeGeneratorARMVIXL::SaveFloatingPointRegister(size_t stack_index ATTRIBUTE_UNUSED,
1208 uint32_t reg_id ATTRIBUTE_UNUSED) {
1209 TODO_VIXL32(FATAL);
1210 return 0;
1211}
1212
1213size_t CodeGeneratorARMVIXL::RestoreFloatingPointRegister(size_t stack_index ATTRIBUTE_UNUSED,
1214 uint32_t reg_id ATTRIBUTE_UNUSED) {
1215 TODO_VIXL32(FATAL);
1216 return 0;
Anton Kirilove28d9ae2016-10-25 18:17:23 +01001217}
1218
Scott Wakelingfe885462016-09-22 10:24:38 +01001219#undef __
1220
1221CodeGeneratorARMVIXL::CodeGeneratorARMVIXL(HGraph* graph,
1222 const ArmInstructionSetFeatures& isa_features,
1223 const CompilerOptions& compiler_options,
1224 OptimizingCompilerStats* stats)
1225 : CodeGenerator(graph,
1226 kNumberOfCoreRegisters,
1227 kNumberOfSRegisters,
1228 kNumberOfRegisterPairs,
1229 kCoreCalleeSaves.GetList(),
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001230 ComputeSRegisterListMask(kFpuCalleeSaves),
Scott Wakelingfe885462016-09-22 10:24:38 +01001231 compiler_options,
1232 stats),
1233 block_labels_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
Artem Serov551b28f2016-10-18 19:11:30 +01001234 jump_tables_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
Scott Wakelingfe885462016-09-22 10:24:38 +01001235 location_builder_(graph, this),
1236 instruction_visitor_(graph, this),
1237 move_resolver_(graph->GetArena(), this),
1238 assembler_(graph->GetArena()),
Artem Serovd4cc5b22016-11-04 11:19:09 +00001239 isa_features_(isa_features),
1240 relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
1241 pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
1242 pc_relative_string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
1243 pc_relative_type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
Scott Wakelingfe885462016-09-22 10:24:38 +01001244 // Always save the LR register to mimic Quick.
1245 AddAllocatedRegister(Location::RegisterLocation(LR));
Alexandre Rames9c19bd62016-10-24 11:50:32 +01001246 // Give d14 and d15 as scratch registers to VIXL.
1247 // They are removed from the register allocator in `SetupBlockedRegisters()`.
1248 // TODO(VIXL): We need two scratch D registers for `EmitSwap` when swapping two double stack
1249 // slots. If that is sufficiently rare, and we have pressure on FP registers, we could instead
1250 // spill in `EmitSwap`. But if we actually are guaranteed to have 32 D registers, we could give
1251 // d30 and d31 to VIXL to avoid removing registers from the allocator. If that is the case, we may
1252 // also want to investigate giving those 14 other D registers to the allocator.
1253 GetVIXLAssembler()->GetScratchVRegisterList()->Combine(d14);
1254 GetVIXLAssembler()->GetScratchVRegisterList()->Combine(d15);
Scott Wakelingfe885462016-09-22 10:24:38 +01001255}
1256
Artem Serov551b28f2016-10-18 19:11:30 +01001257void JumpTableARMVIXL::EmitTable(CodeGeneratorARMVIXL* codegen) {
1258 uint32_t num_entries = switch_instr_->GetNumEntries();
1259 DCHECK_GE(num_entries, kPackedSwitchCompareJumpThreshold);
1260
1261 // We are about to use the assembler to place literals directly. Make sure we have enough
Scott Wakelingb77051e2016-11-21 19:46:00 +00001262 // underlying code buffer and we have generated a jump table of the right size, using
1263 // codegen->GetVIXLAssembler()->GetBuffer().Align();
Artem Serov551b28f2016-10-18 19:11:30 +01001264 AssemblerAccurateScope aas(codegen->GetVIXLAssembler(),
1265 num_entries * sizeof(int32_t),
1266 CodeBufferCheckScope::kMaximumSize);
1267 // TODO(VIXL): Check that using lower case bind is fine here.
1268 codegen->GetVIXLAssembler()->bind(&table_start_);
Artem Serov09a940d2016-11-11 16:15:11 +00001269 for (uint32_t i = 0; i < num_entries; i++) {
1270 codegen->GetVIXLAssembler()->place(bb_addresses_[i].get());
1271 }
1272}
1273
1274void JumpTableARMVIXL::FixTable(CodeGeneratorARMVIXL* codegen) {
1275 uint32_t num_entries = switch_instr_->GetNumEntries();
1276 DCHECK_GE(num_entries, kPackedSwitchCompareJumpThreshold);
1277
Artem Serov551b28f2016-10-18 19:11:30 +01001278 const ArenaVector<HBasicBlock*>& successors = switch_instr_->GetBlock()->GetSuccessors();
1279 for (uint32_t i = 0; i < num_entries; i++) {
1280 vixl32::Label* target_label = codegen->GetLabelOf(successors[i]);
1281 DCHECK(target_label->IsBound());
1282 int32_t jump_offset = target_label->GetLocation() - table_start_.GetLocation();
1283 // When doing BX to address we need to have lower bit set to 1 in T32.
1284 if (codegen->GetVIXLAssembler()->IsUsingT32()) {
1285 jump_offset++;
1286 }
1287 DCHECK_GT(jump_offset, std::numeric_limits<int32_t>::min());
1288 DCHECK_LE(jump_offset, std::numeric_limits<int32_t>::max());
Artem Serov09a940d2016-11-11 16:15:11 +00001289
Scott Wakelingb77051e2016-11-21 19:46:00 +00001290 bb_addresses_[i].get()->UpdateValue(jump_offset, codegen->GetVIXLAssembler()->GetBuffer());
Artem Serov551b28f2016-10-18 19:11:30 +01001291 }
1292}
1293
Artem Serov09a940d2016-11-11 16:15:11 +00001294void CodeGeneratorARMVIXL::FixJumpTables() {
Artem Serov551b28f2016-10-18 19:11:30 +01001295 for (auto&& jump_table : jump_tables_) {
Artem Serov09a940d2016-11-11 16:15:11 +00001296 jump_table->FixTable(this);
Artem Serov551b28f2016-10-18 19:11:30 +01001297 }
1298}
1299
Andreas Gampeca620d72016-11-08 08:09:33 -08001300#define __ reinterpret_cast<ArmVIXLAssembler*>(GetAssembler())->GetVIXLAssembler()-> // NOLINT
Scott Wakelingfe885462016-09-22 10:24:38 +01001301
1302void CodeGeneratorARMVIXL::Finalize(CodeAllocator* allocator) {
Artem Serov09a940d2016-11-11 16:15:11 +00001303 FixJumpTables();
Scott Wakelingfe885462016-09-22 10:24:38 +01001304 GetAssembler()->FinalizeCode();
1305 CodeGenerator::Finalize(allocator);
1306}
1307
1308void CodeGeneratorARMVIXL::SetupBlockedRegisters() const {
Scott Wakelingfe885462016-09-22 10:24:38 +01001309 // Stack register, LR and PC are always reserved.
1310 blocked_core_registers_[SP] = true;
1311 blocked_core_registers_[LR] = true;
1312 blocked_core_registers_[PC] = true;
1313
1314 // Reserve thread register.
1315 blocked_core_registers_[TR] = true;
1316
1317 // Reserve temp register.
1318 blocked_core_registers_[IP] = true;
1319
Alexandre Rames9c19bd62016-10-24 11:50:32 +01001320 // Registers s28-s31 (d14-d15) are left to VIXL for scratch registers.
1321 // (They are given to the `MacroAssembler` in `CodeGeneratorARMVIXL::CodeGeneratorARMVIXL`.)
1322 blocked_fpu_registers_[28] = true;
1323 blocked_fpu_registers_[29] = true;
1324 blocked_fpu_registers_[30] = true;
1325 blocked_fpu_registers_[31] = true;
1326
Scott Wakelingfe885462016-09-22 10:24:38 +01001327 if (GetGraph()->IsDebuggable()) {
1328 // Stubs do not save callee-save floating point registers. If the graph
1329 // is debuggable, we need to deal with these registers differently. For
1330 // now, just block them.
1331 for (uint32_t i = kFpuCalleeSaves.GetFirstSRegister().GetCode();
1332 i <= kFpuCalleeSaves.GetLastSRegister().GetCode();
1333 ++i) {
1334 blocked_fpu_registers_[i] = true;
1335 }
1336 }
Scott Wakelingfe885462016-09-22 10:24:38 +01001337}
1338
Scott Wakelingfe885462016-09-22 10:24:38 +01001339InstructionCodeGeneratorARMVIXL::InstructionCodeGeneratorARMVIXL(HGraph* graph,
1340 CodeGeneratorARMVIXL* codegen)
1341 : InstructionCodeGenerator(graph, codegen),
1342 assembler_(codegen->GetAssembler()),
1343 codegen_(codegen) {}
1344
1345void CodeGeneratorARMVIXL::ComputeSpillMask() {
1346 core_spill_mask_ = allocated_registers_.GetCoreRegisters() & core_callee_save_mask_;
1347 DCHECK_NE(core_spill_mask_, 0u) << "At least the return address register must be saved";
1348 // There is no easy instruction to restore just the PC on thumb2. We spill and
1349 // restore another arbitrary register.
1350 core_spill_mask_ |= (1 << kCoreAlwaysSpillRegister.GetCode());
1351 fpu_spill_mask_ = allocated_registers_.GetFloatingPointRegisters() & fpu_callee_save_mask_;
1352 // We use vpush and vpop for saving and restoring floating point registers, which take
1353 // a SRegister and the number of registers to save/restore after that SRegister. We
1354 // therefore update the `fpu_spill_mask_` to also contain those registers not allocated,
1355 // but in the range.
1356 if (fpu_spill_mask_ != 0) {
1357 uint32_t least_significant_bit = LeastSignificantBit(fpu_spill_mask_);
1358 uint32_t most_significant_bit = MostSignificantBit(fpu_spill_mask_);
1359 for (uint32_t i = least_significant_bit + 1 ; i < most_significant_bit; ++i) {
1360 fpu_spill_mask_ |= (1 << i);
1361 }
1362 }
1363}
1364
1365void CodeGeneratorARMVIXL::GenerateFrameEntry() {
1366 bool skip_overflow_check =
1367 IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kArm);
1368 DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks());
1369 __ Bind(&frame_entry_label_);
1370
1371 if (HasEmptyFrame()) {
1372 return;
1373 }
1374
Scott Wakelingfe885462016-09-22 10:24:38 +01001375 if (!skip_overflow_check) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001376 UseScratchRegisterScope temps(GetVIXLAssembler());
1377 vixl32::Register temp = temps.Acquire();
Scott Wakelingfe885462016-09-22 10:24:38 +01001378 __ Sub(temp, sp, static_cast<int32_t>(GetStackOverflowReservedBytes(kArm)));
1379 // The load must immediately precede RecordPcInfo.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001380 AssemblerAccurateScope aas(GetVIXLAssembler(),
Alexandre Rames374ddf32016-11-04 10:40:49 +00001381 vixl32::kMaxInstructionSizeInBytes,
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001382 CodeBufferCheckScope::kMaximumSize);
1383 __ ldr(temp, MemOperand(temp));
1384 RecordPcInfo(nullptr, 0);
Scott Wakelingfe885462016-09-22 10:24:38 +01001385 }
1386
1387 __ Push(RegisterList(core_spill_mask_));
1388 GetAssembler()->cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(core_spill_mask_));
1389 GetAssembler()->cfi().RelOffsetForMany(DWARFReg(kMethodRegister),
1390 0,
1391 core_spill_mask_,
1392 kArmWordSize);
1393 if (fpu_spill_mask_ != 0) {
1394 uint32_t first = LeastSignificantBit(fpu_spill_mask_);
1395
1396 // Check that list is contiguous.
1397 DCHECK_EQ(fpu_spill_mask_ >> CTZ(fpu_spill_mask_), ~0u >> (32 - POPCOUNT(fpu_spill_mask_)));
1398
1399 __ Vpush(SRegisterList(vixl32::SRegister(first), POPCOUNT(fpu_spill_mask_)));
1400 GetAssembler()->cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(fpu_spill_mask_));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001401 GetAssembler()->cfi().RelOffsetForMany(DWARFReg(s0), 0, fpu_spill_mask_, kArmWordSize);
Scott Wakelingfe885462016-09-22 10:24:38 +01001402 }
Scott Wakelingbffdc702016-12-07 17:46:03 +00001403
1404 if (GetGraph()->HasShouldDeoptimizeFlag()) {
1405 UseScratchRegisterScope temps(GetVIXLAssembler());
1406 vixl32::Register temp = temps.Acquire();
1407 // Initialize should_deoptimize flag to 0.
1408 __ Mov(temp, 0);
1409 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, -kShouldDeoptimizeFlagSize);
1410 }
1411
Scott Wakelingfe885462016-09-22 10:24:38 +01001412 int adjust = GetFrameSize() - FrameEntrySpillSize();
1413 __ Sub(sp, sp, adjust);
1414 GetAssembler()->cfi().AdjustCFAOffset(adjust);
Scott Wakelingbffdc702016-12-07 17:46:03 +00001415
1416 // Save the current method if we need it. Note that we do not
1417 // do this in HCurrentMethod, as the instruction might have been removed
1418 // in the SSA graph.
1419 if (RequiresCurrentMethod()) {
1420 GetAssembler()->StoreToOffset(kStoreWord, kMethodRegister, sp, 0);
1421 }
Scott Wakelingfe885462016-09-22 10:24:38 +01001422}
1423
1424void CodeGeneratorARMVIXL::GenerateFrameExit() {
1425 if (HasEmptyFrame()) {
1426 __ Bx(lr);
1427 return;
1428 }
1429 GetAssembler()->cfi().RememberState();
1430 int adjust = GetFrameSize() - FrameEntrySpillSize();
1431 __ Add(sp, sp, adjust);
1432 GetAssembler()->cfi().AdjustCFAOffset(-adjust);
1433 if (fpu_spill_mask_ != 0) {
1434 uint32_t first = LeastSignificantBit(fpu_spill_mask_);
1435
1436 // Check that list is contiguous.
1437 DCHECK_EQ(fpu_spill_mask_ >> CTZ(fpu_spill_mask_), ~0u >> (32 - POPCOUNT(fpu_spill_mask_)));
1438
1439 __ Vpop(SRegisterList(vixl32::SRegister(first), POPCOUNT(fpu_spill_mask_)));
1440 GetAssembler()->cfi().AdjustCFAOffset(
1441 -static_cast<int>(kArmWordSize) * POPCOUNT(fpu_spill_mask_));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001442 GetAssembler()->cfi().RestoreMany(DWARFReg(vixl32::SRegister(0)), fpu_spill_mask_);
Scott Wakelingfe885462016-09-22 10:24:38 +01001443 }
1444 // Pop LR into PC to return.
1445 DCHECK_NE(core_spill_mask_ & (1 << kLrCode), 0U);
1446 uint32_t pop_mask = (core_spill_mask_ & (~(1 << kLrCode))) | 1 << kPcCode;
1447 __ Pop(RegisterList(pop_mask));
1448 GetAssembler()->cfi().RestoreState();
1449 GetAssembler()->cfi().DefCFAOffset(GetFrameSize());
1450}
1451
1452void CodeGeneratorARMVIXL::Bind(HBasicBlock* block) {
1453 __ Bind(GetLabelOf(block));
1454}
1455
Artem Serovd4cc5b22016-11-04 11:19:09 +00001456Location InvokeDexCallingConventionVisitorARMVIXL::GetNextLocation(Primitive::Type type) {
1457 switch (type) {
1458 case Primitive::kPrimBoolean:
1459 case Primitive::kPrimByte:
1460 case Primitive::kPrimChar:
1461 case Primitive::kPrimShort:
1462 case Primitive::kPrimInt:
1463 case Primitive::kPrimNot: {
1464 uint32_t index = gp_index_++;
1465 uint32_t stack_index = stack_index_++;
1466 if (index < calling_convention.GetNumberOfRegisters()) {
1467 return LocationFrom(calling_convention.GetRegisterAt(index));
1468 } else {
1469 return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index));
1470 }
1471 }
1472
1473 case Primitive::kPrimLong: {
1474 uint32_t index = gp_index_;
1475 uint32_t stack_index = stack_index_;
1476 gp_index_ += 2;
1477 stack_index_ += 2;
1478 if (index + 1 < calling_convention.GetNumberOfRegisters()) {
1479 if (calling_convention.GetRegisterAt(index).Is(r1)) {
1480 // Skip R1, and use R2_R3 instead.
1481 gp_index_++;
1482 index++;
1483 }
1484 }
1485 if (index + 1 < calling_convention.GetNumberOfRegisters()) {
1486 DCHECK_EQ(calling_convention.GetRegisterAt(index).GetCode() + 1,
1487 calling_convention.GetRegisterAt(index + 1).GetCode());
1488
1489 return LocationFrom(calling_convention.GetRegisterAt(index),
1490 calling_convention.GetRegisterAt(index + 1));
1491 } else {
1492 return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index));
1493 }
1494 }
1495
1496 case Primitive::kPrimFloat: {
1497 uint32_t stack_index = stack_index_++;
1498 if (float_index_ % 2 == 0) {
1499 float_index_ = std::max(double_index_, float_index_);
1500 }
1501 if (float_index_ < calling_convention.GetNumberOfFpuRegisters()) {
1502 return LocationFrom(calling_convention.GetFpuRegisterAt(float_index_++));
1503 } else {
1504 return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index));
1505 }
1506 }
1507
1508 case Primitive::kPrimDouble: {
1509 double_index_ = std::max(double_index_, RoundUp(float_index_, 2));
1510 uint32_t stack_index = stack_index_;
1511 stack_index_ += 2;
1512 if (double_index_ + 1 < calling_convention.GetNumberOfFpuRegisters()) {
1513 uint32_t index = double_index_;
1514 double_index_ += 2;
1515 Location result = LocationFrom(
1516 calling_convention.GetFpuRegisterAt(index),
1517 calling_convention.GetFpuRegisterAt(index + 1));
1518 DCHECK(ExpectedPairLayout(result));
1519 return result;
1520 } else {
1521 return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index));
1522 }
1523 }
1524
1525 case Primitive::kPrimVoid:
1526 LOG(FATAL) << "Unexpected parameter type " << type;
1527 break;
1528 }
1529 return Location::NoLocation();
1530}
1531
1532Location InvokeDexCallingConventionVisitorARMVIXL::GetReturnLocation(Primitive::Type type) const {
1533 switch (type) {
1534 case Primitive::kPrimBoolean:
1535 case Primitive::kPrimByte:
1536 case Primitive::kPrimChar:
1537 case Primitive::kPrimShort:
1538 case Primitive::kPrimInt:
1539 case Primitive::kPrimNot: {
1540 return LocationFrom(r0);
1541 }
1542
1543 case Primitive::kPrimFloat: {
1544 return LocationFrom(s0);
1545 }
1546
1547 case Primitive::kPrimLong: {
1548 return LocationFrom(r0, r1);
1549 }
1550
1551 case Primitive::kPrimDouble: {
1552 return LocationFrom(s0, s1);
1553 }
1554
1555 case Primitive::kPrimVoid:
1556 return Location::NoLocation();
1557 }
1558
1559 UNREACHABLE();
1560}
1561
1562Location InvokeDexCallingConventionVisitorARMVIXL::GetMethodLocation() const {
1563 return LocationFrom(kMethodRegister);
1564}
1565
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001566void CodeGeneratorARMVIXL::Move32(Location destination, Location source) {
1567 if (source.Equals(destination)) {
1568 return;
1569 }
1570 if (destination.IsRegister()) {
1571 if (source.IsRegister()) {
1572 __ Mov(RegisterFrom(destination), RegisterFrom(source));
1573 } else if (source.IsFpuRegister()) {
1574 __ Vmov(RegisterFrom(destination), SRegisterFrom(source));
1575 } else {
1576 GetAssembler()->LoadFromOffset(kLoadWord,
1577 RegisterFrom(destination),
1578 sp,
1579 source.GetStackIndex());
1580 }
1581 } else if (destination.IsFpuRegister()) {
1582 if (source.IsRegister()) {
1583 __ Vmov(SRegisterFrom(destination), RegisterFrom(source));
1584 } else if (source.IsFpuRegister()) {
1585 __ Vmov(SRegisterFrom(destination), SRegisterFrom(source));
1586 } else {
1587 GetAssembler()->LoadSFromOffset(SRegisterFrom(destination), sp, source.GetStackIndex());
1588 }
1589 } else {
1590 DCHECK(destination.IsStackSlot()) << destination;
1591 if (source.IsRegister()) {
1592 GetAssembler()->StoreToOffset(kStoreWord,
1593 RegisterFrom(source),
1594 sp,
1595 destination.GetStackIndex());
1596 } else if (source.IsFpuRegister()) {
1597 GetAssembler()->StoreSToOffset(SRegisterFrom(source), sp, destination.GetStackIndex());
1598 } else {
1599 DCHECK(source.IsStackSlot()) << source;
1600 UseScratchRegisterScope temps(GetVIXLAssembler());
1601 vixl32::Register temp = temps.Acquire();
1602 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, source.GetStackIndex());
1603 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
1604 }
1605 }
1606}
1607
Artem Serovcfbe9132016-10-14 15:58:56 +01001608void CodeGeneratorARMVIXL::MoveConstant(Location location, int32_t value) {
1609 DCHECK(location.IsRegister());
1610 __ Mov(RegisterFrom(location), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01001611}
1612
1613void CodeGeneratorARMVIXL::MoveLocation(Location dst, Location src, Primitive::Type dst_type) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001614 // TODO(VIXL): Maybe refactor to have the 'move' implementation here and use it in
1615 // `ParallelMoveResolverARMVIXL::EmitMove`, as is done in the `arm64` backend.
1616 HParallelMove move(GetGraph()->GetArena());
1617 move.AddMove(src, dst, dst_type, nullptr);
1618 GetMoveResolver()->EmitNativeCode(&move);
Scott Wakelingfe885462016-09-22 10:24:38 +01001619}
1620
Artem Serovcfbe9132016-10-14 15:58:56 +01001621void CodeGeneratorARMVIXL::AddLocationAsTemp(Location location, LocationSummary* locations) {
1622 if (location.IsRegister()) {
1623 locations->AddTemp(location);
1624 } else if (location.IsRegisterPair()) {
1625 locations->AddTemp(LocationFrom(LowRegisterFrom(location)));
1626 locations->AddTemp(LocationFrom(HighRegisterFrom(location)));
1627 } else {
1628 UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location;
1629 }
Scott Wakelingfe885462016-09-22 10:24:38 +01001630}
1631
1632void CodeGeneratorARMVIXL::InvokeRuntime(QuickEntrypointEnum entrypoint,
1633 HInstruction* instruction,
1634 uint32_t dex_pc,
1635 SlowPathCode* slow_path) {
1636 ValidateInvokeRuntime(entrypoint, instruction, slow_path);
Alexandre Rames374ddf32016-11-04 10:40:49 +00001637 __ Ldr(lr, MemOperand(tr, GetThreadOffset<kArmPointerSize>(entrypoint).Int32Value()));
1638 // Ensure the pc position is recorded immediately after the `blx` instruction.
1639 // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
1640 AssemblerAccurateScope aas(GetVIXLAssembler(),
1641 vixl32::k16BitT32InstructionSizeInBytes,
1642 CodeBufferCheckScope::kExactSize);
1643 __ blx(lr);
Scott Wakelingfe885462016-09-22 10:24:38 +01001644 if (EntrypointRequiresStackMap(entrypoint)) {
1645 RecordPcInfo(instruction, dex_pc, slow_path);
1646 }
1647}
1648
1649void CodeGeneratorARMVIXL::InvokeRuntimeWithoutRecordingPcInfo(int32_t entry_point_offset,
1650 HInstruction* instruction,
1651 SlowPathCode* slow_path) {
1652 ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction, slow_path);
Alexandre Rames374ddf32016-11-04 10:40:49 +00001653 __ Ldr(lr, MemOperand(tr, entry_point_offset));
Scott Wakelingfe885462016-09-22 10:24:38 +01001654 __ Blx(lr);
1655}
1656
Scott Wakelingfe885462016-09-22 10:24:38 +01001657void InstructionCodeGeneratorARMVIXL::HandleGoto(HInstruction* got, HBasicBlock* successor) {
1658 DCHECK(!successor->IsExitBlock());
1659 HBasicBlock* block = got->GetBlock();
1660 HInstruction* previous = got->GetPrevious();
1661 HLoopInformation* info = block->GetLoopInformation();
1662
1663 if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) {
1664 codegen_->ClearSpillSlotsFromLoopPhisInStackMap(info->GetSuspendCheck());
1665 GenerateSuspendCheck(info->GetSuspendCheck(), successor);
1666 return;
1667 }
1668 if (block->IsEntryBlock() && (previous != nullptr) && previous->IsSuspendCheck()) {
1669 GenerateSuspendCheck(previous->AsSuspendCheck(), nullptr);
1670 }
1671 if (!codegen_->GoesToNextBlock(block, successor)) {
1672 __ B(codegen_->GetLabelOf(successor));
1673 }
1674}
1675
1676void LocationsBuilderARMVIXL::VisitGoto(HGoto* got) {
1677 got->SetLocations(nullptr);
1678}
1679
1680void InstructionCodeGeneratorARMVIXL::VisitGoto(HGoto* got) {
1681 HandleGoto(got, got->GetSuccessor());
1682}
1683
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001684void LocationsBuilderARMVIXL::VisitTryBoundary(HTryBoundary* try_boundary) {
1685 try_boundary->SetLocations(nullptr);
1686}
1687
1688void InstructionCodeGeneratorARMVIXL::VisitTryBoundary(HTryBoundary* try_boundary) {
1689 HBasicBlock* successor = try_boundary->GetNormalFlowSuccessor();
1690 if (!successor->IsExitBlock()) {
1691 HandleGoto(try_boundary, successor);
1692 }
1693}
1694
Scott Wakelingfe885462016-09-22 10:24:38 +01001695void LocationsBuilderARMVIXL::VisitExit(HExit* exit) {
1696 exit->SetLocations(nullptr);
1697}
1698
1699void InstructionCodeGeneratorARMVIXL::VisitExit(HExit* exit ATTRIBUTE_UNUSED) {
1700}
1701
1702void InstructionCodeGeneratorARMVIXL::GenerateVcmp(HInstruction* instruction) {
1703 Primitive::Type type = instruction->InputAt(0)->GetType();
1704 Location lhs_loc = instruction->GetLocations()->InAt(0);
1705 Location rhs_loc = instruction->GetLocations()->InAt(1);
1706 if (rhs_loc.IsConstant()) {
1707 // 0.0 is the only immediate that can be encoded directly in
1708 // a VCMP instruction.
1709 //
1710 // Both the JLS (section 15.20.1) and the JVMS (section 6.5)
1711 // specify that in a floating-point comparison, positive zero
1712 // and negative zero are considered equal, so we can use the
1713 // literal 0.0 for both cases here.
1714 //
1715 // Note however that some methods (Float.equal, Float.compare,
1716 // Float.compareTo, Double.equal, Double.compare,
1717 // Double.compareTo, Math.max, Math.min, StrictMath.max,
1718 // StrictMath.min) consider 0.0 to be (strictly) greater than
1719 // -0.0. So if we ever translate calls to these methods into a
1720 // HCompare instruction, we must handle the -0.0 case with
1721 // care here.
1722 DCHECK(rhs_loc.GetConstant()->IsArithmeticZero());
1723 if (type == Primitive::kPrimFloat) {
1724 __ Vcmp(F32, InputSRegisterAt(instruction, 0), 0.0);
1725 } else {
1726 DCHECK_EQ(type, Primitive::kPrimDouble);
Scott Wakelingc34dba72016-10-03 10:14:44 +01001727 __ Vcmp(F64, DRegisterFrom(lhs_loc), 0.0);
Scott Wakelingfe885462016-09-22 10:24:38 +01001728 }
1729 } else {
1730 if (type == Primitive::kPrimFloat) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001731 __ Vcmp(InputSRegisterAt(instruction, 0), InputSRegisterAt(instruction, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01001732 } else {
1733 DCHECK_EQ(type, Primitive::kPrimDouble);
Scott Wakelingc34dba72016-10-03 10:14:44 +01001734 __ Vcmp(DRegisterFrom(lhs_loc), DRegisterFrom(rhs_loc));
Scott Wakelingfe885462016-09-22 10:24:38 +01001735 }
1736 }
1737}
1738
1739void InstructionCodeGeneratorARMVIXL::GenerateFPJumps(HCondition* cond,
1740 vixl32::Label* true_label,
1741 vixl32::Label* false_label ATTRIBUTE_UNUSED) {
1742 // To branch on the result of the FP compare we transfer FPSCR to APSR (encoded as PC in VMRS).
1743 __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
1744 __ B(ARMFPCondition(cond->GetCondition(), cond->IsGtBias()), true_label);
1745}
1746
1747void InstructionCodeGeneratorARMVIXL::GenerateLongComparesAndJumps(HCondition* cond,
1748 vixl32::Label* true_label,
1749 vixl32::Label* false_label) {
1750 LocationSummary* locations = cond->GetLocations();
1751 Location left = locations->InAt(0);
1752 Location right = locations->InAt(1);
1753 IfCondition if_cond = cond->GetCondition();
1754
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001755 vixl32::Register left_high = HighRegisterFrom(left);
1756 vixl32::Register left_low = LowRegisterFrom(left);
Scott Wakelingfe885462016-09-22 10:24:38 +01001757 IfCondition true_high_cond = if_cond;
1758 IfCondition false_high_cond = cond->GetOppositeCondition();
1759 vixl32::Condition final_condition = ARMUnsignedCondition(if_cond); // unsigned on lower part
1760
1761 // Set the conditions for the test, remembering that == needs to be
1762 // decided using the low words.
1763 // TODO: consider avoiding jumps with temporary and CMP low+SBC high
1764 switch (if_cond) {
1765 case kCondEQ:
1766 case kCondNE:
1767 // Nothing to do.
1768 break;
1769 case kCondLT:
1770 false_high_cond = kCondGT;
1771 break;
1772 case kCondLE:
1773 true_high_cond = kCondLT;
1774 break;
1775 case kCondGT:
1776 false_high_cond = kCondLT;
1777 break;
1778 case kCondGE:
1779 true_high_cond = kCondGT;
1780 break;
1781 case kCondB:
1782 false_high_cond = kCondA;
1783 break;
1784 case kCondBE:
1785 true_high_cond = kCondB;
1786 break;
1787 case kCondA:
1788 false_high_cond = kCondB;
1789 break;
1790 case kCondAE:
1791 true_high_cond = kCondA;
1792 break;
1793 }
1794 if (right.IsConstant()) {
1795 int64_t value = right.GetConstant()->AsLongConstant()->GetValue();
1796 int32_t val_low = Low32Bits(value);
1797 int32_t val_high = High32Bits(value);
1798
1799 __ Cmp(left_high, val_high);
1800 if (if_cond == kCondNE) {
1801 __ B(ARMCondition(true_high_cond), true_label);
1802 } else if (if_cond == kCondEQ) {
1803 __ B(ARMCondition(false_high_cond), false_label);
1804 } else {
1805 __ B(ARMCondition(true_high_cond), true_label);
1806 __ B(ARMCondition(false_high_cond), false_label);
1807 }
1808 // Must be equal high, so compare the lows.
1809 __ Cmp(left_low, val_low);
1810 } else {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001811 vixl32::Register right_high = HighRegisterFrom(right);
1812 vixl32::Register right_low = LowRegisterFrom(right);
Scott Wakelingfe885462016-09-22 10:24:38 +01001813
1814 __ Cmp(left_high, right_high);
1815 if (if_cond == kCondNE) {
1816 __ B(ARMCondition(true_high_cond), true_label);
1817 } else if (if_cond == kCondEQ) {
1818 __ B(ARMCondition(false_high_cond), false_label);
1819 } else {
1820 __ B(ARMCondition(true_high_cond), true_label);
1821 __ B(ARMCondition(false_high_cond), false_label);
1822 }
1823 // Must be equal high, so compare the lows.
1824 __ Cmp(left_low, right_low);
1825 }
1826 // The last comparison might be unsigned.
1827 // TODO: optimize cases where this is always true/false
1828 __ B(final_condition, true_label);
1829}
1830
1831void InstructionCodeGeneratorARMVIXL::GenerateCompareTestAndBranch(HCondition* condition,
1832 vixl32::Label* true_target_in,
1833 vixl32::Label* false_target_in) {
1834 // Generated branching requires both targets to be explicit. If either of the
1835 // targets is nullptr (fallthrough) use and bind `fallthrough` instead.
1836 vixl32::Label fallthrough;
1837 vixl32::Label* true_target = (true_target_in == nullptr) ? &fallthrough : true_target_in;
1838 vixl32::Label* false_target = (false_target_in == nullptr) ? &fallthrough : false_target_in;
1839
1840 Primitive::Type type = condition->InputAt(0)->GetType();
1841 switch (type) {
1842 case Primitive::kPrimLong:
1843 GenerateLongComparesAndJumps(condition, true_target, false_target);
1844 break;
1845 case Primitive::kPrimFloat:
1846 case Primitive::kPrimDouble:
1847 GenerateVcmp(condition);
1848 GenerateFPJumps(condition, true_target, false_target);
1849 break;
1850 default:
1851 LOG(FATAL) << "Unexpected compare type " << type;
1852 }
1853
1854 if (false_target != &fallthrough) {
1855 __ B(false_target);
1856 }
1857
1858 if (true_target_in == nullptr || false_target_in == nullptr) {
1859 __ Bind(&fallthrough);
1860 }
1861}
1862
1863void InstructionCodeGeneratorARMVIXL::GenerateTestAndBranch(HInstruction* instruction,
1864 size_t condition_input_index,
1865 vixl32::Label* true_target,
xueliang.zhongf51bc622016-11-04 09:23:32 +00001866 vixl32::Label* false_target,
1867 bool far_target) {
Scott Wakelingfe885462016-09-22 10:24:38 +01001868 HInstruction* cond = instruction->InputAt(condition_input_index);
1869
1870 if (true_target == nullptr && false_target == nullptr) {
1871 // Nothing to do. The code always falls through.
1872 return;
1873 } else if (cond->IsIntConstant()) {
1874 // Constant condition, statically compared against "true" (integer value 1).
1875 if (cond->AsIntConstant()->IsTrue()) {
1876 if (true_target != nullptr) {
1877 __ B(true_target);
1878 }
1879 } else {
1880 DCHECK(cond->AsIntConstant()->IsFalse()) << cond->AsIntConstant()->GetValue();
1881 if (false_target != nullptr) {
1882 __ B(false_target);
1883 }
1884 }
1885 return;
1886 }
1887
1888 // The following code generates these patterns:
1889 // (1) true_target == nullptr && false_target != nullptr
1890 // - opposite condition true => branch to false_target
1891 // (2) true_target != nullptr && false_target == nullptr
1892 // - condition true => branch to true_target
1893 // (3) true_target != nullptr && false_target != nullptr
1894 // - condition true => branch to true_target
1895 // - branch to false_target
1896 if (IsBooleanValueOrMaterializedCondition(cond)) {
1897 // Condition has been materialized, compare the output to 0.
1898 if (kIsDebugBuild) {
1899 Location cond_val = instruction->GetLocations()->InAt(condition_input_index);
1900 DCHECK(cond_val.IsRegister());
1901 }
1902 if (true_target == nullptr) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00001903 __ CompareAndBranchIfZero(InputRegisterAt(instruction, condition_input_index),
1904 false_target,
1905 far_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01001906 } else {
xueliang.zhongf51bc622016-11-04 09:23:32 +00001907 __ CompareAndBranchIfNonZero(InputRegisterAt(instruction, condition_input_index),
1908 true_target,
1909 far_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01001910 }
1911 } else {
1912 // Condition has not been materialized. Use its inputs as the comparison and
1913 // its condition as the branch condition.
1914 HCondition* condition = cond->AsCondition();
1915
1916 // If this is a long or FP comparison that has been folded into
1917 // the HCondition, generate the comparison directly.
1918 Primitive::Type type = condition->InputAt(0)->GetType();
1919 if (type == Primitive::kPrimLong || Primitive::IsFloatingPointType(type)) {
1920 GenerateCompareTestAndBranch(condition, true_target, false_target);
1921 return;
1922 }
1923
1924 LocationSummary* locations = cond->GetLocations();
1925 DCHECK(locations->InAt(0).IsRegister());
1926 vixl32::Register left = InputRegisterAt(cond, 0);
1927 Location right = locations->InAt(1);
1928 if (right.IsRegister()) {
1929 __ Cmp(left, InputRegisterAt(cond, 1));
1930 } else {
1931 DCHECK(right.IsConstant());
1932 __ Cmp(left, CodeGenerator::GetInt32ValueOf(right.GetConstant()));
1933 }
1934 if (true_target == nullptr) {
1935 __ B(ARMCondition(condition->GetOppositeCondition()), false_target);
1936 } else {
1937 __ B(ARMCondition(condition->GetCondition()), true_target);
1938 }
1939 }
1940
1941 // If neither branch falls through (case 3), the conditional branch to `true_target`
1942 // was already emitted (case 2) and we need to emit a jump to `false_target`.
1943 if (true_target != nullptr && false_target != nullptr) {
1944 __ B(false_target);
1945 }
1946}
1947
1948void LocationsBuilderARMVIXL::VisitIf(HIf* if_instr) {
1949 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr);
1950 if (IsBooleanValueOrMaterializedCondition(if_instr->InputAt(0))) {
1951 locations->SetInAt(0, Location::RequiresRegister());
1952 }
1953}
1954
1955void InstructionCodeGeneratorARMVIXL::VisitIf(HIf* if_instr) {
1956 HBasicBlock* true_successor = if_instr->IfTrueSuccessor();
1957 HBasicBlock* false_successor = if_instr->IfFalseSuccessor();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001958 vixl32::Label* true_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), true_successor) ?
1959 nullptr : codegen_->GetLabelOf(true_successor);
1960 vixl32::Label* false_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), false_successor) ?
1961 nullptr : codegen_->GetLabelOf(false_successor);
Scott Wakelingfe885462016-09-22 10:24:38 +01001962 GenerateTestAndBranch(if_instr, /* condition_input_index */ 0, true_target, false_target);
1963}
1964
Scott Wakelingc34dba72016-10-03 10:14:44 +01001965void LocationsBuilderARMVIXL::VisitDeoptimize(HDeoptimize* deoptimize) {
1966 LocationSummary* locations = new (GetGraph()->GetArena())
1967 LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath);
1968 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
1969 if (IsBooleanValueOrMaterializedCondition(deoptimize->InputAt(0))) {
1970 locations->SetInAt(0, Location::RequiresRegister());
1971 }
1972}
1973
1974void InstructionCodeGeneratorARMVIXL::VisitDeoptimize(HDeoptimize* deoptimize) {
1975 SlowPathCodeARMVIXL* slow_path =
1976 deopt_slow_paths_.NewSlowPath<DeoptimizationSlowPathARMVIXL>(deoptimize);
1977 GenerateTestAndBranch(deoptimize,
1978 /* condition_input_index */ 0,
1979 slow_path->GetEntryLabel(),
1980 /* false_target */ nullptr);
1981}
1982
Artem Serovd4cc5b22016-11-04 11:19:09 +00001983void LocationsBuilderARMVIXL::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
1984 LocationSummary* locations = new (GetGraph()->GetArena())
1985 LocationSummary(flag, LocationSummary::kNoCall);
1986 locations->SetOut(Location::RequiresRegister());
1987}
1988
1989void InstructionCodeGeneratorARMVIXL::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
1990 GetAssembler()->LoadFromOffset(kLoadWord,
1991 OutputRegister(flag),
1992 sp,
1993 codegen_->GetStackOffsetOfShouldDeoptimizeFlag());
1994}
1995
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001996void LocationsBuilderARMVIXL::VisitSelect(HSelect* select) {
1997 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select);
1998 if (Primitive::IsFloatingPointType(select->GetType())) {
1999 locations->SetInAt(0, Location::RequiresFpuRegister());
2000 locations->SetInAt(1, Location::RequiresFpuRegister());
2001 } else {
2002 locations->SetInAt(0, Location::RequiresRegister());
2003 locations->SetInAt(1, Location::RequiresRegister());
2004 }
2005 if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) {
2006 locations->SetInAt(2, Location::RequiresRegister());
2007 }
2008 locations->SetOut(Location::SameAsFirstInput());
2009}
2010
2011void InstructionCodeGeneratorARMVIXL::VisitSelect(HSelect* select) {
2012 LocationSummary* locations = select->GetLocations();
2013 vixl32::Label false_target;
2014 GenerateTestAndBranch(select,
2015 /* condition_input_index */ 2,
2016 /* true_target */ nullptr,
xueliang.zhongf51bc622016-11-04 09:23:32 +00002017 &false_target,
2018 /* far_target */ false);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002019 codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType());
2020 __ Bind(&false_target);
2021}
2022
Artem Serov551b28f2016-10-18 19:11:30 +01002023void LocationsBuilderARMVIXL::VisitNativeDebugInfo(HNativeDebugInfo* info) {
2024 new (GetGraph()->GetArena()) LocationSummary(info);
2025}
2026
2027void InstructionCodeGeneratorARMVIXL::VisitNativeDebugInfo(HNativeDebugInfo*) {
2028 // MaybeRecordNativeDebugInfo is already called implicitly in CodeGenerator::Compile.
2029}
2030
Scott Wakelingfe885462016-09-22 10:24:38 +01002031void CodeGeneratorARMVIXL::GenerateNop() {
2032 __ Nop();
2033}
2034
2035void LocationsBuilderARMVIXL::HandleCondition(HCondition* cond) {
2036 LocationSummary* locations =
2037 new (GetGraph()->GetArena()) LocationSummary(cond, LocationSummary::kNoCall);
2038 // Handle the long/FP comparisons made in instruction simplification.
2039 switch (cond->InputAt(0)->GetType()) {
2040 case Primitive::kPrimLong:
2041 locations->SetInAt(0, Location::RequiresRegister());
2042 locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
2043 if (!cond->IsEmittedAtUseSite()) {
2044 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
2045 }
2046 break;
2047
Scott Wakelingfe885462016-09-22 10:24:38 +01002048 case Primitive::kPrimFloat:
2049 case Primitive::kPrimDouble:
2050 locations->SetInAt(0, Location::RequiresFpuRegister());
Artem Serov657022c2016-11-23 14:19:38 +00002051 locations->SetInAt(1, ArithmeticZeroOrFpuRegister(cond->InputAt(1)));
Scott Wakelingfe885462016-09-22 10:24:38 +01002052 if (!cond->IsEmittedAtUseSite()) {
2053 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2054 }
2055 break;
2056
2057 default:
2058 locations->SetInAt(0, Location::RequiresRegister());
2059 locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
2060 if (!cond->IsEmittedAtUseSite()) {
2061 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2062 }
2063 }
2064}
2065
2066void InstructionCodeGeneratorARMVIXL::HandleCondition(HCondition* cond) {
2067 if (cond->IsEmittedAtUseSite()) {
2068 return;
2069 }
2070
Artem Serov657022c2016-11-23 14:19:38 +00002071 Location right = cond->GetLocations()->InAt(1);
Scott Wakelingfe885462016-09-22 10:24:38 +01002072 vixl32::Register out = OutputRegister(cond);
2073 vixl32::Label true_label, false_label;
2074
2075 switch (cond->InputAt(0)->GetType()) {
2076 default: {
2077 // Integer case.
Artem Serov657022c2016-11-23 14:19:38 +00002078 if (right.IsRegister()) {
2079 __ Cmp(InputRegisterAt(cond, 0), InputOperandAt(cond, 1));
2080 } else {
2081 DCHECK(right.IsConstant());
2082 __ Cmp(InputRegisterAt(cond, 0),
2083 CodeGenerator::GetInt32ValueOf(right.GetConstant()));
2084 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002085 AssemblerAccurateScope aas(GetVIXLAssembler(),
Alexandre Rames374ddf32016-11-04 10:40:49 +00002086 3 * vixl32::kMaxInstructionSizeInBytes,
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002087 CodeBufferCheckScope::kMaximumSize);
2088 __ ite(ARMCondition(cond->GetCondition()));
2089 __ mov(ARMCondition(cond->GetCondition()), OutputRegister(cond), 1);
2090 __ mov(ARMCondition(cond->GetOppositeCondition()), OutputRegister(cond), 0);
Scott Wakelingfe885462016-09-22 10:24:38 +01002091 return;
2092 }
2093 case Primitive::kPrimLong:
2094 GenerateLongComparesAndJumps(cond, &true_label, &false_label);
2095 break;
2096 case Primitive::kPrimFloat:
2097 case Primitive::kPrimDouble:
2098 GenerateVcmp(cond);
2099 GenerateFPJumps(cond, &true_label, &false_label);
2100 break;
2101 }
2102
2103 // Convert the jumps into the result.
2104 vixl32::Label done_label;
2105
2106 // False case: result = 0.
2107 __ Bind(&false_label);
2108 __ Mov(out, 0);
2109 __ B(&done_label);
2110
2111 // True case: result = 1.
2112 __ Bind(&true_label);
2113 __ Mov(out, 1);
2114 __ Bind(&done_label);
2115}
2116
2117void LocationsBuilderARMVIXL::VisitEqual(HEqual* comp) {
2118 HandleCondition(comp);
2119}
2120
2121void InstructionCodeGeneratorARMVIXL::VisitEqual(HEqual* comp) {
2122 HandleCondition(comp);
2123}
2124
2125void LocationsBuilderARMVIXL::VisitNotEqual(HNotEqual* comp) {
2126 HandleCondition(comp);
2127}
2128
2129void InstructionCodeGeneratorARMVIXL::VisitNotEqual(HNotEqual* comp) {
2130 HandleCondition(comp);
2131}
2132
2133void LocationsBuilderARMVIXL::VisitLessThan(HLessThan* comp) {
2134 HandleCondition(comp);
2135}
2136
2137void InstructionCodeGeneratorARMVIXL::VisitLessThan(HLessThan* comp) {
2138 HandleCondition(comp);
2139}
2140
2141void LocationsBuilderARMVIXL::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
2142 HandleCondition(comp);
2143}
2144
2145void InstructionCodeGeneratorARMVIXL::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
2146 HandleCondition(comp);
2147}
2148
2149void LocationsBuilderARMVIXL::VisitGreaterThan(HGreaterThan* comp) {
2150 HandleCondition(comp);
2151}
2152
2153void InstructionCodeGeneratorARMVIXL::VisitGreaterThan(HGreaterThan* comp) {
2154 HandleCondition(comp);
2155}
2156
2157void LocationsBuilderARMVIXL::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
2158 HandleCondition(comp);
2159}
2160
2161void InstructionCodeGeneratorARMVIXL::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
2162 HandleCondition(comp);
2163}
2164
2165void LocationsBuilderARMVIXL::VisitBelow(HBelow* comp) {
2166 HandleCondition(comp);
2167}
2168
2169void InstructionCodeGeneratorARMVIXL::VisitBelow(HBelow* comp) {
2170 HandleCondition(comp);
2171}
2172
2173void LocationsBuilderARMVIXL::VisitBelowOrEqual(HBelowOrEqual* comp) {
2174 HandleCondition(comp);
2175}
2176
2177void InstructionCodeGeneratorARMVIXL::VisitBelowOrEqual(HBelowOrEqual* comp) {
2178 HandleCondition(comp);
2179}
2180
2181void LocationsBuilderARMVIXL::VisitAbove(HAbove* comp) {
2182 HandleCondition(comp);
2183}
2184
2185void InstructionCodeGeneratorARMVIXL::VisitAbove(HAbove* comp) {
2186 HandleCondition(comp);
2187}
2188
2189void LocationsBuilderARMVIXL::VisitAboveOrEqual(HAboveOrEqual* comp) {
2190 HandleCondition(comp);
2191}
2192
2193void InstructionCodeGeneratorARMVIXL::VisitAboveOrEqual(HAboveOrEqual* comp) {
2194 HandleCondition(comp);
2195}
2196
2197void LocationsBuilderARMVIXL::VisitIntConstant(HIntConstant* constant) {
2198 LocationSummary* locations =
2199 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
2200 locations->SetOut(Location::ConstantLocation(constant));
2201}
2202
2203void InstructionCodeGeneratorARMVIXL::VisitIntConstant(HIntConstant* constant ATTRIBUTE_UNUSED) {
2204 // Will be generated at use site.
2205}
2206
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002207void LocationsBuilderARMVIXL::VisitNullConstant(HNullConstant* constant) {
2208 LocationSummary* locations =
2209 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
2210 locations->SetOut(Location::ConstantLocation(constant));
2211}
2212
2213void InstructionCodeGeneratorARMVIXL::VisitNullConstant(HNullConstant* constant ATTRIBUTE_UNUSED) {
2214 // Will be generated at use site.
2215}
2216
Scott Wakelingfe885462016-09-22 10:24:38 +01002217void LocationsBuilderARMVIXL::VisitLongConstant(HLongConstant* constant) {
2218 LocationSummary* locations =
2219 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
2220 locations->SetOut(Location::ConstantLocation(constant));
2221}
2222
2223void InstructionCodeGeneratorARMVIXL::VisitLongConstant(HLongConstant* constant ATTRIBUTE_UNUSED) {
2224 // Will be generated at use site.
2225}
2226
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01002227void LocationsBuilderARMVIXL::VisitFloatConstant(HFloatConstant* constant) {
2228 LocationSummary* locations =
2229 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
2230 locations->SetOut(Location::ConstantLocation(constant));
2231}
2232
Scott Wakelingc34dba72016-10-03 10:14:44 +01002233void InstructionCodeGeneratorARMVIXL::VisitFloatConstant(
2234 HFloatConstant* constant ATTRIBUTE_UNUSED) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01002235 // Will be generated at use site.
2236}
2237
2238void LocationsBuilderARMVIXL::VisitDoubleConstant(HDoubleConstant* constant) {
2239 LocationSummary* locations =
2240 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
2241 locations->SetOut(Location::ConstantLocation(constant));
2242}
2243
Scott Wakelingc34dba72016-10-03 10:14:44 +01002244void InstructionCodeGeneratorARMVIXL::VisitDoubleConstant(
2245 HDoubleConstant* constant ATTRIBUTE_UNUSED) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01002246 // Will be generated at use site.
2247}
2248
Scott Wakelingfe885462016-09-22 10:24:38 +01002249void LocationsBuilderARMVIXL::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
2250 memory_barrier->SetLocations(nullptr);
2251}
2252
2253void InstructionCodeGeneratorARMVIXL::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
2254 codegen_->GenerateMemoryBarrier(memory_barrier->GetBarrierKind());
2255}
2256
2257void LocationsBuilderARMVIXL::VisitReturnVoid(HReturnVoid* ret) {
2258 ret->SetLocations(nullptr);
2259}
2260
2261void InstructionCodeGeneratorARMVIXL::VisitReturnVoid(HReturnVoid* ret ATTRIBUTE_UNUSED) {
2262 codegen_->GenerateFrameExit();
2263}
2264
2265void LocationsBuilderARMVIXL::VisitReturn(HReturn* ret) {
2266 LocationSummary* locations =
2267 new (GetGraph()->GetArena()) LocationSummary(ret, LocationSummary::kNoCall);
2268 locations->SetInAt(0, parameter_visitor_.GetReturnLocation(ret->InputAt(0)->GetType()));
2269}
2270
2271void InstructionCodeGeneratorARMVIXL::VisitReturn(HReturn* ret ATTRIBUTE_UNUSED) {
2272 codegen_->GenerateFrameExit();
2273}
2274
Artem Serovcfbe9132016-10-14 15:58:56 +01002275void LocationsBuilderARMVIXL::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
2276 // The trampoline uses the same calling convention as dex calling conventions,
2277 // except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain
2278 // the method_idx.
2279 HandleInvoke(invoke);
2280}
2281
2282void InstructionCodeGeneratorARMVIXL::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
2283 codegen_->GenerateInvokeUnresolvedRuntimeCall(invoke);
2284}
2285
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002286void LocationsBuilderARMVIXL::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
2287 // Explicit clinit checks triggered by static invokes must have been pruned by
2288 // art::PrepareForRegisterAllocation.
2289 DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
2290
Anton Kirilov5ec62182016-10-13 20:16:02 +01002291 IntrinsicLocationsBuilderARMVIXL intrinsic(codegen_);
2292 if (intrinsic.TryDispatch(invoke)) {
2293 if (invoke->GetLocations()->CanCall() && invoke->HasPcRelativeDexCache()) {
2294 invoke->GetLocations()->SetInAt(invoke->GetSpecialInputIndex(), Location::Any());
2295 }
2296 return;
2297 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002298
2299 HandleInvoke(invoke);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01002300
Artem Serovd4cc5b22016-11-04 11:19:09 +00002301 // For PC-relative dex cache the invoke has an extra input, the PC-relative address base.
2302 if (invoke->HasPcRelativeDexCache()) {
2303 invoke->GetLocations()->SetInAt(invoke->GetSpecialInputIndex(), Location::RequiresRegister());
2304 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002305}
2306
Anton Kirilov5ec62182016-10-13 20:16:02 +01002307static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorARMVIXL* codegen) {
2308 if (invoke->GetLocations()->Intrinsified()) {
2309 IntrinsicCodeGeneratorARMVIXL intrinsic(codegen);
2310 intrinsic.Dispatch(invoke);
2311 return true;
2312 }
2313 return false;
2314}
2315
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002316void InstructionCodeGeneratorARMVIXL::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
2317 // Explicit clinit checks triggered by static invokes must have been pruned by
2318 // art::PrepareForRegisterAllocation.
2319 DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
2320
Anton Kirilov5ec62182016-10-13 20:16:02 +01002321 if (TryGenerateIntrinsicCode(invoke, codegen_)) {
2322 return;
2323 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002324
2325 LocationSummary* locations = invoke->GetLocations();
Artem Serovd4cc5b22016-11-04 11:19:09 +00002326 codegen_->GenerateStaticOrDirectCall(
2327 invoke, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002328 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
2329}
2330
2331void LocationsBuilderARMVIXL::HandleInvoke(HInvoke* invoke) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002332 InvokeDexCallingConventionVisitorARMVIXL calling_convention_visitor;
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002333 CodeGenerator::CreateCommonInvokeLocationSummary(invoke, &calling_convention_visitor);
2334}
2335
2336void LocationsBuilderARMVIXL::VisitInvokeVirtual(HInvokeVirtual* invoke) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01002337 IntrinsicLocationsBuilderARMVIXL intrinsic(codegen_);
2338 if (intrinsic.TryDispatch(invoke)) {
2339 return;
2340 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002341
2342 HandleInvoke(invoke);
2343}
2344
2345void InstructionCodeGeneratorARMVIXL::VisitInvokeVirtual(HInvokeVirtual* invoke) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01002346 if (TryGenerateIntrinsicCode(invoke, codegen_)) {
2347 return;
2348 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002349
2350 codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002351 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
Alexandre Rames374ddf32016-11-04 10:40:49 +00002352 DCHECK(!codegen_->IsLeafMethod());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002353}
2354
Artem Serovcfbe9132016-10-14 15:58:56 +01002355void LocationsBuilderARMVIXL::VisitInvokeInterface(HInvokeInterface* invoke) {
2356 HandleInvoke(invoke);
2357 // Add the hidden argument.
2358 invoke->GetLocations()->AddTemp(LocationFrom(r12));
2359}
2360
2361void InstructionCodeGeneratorARMVIXL::VisitInvokeInterface(HInvokeInterface* invoke) {
2362 // TODO: b/18116999, our IMTs can miss an IncompatibleClassChangeError.
2363 LocationSummary* locations = invoke->GetLocations();
2364 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
2365 vixl32::Register hidden_reg = RegisterFrom(locations->GetTemp(1));
2366 Location receiver = locations->InAt(0);
2367 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
2368
2369 DCHECK(!receiver.IsStackSlot());
2370
Alexandre Rames374ddf32016-11-04 10:40:49 +00002371 // Ensure the pc position is recorded immediately after the `ldr` instruction.
2372 {
2373 AssemblerAccurateScope aas(GetVIXLAssembler(),
2374 vixl32::kMaxInstructionSizeInBytes,
2375 CodeBufferCheckScope::kMaximumSize);
2376 // /* HeapReference<Class> */ temp = receiver->klass_
2377 __ ldr(temp, MemOperand(RegisterFrom(receiver), class_offset));
2378 codegen_->MaybeRecordImplicitNullCheck(invoke);
2379 }
Artem Serovcfbe9132016-10-14 15:58:56 +01002380 // Instead of simply (possibly) unpoisoning `temp` here, we should
2381 // emit a read barrier for the previous class reference load.
2382 // However this is not required in practice, as this is an
2383 // intermediate/temporary reference and because the current
2384 // concurrent copying collector keeps the from-space memory
2385 // intact/accessible until the end of the marking phase (the
2386 // concurrent copying collector may not in the future).
2387 GetAssembler()->MaybeUnpoisonHeapReference(temp);
2388 GetAssembler()->LoadFromOffset(kLoadWord,
2389 temp,
2390 temp,
2391 mirror::Class::ImtPtrOffset(kArmPointerSize).Uint32Value());
2392 uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement(
2393 invoke->GetImtIndex(), kArmPointerSize));
2394 // temp = temp->GetImtEntryAt(method_offset);
2395 GetAssembler()->LoadFromOffset(kLoadWord, temp, temp, method_offset);
2396 uint32_t entry_point =
2397 ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value();
2398 // LR = temp->GetEntryPoint();
2399 GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, entry_point);
2400
2401 // Set the hidden (in r12) argument. It is done here, right before a BLX to prevent other
2402 // instruction from clobbering it as they might use r12 as a scratch register.
2403 DCHECK(hidden_reg.Is(r12));
Scott Wakelingb77051e2016-11-21 19:46:00 +00002404
2405 {
2406 // The VIXL macro assembler may clobber any of the scratch registers that are available to it,
2407 // so it checks if the application is using them (by passing them to the macro assembler
2408 // methods). The following application of UseScratchRegisterScope corrects VIXL's notion of
2409 // what is available, and is the opposite of the standard usage: Instead of requesting a
2410 // temporary location, it imposes an external constraint (i.e. a specific register is reserved
2411 // for the hidden argument). Note that this works even if VIXL needs a scratch register itself
2412 // (to materialize the constant), since the destination register becomes available for such use
2413 // internally for the duration of the macro instruction.
2414 UseScratchRegisterScope temps(GetVIXLAssembler());
2415 temps.Exclude(hidden_reg);
2416 __ Mov(hidden_reg, invoke->GetDexMethodIndex());
2417 }
Artem Serovcfbe9132016-10-14 15:58:56 +01002418 {
Alexandre Rames374ddf32016-11-04 10:40:49 +00002419 // Ensure the pc position is recorded immediately after the `blx` instruction.
2420 // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
Artem Serovcfbe9132016-10-14 15:58:56 +01002421 AssemblerAccurateScope aas(GetVIXLAssembler(),
Alexandre Rames374ddf32016-11-04 10:40:49 +00002422 vixl32::k16BitT32InstructionSizeInBytes,
2423 CodeBufferCheckScope::kExactSize);
Artem Serovcfbe9132016-10-14 15:58:56 +01002424 // LR();
2425 __ blx(lr);
Artem Serovcfbe9132016-10-14 15:58:56 +01002426 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
Alexandre Rames374ddf32016-11-04 10:40:49 +00002427 DCHECK(!codegen_->IsLeafMethod());
Artem Serovcfbe9132016-10-14 15:58:56 +01002428 }
2429}
2430
Artem Serov02109dd2016-09-23 17:17:54 +01002431void LocationsBuilderARMVIXL::VisitNeg(HNeg* neg) {
2432 LocationSummary* locations =
2433 new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall);
2434 switch (neg->GetResultType()) {
2435 case Primitive::kPrimInt: {
2436 locations->SetInAt(0, Location::RequiresRegister());
2437 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2438 break;
2439 }
2440 case Primitive::kPrimLong: {
2441 locations->SetInAt(0, Location::RequiresRegister());
2442 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
2443 break;
2444 }
2445
2446 case Primitive::kPrimFloat:
2447 case Primitive::kPrimDouble:
2448 locations->SetInAt(0, Location::RequiresFpuRegister());
2449 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2450 break;
2451
2452 default:
2453 LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
2454 }
2455}
2456
2457void InstructionCodeGeneratorARMVIXL::VisitNeg(HNeg* neg) {
2458 LocationSummary* locations = neg->GetLocations();
2459 Location out = locations->Out();
2460 Location in = locations->InAt(0);
2461 switch (neg->GetResultType()) {
2462 case Primitive::kPrimInt:
2463 __ Rsb(OutputRegister(neg), InputRegisterAt(neg, 0), 0);
2464 break;
2465
2466 case Primitive::kPrimLong:
2467 // out.lo = 0 - in.lo (and update the carry/borrow (C) flag)
2468 __ Rsbs(LowRegisterFrom(out), LowRegisterFrom(in), 0);
2469 // We cannot emit an RSC (Reverse Subtract with Carry)
2470 // instruction here, as it does not exist in the Thumb-2
2471 // instruction set. We use the following approach
2472 // using SBC and SUB instead.
2473 //
2474 // out.hi = -C
2475 __ Sbc(HighRegisterFrom(out), HighRegisterFrom(out), HighRegisterFrom(out));
2476 // out.hi = out.hi - in.hi
2477 __ Sub(HighRegisterFrom(out), HighRegisterFrom(out), HighRegisterFrom(in));
2478 break;
2479
2480 case Primitive::kPrimFloat:
2481 case Primitive::kPrimDouble:
Anton Kirilove28d9ae2016-10-25 18:17:23 +01002482 // TODO(VIXL): Consider introducing an InputVRegister()
2483 // helper function (equivalent to InputRegister()).
Artem Serov02109dd2016-09-23 17:17:54 +01002484 __ Vneg(OutputVRegister(neg), InputVRegisterAt(neg, 0));
2485 break;
2486
2487 default:
2488 LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
2489 }
2490}
2491
Scott Wakelingfe885462016-09-22 10:24:38 +01002492void LocationsBuilderARMVIXL::VisitTypeConversion(HTypeConversion* conversion) {
2493 Primitive::Type result_type = conversion->GetResultType();
2494 Primitive::Type input_type = conversion->GetInputType();
2495 DCHECK_NE(result_type, input_type);
2496
2497 // The float-to-long, double-to-long and long-to-float type conversions
2498 // rely on a call to the runtime.
2499 LocationSummary::CallKind call_kind =
2500 (((input_type == Primitive::kPrimFloat || input_type == Primitive::kPrimDouble)
2501 && result_type == Primitive::kPrimLong)
2502 || (input_type == Primitive::kPrimLong && result_type == Primitive::kPrimFloat))
2503 ? LocationSummary::kCallOnMainOnly
2504 : LocationSummary::kNoCall;
2505 LocationSummary* locations =
2506 new (GetGraph()->GetArena()) LocationSummary(conversion, call_kind);
2507
2508 // The Java language does not allow treating boolean as an integral type but
2509 // our bit representation makes it safe.
2510
2511 switch (result_type) {
2512 case Primitive::kPrimByte:
2513 switch (input_type) {
2514 case Primitive::kPrimLong:
2515 // Type conversion from long to byte is a result of code transformations.
2516 case Primitive::kPrimBoolean:
2517 // Boolean input is a result of code transformations.
2518 case Primitive::kPrimShort:
2519 case Primitive::kPrimInt:
2520 case Primitive::kPrimChar:
2521 // Processing a Dex `int-to-byte' instruction.
2522 locations->SetInAt(0, Location::RequiresRegister());
2523 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2524 break;
2525
2526 default:
2527 LOG(FATAL) << "Unexpected type conversion from " << input_type
2528 << " to " << result_type;
2529 }
2530 break;
2531
2532 case Primitive::kPrimShort:
2533 switch (input_type) {
2534 case Primitive::kPrimLong:
2535 // Type conversion from long to short is a result of code transformations.
2536 case Primitive::kPrimBoolean:
2537 // Boolean input is a result of code transformations.
2538 case Primitive::kPrimByte:
2539 case Primitive::kPrimInt:
2540 case Primitive::kPrimChar:
2541 // Processing a Dex `int-to-short' instruction.
2542 locations->SetInAt(0, Location::RequiresRegister());
2543 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2544 break;
2545
2546 default:
2547 LOG(FATAL) << "Unexpected type conversion from " << input_type
2548 << " to " << result_type;
2549 }
2550 break;
2551
2552 case Primitive::kPrimInt:
2553 switch (input_type) {
2554 case Primitive::kPrimLong:
2555 // Processing a Dex `long-to-int' instruction.
2556 locations->SetInAt(0, Location::Any());
2557 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2558 break;
2559
2560 case Primitive::kPrimFloat:
2561 // Processing a Dex `float-to-int' instruction.
2562 locations->SetInAt(0, Location::RequiresFpuRegister());
2563 locations->SetOut(Location::RequiresRegister());
2564 locations->AddTemp(Location::RequiresFpuRegister());
2565 break;
2566
2567 case Primitive::kPrimDouble:
2568 // Processing a Dex `double-to-int' instruction.
2569 locations->SetInAt(0, Location::RequiresFpuRegister());
2570 locations->SetOut(Location::RequiresRegister());
2571 locations->AddTemp(Location::RequiresFpuRegister());
2572 break;
2573
2574 default:
2575 LOG(FATAL) << "Unexpected type conversion from " << input_type
2576 << " to " << result_type;
2577 }
2578 break;
2579
2580 case Primitive::kPrimLong:
2581 switch (input_type) {
2582 case Primitive::kPrimBoolean:
2583 // Boolean input is a result of code transformations.
2584 case Primitive::kPrimByte:
2585 case Primitive::kPrimShort:
2586 case Primitive::kPrimInt:
2587 case Primitive::kPrimChar:
2588 // Processing a Dex `int-to-long' instruction.
2589 locations->SetInAt(0, Location::RequiresRegister());
2590 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2591 break;
2592
2593 case Primitive::kPrimFloat: {
2594 // Processing a Dex `float-to-long' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002595 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2596 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
2597 locations->SetOut(LocationFrom(r0, r1));
Scott Wakelingfe885462016-09-22 10:24:38 +01002598 break;
2599 }
2600
2601 case Primitive::kPrimDouble: {
2602 // Processing a Dex `double-to-long' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002603 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2604 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0),
2605 calling_convention.GetFpuRegisterAt(1)));
2606 locations->SetOut(LocationFrom(r0, r1));
Scott Wakelingfe885462016-09-22 10:24:38 +01002607 break;
2608 }
2609
2610 default:
2611 LOG(FATAL) << "Unexpected type conversion from " << input_type
2612 << " to " << result_type;
2613 }
2614 break;
2615
2616 case Primitive::kPrimChar:
2617 switch (input_type) {
2618 case Primitive::kPrimLong:
2619 // Type conversion from long to char is a result of code transformations.
2620 case Primitive::kPrimBoolean:
2621 // Boolean input is a result of code transformations.
2622 case Primitive::kPrimByte:
2623 case Primitive::kPrimShort:
2624 case Primitive::kPrimInt:
2625 // Processing a Dex `int-to-char' instruction.
2626 locations->SetInAt(0, Location::RequiresRegister());
2627 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2628 break;
2629
2630 default:
2631 LOG(FATAL) << "Unexpected type conversion from " << input_type
2632 << " to " << result_type;
2633 }
2634 break;
2635
2636 case Primitive::kPrimFloat:
2637 switch (input_type) {
2638 case Primitive::kPrimBoolean:
2639 // Boolean input is a result of code transformations.
2640 case Primitive::kPrimByte:
2641 case Primitive::kPrimShort:
2642 case Primitive::kPrimInt:
2643 case Primitive::kPrimChar:
2644 // Processing a Dex `int-to-float' instruction.
2645 locations->SetInAt(0, Location::RequiresRegister());
2646 locations->SetOut(Location::RequiresFpuRegister());
2647 break;
2648
2649 case Primitive::kPrimLong: {
2650 // Processing a Dex `long-to-float' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002651 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2652 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0),
2653 calling_convention.GetRegisterAt(1)));
2654 locations->SetOut(LocationFrom(calling_convention.GetFpuRegisterAt(0)));
Scott Wakelingfe885462016-09-22 10:24:38 +01002655 break;
2656 }
2657
2658 case Primitive::kPrimDouble:
2659 // Processing a Dex `double-to-float' instruction.
2660 locations->SetInAt(0, Location::RequiresFpuRegister());
2661 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2662 break;
2663
2664 default:
2665 LOG(FATAL) << "Unexpected type conversion from " << input_type
2666 << " to " << result_type;
2667 };
2668 break;
2669
2670 case Primitive::kPrimDouble:
2671 switch (input_type) {
2672 case Primitive::kPrimBoolean:
2673 // Boolean input is a result of code transformations.
2674 case Primitive::kPrimByte:
2675 case Primitive::kPrimShort:
2676 case Primitive::kPrimInt:
2677 case Primitive::kPrimChar:
2678 // Processing a Dex `int-to-double' instruction.
2679 locations->SetInAt(0, Location::RequiresRegister());
2680 locations->SetOut(Location::RequiresFpuRegister());
2681 break;
2682
2683 case Primitive::kPrimLong:
2684 // Processing a Dex `long-to-double' instruction.
2685 locations->SetInAt(0, Location::RequiresRegister());
2686 locations->SetOut(Location::RequiresFpuRegister());
2687 locations->AddTemp(Location::RequiresFpuRegister());
2688 locations->AddTemp(Location::RequiresFpuRegister());
2689 break;
2690
2691 case Primitive::kPrimFloat:
2692 // Processing a Dex `float-to-double' instruction.
2693 locations->SetInAt(0, Location::RequiresFpuRegister());
2694 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2695 break;
2696
2697 default:
2698 LOG(FATAL) << "Unexpected type conversion from " << input_type
2699 << " to " << result_type;
2700 };
2701 break;
2702
2703 default:
2704 LOG(FATAL) << "Unexpected type conversion from " << input_type
2705 << " to " << result_type;
2706 }
2707}
2708
2709void InstructionCodeGeneratorARMVIXL::VisitTypeConversion(HTypeConversion* conversion) {
2710 LocationSummary* locations = conversion->GetLocations();
2711 Location out = locations->Out();
2712 Location in = locations->InAt(0);
2713 Primitive::Type result_type = conversion->GetResultType();
2714 Primitive::Type input_type = conversion->GetInputType();
2715 DCHECK_NE(result_type, input_type);
2716 switch (result_type) {
2717 case Primitive::kPrimByte:
2718 switch (input_type) {
2719 case Primitive::kPrimLong:
2720 // Type conversion from long to byte is a result of code transformations.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002721 __ Sbfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 8);
Scott Wakelingfe885462016-09-22 10:24:38 +01002722 break;
2723 case Primitive::kPrimBoolean:
2724 // Boolean input is a result of code transformations.
2725 case Primitive::kPrimShort:
2726 case Primitive::kPrimInt:
2727 case Primitive::kPrimChar:
2728 // Processing a Dex `int-to-byte' instruction.
2729 __ Sbfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 8);
2730 break;
2731
2732 default:
2733 LOG(FATAL) << "Unexpected type conversion from " << input_type
2734 << " to " << result_type;
2735 }
2736 break;
2737
2738 case Primitive::kPrimShort:
2739 switch (input_type) {
2740 case Primitive::kPrimLong:
2741 // Type conversion from long to short is a result of code transformations.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002742 __ Sbfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 16);
Scott Wakelingfe885462016-09-22 10:24:38 +01002743 break;
2744 case Primitive::kPrimBoolean:
2745 // Boolean input is a result of code transformations.
2746 case Primitive::kPrimByte:
2747 case Primitive::kPrimInt:
2748 case Primitive::kPrimChar:
2749 // Processing a Dex `int-to-short' instruction.
2750 __ Sbfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 16);
2751 break;
2752
2753 default:
2754 LOG(FATAL) << "Unexpected type conversion from " << input_type
2755 << " to " << result_type;
2756 }
2757 break;
2758
2759 case Primitive::kPrimInt:
2760 switch (input_type) {
2761 case Primitive::kPrimLong:
2762 // Processing a Dex `long-to-int' instruction.
2763 DCHECK(out.IsRegister());
2764 if (in.IsRegisterPair()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002765 __ Mov(OutputRegister(conversion), LowRegisterFrom(in));
Scott Wakelingfe885462016-09-22 10:24:38 +01002766 } else if (in.IsDoubleStackSlot()) {
2767 GetAssembler()->LoadFromOffset(kLoadWord,
2768 OutputRegister(conversion),
2769 sp,
2770 in.GetStackIndex());
2771 } else {
2772 DCHECK(in.IsConstant());
2773 DCHECK(in.GetConstant()->IsLongConstant());
2774 int64_t value = in.GetConstant()->AsLongConstant()->GetValue();
2775 __ Mov(OutputRegister(conversion), static_cast<int32_t>(value));
2776 }
2777 break;
2778
2779 case Primitive::kPrimFloat: {
2780 // Processing a Dex `float-to-int' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002781 vixl32::SRegister temp = LowSRegisterFrom(locations->GetTemp(0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01002782 __ Vcvt(S32, F32, temp, InputSRegisterAt(conversion, 0));
Scott Wakelingfe885462016-09-22 10:24:38 +01002783 __ Vmov(OutputRegister(conversion), temp);
2784 break;
2785 }
2786
2787 case Primitive::kPrimDouble: {
2788 // Processing a Dex `double-to-int' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002789 vixl32::SRegister temp_s = LowSRegisterFrom(locations->GetTemp(0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01002790 __ Vcvt(S32, F64, temp_s, DRegisterFrom(in));
Scott Wakelingfe885462016-09-22 10:24:38 +01002791 __ Vmov(OutputRegister(conversion), temp_s);
2792 break;
2793 }
2794
2795 default:
2796 LOG(FATAL) << "Unexpected type conversion from " << input_type
2797 << " to " << result_type;
2798 }
2799 break;
2800
2801 case Primitive::kPrimLong:
2802 switch (input_type) {
2803 case Primitive::kPrimBoolean:
2804 // Boolean input is a result of code transformations.
2805 case Primitive::kPrimByte:
2806 case Primitive::kPrimShort:
2807 case Primitive::kPrimInt:
2808 case Primitive::kPrimChar:
2809 // Processing a Dex `int-to-long' instruction.
2810 DCHECK(out.IsRegisterPair());
2811 DCHECK(in.IsRegister());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002812 __ Mov(LowRegisterFrom(out), InputRegisterAt(conversion, 0));
Scott Wakelingfe885462016-09-22 10:24:38 +01002813 // Sign extension.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002814 __ Asr(HighRegisterFrom(out), LowRegisterFrom(out), 31);
Scott Wakelingfe885462016-09-22 10:24:38 +01002815 break;
2816
2817 case Primitive::kPrimFloat:
2818 // Processing a Dex `float-to-long' instruction.
2819 codegen_->InvokeRuntime(kQuickF2l, conversion, conversion->GetDexPc());
2820 CheckEntrypointTypes<kQuickF2l, int64_t, float>();
2821 break;
2822
2823 case Primitive::kPrimDouble:
2824 // Processing a Dex `double-to-long' instruction.
2825 codegen_->InvokeRuntime(kQuickD2l, conversion, conversion->GetDexPc());
2826 CheckEntrypointTypes<kQuickD2l, int64_t, double>();
2827 break;
2828
2829 default:
2830 LOG(FATAL) << "Unexpected type conversion from " << input_type
2831 << " to " << result_type;
2832 }
2833 break;
2834
2835 case Primitive::kPrimChar:
2836 switch (input_type) {
2837 case Primitive::kPrimLong:
2838 // Type conversion from long to char is a result of code transformations.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002839 __ Ubfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 16);
Scott Wakelingfe885462016-09-22 10:24:38 +01002840 break;
2841 case Primitive::kPrimBoolean:
2842 // Boolean input is a result of code transformations.
2843 case Primitive::kPrimByte:
2844 case Primitive::kPrimShort:
2845 case Primitive::kPrimInt:
2846 // Processing a Dex `int-to-char' instruction.
2847 __ Ubfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 16);
2848 break;
2849
2850 default:
2851 LOG(FATAL) << "Unexpected type conversion from " << input_type
2852 << " to " << result_type;
2853 }
2854 break;
2855
2856 case Primitive::kPrimFloat:
2857 switch (input_type) {
2858 case Primitive::kPrimBoolean:
2859 // Boolean input is a result of code transformations.
2860 case Primitive::kPrimByte:
2861 case Primitive::kPrimShort:
2862 case Primitive::kPrimInt:
2863 case Primitive::kPrimChar: {
2864 // Processing a Dex `int-to-float' instruction.
2865 __ Vmov(OutputSRegister(conversion), InputRegisterAt(conversion, 0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01002866 __ Vcvt(F32, S32, OutputSRegister(conversion), OutputSRegister(conversion));
Scott Wakelingfe885462016-09-22 10:24:38 +01002867 break;
2868 }
2869
2870 case Primitive::kPrimLong:
2871 // Processing a Dex `long-to-float' instruction.
2872 codegen_->InvokeRuntime(kQuickL2f, conversion, conversion->GetDexPc());
2873 CheckEntrypointTypes<kQuickL2f, float, int64_t>();
2874 break;
2875
2876 case Primitive::kPrimDouble:
2877 // Processing a Dex `double-to-float' instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01002878 __ Vcvt(F32, F64, OutputSRegister(conversion), DRegisterFrom(in));
Scott Wakelingfe885462016-09-22 10:24:38 +01002879 break;
2880
2881 default:
2882 LOG(FATAL) << "Unexpected type conversion from " << input_type
2883 << " to " << result_type;
2884 };
2885 break;
2886
2887 case Primitive::kPrimDouble:
2888 switch (input_type) {
2889 case Primitive::kPrimBoolean:
2890 // Boolean input is a result of code transformations.
2891 case Primitive::kPrimByte:
2892 case Primitive::kPrimShort:
2893 case Primitive::kPrimInt:
2894 case Primitive::kPrimChar: {
2895 // Processing a Dex `int-to-double' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002896 __ Vmov(LowSRegisterFrom(out), InputRegisterAt(conversion, 0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01002897 __ Vcvt(F64, S32, DRegisterFrom(out), LowSRegisterFrom(out));
Scott Wakelingfe885462016-09-22 10:24:38 +01002898 break;
2899 }
2900
2901 case Primitive::kPrimLong: {
2902 // Processing a Dex `long-to-double' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002903 vixl32::Register low = LowRegisterFrom(in);
2904 vixl32::Register high = HighRegisterFrom(in);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002905 vixl32::SRegister out_s = LowSRegisterFrom(out);
Scott Wakelingc34dba72016-10-03 10:14:44 +01002906 vixl32::DRegister out_d = DRegisterFrom(out);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002907 vixl32::SRegister temp_s = LowSRegisterFrom(locations->GetTemp(0));
Scott Wakelingc34dba72016-10-03 10:14:44 +01002908 vixl32::DRegister temp_d = DRegisterFrom(locations->GetTemp(0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01002909 vixl32::DRegister constant_d = DRegisterFrom(locations->GetTemp(1));
Scott Wakelingfe885462016-09-22 10:24:38 +01002910
2911 // temp_d = int-to-double(high)
2912 __ Vmov(temp_s, high);
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01002913 __ Vcvt(F64, S32, temp_d, temp_s);
Scott Wakelingfe885462016-09-22 10:24:38 +01002914 // constant_d = k2Pow32EncodingForDouble
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002915 __ Vmov(constant_d, bit_cast<double, int64_t>(k2Pow32EncodingForDouble));
Scott Wakelingfe885462016-09-22 10:24:38 +01002916 // out_d = unsigned-to-double(low)
2917 __ Vmov(out_s, low);
2918 __ Vcvt(F64, U32, out_d, out_s);
2919 // out_d += temp_d * constant_d
2920 __ Vmla(F64, out_d, temp_d, constant_d);
2921 break;
2922 }
2923
2924 case Primitive::kPrimFloat:
2925 // Processing a Dex `float-to-double' instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01002926 __ Vcvt(F64, F32, DRegisterFrom(out), InputSRegisterAt(conversion, 0));
Scott Wakelingfe885462016-09-22 10:24:38 +01002927 break;
2928
2929 default:
2930 LOG(FATAL) << "Unexpected type conversion from " << input_type
2931 << " to " << result_type;
2932 };
2933 break;
2934
2935 default:
2936 LOG(FATAL) << "Unexpected type conversion from " << input_type
2937 << " to " << result_type;
2938 }
2939}
2940
2941void LocationsBuilderARMVIXL::VisitAdd(HAdd* add) {
2942 LocationSummary* locations =
2943 new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall);
2944 switch (add->GetResultType()) {
2945 case Primitive::kPrimInt: {
2946 locations->SetInAt(0, Location::RequiresRegister());
2947 locations->SetInAt(1, Location::RegisterOrConstant(add->InputAt(1)));
2948 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2949 break;
2950 }
2951
Scott Wakelingfe885462016-09-22 10:24:38 +01002952 case Primitive::kPrimLong: {
2953 locations->SetInAt(0, Location::RequiresRegister());
Anton Kirilovdda43962016-11-21 19:55:20 +00002954 locations->SetInAt(1, ArmEncodableConstantOrRegister(add->InputAt(1), ADD));
Scott Wakelingfe885462016-09-22 10:24:38 +01002955 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2956 break;
2957 }
2958
2959 case Primitive::kPrimFloat:
2960 case Primitive::kPrimDouble: {
2961 locations->SetInAt(0, Location::RequiresFpuRegister());
2962 locations->SetInAt(1, Location::RequiresFpuRegister());
2963 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2964 break;
2965 }
2966
2967 default:
2968 LOG(FATAL) << "Unexpected add type " << add->GetResultType();
2969 }
2970}
2971
2972void InstructionCodeGeneratorARMVIXL::VisitAdd(HAdd* add) {
2973 LocationSummary* locations = add->GetLocations();
2974 Location out = locations->Out();
2975 Location first = locations->InAt(0);
2976 Location second = locations->InAt(1);
2977
2978 switch (add->GetResultType()) {
2979 case Primitive::kPrimInt: {
2980 __ Add(OutputRegister(add), InputRegisterAt(add, 0), InputOperandAt(add, 1));
2981 }
2982 break;
2983
Scott Wakelingfe885462016-09-22 10:24:38 +01002984 case Primitive::kPrimLong: {
Anton Kirilovdda43962016-11-21 19:55:20 +00002985 if (second.IsConstant()) {
2986 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
2987 GenerateAddLongConst(out, first, value);
2988 } else {
2989 DCHECK(second.IsRegisterPair());
2990 __ Adds(LowRegisterFrom(out), LowRegisterFrom(first), LowRegisterFrom(second));
2991 __ Adc(HighRegisterFrom(out), HighRegisterFrom(first), HighRegisterFrom(second));
2992 }
Scott Wakelingfe885462016-09-22 10:24:38 +01002993 break;
2994 }
2995
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002996 case Primitive::kPrimFloat:
Scott Wakelingfe885462016-09-22 10:24:38 +01002997 case Primitive::kPrimDouble:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002998 __ Vadd(OutputVRegister(add), InputVRegisterAt(add, 0), InputVRegisterAt(add, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01002999 break;
3000
3001 default:
3002 LOG(FATAL) << "Unexpected add type " << add->GetResultType();
3003 }
3004}
3005
3006void LocationsBuilderARMVIXL::VisitSub(HSub* sub) {
3007 LocationSummary* locations =
3008 new (GetGraph()->GetArena()) LocationSummary(sub, LocationSummary::kNoCall);
3009 switch (sub->GetResultType()) {
3010 case Primitive::kPrimInt: {
3011 locations->SetInAt(0, Location::RequiresRegister());
3012 locations->SetInAt(1, Location::RegisterOrConstant(sub->InputAt(1)));
3013 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3014 break;
3015 }
3016
Scott Wakelingfe885462016-09-22 10:24:38 +01003017 case Primitive::kPrimLong: {
3018 locations->SetInAt(0, Location::RequiresRegister());
Anton Kirilovdda43962016-11-21 19:55:20 +00003019 locations->SetInAt(1, ArmEncodableConstantOrRegister(sub->InputAt(1), SUB));
Scott Wakelingfe885462016-09-22 10:24:38 +01003020 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3021 break;
3022 }
3023 case Primitive::kPrimFloat:
3024 case Primitive::kPrimDouble: {
3025 locations->SetInAt(0, Location::RequiresFpuRegister());
3026 locations->SetInAt(1, Location::RequiresFpuRegister());
3027 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3028 break;
3029 }
3030 default:
3031 LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
3032 }
3033}
3034
3035void InstructionCodeGeneratorARMVIXL::VisitSub(HSub* sub) {
3036 LocationSummary* locations = sub->GetLocations();
3037 Location out = locations->Out();
3038 Location first = locations->InAt(0);
3039 Location second = locations->InAt(1);
3040 switch (sub->GetResultType()) {
3041 case Primitive::kPrimInt: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003042 __ Sub(OutputRegister(sub), InputRegisterAt(sub, 0), InputOperandAt(sub, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01003043 break;
3044 }
3045
Scott Wakelingfe885462016-09-22 10:24:38 +01003046 case Primitive::kPrimLong: {
Anton Kirilovdda43962016-11-21 19:55:20 +00003047 if (second.IsConstant()) {
3048 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
3049 GenerateAddLongConst(out, first, -value);
3050 } else {
3051 DCHECK(second.IsRegisterPair());
3052 __ Subs(LowRegisterFrom(out), LowRegisterFrom(first), LowRegisterFrom(second));
3053 __ Sbc(HighRegisterFrom(out), HighRegisterFrom(first), HighRegisterFrom(second));
3054 }
Scott Wakelingfe885462016-09-22 10:24:38 +01003055 break;
3056 }
3057
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003058 case Primitive::kPrimFloat:
3059 case Primitive::kPrimDouble:
3060 __ Vsub(OutputVRegister(sub), InputVRegisterAt(sub, 0), InputVRegisterAt(sub, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01003061 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01003062
3063 default:
3064 LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
3065 }
3066}
3067
3068void LocationsBuilderARMVIXL::VisitMul(HMul* mul) {
3069 LocationSummary* locations =
3070 new (GetGraph()->GetArena()) LocationSummary(mul, LocationSummary::kNoCall);
3071 switch (mul->GetResultType()) {
3072 case Primitive::kPrimInt:
3073 case Primitive::kPrimLong: {
3074 locations->SetInAt(0, Location::RequiresRegister());
3075 locations->SetInAt(1, Location::RequiresRegister());
3076 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3077 break;
3078 }
3079
3080 case Primitive::kPrimFloat:
3081 case Primitive::kPrimDouble: {
3082 locations->SetInAt(0, Location::RequiresFpuRegister());
3083 locations->SetInAt(1, Location::RequiresFpuRegister());
3084 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3085 break;
3086 }
3087
3088 default:
3089 LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
3090 }
3091}
3092
3093void InstructionCodeGeneratorARMVIXL::VisitMul(HMul* mul) {
3094 LocationSummary* locations = mul->GetLocations();
3095 Location out = locations->Out();
3096 Location first = locations->InAt(0);
3097 Location second = locations->InAt(1);
3098 switch (mul->GetResultType()) {
3099 case Primitive::kPrimInt: {
3100 __ Mul(OutputRegister(mul), InputRegisterAt(mul, 0), InputRegisterAt(mul, 1));
3101 break;
3102 }
3103 case Primitive::kPrimLong: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003104 vixl32::Register out_hi = HighRegisterFrom(out);
3105 vixl32::Register out_lo = LowRegisterFrom(out);
3106 vixl32::Register in1_hi = HighRegisterFrom(first);
3107 vixl32::Register in1_lo = LowRegisterFrom(first);
3108 vixl32::Register in2_hi = HighRegisterFrom(second);
3109 vixl32::Register in2_lo = LowRegisterFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01003110
3111 // Extra checks to protect caused by the existence of R1_R2.
3112 // The algorithm is wrong if out.hi is either in1.lo or in2.lo:
3113 // (e.g. in1=r0_r1, in2=r2_r3 and out=r1_r2);
3114 DCHECK_NE(out_hi.GetCode(), in1_lo.GetCode());
3115 DCHECK_NE(out_hi.GetCode(), in2_lo.GetCode());
3116
3117 // input: in1 - 64 bits, in2 - 64 bits
3118 // output: out
3119 // formula: out.hi : out.lo = (in1.lo * in2.hi + in1.hi * in2.lo)* 2^32 + in1.lo * in2.lo
3120 // parts: out.hi = in1.lo * in2.hi + in1.hi * in2.lo + (in1.lo * in2.lo)[63:32]
3121 // parts: out.lo = (in1.lo * in2.lo)[31:0]
3122
3123 UseScratchRegisterScope temps(GetVIXLAssembler());
3124 vixl32::Register temp = temps.Acquire();
3125 // temp <- in1.lo * in2.hi
3126 __ Mul(temp, in1_lo, in2_hi);
3127 // out.hi <- in1.lo * in2.hi + in1.hi * in2.lo
3128 __ Mla(out_hi, in1_hi, in2_lo, temp);
3129 // out.lo <- (in1.lo * in2.lo)[31:0];
3130 __ Umull(out_lo, temp, in1_lo, in2_lo);
3131 // out.hi <- in2.hi * in1.lo + in2.lo * in1.hi + (in1.lo * in2.lo)[63:32]
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003132 __ Add(out_hi, out_hi, temp);
Scott Wakelingfe885462016-09-22 10:24:38 +01003133 break;
3134 }
3135
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003136 case Primitive::kPrimFloat:
3137 case Primitive::kPrimDouble:
3138 __ Vmul(OutputVRegister(mul), InputVRegisterAt(mul, 0), InputVRegisterAt(mul, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01003139 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01003140
3141 default:
3142 LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
3143 }
3144}
3145
Scott Wakelingfe885462016-09-22 10:24:38 +01003146void InstructionCodeGeneratorARMVIXL::DivRemOneOrMinusOne(HBinaryOperation* instruction) {
3147 DCHECK(instruction->IsDiv() || instruction->IsRem());
3148 DCHECK(instruction->GetResultType() == Primitive::kPrimInt);
3149
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003150 Location second = instruction->GetLocations()->InAt(1);
Scott Wakelingfe885462016-09-22 10:24:38 +01003151 DCHECK(second.IsConstant());
3152
3153 vixl32::Register out = OutputRegister(instruction);
3154 vixl32::Register dividend = InputRegisterAt(instruction, 0);
3155 int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
3156 DCHECK(imm == 1 || imm == -1);
3157
3158 if (instruction->IsRem()) {
3159 __ Mov(out, 0);
3160 } else {
3161 if (imm == 1) {
3162 __ Mov(out, dividend);
3163 } else {
3164 __ Rsb(out, dividend, 0);
3165 }
3166 }
3167}
3168
3169void InstructionCodeGeneratorARMVIXL::DivRemByPowerOfTwo(HBinaryOperation* instruction) {
3170 DCHECK(instruction->IsDiv() || instruction->IsRem());
3171 DCHECK(instruction->GetResultType() == Primitive::kPrimInt);
3172
3173 LocationSummary* locations = instruction->GetLocations();
3174 Location second = locations->InAt(1);
3175 DCHECK(second.IsConstant());
3176
3177 vixl32::Register out = OutputRegister(instruction);
3178 vixl32::Register dividend = InputRegisterAt(instruction, 0);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003179 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
Scott Wakelingfe885462016-09-22 10:24:38 +01003180 int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
3181 uint32_t abs_imm = static_cast<uint32_t>(AbsOrMin(imm));
3182 int ctz_imm = CTZ(abs_imm);
3183
3184 if (ctz_imm == 1) {
3185 __ Lsr(temp, dividend, 32 - ctz_imm);
3186 } else {
3187 __ Asr(temp, dividend, 31);
3188 __ Lsr(temp, temp, 32 - ctz_imm);
3189 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003190 __ Add(out, temp, dividend);
Scott Wakelingfe885462016-09-22 10:24:38 +01003191
3192 if (instruction->IsDiv()) {
3193 __ Asr(out, out, ctz_imm);
3194 if (imm < 0) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003195 __ Rsb(out, out, 0);
Scott Wakelingfe885462016-09-22 10:24:38 +01003196 }
3197 } else {
3198 __ Ubfx(out, out, 0, ctz_imm);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003199 __ Sub(out, out, temp);
Scott Wakelingfe885462016-09-22 10:24:38 +01003200 }
3201}
3202
3203void InstructionCodeGeneratorARMVIXL::GenerateDivRemWithAnyConstant(HBinaryOperation* instruction) {
3204 DCHECK(instruction->IsDiv() || instruction->IsRem());
3205 DCHECK(instruction->GetResultType() == Primitive::kPrimInt);
3206
3207 LocationSummary* locations = instruction->GetLocations();
3208 Location second = locations->InAt(1);
3209 DCHECK(second.IsConstant());
3210
3211 vixl32::Register out = OutputRegister(instruction);
3212 vixl32::Register dividend = InputRegisterAt(instruction, 0);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003213 vixl32::Register temp1 = RegisterFrom(locations->GetTemp(0));
3214 vixl32::Register temp2 = RegisterFrom(locations->GetTemp(1));
Scott Wakelingb77051e2016-11-21 19:46:00 +00003215 int32_t imm = Int32ConstantFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01003216
3217 int64_t magic;
3218 int shift;
3219 CalculateMagicAndShiftForDivRem(imm, false /* is_long */, &magic, &shift);
3220
Anton Kirilovdda43962016-11-21 19:55:20 +00003221 // TODO(VIXL): Change the static cast to Operand::From() after VIXL is fixed.
3222 __ Mov(temp1, static_cast<int32_t>(magic));
Scott Wakelingfe885462016-09-22 10:24:38 +01003223 __ Smull(temp2, temp1, dividend, temp1);
3224
3225 if (imm > 0 && magic < 0) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003226 __ Add(temp1, temp1, dividend);
Scott Wakelingfe885462016-09-22 10:24:38 +01003227 } else if (imm < 0 && magic > 0) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003228 __ Sub(temp1, temp1, dividend);
Scott Wakelingfe885462016-09-22 10:24:38 +01003229 }
3230
3231 if (shift != 0) {
3232 __ Asr(temp1, temp1, shift);
3233 }
3234
3235 if (instruction->IsDiv()) {
3236 __ Sub(out, temp1, Operand(temp1, vixl32::Shift(ASR), 31));
3237 } else {
3238 __ Sub(temp1, temp1, Operand(temp1, vixl32::Shift(ASR), 31));
3239 // TODO: Strength reduction for mls.
3240 __ Mov(temp2, imm);
3241 __ Mls(out, temp1, temp2, dividend);
3242 }
3243}
3244
3245void InstructionCodeGeneratorARMVIXL::GenerateDivRemConstantIntegral(
3246 HBinaryOperation* instruction) {
3247 DCHECK(instruction->IsDiv() || instruction->IsRem());
3248 DCHECK(instruction->GetResultType() == Primitive::kPrimInt);
3249
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003250 Location second = instruction->GetLocations()->InAt(1);
Scott Wakelingfe885462016-09-22 10:24:38 +01003251 DCHECK(second.IsConstant());
3252
3253 int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
3254 if (imm == 0) {
3255 // Do not generate anything. DivZeroCheck would prevent any code to be executed.
3256 } else if (imm == 1 || imm == -1) {
3257 DivRemOneOrMinusOne(instruction);
3258 } else if (IsPowerOfTwo(AbsOrMin(imm))) {
3259 DivRemByPowerOfTwo(instruction);
3260 } else {
3261 DCHECK(imm <= -2 || imm >= 2);
3262 GenerateDivRemWithAnyConstant(instruction);
3263 }
3264}
3265
3266void LocationsBuilderARMVIXL::VisitDiv(HDiv* div) {
3267 LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
3268 if (div->GetResultType() == Primitive::kPrimLong) {
3269 // pLdiv runtime call.
3270 call_kind = LocationSummary::kCallOnMainOnly;
3271 } else if (div->GetResultType() == Primitive::kPrimInt && div->InputAt(1)->IsConstant()) {
3272 // sdiv will be replaced by other instruction sequence.
3273 } else if (div->GetResultType() == Primitive::kPrimInt &&
3274 !codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
3275 // pIdivmod runtime call.
3276 call_kind = LocationSummary::kCallOnMainOnly;
3277 }
3278
3279 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(div, call_kind);
3280
3281 switch (div->GetResultType()) {
3282 case Primitive::kPrimInt: {
3283 if (div->InputAt(1)->IsConstant()) {
3284 locations->SetInAt(0, Location::RequiresRegister());
3285 locations->SetInAt(1, Location::ConstantLocation(div->InputAt(1)->AsConstant()));
3286 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3287 int32_t value = div->InputAt(1)->AsIntConstant()->GetValue();
3288 if (value == 1 || value == 0 || value == -1) {
3289 // No temp register required.
3290 } else {
3291 locations->AddTemp(Location::RequiresRegister());
3292 if (!IsPowerOfTwo(AbsOrMin(value))) {
3293 locations->AddTemp(Location::RequiresRegister());
3294 }
3295 }
3296 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
3297 locations->SetInAt(0, Location::RequiresRegister());
3298 locations->SetInAt(1, Location::RequiresRegister());
3299 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3300 } else {
Artem Serov551b28f2016-10-18 19:11:30 +01003301 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3302 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
3303 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
3304 // Note: divrem will compute both the quotient and the remainder as the pair R0 and R1, but
3305 // we only need the former.
3306 locations->SetOut(LocationFrom(r0));
Scott Wakelingfe885462016-09-22 10:24:38 +01003307 }
3308 break;
3309 }
3310 case Primitive::kPrimLong: {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01003311 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3312 locations->SetInAt(0, LocationFrom(
3313 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
3314 locations->SetInAt(1, LocationFrom(
3315 calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3)));
3316 locations->SetOut(LocationFrom(r0, r1));
Scott Wakelingfe885462016-09-22 10:24:38 +01003317 break;
3318 }
3319 case Primitive::kPrimFloat:
3320 case Primitive::kPrimDouble: {
3321 locations->SetInAt(0, Location::RequiresFpuRegister());
3322 locations->SetInAt(1, Location::RequiresFpuRegister());
3323 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3324 break;
3325 }
3326
3327 default:
3328 LOG(FATAL) << "Unexpected div type " << div->GetResultType();
3329 }
3330}
3331
3332void InstructionCodeGeneratorARMVIXL::VisitDiv(HDiv* div) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01003333 Location lhs = div->GetLocations()->InAt(0);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003334 Location rhs = div->GetLocations()->InAt(1);
Scott Wakelingfe885462016-09-22 10:24:38 +01003335
3336 switch (div->GetResultType()) {
3337 case Primitive::kPrimInt: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003338 if (rhs.IsConstant()) {
Scott Wakelingfe885462016-09-22 10:24:38 +01003339 GenerateDivRemConstantIntegral(div);
3340 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
3341 __ Sdiv(OutputRegister(div), InputRegisterAt(div, 0), InputRegisterAt(div, 1));
3342 } else {
Artem Serov551b28f2016-10-18 19:11:30 +01003343 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3344 DCHECK(calling_convention.GetRegisterAt(0).Is(RegisterFrom(lhs)));
3345 DCHECK(calling_convention.GetRegisterAt(1).Is(RegisterFrom(rhs)));
3346 DCHECK(r0.Is(OutputRegister(div)));
3347
3348 codegen_->InvokeRuntime(kQuickIdivmod, div, div->GetDexPc());
3349 CheckEntrypointTypes<kQuickIdivmod, int32_t, int32_t, int32_t>();
Scott Wakelingfe885462016-09-22 10:24:38 +01003350 }
3351 break;
3352 }
3353
3354 case Primitive::kPrimLong: {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01003355 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3356 DCHECK(calling_convention.GetRegisterAt(0).Is(LowRegisterFrom(lhs)));
3357 DCHECK(calling_convention.GetRegisterAt(1).Is(HighRegisterFrom(lhs)));
3358 DCHECK(calling_convention.GetRegisterAt(2).Is(LowRegisterFrom(rhs)));
3359 DCHECK(calling_convention.GetRegisterAt(3).Is(HighRegisterFrom(rhs)));
3360 DCHECK(LowRegisterFrom(div->GetLocations()->Out()).Is(r0));
3361 DCHECK(HighRegisterFrom(div->GetLocations()->Out()).Is(r1));
3362
3363 codegen_->InvokeRuntime(kQuickLdiv, div, div->GetDexPc());
3364 CheckEntrypointTypes<kQuickLdiv, int64_t, int64_t, int64_t>();
Scott Wakelingfe885462016-09-22 10:24:38 +01003365 break;
3366 }
3367
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003368 case Primitive::kPrimFloat:
3369 case Primitive::kPrimDouble:
3370 __ Vdiv(OutputVRegister(div), InputVRegisterAt(div, 0), InputVRegisterAt(div, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01003371 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01003372
3373 default:
3374 LOG(FATAL) << "Unexpected div type " << div->GetResultType();
3375 }
3376}
3377
Artem Serov551b28f2016-10-18 19:11:30 +01003378void LocationsBuilderARMVIXL::VisitRem(HRem* rem) {
3379 Primitive::Type type = rem->GetResultType();
3380
3381 // Most remainders are implemented in the runtime.
3382 LocationSummary::CallKind call_kind = LocationSummary::kCallOnMainOnly;
3383 if (rem->GetResultType() == Primitive::kPrimInt && rem->InputAt(1)->IsConstant()) {
3384 // sdiv will be replaced by other instruction sequence.
3385 call_kind = LocationSummary::kNoCall;
3386 } else if ((rem->GetResultType() == Primitive::kPrimInt)
3387 && codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
3388 // Have hardware divide instruction for int, do it with three instructions.
3389 call_kind = LocationSummary::kNoCall;
3390 }
3391
3392 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(rem, call_kind);
3393
3394 switch (type) {
3395 case Primitive::kPrimInt: {
3396 if (rem->InputAt(1)->IsConstant()) {
3397 locations->SetInAt(0, Location::RequiresRegister());
3398 locations->SetInAt(1, Location::ConstantLocation(rem->InputAt(1)->AsConstant()));
3399 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3400 int32_t value = rem->InputAt(1)->AsIntConstant()->GetValue();
3401 if (value == 1 || value == 0 || value == -1) {
3402 // No temp register required.
3403 } else {
3404 locations->AddTemp(Location::RequiresRegister());
3405 if (!IsPowerOfTwo(AbsOrMin(value))) {
3406 locations->AddTemp(Location::RequiresRegister());
3407 }
3408 }
3409 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
3410 locations->SetInAt(0, Location::RequiresRegister());
3411 locations->SetInAt(1, Location::RequiresRegister());
3412 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3413 locations->AddTemp(Location::RequiresRegister());
3414 } else {
3415 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3416 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
3417 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
3418 // Note: divrem will compute both the quotient and the remainder as the pair R0 and R1, but
3419 // we only need the latter.
3420 locations->SetOut(LocationFrom(r1));
3421 }
3422 break;
3423 }
3424 case Primitive::kPrimLong: {
3425 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3426 locations->SetInAt(0, LocationFrom(
3427 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
3428 locations->SetInAt(1, LocationFrom(
3429 calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3)));
3430 // The runtime helper puts the output in R2,R3.
3431 locations->SetOut(LocationFrom(r2, r3));
3432 break;
3433 }
3434 case Primitive::kPrimFloat: {
3435 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3436 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
3437 locations->SetInAt(1, LocationFrom(calling_convention.GetFpuRegisterAt(1)));
3438 locations->SetOut(LocationFrom(s0));
3439 break;
3440 }
3441
3442 case Primitive::kPrimDouble: {
3443 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3444 locations->SetInAt(0, LocationFrom(
3445 calling_convention.GetFpuRegisterAt(0), calling_convention.GetFpuRegisterAt(1)));
3446 locations->SetInAt(1, LocationFrom(
3447 calling_convention.GetFpuRegisterAt(2), calling_convention.GetFpuRegisterAt(3)));
3448 locations->SetOut(LocationFrom(s0, s1));
3449 break;
3450 }
3451
3452 default:
3453 LOG(FATAL) << "Unexpected rem type " << type;
3454 }
3455}
3456
3457void InstructionCodeGeneratorARMVIXL::VisitRem(HRem* rem) {
3458 LocationSummary* locations = rem->GetLocations();
3459 Location second = locations->InAt(1);
3460
3461 Primitive::Type type = rem->GetResultType();
3462 switch (type) {
3463 case Primitive::kPrimInt: {
3464 vixl32::Register reg1 = InputRegisterAt(rem, 0);
3465 vixl32::Register out_reg = OutputRegister(rem);
3466 if (second.IsConstant()) {
3467 GenerateDivRemConstantIntegral(rem);
3468 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
3469 vixl32::Register reg2 = RegisterFrom(second);
3470 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
3471
3472 // temp = reg1 / reg2 (integer division)
3473 // dest = reg1 - temp * reg2
3474 __ Sdiv(temp, reg1, reg2);
3475 __ Mls(out_reg, temp, reg2, reg1);
3476 } else {
3477 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3478 DCHECK(reg1.Is(calling_convention.GetRegisterAt(0)));
3479 DCHECK(RegisterFrom(second).Is(calling_convention.GetRegisterAt(1)));
3480 DCHECK(out_reg.Is(r1));
3481
3482 codegen_->InvokeRuntime(kQuickIdivmod, rem, rem->GetDexPc());
3483 CheckEntrypointTypes<kQuickIdivmod, int32_t, int32_t, int32_t>();
3484 }
3485 break;
3486 }
3487
3488 case Primitive::kPrimLong: {
3489 codegen_->InvokeRuntime(kQuickLmod, rem, rem->GetDexPc());
3490 CheckEntrypointTypes<kQuickLmod, int64_t, int64_t, int64_t>();
3491 break;
3492 }
3493
3494 case Primitive::kPrimFloat: {
3495 codegen_->InvokeRuntime(kQuickFmodf, rem, rem->GetDexPc());
3496 CheckEntrypointTypes<kQuickFmodf, float, float, float>();
3497 break;
3498 }
3499
3500 case Primitive::kPrimDouble: {
3501 codegen_->InvokeRuntime(kQuickFmod, rem, rem->GetDexPc());
3502 CheckEntrypointTypes<kQuickFmod, double, double, double>();
3503 break;
3504 }
3505
3506 default:
3507 LOG(FATAL) << "Unexpected rem type " << type;
3508 }
3509}
3510
3511
Scott Wakelingfe885462016-09-22 10:24:38 +01003512void LocationsBuilderARMVIXL::VisitDivZeroCheck(HDivZeroCheck* instruction) {
Artem Serov657022c2016-11-23 14:19:38 +00003513 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
Scott Wakelingfe885462016-09-22 10:24:38 +01003514 locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0)));
Scott Wakelingfe885462016-09-22 10:24:38 +01003515}
3516
3517void InstructionCodeGeneratorARMVIXL::VisitDivZeroCheck(HDivZeroCheck* instruction) {
3518 DivZeroCheckSlowPathARMVIXL* slow_path =
3519 new (GetGraph()->GetArena()) DivZeroCheckSlowPathARMVIXL(instruction);
3520 codegen_->AddSlowPath(slow_path);
3521
3522 LocationSummary* locations = instruction->GetLocations();
3523 Location value = locations->InAt(0);
3524
3525 switch (instruction->GetType()) {
3526 case Primitive::kPrimBoolean:
3527 case Primitive::kPrimByte:
3528 case Primitive::kPrimChar:
3529 case Primitive::kPrimShort:
3530 case Primitive::kPrimInt: {
3531 if (value.IsRegister()) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00003532 __ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
Scott Wakelingfe885462016-09-22 10:24:38 +01003533 } else {
3534 DCHECK(value.IsConstant()) << value;
3535 if (value.GetConstant()->AsIntConstant()->GetValue() == 0) {
3536 __ B(slow_path->GetEntryLabel());
3537 }
3538 }
3539 break;
3540 }
3541 case Primitive::kPrimLong: {
3542 if (value.IsRegisterPair()) {
3543 UseScratchRegisterScope temps(GetVIXLAssembler());
3544 vixl32::Register temp = temps.Acquire();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003545 __ Orrs(temp, LowRegisterFrom(value), HighRegisterFrom(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01003546 __ B(eq, slow_path->GetEntryLabel());
3547 } else {
3548 DCHECK(value.IsConstant()) << value;
3549 if (value.GetConstant()->AsLongConstant()->GetValue() == 0) {
3550 __ B(slow_path->GetEntryLabel());
3551 }
3552 }
3553 break;
3554 }
3555 default:
3556 LOG(FATAL) << "Unexpected type for HDivZeroCheck " << instruction->GetType();
3557 }
3558}
3559
Artem Serov02109dd2016-09-23 17:17:54 +01003560void InstructionCodeGeneratorARMVIXL::HandleIntegerRotate(HRor* ror) {
3561 LocationSummary* locations = ror->GetLocations();
3562 vixl32::Register in = InputRegisterAt(ror, 0);
3563 Location rhs = locations->InAt(1);
3564 vixl32::Register out = OutputRegister(ror);
3565
3566 if (rhs.IsConstant()) {
3567 // Arm32 and Thumb2 assemblers require a rotation on the interval [1,31],
3568 // so map all rotations to a +ve. equivalent in that range.
3569 // (e.g. left *or* right by -2 bits == 30 bits in the same direction.)
3570 uint32_t rot = CodeGenerator::GetInt32ValueOf(rhs.GetConstant()) & 0x1F;
3571 if (rot) {
3572 // Rotate, mapping left rotations to right equivalents if necessary.
3573 // (e.g. left by 2 bits == right by 30.)
3574 __ Ror(out, in, rot);
3575 } else if (!out.Is(in)) {
3576 __ Mov(out, in);
3577 }
3578 } else {
3579 __ Ror(out, in, RegisterFrom(rhs));
3580 }
3581}
3582
3583// Gain some speed by mapping all Long rotates onto equivalent pairs of Integer
3584// rotates by swapping input regs (effectively rotating by the first 32-bits of
3585// a larger rotation) or flipping direction (thus treating larger right/left
3586// rotations as sub-word sized rotations in the other direction) as appropriate.
3587void InstructionCodeGeneratorARMVIXL::HandleLongRotate(HRor* ror) {
3588 LocationSummary* locations = ror->GetLocations();
3589 vixl32::Register in_reg_lo = LowRegisterFrom(locations->InAt(0));
3590 vixl32::Register in_reg_hi = HighRegisterFrom(locations->InAt(0));
3591 Location rhs = locations->InAt(1);
3592 vixl32::Register out_reg_lo = LowRegisterFrom(locations->Out());
3593 vixl32::Register out_reg_hi = HighRegisterFrom(locations->Out());
3594
3595 if (rhs.IsConstant()) {
3596 uint64_t rot = CodeGenerator::GetInt64ValueOf(rhs.GetConstant());
3597 // Map all rotations to +ve. equivalents on the interval [0,63].
3598 rot &= kMaxLongShiftDistance;
3599 // For rotates over a word in size, 'pre-rotate' by 32-bits to keep rotate
3600 // logic below to a simple pair of binary orr.
3601 // (e.g. 34 bits == in_reg swap + 2 bits right.)
3602 if (rot >= kArmBitsPerWord) {
3603 rot -= kArmBitsPerWord;
3604 std::swap(in_reg_hi, in_reg_lo);
3605 }
3606 // Rotate, or mov to out for zero or word size rotations.
3607 if (rot != 0u) {
Scott Wakelingb77051e2016-11-21 19:46:00 +00003608 __ Lsr(out_reg_hi, in_reg_hi, Operand::From(rot));
Artem Serov02109dd2016-09-23 17:17:54 +01003609 __ Orr(out_reg_hi, out_reg_hi, Operand(in_reg_lo, ShiftType::LSL, kArmBitsPerWord - rot));
Scott Wakelingb77051e2016-11-21 19:46:00 +00003610 __ Lsr(out_reg_lo, in_reg_lo, Operand::From(rot));
Artem Serov02109dd2016-09-23 17:17:54 +01003611 __ Orr(out_reg_lo, out_reg_lo, Operand(in_reg_hi, ShiftType::LSL, kArmBitsPerWord - rot));
3612 } else {
3613 __ Mov(out_reg_lo, in_reg_lo);
3614 __ Mov(out_reg_hi, in_reg_hi);
3615 }
3616 } else {
3617 vixl32::Register shift_right = RegisterFrom(locations->GetTemp(0));
3618 vixl32::Register shift_left = RegisterFrom(locations->GetTemp(1));
3619 vixl32::Label end;
3620 vixl32::Label shift_by_32_plus_shift_right;
3621
3622 __ And(shift_right, RegisterFrom(rhs), 0x1F);
3623 __ Lsrs(shift_left, RegisterFrom(rhs), 6);
Scott Wakelingbffdc702016-12-07 17:46:03 +00003624 __ Rsb(LeaveFlags, shift_left, shift_right, Operand::From(kArmBitsPerWord));
Artem Serov02109dd2016-09-23 17:17:54 +01003625 __ B(cc, &shift_by_32_plus_shift_right);
3626
3627 // out_reg_hi = (reg_hi << shift_left) | (reg_lo >> shift_right).
3628 // out_reg_lo = (reg_lo << shift_left) | (reg_hi >> shift_right).
3629 __ Lsl(out_reg_hi, in_reg_hi, shift_left);
3630 __ Lsr(out_reg_lo, in_reg_lo, shift_right);
3631 __ Add(out_reg_hi, out_reg_hi, out_reg_lo);
3632 __ Lsl(out_reg_lo, in_reg_lo, shift_left);
3633 __ Lsr(shift_left, in_reg_hi, shift_right);
3634 __ Add(out_reg_lo, out_reg_lo, shift_left);
3635 __ B(&end);
3636
3637 __ Bind(&shift_by_32_plus_shift_right); // Shift by 32+shift_right.
3638 // out_reg_hi = (reg_hi >> shift_right) | (reg_lo << shift_left).
3639 // out_reg_lo = (reg_lo >> shift_right) | (reg_hi << shift_left).
3640 __ Lsr(out_reg_hi, in_reg_hi, shift_right);
3641 __ Lsl(out_reg_lo, in_reg_lo, shift_left);
3642 __ Add(out_reg_hi, out_reg_hi, out_reg_lo);
3643 __ Lsr(out_reg_lo, in_reg_lo, shift_right);
3644 __ Lsl(shift_right, in_reg_hi, shift_left);
3645 __ Add(out_reg_lo, out_reg_lo, shift_right);
3646
3647 __ Bind(&end);
3648 }
3649}
3650
3651void LocationsBuilderARMVIXL::VisitRor(HRor* ror) {
3652 LocationSummary* locations =
3653 new (GetGraph()->GetArena()) LocationSummary(ror, LocationSummary::kNoCall);
3654 switch (ror->GetResultType()) {
3655 case Primitive::kPrimInt: {
3656 locations->SetInAt(0, Location::RequiresRegister());
3657 locations->SetInAt(1, Location::RegisterOrConstant(ror->InputAt(1)));
3658 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3659 break;
3660 }
3661 case Primitive::kPrimLong: {
3662 locations->SetInAt(0, Location::RequiresRegister());
3663 if (ror->InputAt(1)->IsConstant()) {
3664 locations->SetInAt(1, Location::ConstantLocation(ror->InputAt(1)->AsConstant()));
3665 } else {
3666 locations->SetInAt(1, Location::RequiresRegister());
3667 locations->AddTemp(Location::RequiresRegister());
3668 locations->AddTemp(Location::RequiresRegister());
3669 }
3670 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
3671 break;
3672 }
3673 default:
3674 LOG(FATAL) << "Unexpected operation type " << ror->GetResultType();
3675 }
3676}
3677
3678void InstructionCodeGeneratorARMVIXL::VisitRor(HRor* ror) {
3679 Primitive::Type type = ror->GetResultType();
3680 switch (type) {
3681 case Primitive::kPrimInt: {
3682 HandleIntegerRotate(ror);
3683 break;
3684 }
3685 case Primitive::kPrimLong: {
3686 HandleLongRotate(ror);
3687 break;
3688 }
3689 default:
3690 LOG(FATAL) << "Unexpected operation type " << type;
3691 UNREACHABLE();
3692 }
3693}
3694
Artem Serov02d37832016-10-25 15:25:33 +01003695void LocationsBuilderARMVIXL::HandleShift(HBinaryOperation* op) {
3696 DCHECK(op->IsShl() || op->IsShr() || op->IsUShr());
3697
3698 LocationSummary* locations =
3699 new (GetGraph()->GetArena()) LocationSummary(op, LocationSummary::kNoCall);
3700
3701 switch (op->GetResultType()) {
3702 case Primitive::kPrimInt: {
3703 locations->SetInAt(0, Location::RequiresRegister());
3704 if (op->InputAt(1)->IsConstant()) {
3705 locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant()));
3706 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3707 } else {
3708 locations->SetInAt(1, Location::RequiresRegister());
3709 // Make the output overlap, as it will be used to hold the masked
3710 // second input.
3711 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
3712 }
3713 break;
3714 }
3715 case Primitive::kPrimLong: {
3716 locations->SetInAt(0, Location::RequiresRegister());
3717 if (op->InputAt(1)->IsConstant()) {
3718 locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant()));
3719 // For simplicity, use kOutputOverlap even though we only require that low registers
3720 // don't clash with high registers which the register allocator currently guarantees.
3721 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
3722 } else {
3723 locations->SetInAt(1, Location::RequiresRegister());
3724 locations->AddTemp(Location::RequiresRegister());
3725 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
3726 }
3727 break;
3728 }
3729 default:
3730 LOG(FATAL) << "Unexpected operation type " << op->GetResultType();
3731 }
3732}
3733
3734void InstructionCodeGeneratorARMVIXL::HandleShift(HBinaryOperation* op) {
3735 DCHECK(op->IsShl() || op->IsShr() || op->IsUShr());
3736
3737 LocationSummary* locations = op->GetLocations();
3738 Location out = locations->Out();
3739 Location first = locations->InAt(0);
3740 Location second = locations->InAt(1);
3741
3742 Primitive::Type type = op->GetResultType();
3743 switch (type) {
3744 case Primitive::kPrimInt: {
3745 vixl32::Register out_reg = OutputRegister(op);
3746 vixl32::Register first_reg = InputRegisterAt(op, 0);
3747 if (second.IsRegister()) {
3748 vixl32::Register second_reg = RegisterFrom(second);
3749 // ARM doesn't mask the shift count so we need to do it ourselves.
3750 __ And(out_reg, second_reg, kMaxIntShiftDistance);
3751 if (op->IsShl()) {
3752 __ Lsl(out_reg, first_reg, out_reg);
3753 } else if (op->IsShr()) {
3754 __ Asr(out_reg, first_reg, out_reg);
3755 } else {
3756 __ Lsr(out_reg, first_reg, out_reg);
3757 }
3758 } else {
3759 int32_t cst = second.GetConstant()->AsIntConstant()->GetValue();
3760 uint32_t shift_value = cst & kMaxIntShiftDistance;
3761 if (shift_value == 0) { // ARM does not support shifting with 0 immediate.
3762 __ Mov(out_reg, first_reg);
3763 } else if (op->IsShl()) {
3764 __ Lsl(out_reg, first_reg, shift_value);
3765 } else if (op->IsShr()) {
3766 __ Asr(out_reg, first_reg, shift_value);
3767 } else {
3768 __ Lsr(out_reg, first_reg, shift_value);
3769 }
3770 }
3771 break;
3772 }
3773 case Primitive::kPrimLong: {
3774 vixl32::Register o_h = HighRegisterFrom(out);
3775 vixl32::Register o_l = LowRegisterFrom(out);
3776
3777 vixl32::Register high = HighRegisterFrom(first);
3778 vixl32::Register low = LowRegisterFrom(first);
3779
3780 if (second.IsRegister()) {
3781 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
3782
3783 vixl32::Register second_reg = RegisterFrom(second);
3784
3785 if (op->IsShl()) {
3786 __ And(o_l, second_reg, kMaxLongShiftDistance);
3787 // Shift the high part
3788 __ Lsl(o_h, high, o_l);
3789 // Shift the low part and `or` what overflew on the high part
Scott Wakelingb77051e2016-11-21 19:46:00 +00003790 __ Rsb(temp, o_l, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01003791 __ Lsr(temp, low, temp);
3792 __ Orr(o_h, o_h, temp);
3793 // If the shift is > 32 bits, override the high part
Scott Wakelingb77051e2016-11-21 19:46:00 +00003794 __ Subs(temp, o_l, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01003795 {
3796 AssemblerAccurateScope guard(GetVIXLAssembler(),
Alexandre Rames374ddf32016-11-04 10:40:49 +00003797 2 * vixl32::kMaxInstructionSizeInBytes,
Artem Serov02d37832016-10-25 15:25:33 +01003798 CodeBufferCheckScope::kMaximumSize);
3799 __ it(pl);
3800 __ lsl(pl, o_h, low, temp);
3801 }
3802 // Shift the low part
3803 __ Lsl(o_l, low, o_l);
3804 } else if (op->IsShr()) {
3805 __ And(o_h, second_reg, kMaxLongShiftDistance);
3806 // Shift the low part
3807 __ Lsr(o_l, low, o_h);
3808 // Shift the high part and `or` what underflew on the low part
Scott Wakelingb77051e2016-11-21 19:46:00 +00003809 __ Rsb(temp, o_h, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01003810 __ Lsl(temp, high, temp);
3811 __ Orr(o_l, o_l, temp);
3812 // If the shift is > 32 bits, override the low part
Scott Wakelingb77051e2016-11-21 19:46:00 +00003813 __ Subs(temp, o_h, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01003814 {
3815 AssemblerAccurateScope guard(GetVIXLAssembler(),
Alexandre Rames374ddf32016-11-04 10:40:49 +00003816 2 * vixl32::kMaxInstructionSizeInBytes,
Artem Serov02d37832016-10-25 15:25:33 +01003817 CodeBufferCheckScope::kMaximumSize);
3818 __ it(pl);
3819 __ asr(pl, o_l, high, temp);
3820 }
3821 // Shift the high part
3822 __ Asr(o_h, high, o_h);
3823 } else {
3824 __ And(o_h, second_reg, kMaxLongShiftDistance);
3825 // same as Shr except we use `Lsr`s and not `Asr`s
3826 __ Lsr(o_l, low, o_h);
Scott Wakelingb77051e2016-11-21 19:46:00 +00003827 __ Rsb(temp, o_h, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01003828 __ Lsl(temp, high, temp);
3829 __ Orr(o_l, o_l, temp);
Scott Wakelingb77051e2016-11-21 19:46:00 +00003830 __ Subs(temp, o_h, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01003831 {
3832 AssemblerAccurateScope guard(GetVIXLAssembler(),
Alexandre Rames374ddf32016-11-04 10:40:49 +00003833 2 * vixl32::kMaxInstructionSizeInBytes,
Artem Serov02d37832016-10-25 15:25:33 +01003834 CodeBufferCheckScope::kMaximumSize);
3835 __ it(pl);
3836 __ lsr(pl, o_l, high, temp);
3837 }
3838 __ Lsr(o_h, high, o_h);
3839 }
3840 } else {
3841 // Register allocator doesn't create partial overlap.
3842 DCHECK(!o_l.Is(high));
3843 DCHECK(!o_h.Is(low));
3844 int32_t cst = second.GetConstant()->AsIntConstant()->GetValue();
3845 uint32_t shift_value = cst & kMaxLongShiftDistance;
3846 if (shift_value > 32) {
3847 if (op->IsShl()) {
3848 __ Lsl(o_h, low, shift_value - 32);
3849 __ Mov(o_l, 0);
3850 } else if (op->IsShr()) {
3851 __ Asr(o_l, high, shift_value - 32);
3852 __ Asr(o_h, high, 31);
3853 } else {
3854 __ Lsr(o_l, high, shift_value - 32);
3855 __ Mov(o_h, 0);
3856 }
3857 } else if (shift_value == 32) {
3858 if (op->IsShl()) {
3859 __ Mov(o_h, low);
3860 __ Mov(o_l, 0);
3861 } else if (op->IsShr()) {
3862 __ Mov(o_l, high);
3863 __ Asr(o_h, high, 31);
3864 } else {
3865 __ Mov(o_l, high);
3866 __ Mov(o_h, 0);
3867 }
3868 } else if (shift_value == 1) {
3869 if (op->IsShl()) {
3870 __ Lsls(o_l, low, 1);
3871 __ Adc(o_h, high, high);
3872 } else if (op->IsShr()) {
3873 __ Asrs(o_h, high, 1);
3874 __ Rrx(o_l, low);
3875 } else {
3876 __ Lsrs(o_h, high, 1);
3877 __ Rrx(o_l, low);
3878 }
3879 } else {
3880 DCHECK(2 <= shift_value && shift_value < 32) << shift_value;
3881 if (op->IsShl()) {
3882 __ Lsl(o_h, high, shift_value);
3883 __ Orr(o_h, o_h, Operand(low, ShiftType::LSR, 32 - shift_value));
3884 __ Lsl(o_l, low, shift_value);
3885 } else if (op->IsShr()) {
3886 __ Lsr(o_l, low, shift_value);
3887 __ Orr(o_l, o_l, Operand(high, ShiftType::LSL, 32 - shift_value));
3888 __ Asr(o_h, high, shift_value);
3889 } else {
3890 __ Lsr(o_l, low, shift_value);
3891 __ Orr(o_l, o_l, Operand(high, ShiftType::LSL, 32 - shift_value));
3892 __ Lsr(o_h, high, shift_value);
3893 }
3894 }
3895 }
3896 break;
3897 }
3898 default:
3899 LOG(FATAL) << "Unexpected operation type " << type;
3900 UNREACHABLE();
3901 }
3902}
3903
3904void LocationsBuilderARMVIXL::VisitShl(HShl* shl) {
3905 HandleShift(shl);
3906}
3907
3908void InstructionCodeGeneratorARMVIXL::VisitShl(HShl* shl) {
3909 HandleShift(shl);
3910}
3911
3912void LocationsBuilderARMVIXL::VisitShr(HShr* shr) {
3913 HandleShift(shr);
3914}
3915
3916void InstructionCodeGeneratorARMVIXL::VisitShr(HShr* shr) {
3917 HandleShift(shr);
3918}
3919
3920void LocationsBuilderARMVIXL::VisitUShr(HUShr* ushr) {
3921 HandleShift(ushr);
3922}
3923
3924void InstructionCodeGeneratorARMVIXL::VisitUShr(HUShr* ushr) {
3925 HandleShift(ushr);
3926}
3927
3928void LocationsBuilderARMVIXL::VisitNewInstance(HNewInstance* instruction) {
3929 LocationSummary* locations =
3930 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
3931 if (instruction->IsStringAlloc()) {
3932 locations->AddTemp(LocationFrom(kMethodRegister));
3933 } else {
3934 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3935 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
3936 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
3937 }
3938 locations->SetOut(LocationFrom(r0));
3939}
3940
3941void InstructionCodeGeneratorARMVIXL::VisitNewInstance(HNewInstance* instruction) {
3942 // Note: if heap poisoning is enabled, the entry point takes cares
3943 // of poisoning the reference.
3944 if (instruction->IsStringAlloc()) {
3945 // String is allocated through StringFactory. Call NewEmptyString entry point.
3946 vixl32::Register temp = RegisterFrom(instruction->GetLocations()->GetTemp(0));
3947 MemberOffset code_offset = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize);
3948 GetAssembler()->LoadFromOffset(kLoadWord, temp, tr, QUICK_ENTRY_POINT(pNewEmptyString));
3949 GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, code_offset.Int32Value());
Alexandre Rames374ddf32016-11-04 10:40:49 +00003950 // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
Artem Serov02d37832016-10-25 15:25:33 +01003951 AssemblerAccurateScope aas(GetVIXLAssembler(),
Alexandre Rames374ddf32016-11-04 10:40:49 +00003952 vixl32::k16BitT32InstructionSizeInBytes,
3953 CodeBufferCheckScope::kExactSize);
Artem Serov02d37832016-10-25 15:25:33 +01003954 __ blx(lr);
3955 codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
3956 } else {
3957 codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
3958 CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, ArtMethod*>();
3959 }
3960}
3961
3962void LocationsBuilderARMVIXL::VisitNewArray(HNewArray* instruction) {
3963 LocationSummary* locations =
3964 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
3965 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3966 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(0)));
3967 locations->SetOut(LocationFrom(r0));
3968 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(1)));
3969 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(2)));
3970}
3971
3972void InstructionCodeGeneratorARMVIXL::VisitNewArray(HNewArray* instruction) {
3973 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Andreas Gampea5b09a62016-11-17 15:21:22 -08003974 __ Mov(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex().index_);
Artem Serov02d37832016-10-25 15:25:33 +01003975 // Note: if heap poisoning is enabled, the entry point takes cares
3976 // of poisoning the reference.
3977 codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
3978 CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>();
3979}
3980
3981void LocationsBuilderARMVIXL::VisitParameterValue(HParameterValue* instruction) {
3982 LocationSummary* locations =
3983 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
3984 Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
3985 if (location.IsStackSlot()) {
3986 location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
3987 } else if (location.IsDoubleStackSlot()) {
3988 location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
3989 }
3990 locations->SetOut(location);
3991}
3992
3993void InstructionCodeGeneratorARMVIXL::VisitParameterValue(
3994 HParameterValue* instruction ATTRIBUTE_UNUSED) {
3995 // Nothing to do, the parameter is already at its location.
3996}
3997
3998void LocationsBuilderARMVIXL::VisitCurrentMethod(HCurrentMethod* instruction) {
3999 LocationSummary* locations =
4000 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
4001 locations->SetOut(LocationFrom(kMethodRegister));
4002}
4003
4004void InstructionCodeGeneratorARMVIXL::VisitCurrentMethod(
4005 HCurrentMethod* instruction ATTRIBUTE_UNUSED) {
4006 // Nothing to do, the method is already at its location.
4007}
4008
4009void LocationsBuilderARMVIXL::VisitNot(HNot* not_) {
4010 LocationSummary* locations =
4011 new (GetGraph()->GetArena()) LocationSummary(not_, LocationSummary::kNoCall);
4012 locations->SetInAt(0, Location::RequiresRegister());
4013 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4014}
4015
4016void InstructionCodeGeneratorARMVIXL::VisitNot(HNot* not_) {
4017 LocationSummary* locations = not_->GetLocations();
4018 Location out = locations->Out();
4019 Location in = locations->InAt(0);
4020 switch (not_->GetResultType()) {
4021 case Primitive::kPrimInt:
4022 __ Mvn(OutputRegister(not_), InputRegisterAt(not_, 0));
4023 break;
4024
4025 case Primitive::kPrimLong:
4026 __ Mvn(LowRegisterFrom(out), LowRegisterFrom(in));
4027 __ Mvn(HighRegisterFrom(out), HighRegisterFrom(in));
4028 break;
4029
4030 default:
4031 LOG(FATAL) << "Unimplemented type for not operation " << not_->GetResultType();
4032 }
4033}
4034
Scott Wakelingc34dba72016-10-03 10:14:44 +01004035void LocationsBuilderARMVIXL::VisitBooleanNot(HBooleanNot* bool_not) {
4036 LocationSummary* locations =
4037 new (GetGraph()->GetArena()) LocationSummary(bool_not, LocationSummary::kNoCall);
4038 locations->SetInAt(0, Location::RequiresRegister());
4039 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4040}
4041
4042void InstructionCodeGeneratorARMVIXL::VisitBooleanNot(HBooleanNot* bool_not) {
4043 __ Eor(OutputRegister(bool_not), InputRegister(bool_not), 1);
4044}
4045
Artem Serov02d37832016-10-25 15:25:33 +01004046void LocationsBuilderARMVIXL::VisitCompare(HCompare* compare) {
4047 LocationSummary* locations =
4048 new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall);
4049 switch (compare->InputAt(0)->GetType()) {
4050 case Primitive::kPrimBoolean:
4051 case Primitive::kPrimByte:
4052 case Primitive::kPrimShort:
4053 case Primitive::kPrimChar:
4054 case Primitive::kPrimInt:
4055 case Primitive::kPrimLong: {
4056 locations->SetInAt(0, Location::RequiresRegister());
4057 locations->SetInAt(1, Location::RequiresRegister());
4058 // Output overlaps because it is written before doing the low comparison.
4059 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
4060 break;
4061 }
4062 case Primitive::kPrimFloat:
4063 case Primitive::kPrimDouble: {
4064 locations->SetInAt(0, Location::RequiresFpuRegister());
4065 locations->SetInAt(1, ArithmeticZeroOrFpuRegister(compare->InputAt(1)));
4066 locations->SetOut(Location::RequiresRegister());
4067 break;
4068 }
4069 default:
4070 LOG(FATAL) << "Unexpected type for compare operation " << compare->InputAt(0)->GetType();
4071 }
4072}
4073
4074void InstructionCodeGeneratorARMVIXL::VisitCompare(HCompare* compare) {
4075 LocationSummary* locations = compare->GetLocations();
4076 vixl32::Register out = OutputRegister(compare);
4077 Location left = locations->InAt(0);
4078 Location right = locations->InAt(1);
4079
4080 vixl32::Label less, greater, done;
4081 Primitive::Type type = compare->InputAt(0)->GetType();
4082 vixl32::Condition less_cond = vixl32::Condition(kNone);
4083 switch (type) {
4084 case Primitive::kPrimBoolean:
4085 case Primitive::kPrimByte:
4086 case Primitive::kPrimShort:
4087 case Primitive::kPrimChar:
4088 case Primitive::kPrimInt: {
4089 // Emit move to `out` before the `Cmp`, as `Mov` might affect the status flags.
4090 __ Mov(out, 0);
4091 __ Cmp(RegisterFrom(left), RegisterFrom(right)); // Signed compare.
4092 less_cond = lt;
4093 break;
4094 }
4095 case Primitive::kPrimLong: {
4096 __ Cmp(HighRegisterFrom(left), HighRegisterFrom(right)); // Signed compare.
4097 __ B(lt, &less);
4098 __ B(gt, &greater);
4099 // Emit move to `out` before the last `Cmp`, as `Mov` might affect the status flags.
4100 __ Mov(out, 0);
4101 __ Cmp(LowRegisterFrom(left), LowRegisterFrom(right)); // Unsigned compare.
4102 less_cond = lo;
4103 break;
4104 }
4105 case Primitive::kPrimFloat:
4106 case Primitive::kPrimDouble: {
4107 __ Mov(out, 0);
4108 GenerateVcmp(compare);
4109 // To branch on the FP compare result we transfer FPSCR to APSR (encoded as PC in VMRS).
4110 __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
4111 less_cond = ARMFPCondition(kCondLT, compare->IsGtBias());
4112 break;
4113 }
4114 default:
4115 LOG(FATAL) << "Unexpected compare type " << type;
4116 UNREACHABLE();
4117 }
4118
4119 __ B(eq, &done);
4120 __ B(less_cond, &less);
4121
4122 __ Bind(&greater);
4123 __ Mov(out, 1);
4124 __ B(&done);
4125
4126 __ Bind(&less);
4127 __ Mov(out, -1);
4128
4129 __ Bind(&done);
4130}
4131
4132void LocationsBuilderARMVIXL::VisitPhi(HPhi* instruction) {
4133 LocationSummary* locations =
4134 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
4135 for (size_t i = 0, e = locations->GetInputCount(); i < e; ++i) {
4136 locations->SetInAt(i, Location::Any());
4137 }
4138 locations->SetOut(Location::Any());
4139}
4140
4141void InstructionCodeGeneratorARMVIXL::VisitPhi(HPhi* instruction ATTRIBUTE_UNUSED) {
4142 LOG(FATAL) << "Unreachable";
4143}
4144
4145void CodeGeneratorARMVIXL::GenerateMemoryBarrier(MemBarrierKind kind) {
4146 // TODO (ported from quick): revisit ARM barrier kinds.
4147 DmbOptions flavor = DmbOptions::ISH; // Quiet C++ warnings.
4148 switch (kind) {
4149 case MemBarrierKind::kAnyStore:
4150 case MemBarrierKind::kLoadAny:
4151 case MemBarrierKind::kAnyAny: {
4152 flavor = DmbOptions::ISH;
4153 break;
4154 }
4155 case MemBarrierKind::kStoreStore: {
4156 flavor = DmbOptions::ISHST;
4157 break;
4158 }
4159 default:
4160 LOG(FATAL) << "Unexpected memory barrier " << kind;
4161 }
4162 __ Dmb(flavor);
4163}
4164
4165void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicLoad(vixl32::Register addr,
4166 uint32_t offset,
4167 vixl32::Register out_lo,
4168 vixl32::Register out_hi) {
4169 UseScratchRegisterScope temps(GetVIXLAssembler());
4170 if (offset != 0) {
4171 vixl32::Register temp = temps.Acquire();
4172 __ Add(temp, addr, offset);
4173 addr = temp;
4174 }
Scott Wakelingb77051e2016-11-21 19:46:00 +00004175 __ Ldrexd(out_lo, out_hi, MemOperand(addr));
Artem Serov02d37832016-10-25 15:25:33 +01004176}
4177
4178void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicStore(vixl32::Register addr,
4179 uint32_t offset,
4180 vixl32::Register value_lo,
4181 vixl32::Register value_hi,
4182 vixl32::Register temp1,
4183 vixl32::Register temp2,
4184 HInstruction* instruction) {
4185 UseScratchRegisterScope temps(GetVIXLAssembler());
4186 vixl32::Label fail;
4187 if (offset != 0) {
4188 vixl32::Register temp = temps.Acquire();
4189 __ Add(temp, addr, offset);
4190 addr = temp;
4191 }
4192 __ Bind(&fail);
Alexandre Rames374ddf32016-11-04 10:40:49 +00004193 {
4194 // Ensure the pc position is recorded immediately after the `ldrexd` instruction.
4195 AssemblerAccurateScope aas(GetVIXLAssembler(),
4196 vixl32::kMaxInstructionSizeInBytes,
4197 CodeBufferCheckScope::kMaximumSize);
4198 // We need a load followed by store. (The address used in a STREX instruction must
4199 // be the same as the address in the most recently executed LDREX instruction.)
4200 __ ldrexd(temp1, temp2, MemOperand(addr));
4201 codegen_->MaybeRecordImplicitNullCheck(instruction);
4202 }
Scott Wakelingb77051e2016-11-21 19:46:00 +00004203 __ Strexd(temp1, value_lo, value_hi, MemOperand(addr));
xueliang.zhongf51bc622016-11-04 09:23:32 +00004204 __ CompareAndBranchIfNonZero(temp1, &fail);
Artem Serov02d37832016-10-25 15:25:33 +01004205}
Artem Serov02109dd2016-09-23 17:17:54 +01004206
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004207void LocationsBuilderARMVIXL::HandleFieldSet(
4208 HInstruction* instruction, const FieldInfo& field_info) {
4209 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
4210
4211 LocationSummary* locations =
4212 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
4213 locations->SetInAt(0, Location::RequiresRegister());
4214
4215 Primitive::Type field_type = field_info.GetFieldType();
4216 if (Primitive::IsFloatingPointType(field_type)) {
4217 locations->SetInAt(1, Location::RequiresFpuRegister());
4218 } else {
4219 locations->SetInAt(1, Location::RequiresRegister());
4220 }
4221
4222 bool is_wide = field_type == Primitive::kPrimLong || field_type == Primitive::kPrimDouble;
4223 bool generate_volatile = field_info.IsVolatile()
4224 && is_wide
4225 && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
4226 bool needs_write_barrier =
4227 CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1));
4228 // Temporary registers for the write barrier.
4229 // TODO: consider renaming StoreNeedsWriteBarrier to StoreNeedsGCMark.
4230 if (needs_write_barrier) {
4231 locations->AddTemp(Location::RequiresRegister()); // Possibly used for reference poisoning too.
4232 locations->AddTemp(Location::RequiresRegister());
4233 } else if (generate_volatile) {
4234 // ARM encoding have some additional constraints for ldrexd/strexd:
4235 // - registers need to be consecutive
4236 // - the first register should be even but not R14.
4237 // We don't test for ARM yet, and the assertion makes sure that we
4238 // revisit this if we ever enable ARM encoding.
4239 DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet());
4240
4241 locations->AddTemp(Location::RequiresRegister());
4242 locations->AddTemp(Location::RequiresRegister());
4243 if (field_type == Primitive::kPrimDouble) {
4244 // For doubles we need two more registers to copy the value.
4245 locations->AddTemp(LocationFrom(r2));
4246 locations->AddTemp(LocationFrom(r3));
4247 }
4248 }
4249}
4250
4251void InstructionCodeGeneratorARMVIXL::HandleFieldSet(HInstruction* instruction,
4252 const FieldInfo& field_info,
4253 bool value_can_be_null) {
4254 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
4255
4256 LocationSummary* locations = instruction->GetLocations();
4257 vixl32::Register base = InputRegisterAt(instruction, 0);
4258 Location value = locations->InAt(1);
4259
4260 bool is_volatile = field_info.IsVolatile();
4261 bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
4262 Primitive::Type field_type = field_info.GetFieldType();
4263 uint32_t offset = field_info.GetFieldOffset().Uint32Value();
4264 bool needs_write_barrier =
4265 CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1));
4266
4267 if (is_volatile) {
4268 codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
4269 }
4270
4271 switch (field_type) {
4272 case Primitive::kPrimBoolean:
4273 case Primitive::kPrimByte: {
4274 GetAssembler()->StoreToOffset(kStoreByte, RegisterFrom(value), base, offset);
4275 break;
4276 }
4277
4278 case Primitive::kPrimShort:
4279 case Primitive::kPrimChar: {
4280 GetAssembler()->StoreToOffset(kStoreHalfword, RegisterFrom(value), base, offset);
4281 break;
4282 }
4283
4284 case Primitive::kPrimInt:
4285 case Primitive::kPrimNot: {
4286 if (kPoisonHeapReferences && needs_write_barrier) {
4287 // Note that in the case where `value` is a null reference,
4288 // we do not enter this block, as a null reference does not
4289 // need poisoning.
4290 DCHECK_EQ(field_type, Primitive::kPrimNot);
4291 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
4292 __ Mov(temp, RegisterFrom(value));
4293 GetAssembler()->PoisonHeapReference(temp);
4294 GetAssembler()->StoreToOffset(kStoreWord, temp, base, offset);
4295 } else {
4296 GetAssembler()->StoreToOffset(kStoreWord, RegisterFrom(value), base, offset);
4297 }
4298 break;
4299 }
4300
4301 case Primitive::kPrimLong: {
4302 if (is_volatile && !atomic_ldrd_strd) {
4303 GenerateWideAtomicStore(base,
4304 offset,
4305 LowRegisterFrom(value),
4306 HighRegisterFrom(value),
4307 RegisterFrom(locations->GetTemp(0)),
4308 RegisterFrom(locations->GetTemp(1)),
4309 instruction);
4310 } else {
4311 GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), base, offset);
4312 codegen_->MaybeRecordImplicitNullCheck(instruction);
4313 }
4314 break;
4315 }
4316
4317 case Primitive::kPrimFloat: {
4318 GetAssembler()->StoreSToOffset(SRegisterFrom(value), base, offset);
4319 break;
4320 }
4321
4322 case Primitive::kPrimDouble: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01004323 vixl32::DRegister value_reg = DRegisterFrom(value);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004324 if (is_volatile && !atomic_ldrd_strd) {
4325 vixl32::Register value_reg_lo = RegisterFrom(locations->GetTemp(0));
4326 vixl32::Register value_reg_hi = RegisterFrom(locations->GetTemp(1));
4327
4328 __ Vmov(value_reg_lo, value_reg_hi, value_reg);
4329
4330 GenerateWideAtomicStore(base,
4331 offset,
4332 value_reg_lo,
4333 value_reg_hi,
4334 RegisterFrom(locations->GetTemp(2)),
4335 RegisterFrom(locations->GetTemp(3)),
4336 instruction);
4337 } else {
4338 GetAssembler()->StoreDToOffset(value_reg, base, offset);
4339 codegen_->MaybeRecordImplicitNullCheck(instruction);
4340 }
4341 break;
4342 }
4343
4344 case Primitive::kPrimVoid:
4345 LOG(FATAL) << "Unreachable type " << field_type;
4346 UNREACHABLE();
4347 }
4348
4349 // Longs and doubles are handled in the switch.
4350 if (field_type != Primitive::kPrimLong && field_type != Primitive::kPrimDouble) {
Alexandre Rames374ddf32016-11-04 10:40:49 +00004351 // TODO(VIXL): Here and for other calls to `MaybeRecordImplicitNullCheck` in this method, we
4352 // should use a scope and the assembler to emit the store instruction to guarantee that we
4353 // record the pc at the correct position. But the `Assembler` does not automatically handle
4354 // unencodable offsets. Practically, everything is fine because the helper and VIXL, at the time
4355 // of writing, do generate the store instruction last.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004356 codegen_->MaybeRecordImplicitNullCheck(instruction);
4357 }
4358
4359 if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
4360 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
4361 vixl32::Register card = RegisterFrom(locations->GetTemp(1));
4362 codegen_->MarkGCCard(temp, card, base, RegisterFrom(value), value_can_be_null);
4363 }
4364
4365 if (is_volatile) {
4366 codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
4367 }
4368}
4369
Artem Serov02d37832016-10-25 15:25:33 +01004370void LocationsBuilderARMVIXL::HandleFieldGet(HInstruction* instruction,
4371 const FieldInfo& field_info) {
4372 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
4373
4374 bool object_field_get_with_read_barrier =
4375 kEmitCompilerReadBarrier && (field_info.GetFieldType() == Primitive::kPrimNot);
4376 LocationSummary* locations =
4377 new (GetGraph()->GetArena()) LocationSummary(instruction,
4378 object_field_get_with_read_barrier ?
4379 LocationSummary::kCallOnSlowPath :
4380 LocationSummary::kNoCall);
4381 if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
4382 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
4383 }
4384 locations->SetInAt(0, Location::RequiresRegister());
4385
4386 bool volatile_for_double = field_info.IsVolatile()
4387 && (field_info.GetFieldType() == Primitive::kPrimDouble)
4388 && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
4389 // The output overlaps in case of volatile long: we don't want the
4390 // code generated by GenerateWideAtomicLoad to overwrite the
4391 // object's location. Likewise, in the case of an object field get
4392 // with read barriers enabled, we do not want the load to overwrite
4393 // the object's location, as we need it to emit the read barrier.
4394 bool overlap = (field_info.IsVolatile() && (field_info.GetFieldType() == Primitive::kPrimLong)) ||
4395 object_field_get_with_read_barrier;
4396
4397 if (Primitive::IsFloatingPointType(instruction->GetType())) {
4398 locations->SetOut(Location::RequiresFpuRegister());
4399 } else {
4400 locations->SetOut(Location::RequiresRegister(),
4401 (overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap));
4402 }
4403 if (volatile_for_double) {
4404 // ARM encoding have some additional constraints for ldrexd/strexd:
4405 // - registers need to be consecutive
4406 // - the first register should be even but not R14.
4407 // We don't test for ARM yet, and the assertion makes sure that we
4408 // revisit this if we ever enable ARM encoding.
4409 DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet());
4410 locations->AddTemp(Location::RequiresRegister());
4411 locations->AddTemp(Location::RequiresRegister());
4412 } else if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
4413 // We need a temporary register for the read barrier marking slow
4414 // path in CodeGeneratorARM::GenerateFieldLoadWithBakerReadBarrier.
4415 locations->AddTemp(Location::RequiresRegister());
4416 }
4417}
4418
4419Location LocationsBuilderARMVIXL::ArithmeticZeroOrFpuRegister(HInstruction* input) {
4420 DCHECK(Primitive::IsFloatingPointType(input->GetType())) << input->GetType();
4421 if ((input->IsFloatConstant() && (input->AsFloatConstant()->IsArithmeticZero())) ||
4422 (input->IsDoubleConstant() && (input->AsDoubleConstant()->IsArithmeticZero()))) {
4423 return Location::ConstantLocation(input->AsConstant());
4424 } else {
4425 return Location::RequiresFpuRegister();
4426 }
4427}
4428
Artem Serov02109dd2016-09-23 17:17:54 +01004429Location LocationsBuilderARMVIXL::ArmEncodableConstantOrRegister(HInstruction* constant,
4430 Opcode opcode) {
4431 DCHECK(!Primitive::IsFloatingPointType(constant->GetType()));
4432 if (constant->IsConstant() &&
4433 CanEncodeConstantAsImmediate(constant->AsConstant(), opcode)) {
4434 return Location::ConstantLocation(constant->AsConstant());
4435 }
4436 return Location::RequiresRegister();
4437}
4438
4439bool LocationsBuilderARMVIXL::CanEncodeConstantAsImmediate(HConstant* input_cst,
4440 Opcode opcode) {
4441 uint64_t value = static_cast<uint64_t>(Int64FromConstant(input_cst));
4442 if (Primitive::Is64BitType(input_cst->GetType())) {
4443 Opcode high_opcode = opcode;
4444 SetCc low_set_cc = kCcDontCare;
4445 switch (opcode) {
4446 case SUB:
4447 // Flip the operation to an ADD.
4448 value = -value;
4449 opcode = ADD;
4450 FALLTHROUGH_INTENDED;
4451 case ADD:
4452 if (Low32Bits(value) == 0u) {
4453 return CanEncodeConstantAsImmediate(High32Bits(value), opcode, kCcDontCare);
4454 }
4455 high_opcode = ADC;
4456 low_set_cc = kCcSet;
4457 break;
4458 default:
4459 break;
4460 }
4461 return CanEncodeConstantAsImmediate(Low32Bits(value), opcode, low_set_cc) &&
4462 CanEncodeConstantAsImmediate(High32Bits(value), high_opcode, kCcDontCare);
4463 } else {
4464 return CanEncodeConstantAsImmediate(Low32Bits(value), opcode);
4465 }
4466}
4467
4468// TODO(VIXL): Replace art::arm::SetCc` with `vixl32::FlagsUpdate after flags set optimization
4469// enabled.
4470bool LocationsBuilderARMVIXL::CanEncodeConstantAsImmediate(uint32_t value,
4471 Opcode opcode,
4472 SetCc set_cc) {
4473 ArmVIXLAssembler* assembler = codegen_->GetAssembler();
4474 if (assembler->ShifterOperandCanHold(opcode, value, set_cc)) {
4475 return true;
4476 }
4477 Opcode neg_opcode = kNoOperand;
4478 switch (opcode) {
4479 case AND: neg_opcode = BIC; value = ~value; break;
4480 case ORR: neg_opcode = ORN; value = ~value; break;
4481 case ADD: neg_opcode = SUB; value = -value; break;
4482 case ADC: neg_opcode = SBC; value = ~value; break;
4483 case SUB: neg_opcode = ADD; value = -value; break;
4484 case SBC: neg_opcode = ADC; value = ~value; break;
4485 default:
4486 return false;
4487 }
4488 return assembler->ShifterOperandCanHold(neg_opcode, value, set_cc);
4489}
4490
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004491void InstructionCodeGeneratorARMVIXL::HandleFieldGet(HInstruction* instruction,
4492 const FieldInfo& field_info) {
4493 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
4494
4495 LocationSummary* locations = instruction->GetLocations();
4496 vixl32::Register base = InputRegisterAt(instruction, 0);
4497 Location out = locations->Out();
4498 bool is_volatile = field_info.IsVolatile();
4499 bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
4500 Primitive::Type field_type = field_info.GetFieldType();
4501 uint32_t offset = field_info.GetFieldOffset().Uint32Value();
4502
4503 switch (field_type) {
4504 case Primitive::kPrimBoolean:
4505 GetAssembler()->LoadFromOffset(kLoadUnsignedByte, RegisterFrom(out), base, offset);
4506 break;
4507
4508 case Primitive::kPrimByte:
4509 GetAssembler()->LoadFromOffset(kLoadSignedByte, RegisterFrom(out), base, offset);
4510 break;
4511
4512 case Primitive::kPrimShort:
4513 GetAssembler()->LoadFromOffset(kLoadSignedHalfword, RegisterFrom(out), base, offset);
4514 break;
4515
4516 case Primitive::kPrimChar:
4517 GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, RegisterFrom(out), base, offset);
4518 break;
4519
4520 case Primitive::kPrimInt:
4521 GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(out), base, offset);
4522 break;
4523
4524 case Primitive::kPrimNot: {
4525 // /* HeapReference<Object> */ out = *(base + offset)
4526 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00004527 Location temp_loc = locations->GetTemp(0);
4528 // Note that a potential implicit null check is handled in this
4529 // CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier call.
4530 codegen_->GenerateFieldLoadWithBakerReadBarrier(
4531 instruction, out, base, offset, temp_loc, /* needs_null_check */ true);
4532 if (is_volatile) {
4533 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
4534 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004535 } else {
4536 GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(out), base, offset);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004537 codegen_->MaybeRecordImplicitNullCheck(instruction);
4538 if (is_volatile) {
4539 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
4540 }
4541 // If read barriers are enabled, emit read barriers other than
4542 // Baker's using a slow path (and also unpoison the loaded
4543 // reference, if heap poisoning is enabled).
4544 codegen_->MaybeGenerateReadBarrierSlow(instruction, out, out, locations->InAt(0), offset);
4545 }
4546 break;
4547 }
4548
4549 case Primitive::kPrimLong:
4550 if (is_volatile && !atomic_ldrd_strd) {
4551 GenerateWideAtomicLoad(base, offset, LowRegisterFrom(out), HighRegisterFrom(out));
4552 } else {
4553 GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out), base, offset);
4554 }
4555 break;
4556
4557 case Primitive::kPrimFloat:
4558 GetAssembler()->LoadSFromOffset(SRegisterFrom(out), base, offset);
4559 break;
4560
4561 case Primitive::kPrimDouble: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01004562 vixl32::DRegister out_dreg = DRegisterFrom(out);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004563 if (is_volatile && !atomic_ldrd_strd) {
4564 vixl32::Register lo = RegisterFrom(locations->GetTemp(0));
4565 vixl32::Register hi = RegisterFrom(locations->GetTemp(1));
4566 GenerateWideAtomicLoad(base, offset, lo, hi);
4567 // TODO(VIXL): Do we need to be immediately after the ldrexd instruction? If so we need a
4568 // scope.
4569 codegen_->MaybeRecordImplicitNullCheck(instruction);
4570 __ Vmov(out_dreg, lo, hi);
4571 } else {
4572 GetAssembler()->LoadDFromOffset(out_dreg, base, offset);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004573 codegen_->MaybeRecordImplicitNullCheck(instruction);
4574 }
4575 break;
4576 }
4577
4578 case Primitive::kPrimVoid:
4579 LOG(FATAL) << "Unreachable type " << field_type;
4580 UNREACHABLE();
4581 }
4582
4583 if (field_type == Primitive::kPrimNot || field_type == Primitive::kPrimDouble) {
4584 // Potential implicit null checks, in the case of reference or
4585 // double fields, are handled in the previous switch statement.
4586 } else {
4587 // Address cases other than reference and double that may require an implicit null check.
Alexandre Rames374ddf32016-11-04 10:40:49 +00004588 // TODO(VIXL): Here and for other calls to `MaybeRecordImplicitNullCheck` in this method, we
4589 // should use a scope and the assembler to emit the load instruction to guarantee that we
4590 // record the pc at the correct position. But the `Assembler` does not automatically handle
4591 // unencodable offsets. Practically, everything is fine because the helper and VIXL, at the time
4592 // of writing, do generate the store instruction last.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004593 codegen_->MaybeRecordImplicitNullCheck(instruction);
4594 }
4595
4596 if (is_volatile) {
4597 if (field_type == Primitive::kPrimNot) {
4598 // Memory barriers, in the case of references, are also handled
4599 // in the previous switch statement.
4600 } else {
4601 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
4602 }
4603 }
4604}
4605
4606void LocationsBuilderARMVIXL::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
4607 HandleFieldSet(instruction, instruction->GetFieldInfo());
4608}
4609
4610void InstructionCodeGeneratorARMVIXL::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
4611 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
4612}
4613
4614void LocationsBuilderARMVIXL::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
4615 HandleFieldGet(instruction, instruction->GetFieldInfo());
4616}
4617
4618void InstructionCodeGeneratorARMVIXL::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
4619 HandleFieldGet(instruction, instruction->GetFieldInfo());
4620}
4621
4622void LocationsBuilderARMVIXL::VisitStaticFieldGet(HStaticFieldGet* instruction) {
4623 HandleFieldGet(instruction, instruction->GetFieldInfo());
4624}
4625
4626void InstructionCodeGeneratorARMVIXL::VisitStaticFieldGet(HStaticFieldGet* instruction) {
4627 HandleFieldGet(instruction, instruction->GetFieldInfo());
4628}
4629
Scott Wakelingc34dba72016-10-03 10:14:44 +01004630void LocationsBuilderARMVIXL::VisitStaticFieldSet(HStaticFieldSet* instruction) {
4631 HandleFieldSet(instruction, instruction->GetFieldInfo());
4632}
4633
4634void InstructionCodeGeneratorARMVIXL::VisitStaticFieldSet(HStaticFieldSet* instruction) {
4635 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
4636}
4637
Artem Serovcfbe9132016-10-14 15:58:56 +01004638void LocationsBuilderARMVIXL::VisitUnresolvedInstanceFieldGet(
4639 HUnresolvedInstanceFieldGet* instruction) {
4640 FieldAccessCallingConventionARMVIXL calling_convention;
4641 codegen_->CreateUnresolvedFieldLocationSummary(
4642 instruction, instruction->GetFieldType(), calling_convention);
4643}
4644
4645void InstructionCodeGeneratorARMVIXL::VisitUnresolvedInstanceFieldGet(
4646 HUnresolvedInstanceFieldGet* instruction) {
4647 FieldAccessCallingConventionARMVIXL calling_convention;
4648 codegen_->GenerateUnresolvedFieldAccess(instruction,
4649 instruction->GetFieldType(),
4650 instruction->GetFieldIndex(),
4651 instruction->GetDexPc(),
4652 calling_convention);
4653}
4654
4655void LocationsBuilderARMVIXL::VisitUnresolvedInstanceFieldSet(
4656 HUnresolvedInstanceFieldSet* instruction) {
4657 FieldAccessCallingConventionARMVIXL calling_convention;
4658 codegen_->CreateUnresolvedFieldLocationSummary(
4659 instruction, instruction->GetFieldType(), calling_convention);
4660}
4661
4662void InstructionCodeGeneratorARMVIXL::VisitUnresolvedInstanceFieldSet(
4663 HUnresolvedInstanceFieldSet* instruction) {
4664 FieldAccessCallingConventionARMVIXL calling_convention;
4665 codegen_->GenerateUnresolvedFieldAccess(instruction,
4666 instruction->GetFieldType(),
4667 instruction->GetFieldIndex(),
4668 instruction->GetDexPc(),
4669 calling_convention);
4670}
4671
4672void LocationsBuilderARMVIXL::VisitUnresolvedStaticFieldGet(
4673 HUnresolvedStaticFieldGet* instruction) {
4674 FieldAccessCallingConventionARMVIXL calling_convention;
4675 codegen_->CreateUnresolvedFieldLocationSummary(
4676 instruction, instruction->GetFieldType(), calling_convention);
4677}
4678
4679void InstructionCodeGeneratorARMVIXL::VisitUnresolvedStaticFieldGet(
4680 HUnresolvedStaticFieldGet* instruction) {
4681 FieldAccessCallingConventionARMVIXL calling_convention;
4682 codegen_->GenerateUnresolvedFieldAccess(instruction,
4683 instruction->GetFieldType(),
4684 instruction->GetFieldIndex(),
4685 instruction->GetDexPc(),
4686 calling_convention);
4687}
4688
4689void LocationsBuilderARMVIXL::VisitUnresolvedStaticFieldSet(
4690 HUnresolvedStaticFieldSet* instruction) {
4691 FieldAccessCallingConventionARMVIXL calling_convention;
4692 codegen_->CreateUnresolvedFieldLocationSummary(
4693 instruction, instruction->GetFieldType(), calling_convention);
4694}
4695
4696void InstructionCodeGeneratorARMVIXL::VisitUnresolvedStaticFieldSet(
4697 HUnresolvedStaticFieldSet* instruction) {
4698 FieldAccessCallingConventionARMVIXL calling_convention;
4699 codegen_->GenerateUnresolvedFieldAccess(instruction,
4700 instruction->GetFieldType(),
4701 instruction->GetFieldIndex(),
4702 instruction->GetDexPc(),
4703 calling_convention);
4704}
4705
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004706void LocationsBuilderARMVIXL::VisitNullCheck(HNullCheck* instruction) {
Artem Serov657022c2016-11-23 14:19:38 +00004707 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004708 locations->SetInAt(0, Location::RequiresRegister());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004709}
4710
4711void CodeGeneratorARMVIXL::GenerateImplicitNullCheck(HNullCheck* instruction) {
4712 if (CanMoveNullCheckToUser(instruction)) {
4713 return;
4714 }
4715
4716 UseScratchRegisterScope temps(GetVIXLAssembler());
Alexandre Rames374ddf32016-11-04 10:40:49 +00004717 // Ensure the pc position is recorded immediately after the `ldr` instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004718 AssemblerAccurateScope aas(GetVIXLAssembler(),
Alexandre Rames374ddf32016-11-04 10:40:49 +00004719 vixl32::kMaxInstructionSizeInBytes,
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004720 CodeBufferCheckScope::kMaximumSize);
4721 __ ldr(temps.Acquire(), MemOperand(InputRegisterAt(instruction, 0)));
4722 RecordPcInfo(instruction, instruction->GetDexPc());
4723}
4724
4725void CodeGeneratorARMVIXL::GenerateExplicitNullCheck(HNullCheck* instruction) {
4726 NullCheckSlowPathARMVIXL* slow_path =
4727 new (GetGraph()->GetArena()) NullCheckSlowPathARMVIXL(instruction);
4728 AddSlowPath(slow_path);
xueliang.zhongf51bc622016-11-04 09:23:32 +00004729 __ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004730}
4731
4732void InstructionCodeGeneratorARMVIXL::VisitNullCheck(HNullCheck* instruction) {
4733 codegen_->GenerateNullCheck(instruction);
4734}
4735
Scott Wakelingc34dba72016-10-03 10:14:44 +01004736static LoadOperandType GetLoadOperandType(Primitive::Type type) {
4737 switch (type) {
4738 case Primitive::kPrimNot:
4739 return kLoadWord;
4740 case Primitive::kPrimBoolean:
4741 return kLoadUnsignedByte;
4742 case Primitive::kPrimByte:
4743 return kLoadSignedByte;
4744 case Primitive::kPrimChar:
4745 return kLoadUnsignedHalfword;
4746 case Primitive::kPrimShort:
4747 return kLoadSignedHalfword;
4748 case Primitive::kPrimInt:
4749 return kLoadWord;
4750 case Primitive::kPrimLong:
4751 return kLoadWordPair;
4752 case Primitive::kPrimFloat:
4753 return kLoadSWord;
4754 case Primitive::kPrimDouble:
4755 return kLoadDWord;
4756 default:
4757 LOG(FATAL) << "Unreachable type " << type;
4758 UNREACHABLE();
4759 }
4760}
4761
4762static StoreOperandType GetStoreOperandType(Primitive::Type type) {
4763 switch (type) {
4764 case Primitive::kPrimNot:
4765 return kStoreWord;
4766 case Primitive::kPrimBoolean:
4767 case Primitive::kPrimByte:
4768 return kStoreByte;
4769 case Primitive::kPrimChar:
4770 case Primitive::kPrimShort:
4771 return kStoreHalfword;
4772 case Primitive::kPrimInt:
4773 return kStoreWord;
4774 case Primitive::kPrimLong:
4775 return kStoreWordPair;
4776 case Primitive::kPrimFloat:
4777 return kStoreSWord;
4778 case Primitive::kPrimDouble:
4779 return kStoreDWord;
4780 default:
4781 LOG(FATAL) << "Unreachable type " << type;
4782 UNREACHABLE();
4783 }
4784}
4785
4786void CodeGeneratorARMVIXL::LoadFromShiftedRegOffset(Primitive::Type type,
4787 Location out_loc,
4788 vixl32::Register base,
4789 vixl32::Register reg_index,
4790 vixl32::Condition cond) {
4791 uint32_t shift_count = Primitive::ComponentSizeShift(type);
4792 MemOperand mem_address(base, reg_index, vixl32::LSL, shift_count);
4793
4794 switch (type) {
4795 case Primitive::kPrimByte:
4796 __ Ldrsb(cond, RegisterFrom(out_loc), mem_address);
4797 break;
4798 case Primitive::kPrimBoolean:
4799 __ Ldrb(cond, RegisterFrom(out_loc), mem_address);
4800 break;
4801 case Primitive::kPrimShort:
4802 __ Ldrsh(cond, RegisterFrom(out_loc), mem_address);
4803 break;
4804 case Primitive::kPrimChar:
4805 __ Ldrh(cond, RegisterFrom(out_loc), mem_address);
4806 break;
4807 case Primitive::kPrimNot:
4808 case Primitive::kPrimInt:
4809 __ Ldr(cond, RegisterFrom(out_loc), mem_address);
4810 break;
4811 // T32 doesn't support LoadFromShiftedRegOffset mem address mode for these types.
4812 case Primitive::kPrimLong:
4813 case Primitive::kPrimFloat:
4814 case Primitive::kPrimDouble:
4815 default:
4816 LOG(FATAL) << "Unreachable type " << type;
4817 UNREACHABLE();
4818 }
4819}
4820
4821void CodeGeneratorARMVIXL::StoreToShiftedRegOffset(Primitive::Type type,
4822 Location loc,
4823 vixl32::Register base,
4824 vixl32::Register reg_index,
4825 vixl32::Condition cond) {
4826 uint32_t shift_count = Primitive::ComponentSizeShift(type);
4827 MemOperand mem_address(base, reg_index, vixl32::LSL, shift_count);
4828
4829 switch (type) {
4830 case Primitive::kPrimByte:
4831 case Primitive::kPrimBoolean:
4832 __ Strb(cond, RegisterFrom(loc), mem_address);
4833 break;
4834 case Primitive::kPrimShort:
4835 case Primitive::kPrimChar:
4836 __ Strh(cond, RegisterFrom(loc), mem_address);
4837 break;
4838 case Primitive::kPrimNot:
4839 case Primitive::kPrimInt:
4840 __ Str(cond, RegisterFrom(loc), mem_address);
4841 break;
4842 // T32 doesn't support StoreToShiftedRegOffset mem address mode for these types.
4843 case Primitive::kPrimLong:
4844 case Primitive::kPrimFloat:
4845 case Primitive::kPrimDouble:
4846 default:
4847 LOG(FATAL) << "Unreachable type " << type;
4848 UNREACHABLE();
4849 }
4850}
4851
4852void LocationsBuilderARMVIXL::VisitArrayGet(HArrayGet* instruction) {
4853 bool object_array_get_with_read_barrier =
4854 kEmitCompilerReadBarrier && (instruction->GetType() == Primitive::kPrimNot);
4855 LocationSummary* locations =
4856 new (GetGraph()->GetArena()) LocationSummary(instruction,
4857 object_array_get_with_read_barrier ?
4858 LocationSummary::kCallOnSlowPath :
4859 LocationSummary::kNoCall);
4860 if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00004861 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Scott Wakelingc34dba72016-10-03 10:14:44 +01004862 }
4863 locations->SetInAt(0, Location::RequiresRegister());
4864 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
4865 if (Primitive::IsFloatingPointType(instruction->GetType())) {
4866 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4867 } else {
4868 // The output overlaps in the case of an object array get with
4869 // read barriers enabled: we do not want the move to overwrite the
4870 // array's location, as we need it to emit the read barrier.
4871 locations->SetOut(
4872 Location::RequiresRegister(),
4873 object_array_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap);
4874 }
4875 // We need a temporary register for the read barrier marking slow
4876 // path in CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier.
4877 // Also need for String compression feature.
4878 if ((object_array_get_with_read_barrier && kUseBakerReadBarrier)
4879 || (mirror::kUseStringCompression && instruction->IsStringCharAt())) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004880 locations->AddTemp(Location::RequiresRegister());
Scott Wakelingc34dba72016-10-03 10:14:44 +01004881 }
4882}
4883
4884void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01004885 LocationSummary* locations = instruction->GetLocations();
4886 Location obj_loc = locations->InAt(0);
4887 vixl32::Register obj = InputRegisterAt(instruction, 0);
4888 Location index = locations->InAt(1);
4889 Location out_loc = locations->Out();
4890 uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
4891 Primitive::Type type = instruction->GetType();
4892 const bool maybe_compressed_char_at = mirror::kUseStringCompression &&
4893 instruction->IsStringCharAt();
4894 HInstruction* array_instr = instruction->GetArray();
4895 bool has_intermediate_address = array_instr->IsIntermediateAddress();
Scott Wakelingc34dba72016-10-03 10:14:44 +01004896
4897 switch (type) {
4898 case Primitive::kPrimBoolean:
4899 case Primitive::kPrimByte:
4900 case Primitive::kPrimShort:
4901 case Primitive::kPrimChar:
4902 case Primitive::kPrimInt: {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01004903 vixl32::Register length;
4904 if (maybe_compressed_char_at) {
4905 length = RegisterFrom(locations->GetTemp(0));
4906 uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
4907 GetAssembler()->LoadFromOffset(kLoadWord, length, obj, count_offset);
4908 codegen_->MaybeRecordImplicitNullCheck(instruction);
4909 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01004910 if (index.IsConstant()) {
4911 int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue();
4912 if (maybe_compressed_char_at) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004913 vixl32::Label uncompressed_load, done;
Vladimir Markofdaf0f42016-10-13 19:29:53 +01004914 __ Lsrs(length, length, 1u); // LSRS has a 16-bit encoding, TST (immediate) does not.
4915 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
4916 "Expecting 0=compressed, 1=uncompressed");
4917 __ B(cs, &uncompressed_load);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004918 GetAssembler()->LoadFromOffset(kLoadUnsignedByte,
4919 RegisterFrom(out_loc),
4920 obj,
4921 data_offset + const_index);
4922 __ B(&done);
4923 __ Bind(&uncompressed_load);
4924 GetAssembler()->LoadFromOffset(GetLoadOperandType(Primitive::kPrimChar),
4925 RegisterFrom(out_loc),
4926 obj,
4927 data_offset + (const_index << 1));
4928 __ Bind(&done);
Scott Wakelingc34dba72016-10-03 10:14:44 +01004929 } else {
4930 uint32_t full_offset = data_offset + (const_index << Primitive::ComponentSizeShift(type));
4931
4932 LoadOperandType load_type = GetLoadOperandType(type);
4933 GetAssembler()->LoadFromOffset(load_type, RegisterFrom(out_loc), obj, full_offset);
4934 }
4935 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00004936 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01004937 vixl32::Register temp = temps.Acquire();
4938
4939 if (has_intermediate_address) {
Artem Serov2bbc9532016-10-21 11:51:50 +01004940 // We do not need to compute the intermediate address from the array: the
4941 // input instruction has done it already. See the comment in
4942 // `TryExtractArrayAccessAddress()`.
4943 if (kIsDebugBuild) {
4944 HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
4945 DCHECK_EQ(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64(), data_offset);
4946 }
4947 temp = obj;
Scott Wakelingc34dba72016-10-03 10:14:44 +01004948 } else {
4949 __ Add(temp, obj, data_offset);
4950 }
4951 if (maybe_compressed_char_at) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004952 vixl32::Label uncompressed_load, done;
Vladimir Markofdaf0f42016-10-13 19:29:53 +01004953 __ Lsrs(length, length, 1u); // LSRS has a 16-bit encoding, TST (immediate) does not.
4954 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
4955 "Expecting 0=compressed, 1=uncompressed");
4956 __ B(cs, &uncompressed_load);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004957 __ Ldrb(RegisterFrom(out_loc), MemOperand(temp, RegisterFrom(index), vixl32::LSL, 0));
4958 __ B(&done);
4959 __ Bind(&uncompressed_load);
4960 __ Ldrh(RegisterFrom(out_loc), MemOperand(temp, RegisterFrom(index), vixl32::LSL, 1));
4961 __ Bind(&done);
Scott Wakelingc34dba72016-10-03 10:14:44 +01004962 } else {
4963 codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index));
4964 }
4965 }
4966 break;
4967 }
4968
4969 case Primitive::kPrimNot: {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00004970 // The read barrier instrumentation of object ArrayGet
4971 // instructions does not support the HIntermediateAddress
4972 // instruction.
4973 DCHECK(!(has_intermediate_address && kEmitCompilerReadBarrier));
4974
Scott Wakelingc34dba72016-10-03 10:14:44 +01004975 static_assert(
4976 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
4977 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
4978 // /* HeapReference<Object> */ out =
4979 // *(obj + data_offset + index * sizeof(HeapReference<Object>))
4980 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00004981 Location temp = locations->GetTemp(0);
4982 // Note that a potential implicit null check is handled in this
4983 // CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier call.
4984 codegen_->GenerateArrayLoadWithBakerReadBarrier(
4985 instruction, out_loc, obj, data_offset, index, temp, /* needs_null_check */ true);
Scott Wakelingc34dba72016-10-03 10:14:44 +01004986 } else {
4987 vixl32::Register out = OutputRegister(instruction);
4988 if (index.IsConstant()) {
4989 size_t offset =
4990 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
4991 GetAssembler()->LoadFromOffset(kLoadWord, out, obj, offset);
Alexandre Rames374ddf32016-11-04 10:40:49 +00004992 // TODO(VIXL): Here and for other calls to `MaybeRecordImplicitNullCheck` in this method,
4993 // we should use a scope and the assembler to emit the load instruction to guarantee that
4994 // we record the pc at the correct position. But the `Assembler` does not automatically
4995 // handle unencodable offsets. Practically, everything is fine because the helper and
4996 // VIXL, at the time of writing, do generate the store instruction last.
Scott Wakelingc34dba72016-10-03 10:14:44 +01004997 codegen_->MaybeRecordImplicitNullCheck(instruction);
4998 // If read barriers are enabled, emit read barriers other than
4999 // Baker's using a slow path (and also unpoison the loaded
5000 // reference, if heap poisoning is enabled).
5001 codegen_->MaybeGenerateReadBarrierSlow(instruction, out_loc, out_loc, obj_loc, offset);
5002 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00005003 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01005004 vixl32::Register temp = temps.Acquire();
5005
5006 if (has_intermediate_address) {
Artem Serov2bbc9532016-10-21 11:51:50 +01005007 // We do not need to compute the intermediate address from the array: the
5008 // input instruction has done it already. See the comment in
5009 // `TryExtractArrayAccessAddress()`.
5010 if (kIsDebugBuild) {
5011 HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
5012 DCHECK_EQ(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64(), data_offset);
5013 }
5014 temp = obj;
Scott Wakelingc34dba72016-10-03 10:14:44 +01005015 } else {
5016 __ Add(temp, obj, data_offset);
5017 }
5018 codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index));
Anton Kirilovedb2ac32016-11-30 15:14:10 +00005019 temps.Close();
Alexandre Rames374ddf32016-11-04 10:40:49 +00005020 // TODO(VIXL): Use a scope to ensure that we record the pc position immediately after the
5021 // load instruction. Practically, everything is fine because the helper and VIXL, at the
5022 // time of writing, do generate the store instruction last.
Scott Wakelingc34dba72016-10-03 10:14:44 +01005023 codegen_->MaybeRecordImplicitNullCheck(instruction);
5024 // If read barriers are enabled, emit read barriers other than
5025 // Baker's using a slow path (and also unpoison the loaded
5026 // reference, if heap poisoning is enabled).
5027 codegen_->MaybeGenerateReadBarrierSlow(
5028 instruction, out_loc, out_loc, obj_loc, data_offset, index);
5029 }
5030 }
5031 break;
5032 }
5033
5034 case Primitive::kPrimLong: {
5035 if (index.IsConstant()) {
5036 size_t offset =
5037 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
5038 GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out_loc), obj, offset);
5039 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00005040 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01005041 vixl32::Register temp = temps.Acquire();
5042 __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
5043 GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out_loc), temp, data_offset);
5044 }
5045 break;
5046 }
5047
5048 case Primitive::kPrimFloat: {
5049 vixl32::SRegister out = SRegisterFrom(out_loc);
5050 if (index.IsConstant()) {
5051 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
5052 GetAssembler()->LoadSFromOffset(out, obj, offset);
5053 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00005054 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01005055 vixl32::Register temp = temps.Acquire();
5056 __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_4));
5057 GetAssembler()->LoadSFromOffset(out, temp, data_offset);
5058 }
5059 break;
5060 }
5061
5062 case Primitive::kPrimDouble: {
5063 if (index.IsConstant()) {
5064 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
5065 GetAssembler()->LoadDFromOffset(DRegisterFrom(out_loc), obj, offset);
5066 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00005067 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01005068 vixl32::Register temp = temps.Acquire();
5069 __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
5070 GetAssembler()->LoadDFromOffset(DRegisterFrom(out_loc), temp, data_offset);
5071 }
5072 break;
5073 }
5074
5075 case Primitive::kPrimVoid:
5076 LOG(FATAL) << "Unreachable type " << type;
5077 UNREACHABLE();
5078 }
5079
5080 if (type == Primitive::kPrimNot) {
5081 // Potential implicit null checks, in the case of reference
5082 // arrays, are handled in the previous switch statement.
5083 } else if (!maybe_compressed_char_at) {
Alexandre Rames374ddf32016-11-04 10:40:49 +00005084 // TODO(VIXL): Use a scope to ensure we record the pc info immediately after
5085 // the preceding load instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01005086 codegen_->MaybeRecordImplicitNullCheck(instruction);
5087 }
5088}
5089
5090void LocationsBuilderARMVIXL::VisitArraySet(HArraySet* instruction) {
5091 Primitive::Type value_type = instruction->GetComponentType();
5092
5093 bool needs_write_barrier =
5094 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
5095 bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck();
5096
5097 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(
5098 instruction,
5099 may_need_runtime_call_for_type_check ?
5100 LocationSummary::kCallOnSlowPath :
5101 LocationSummary::kNoCall);
5102
5103 locations->SetInAt(0, Location::RequiresRegister());
5104 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
5105 if (Primitive::IsFloatingPointType(value_type)) {
5106 locations->SetInAt(2, Location::RequiresFpuRegister());
5107 } else {
5108 locations->SetInAt(2, Location::RequiresRegister());
5109 }
5110 if (needs_write_barrier) {
5111 // Temporary registers for the write barrier.
5112 locations->AddTemp(Location::RequiresRegister()); // Possibly used for ref. poisoning too.
5113 locations->AddTemp(Location::RequiresRegister());
5114 }
5115}
5116
5117void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01005118 LocationSummary* locations = instruction->GetLocations();
5119 vixl32::Register array = InputRegisterAt(instruction, 0);
5120 Location index = locations->InAt(1);
5121 Primitive::Type value_type = instruction->GetComponentType();
5122 bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck();
5123 bool needs_write_barrier =
5124 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
5125 uint32_t data_offset =
5126 mirror::Array::DataOffset(Primitive::ComponentSize(value_type)).Uint32Value();
5127 Location value_loc = locations->InAt(2);
5128 HInstruction* array_instr = instruction->GetArray();
5129 bool has_intermediate_address = array_instr->IsIntermediateAddress();
Scott Wakelingc34dba72016-10-03 10:14:44 +01005130
5131 switch (value_type) {
5132 case Primitive::kPrimBoolean:
5133 case Primitive::kPrimByte:
5134 case Primitive::kPrimShort:
5135 case Primitive::kPrimChar:
5136 case Primitive::kPrimInt: {
5137 if (index.IsConstant()) {
5138 int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue();
5139 uint32_t full_offset =
5140 data_offset + (const_index << Primitive::ComponentSizeShift(value_type));
5141 StoreOperandType store_type = GetStoreOperandType(value_type);
5142 GetAssembler()->StoreToOffset(store_type, RegisterFrom(value_loc), array, full_offset);
5143 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00005144 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01005145 vixl32::Register temp = temps.Acquire();
5146
5147 if (has_intermediate_address) {
Artem Serov2bbc9532016-10-21 11:51:50 +01005148 // We do not need to compute the intermediate address from the array: the
5149 // input instruction has done it already. See the comment in
5150 // `TryExtractArrayAccessAddress()`.
5151 if (kIsDebugBuild) {
5152 HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
5153 DCHECK(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64() == data_offset);
5154 }
5155 temp = array;
Scott Wakelingc34dba72016-10-03 10:14:44 +01005156 } else {
5157 __ Add(temp, array, data_offset);
5158 }
5159 codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
5160 }
5161 break;
5162 }
5163
5164 case Primitive::kPrimNot: {
5165 vixl32::Register value = RegisterFrom(value_loc);
5166 // TryExtractArrayAccessAddress optimization is never applied for non-primitive ArraySet.
5167 // See the comment in instruction_simplifier_shared.cc.
5168 DCHECK(!has_intermediate_address);
5169
5170 if (instruction->InputAt(2)->IsNullConstant()) {
5171 // Just setting null.
5172 if (index.IsConstant()) {
5173 size_t offset =
5174 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
5175 GetAssembler()->StoreToOffset(kStoreWord, value, array, offset);
5176 } else {
5177 DCHECK(index.IsRegister()) << index;
Anton Kirilovedb2ac32016-11-30 15:14:10 +00005178 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01005179 vixl32::Register temp = temps.Acquire();
5180 __ Add(temp, array, data_offset);
5181 codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
5182 }
Alexandre Rames374ddf32016-11-04 10:40:49 +00005183 // TODO(VIXL): Use a scope to ensure we record the pc info immediately after the preceding
5184 // store instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01005185 codegen_->MaybeRecordImplicitNullCheck(instruction);
5186 DCHECK(!needs_write_barrier);
5187 DCHECK(!may_need_runtime_call_for_type_check);
5188 break;
5189 }
5190
5191 DCHECK(needs_write_barrier);
5192 Location temp1_loc = locations->GetTemp(0);
5193 vixl32::Register temp1 = RegisterFrom(temp1_loc);
5194 Location temp2_loc = locations->GetTemp(1);
5195 vixl32::Register temp2 = RegisterFrom(temp2_loc);
5196 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
5197 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
5198 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
5199 vixl32::Label done;
5200 SlowPathCodeARMVIXL* slow_path = nullptr;
5201
5202 if (may_need_runtime_call_for_type_check) {
5203 slow_path = new (GetGraph()->GetArena()) ArraySetSlowPathARMVIXL(instruction);
5204 codegen_->AddSlowPath(slow_path);
5205 if (instruction->GetValueCanBeNull()) {
5206 vixl32::Label non_zero;
xueliang.zhongf51bc622016-11-04 09:23:32 +00005207 __ CompareAndBranchIfNonZero(value, &non_zero);
Scott Wakelingc34dba72016-10-03 10:14:44 +01005208 if (index.IsConstant()) {
5209 size_t offset =
5210 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
5211 GetAssembler()->StoreToOffset(kStoreWord, value, array, offset);
5212 } else {
5213 DCHECK(index.IsRegister()) << index;
Anton Kirilovedb2ac32016-11-30 15:14:10 +00005214 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01005215 vixl32::Register temp = temps.Acquire();
5216 __ Add(temp, array, data_offset);
5217 codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
5218 }
Alexandre Rames374ddf32016-11-04 10:40:49 +00005219 // TODO(VIXL): Use a scope to ensure we record the pc info immediately after the preceding
5220 // store instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01005221 codegen_->MaybeRecordImplicitNullCheck(instruction);
5222 __ B(&done);
5223 __ Bind(&non_zero);
5224 }
5225
5226 // Note that when read barriers are enabled, the type checks
5227 // are performed without read barriers. This is fine, even in
5228 // the case where a class object is in the from-space after
5229 // the flip, as a comparison involving such a type would not
5230 // produce a false positive; it may of course produce a false
5231 // negative, in which case we would take the ArraySet slow
5232 // path.
5233
Alexandre Rames374ddf32016-11-04 10:40:49 +00005234 {
5235 // Ensure we record the pc position immediately after the `ldr` instruction.
5236 AssemblerAccurateScope aas(GetVIXLAssembler(),
5237 vixl32::kMaxInstructionSizeInBytes,
5238 CodeBufferCheckScope::kMaximumSize);
5239 // /* HeapReference<Class> */ temp1 = array->klass_
5240 __ ldr(temp1, MemOperand(array, class_offset));
5241 codegen_->MaybeRecordImplicitNullCheck(instruction);
5242 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01005243 GetAssembler()->MaybeUnpoisonHeapReference(temp1);
5244
5245 // /* HeapReference<Class> */ temp1 = temp1->component_type_
5246 GetAssembler()->LoadFromOffset(kLoadWord, temp1, temp1, component_offset);
5247 // /* HeapReference<Class> */ temp2 = value->klass_
5248 GetAssembler()->LoadFromOffset(kLoadWord, temp2, value, class_offset);
5249 // If heap poisoning is enabled, no need to unpoison `temp1`
5250 // nor `temp2`, as we are comparing two poisoned references.
5251 __ Cmp(temp1, temp2);
5252
5253 if (instruction->StaticTypeOfArrayIsObjectArray()) {
5254 vixl32::Label do_put;
5255 __ B(eq, &do_put);
5256 // If heap poisoning is enabled, the `temp1` reference has
5257 // not been unpoisoned yet; unpoison it now.
5258 GetAssembler()->MaybeUnpoisonHeapReference(temp1);
5259
5260 // /* HeapReference<Class> */ temp1 = temp1->super_class_
5261 GetAssembler()->LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
5262 // If heap poisoning is enabled, no need to unpoison
5263 // `temp1`, as we are comparing against null below.
xueliang.zhongf51bc622016-11-04 09:23:32 +00005264 __ CompareAndBranchIfNonZero(temp1, slow_path->GetEntryLabel());
Scott Wakelingc34dba72016-10-03 10:14:44 +01005265 __ Bind(&do_put);
5266 } else {
5267 __ B(ne, slow_path->GetEntryLabel());
5268 }
5269 }
5270
5271 vixl32::Register source = value;
5272 if (kPoisonHeapReferences) {
5273 // Note that in the case where `value` is a null reference,
5274 // we do not enter this block, as a null reference does not
5275 // need poisoning.
5276 DCHECK_EQ(value_type, Primitive::kPrimNot);
5277 __ Mov(temp1, value);
5278 GetAssembler()->PoisonHeapReference(temp1);
5279 source = temp1;
5280 }
5281
5282 if (index.IsConstant()) {
5283 size_t offset =
5284 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
5285 GetAssembler()->StoreToOffset(kStoreWord, source, array, offset);
5286 } else {
5287 DCHECK(index.IsRegister()) << index;
5288
Anton Kirilovedb2ac32016-11-30 15:14:10 +00005289 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01005290 vixl32::Register temp = temps.Acquire();
5291 __ Add(temp, array, data_offset);
5292 codegen_->StoreToShiftedRegOffset(value_type,
5293 LocationFrom(source),
5294 temp,
5295 RegisterFrom(index));
5296 }
5297
5298 if (!may_need_runtime_call_for_type_check) {
Alexandre Rames374ddf32016-11-04 10:40:49 +00005299 // TODO(VIXL): Ensure we record the pc position immediately after the preceding store
5300 // instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01005301 codegen_->MaybeRecordImplicitNullCheck(instruction);
5302 }
5303
5304 codegen_->MarkGCCard(temp1, temp2, array, value, instruction->GetValueCanBeNull());
5305
5306 if (done.IsReferenced()) {
5307 __ Bind(&done);
5308 }
5309
5310 if (slow_path != nullptr) {
5311 __ Bind(slow_path->GetExitLabel());
5312 }
5313
5314 break;
5315 }
5316
5317 case Primitive::kPrimLong: {
5318 Location value = locations->InAt(2);
5319 if (index.IsConstant()) {
5320 size_t offset =
5321 (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
5322 GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), array, offset);
5323 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00005324 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01005325 vixl32::Register temp = temps.Acquire();
5326 __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
5327 GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), temp, data_offset);
5328 }
5329 break;
5330 }
5331
5332 case Primitive::kPrimFloat: {
5333 Location value = locations->InAt(2);
5334 DCHECK(value.IsFpuRegister());
5335 if (index.IsConstant()) {
5336 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
5337 GetAssembler()->StoreSToOffset(SRegisterFrom(value), array, offset);
5338 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00005339 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01005340 vixl32::Register temp = temps.Acquire();
5341 __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_4));
5342 GetAssembler()->StoreSToOffset(SRegisterFrom(value), temp, data_offset);
5343 }
5344 break;
5345 }
5346
5347 case Primitive::kPrimDouble: {
5348 Location value = locations->InAt(2);
5349 DCHECK(value.IsFpuRegisterPair());
5350 if (index.IsConstant()) {
5351 size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
5352 GetAssembler()->StoreDToOffset(DRegisterFrom(value), array, offset);
5353 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00005354 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01005355 vixl32::Register temp = temps.Acquire();
5356 __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
5357 GetAssembler()->StoreDToOffset(DRegisterFrom(value), temp, data_offset);
5358 }
5359 break;
5360 }
5361
5362 case Primitive::kPrimVoid:
5363 LOG(FATAL) << "Unreachable type " << value_type;
5364 UNREACHABLE();
5365 }
5366
5367 // Objects are handled in the switch.
5368 if (value_type != Primitive::kPrimNot) {
Alexandre Rames374ddf32016-11-04 10:40:49 +00005369 // TODO(VIXL): Ensure we record the pc position immediately after the preceding store
5370 // instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01005371 codegen_->MaybeRecordImplicitNullCheck(instruction);
5372 }
5373}
5374
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005375void LocationsBuilderARMVIXL::VisitArrayLength(HArrayLength* instruction) {
5376 LocationSummary* locations =
5377 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
5378 locations->SetInAt(0, Location::RequiresRegister());
5379 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5380}
5381
5382void InstructionCodeGeneratorARMVIXL::VisitArrayLength(HArrayLength* instruction) {
5383 uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction);
5384 vixl32::Register obj = InputRegisterAt(instruction, 0);
5385 vixl32::Register out = OutputRegister(instruction);
Alexandre Rames374ddf32016-11-04 10:40:49 +00005386 {
5387 AssemblerAccurateScope aas(GetVIXLAssembler(),
5388 vixl32::kMaxInstructionSizeInBytes,
5389 CodeBufferCheckScope::kMaximumSize);
5390 __ ldr(out, MemOperand(obj, offset));
5391 codegen_->MaybeRecordImplicitNullCheck(instruction);
5392 }
Anton Kirilove28d9ae2016-10-25 18:17:23 +01005393 // Mask out compression flag from String's array length.
5394 if (mirror::kUseStringCompression && instruction->IsStringLength()) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01005395 __ Lsr(out, out, 1u);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01005396 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005397}
5398
Artem Serov2bbc9532016-10-21 11:51:50 +01005399void LocationsBuilderARMVIXL::VisitIntermediateAddress(HIntermediateAddress* instruction) {
Artem Serov2bbc9532016-10-21 11:51:50 +01005400 LocationSummary* locations =
5401 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
5402
5403 locations->SetInAt(0, Location::RequiresRegister());
5404 locations->SetInAt(1, Location::RegisterOrConstant(instruction->GetOffset()));
5405 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5406}
5407
5408void InstructionCodeGeneratorARMVIXL::VisitIntermediateAddress(HIntermediateAddress* instruction) {
5409 vixl32::Register out = OutputRegister(instruction);
5410 vixl32::Register first = InputRegisterAt(instruction, 0);
5411 Location second = instruction->GetLocations()->InAt(1);
5412
Artem Serov2bbc9532016-10-21 11:51:50 +01005413 if (second.IsRegister()) {
5414 __ Add(out, first, RegisterFrom(second));
5415 } else {
5416 __ Add(out, first, second.GetConstant()->AsIntConstant()->GetValue());
5417 }
5418}
5419
Scott Wakelingc34dba72016-10-03 10:14:44 +01005420void LocationsBuilderARMVIXL::VisitBoundsCheck(HBoundsCheck* instruction) {
5421 RegisterSet caller_saves = RegisterSet::Empty();
5422 InvokeRuntimeCallingConventionARMVIXL calling_convention;
5423 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0)));
5424 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(1)));
5425 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction, caller_saves);
5426 locations->SetInAt(0, Location::RequiresRegister());
5427 locations->SetInAt(1, Location::RequiresRegister());
5428}
5429
5430void InstructionCodeGeneratorARMVIXL::VisitBoundsCheck(HBoundsCheck* instruction) {
5431 SlowPathCodeARMVIXL* slow_path =
5432 new (GetGraph()->GetArena()) BoundsCheckSlowPathARMVIXL(instruction);
5433 codegen_->AddSlowPath(slow_path);
5434
5435 vixl32::Register index = InputRegisterAt(instruction, 0);
5436 vixl32::Register length = InputRegisterAt(instruction, 1);
5437
5438 __ Cmp(index, length);
5439 __ B(hs, slow_path->GetEntryLabel());
5440}
5441
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005442void CodeGeneratorARMVIXL::MarkGCCard(vixl32::Register temp,
5443 vixl32::Register card,
5444 vixl32::Register object,
5445 vixl32::Register value,
5446 bool can_be_null) {
5447 vixl32::Label is_null;
5448 if (can_be_null) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00005449 __ CompareAndBranchIfZero(value, &is_null);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005450 }
5451 GetAssembler()->LoadFromOffset(
5452 kLoadWord, card, tr, Thread::CardTableOffset<kArmPointerSize>().Int32Value());
Scott Wakelingb77051e2016-11-21 19:46:00 +00005453 __ Lsr(temp, object, Operand::From(gc::accounting::CardTable::kCardShift));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005454 __ Strb(card, MemOperand(card, temp));
5455 if (can_be_null) {
5456 __ Bind(&is_null);
5457 }
5458}
5459
Scott Wakelingfe885462016-09-22 10:24:38 +01005460void LocationsBuilderARMVIXL::VisitParallelMove(HParallelMove* instruction ATTRIBUTE_UNUSED) {
5461 LOG(FATAL) << "Unreachable";
5462}
5463
5464void InstructionCodeGeneratorARMVIXL::VisitParallelMove(HParallelMove* instruction) {
5465 codegen_->GetMoveResolver()->EmitNativeCode(instruction);
5466}
5467
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005468void LocationsBuilderARMVIXL::VisitSuspendCheck(HSuspendCheck* instruction) {
Artem Serov657022c2016-11-23 14:19:38 +00005469 LocationSummary* locations =
5470 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
5471 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005472}
5473
5474void InstructionCodeGeneratorARMVIXL::VisitSuspendCheck(HSuspendCheck* instruction) {
5475 HBasicBlock* block = instruction->GetBlock();
5476 if (block->GetLoopInformation() != nullptr) {
5477 DCHECK(block->GetLoopInformation()->GetSuspendCheck() == instruction);
5478 // The back edge will generate the suspend check.
5479 return;
5480 }
5481 if (block->IsEntryBlock() && instruction->GetNext()->IsGoto()) {
5482 // The goto will generate the suspend check.
5483 return;
5484 }
5485 GenerateSuspendCheck(instruction, nullptr);
5486}
5487
5488void InstructionCodeGeneratorARMVIXL::GenerateSuspendCheck(HSuspendCheck* instruction,
5489 HBasicBlock* successor) {
5490 SuspendCheckSlowPathARMVIXL* slow_path =
5491 down_cast<SuspendCheckSlowPathARMVIXL*>(instruction->GetSlowPath());
5492 if (slow_path == nullptr) {
5493 slow_path = new (GetGraph()->GetArena()) SuspendCheckSlowPathARMVIXL(instruction, successor);
5494 instruction->SetSlowPath(slow_path);
5495 codegen_->AddSlowPath(slow_path);
5496 if (successor != nullptr) {
5497 DCHECK(successor->IsLoopHeader());
5498 codegen_->ClearSpillSlotsFromLoopPhisInStackMap(instruction);
5499 }
5500 } else {
5501 DCHECK_EQ(slow_path->GetSuccessor(), successor);
5502 }
5503
Anton Kirilovedb2ac32016-11-30 15:14:10 +00005504 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005505 vixl32::Register temp = temps.Acquire();
5506 GetAssembler()->LoadFromOffset(
5507 kLoadUnsignedHalfword, temp, tr, Thread::ThreadFlagsOffset<kArmPointerSize>().Int32Value());
5508 if (successor == nullptr) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00005509 __ CompareAndBranchIfNonZero(temp, slow_path->GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005510 __ Bind(slow_path->GetReturnLabel());
5511 } else {
xueliang.zhongf51bc622016-11-04 09:23:32 +00005512 __ CompareAndBranchIfZero(temp, codegen_->GetLabelOf(successor));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005513 __ B(slow_path->GetEntryLabel());
5514 }
5515}
5516
Scott Wakelingfe885462016-09-22 10:24:38 +01005517ArmVIXLAssembler* ParallelMoveResolverARMVIXL::GetAssembler() const {
5518 return codegen_->GetAssembler();
5519}
5520
5521void ParallelMoveResolverARMVIXL::EmitMove(size_t index) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005522 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
Scott Wakelingfe885462016-09-22 10:24:38 +01005523 MoveOperands* move = moves_[index];
5524 Location source = move->GetSource();
5525 Location destination = move->GetDestination();
5526
5527 if (source.IsRegister()) {
5528 if (destination.IsRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005529 __ Mov(RegisterFrom(destination), RegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01005530 } else if (destination.IsFpuRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005531 __ Vmov(SRegisterFrom(destination), RegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01005532 } else {
5533 DCHECK(destination.IsStackSlot());
5534 GetAssembler()->StoreToOffset(kStoreWord,
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005535 RegisterFrom(source),
Scott Wakelingfe885462016-09-22 10:24:38 +01005536 sp,
5537 destination.GetStackIndex());
5538 }
5539 } else if (source.IsStackSlot()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005540 if (destination.IsRegister()) {
5541 GetAssembler()->LoadFromOffset(kLoadWord,
5542 RegisterFrom(destination),
5543 sp,
5544 source.GetStackIndex());
5545 } else if (destination.IsFpuRegister()) {
5546 GetAssembler()->LoadSFromOffset(SRegisterFrom(destination), sp, source.GetStackIndex());
5547 } else {
5548 DCHECK(destination.IsStackSlot());
5549 vixl32::Register temp = temps.Acquire();
5550 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, source.GetStackIndex());
5551 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
5552 }
Scott Wakelingfe885462016-09-22 10:24:38 +01005553 } else if (source.IsFpuRegister()) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01005554 if (destination.IsRegister()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01005555 __ Vmov(RegisterFrom(destination), SRegisterFrom(source));
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01005556 } else if (destination.IsFpuRegister()) {
5557 __ Vmov(SRegisterFrom(destination), SRegisterFrom(source));
5558 } else {
5559 DCHECK(destination.IsStackSlot());
5560 GetAssembler()->StoreSToOffset(SRegisterFrom(source), sp, destination.GetStackIndex());
5561 }
Scott Wakelingfe885462016-09-22 10:24:38 +01005562 } else if (source.IsDoubleStackSlot()) {
Alexandre Rames9c19bd62016-10-24 11:50:32 +01005563 if (destination.IsDoubleStackSlot()) {
5564 vixl32::DRegister temp = temps.AcquireD();
5565 GetAssembler()->LoadDFromOffset(temp, sp, source.GetStackIndex());
5566 GetAssembler()->StoreDToOffset(temp, sp, destination.GetStackIndex());
5567 } else if (destination.IsRegisterPair()) {
5568 DCHECK(ExpectedPairLayout(destination));
5569 GetAssembler()->LoadFromOffset(
5570 kLoadWordPair, LowRegisterFrom(destination), sp, source.GetStackIndex());
5571 } else {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01005572 DCHECK(destination.IsFpuRegisterPair()) << destination;
5573 GetAssembler()->LoadDFromOffset(DRegisterFrom(destination), sp, source.GetStackIndex());
Alexandre Rames9c19bd62016-10-24 11:50:32 +01005574 }
Scott Wakelingfe885462016-09-22 10:24:38 +01005575 } else if (source.IsRegisterPair()) {
5576 if (destination.IsRegisterPair()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005577 __ Mov(LowRegisterFrom(destination), LowRegisterFrom(source));
5578 __ Mov(HighRegisterFrom(destination), HighRegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01005579 } else if (destination.IsFpuRegisterPair()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01005580 __ Vmov(DRegisterFrom(destination), LowRegisterFrom(source), HighRegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01005581 } else {
5582 DCHECK(destination.IsDoubleStackSlot()) << destination;
5583 DCHECK(ExpectedPairLayout(source));
5584 GetAssembler()->StoreToOffset(kStoreWordPair,
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005585 LowRegisterFrom(source),
Scott Wakelingfe885462016-09-22 10:24:38 +01005586 sp,
5587 destination.GetStackIndex());
5588 }
5589 } else if (source.IsFpuRegisterPair()) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01005590 if (destination.IsRegisterPair()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01005591 __ Vmov(LowRegisterFrom(destination), HighRegisterFrom(destination), DRegisterFrom(source));
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01005592 } else if (destination.IsFpuRegisterPair()) {
5593 __ Vmov(DRegisterFrom(destination), DRegisterFrom(source));
5594 } else {
5595 DCHECK(destination.IsDoubleStackSlot()) << destination;
5596 GetAssembler()->StoreDToOffset(DRegisterFrom(source), sp, destination.GetStackIndex());
5597 }
Scott Wakelingfe885462016-09-22 10:24:38 +01005598 } else {
5599 DCHECK(source.IsConstant()) << source;
5600 HConstant* constant = source.GetConstant();
5601 if (constant->IsIntConstant() || constant->IsNullConstant()) {
5602 int32_t value = CodeGenerator::GetInt32ValueOf(constant);
5603 if (destination.IsRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005604 __ Mov(RegisterFrom(destination), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01005605 } else {
5606 DCHECK(destination.IsStackSlot());
Scott Wakelingfe885462016-09-22 10:24:38 +01005607 vixl32::Register temp = temps.Acquire();
5608 __ Mov(temp, value);
5609 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
5610 }
5611 } else if (constant->IsLongConstant()) {
5612 int64_t value = constant->AsLongConstant()->GetValue();
5613 if (destination.IsRegisterPair()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005614 __ Mov(LowRegisterFrom(destination), Low32Bits(value));
5615 __ Mov(HighRegisterFrom(destination), High32Bits(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01005616 } else {
5617 DCHECK(destination.IsDoubleStackSlot()) << destination;
Scott Wakelingfe885462016-09-22 10:24:38 +01005618 vixl32::Register temp = temps.Acquire();
5619 __ Mov(temp, Low32Bits(value));
5620 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
5621 __ Mov(temp, High32Bits(value));
5622 GetAssembler()->StoreToOffset(kStoreWord,
5623 temp,
5624 sp,
5625 destination.GetHighStackIndex(kArmWordSize));
5626 }
5627 } else if (constant->IsDoubleConstant()) {
5628 double value = constant->AsDoubleConstant()->GetValue();
5629 if (destination.IsFpuRegisterPair()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01005630 __ Vmov(DRegisterFrom(destination), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01005631 } else {
5632 DCHECK(destination.IsDoubleStackSlot()) << destination;
5633 uint64_t int_value = bit_cast<uint64_t, double>(value);
Scott Wakelingfe885462016-09-22 10:24:38 +01005634 vixl32::Register temp = temps.Acquire();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005635 __ Mov(temp, Low32Bits(int_value));
Scott Wakelingfe885462016-09-22 10:24:38 +01005636 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005637 __ Mov(temp, High32Bits(int_value));
Scott Wakelingfe885462016-09-22 10:24:38 +01005638 GetAssembler()->StoreToOffset(kStoreWord,
5639 temp,
5640 sp,
5641 destination.GetHighStackIndex(kArmWordSize));
5642 }
5643 } else {
5644 DCHECK(constant->IsFloatConstant()) << constant->DebugName();
5645 float value = constant->AsFloatConstant()->GetValue();
5646 if (destination.IsFpuRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005647 __ Vmov(SRegisterFrom(destination), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01005648 } else {
5649 DCHECK(destination.IsStackSlot());
Scott Wakelingfe885462016-09-22 10:24:38 +01005650 vixl32::Register temp = temps.Acquire();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005651 __ Mov(temp, bit_cast<int32_t, float>(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01005652 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
5653 }
5654 }
5655 }
5656}
5657
Alexandre Rames9c19bd62016-10-24 11:50:32 +01005658void ParallelMoveResolverARMVIXL::Exchange(vixl32::Register reg, int mem) {
5659 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
5660 vixl32::Register temp = temps.Acquire();
5661 __ Mov(temp, reg);
5662 GetAssembler()->LoadFromOffset(kLoadWord, reg, sp, mem);
5663 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, mem);
Scott Wakelingfe885462016-09-22 10:24:38 +01005664}
5665
Alexandre Rames9c19bd62016-10-24 11:50:32 +01005666void ParallelMoveResolverARMVIXL::Exchange(int mem1, int mem2) {
5667 // TODO(VIXL32): Double check the performance of this implementation.
5668 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
5669 vixl32::Register temp = temps.Acquire();
5670 vixl32::SRegister temp_s = temps.AcquireS();
5671
5672 __ Ldr(temp, MemOperand(sp, mem1));
5673 __ Vldr(temp_s, MemOperand(sp, mem2));
5674 __ Str(temp, MemOperand(sp, mem2));
5675 __ Vstr(temp_s, MemOperand(sp, mem1));
Scott Wakelingfe885462016-09-22 10:24:38 +01005676}
5677
Alexandre Rames9c19bd62016-10-24 11:50:32 +01005678void ParallelMoveResolverARMVIXL::EmitSwap(size_t index) {
5679 MoveOperands* move = moves_[index];
5680 Location source = move->GetSource();
5681 Location destination = move->GetDestination();
5682 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
5683
5684 if (source.IsRegister() && destination.IsRegister()) {
5685 vixl32::Register temp = temps.Acquire();
5686 DCHECK(!RegisterFrom(source).Is(temp));
5687 DCHECK(!RegisterFrom(destination).Is(temp));
5688 __ Mov(temp, RegisterFrom(destination));
5689 __ Mov(RegisterFrom(destination), RegisterFrom(source));
5690 __ Mov(RegisterFrom(source), temp);
5691 } else if (source.IsRegister() && destination.IsStackSlot()) {
5692 Exchange(RegisterFrom(source), destination.GetStackIndex());
5693 } else if (source.IsStackSlot() && destination.IsRegister()) {
5694 Exchange(RegisterFrom(destination), source.GetStackIndex());
5695 } else if (source.IsStackSlot() && destination.IsStackSlot()) {
Anton Kirilovdda43962016-11-21 19:55:20 +00005696 Exchange(source.GetStackIndex(), destination.GetStackIndex());
Alexandre Rames9c19bd62016-10-24 11:50:32 +01005697 } else if (source.IsFpuRegister() && destination.IsFpuRegister()) {
Anton Kirilovdda43962016-11-21 19:55:20 +00005698 vixl32::SRegister temp = temps.AcquireS();
5699 __ Vmov(temp, SRegisterFrom(source));
5700 __ Vmov(SRegisterFrom(source), SRegisterFrom(destination));
5701 __ Vmov(SRegisterFrom(destination), temp);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01005702 } else if (source.IsRegisterPair() && destination.IsRegisterPair()) {
5703 vixl32::DRegister temp = temps.AcquireD();
5704 __ Vmov(temp, LowRegisterFrom(source), HighRegisterFrom(source));
5705 __ Mov(LowRegisterFrom(source), LowRegisterFrom(destination));
5706 __ Mov(HighRegisterFrom(source), HighRegisterFrom(destination));
5707 __ Vmov(LowRegisterFrom(destination), HighRegisterFrom(destination), temp);
5708 } else if (source.IsRegisterPair() || destination.IsRegisterPair()) {
5709 vixl32::Register low_reg = LowRegisterFrom(source.IsRegisterPair() ? source : destination);
5710 int mem = source.IsRegisterPair() ? destination.GetStackIndex() : source.GetStackIndex();
5711 DCHECK(ExpectedPairLayout(source.IsRegisterPair() ? source : destination));
5712 vixl32::DRegister temp = temps.AcquireD();
5713 __ Vmov(temp, low_reg, vixl32::Register(low_reg.GetCode() + 1));
5714 GetAssembler()->LoadFromOffset(kLoadWordPair, low_reg, sp, mem);
5715 GetAssembler()->StoreDToOffset(temp, sp, mem);
5716 } else if (source.IsFpuRegisterPair() && destination.IsFpuRegisterPair()) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01005717 vixl32::DRegister first = DRegisterFrom(source);
5718 vixl32::DRegister second = DRegisterFrom(destination);
5719 vixl32::DRegister temp = temps.AcquireD();
5720 __ Vmov(temp, first);
5721 __ Vmov(first, second);
5722 __ Vmov(second, temp);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01005723 } else if (source.IsFpuRegisterPair() || destination.IsFpuRegisterPair()) {
Anton Kirilovdda43962016-11-21 19:55:20 +00005724 vixl32::DRegister reg = source.IsFpuRegisterPair()
5725 ? DRegisterFrom(source)
5726 : DRegisterFrom(destination);
5727 int mem = source.IsFpuRegisterPair()
5728 ? destination.GetStackIndex()
5729 : source.GetStackIndex();
5730 vixl32::DRegister temp = temps.AcquireD();
5731 __ Vmov(temp, reg);
5732 GetAssembler()->LoadDFromOffset(reg, sp, mem);
5733 GetAssembler()->StoreDToOffset(temp, sp, mem);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01005734 } else if (source.IsFpuRegister() || destination.IsFpuRegister()) {
Anton Kirilovdda43962016-11-21 19:55:20 +00005735 vixl32::SRegister reg = source.IsFpuRegister()
5736 ? SRegisterFrom(source)
5737 : SRegisterFrom(destination);
5738 int mem = source.IsFpuRegister()
5739 ? destination.GetStackIndex()
5740 : source.GetStackIndex();
5741 vixl32::Register temp = temps.Acquire();
5742 __ Vmov(temp, reg);
5743 GetAssembler()->LoadSFromOffset(reg, sp, mem);
5744 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, mem);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01005745 } else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) {
5746 vixl32::DRegister temp1 = temps.AcquireD();
5747 vixl32::DRegister temp2 = temps.AcquireD();
5748 __ Vldr(temp1, MemOperand(sp, source.GetStackIndex()));
5749 __ Vldr(temp2, MemOperand(sp, destination.GetStackIndex()));
5750 __ Vstr(temp1, MemOperand(sp, destination.GetStackIndex()));
5751 __ Vstr(temp2, MemOperand(sp, source.GetStackIndex()));
5752 } else {
5753 LOG(FATAL) << "Unimplemented" << source << " <-> " << destination;
5754 }
Scott Wakelingfe885462016-09-22 10:24:38 +01005755}
5756
5757void ParallelMoveResolverARMVIXL::SpillScratch(int reg ATTRIBUTE_UNUSED) {
5758 TODO_VIXL32(FATAL);
5759}
5760
5761void ParallelMoveResolverARMVIXL::RestoreScratch(int reg ATTRIBUTE_UNUSED) {
5762 TODO_VIXL32(FATAL);
5763}
5764
Artem Serov02d37832016-10-25 15:25:33 +01005765HLoadClass::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadClassKind(
Artem Serovd4cc5b22016-11-04 11:19:09 +00005766 HLoadClass::LoadKind desired_class_load_kind) {
5767 switch (desired_class_load_kind) {
5768 case HLoadClass::LoadKind::kReferrersClass:
5769 break;
5770 case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
5771 // TODO(VIXL): Enable it back when literal pools are fixed in VIXL.
5772 return HLoadClass::LoadKind::kDexCacheViaMethod;
5773 case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
5774 DCHECK(GetCompilerOptions().GetCompilePic());
5775 break;
5776 case HLoadClass::LoadKind::kBootImageAddress:
5777 // TODO(VIXL): Enable it back when literal pools are fixed in VIXL.
5778 return HLoadClass::LoadKind::kDexCacheViaMethod;
Nicolas Geoffray22384ae2016-12-12 22:33:36 +00005779 case HLoadClass::LoadKind::kJitTableAddress:
Artem Serovd4cc5b22016-11-04 11:19:09 +00005780 // TODO(VIXL): Enable it back when literal pools are fixed in VIXL.
5781 return HLoadClass::LoadKind::kDexCacheViaMethod;
5782 case HLoadClass::LoadKind::kDexCachePcRelative:
5783 DCHECK(!Runtime::Current()->UseJitCompilation());
5784 // We disable pc-relative load when there is an irreducible loop, as the optimization
5785 // is incompatible with it.
5786 // TODO: Create as many ArmDexCacheArraysBase instructions as needed for methods
5787 // with irreducible loops.
5788 if (GetGraph()->HasIrreducibleLoops()) {
5789 return HLoadClass::LoadKind::kDexCacheViaMethod;
5790 }
5791 break;
5792 case HLoadClass::LoadKind::kDexCacheViaMethod:
5793 break;
5794 }
5795 return desired_class_load_kind;
Artem Serov02d37832016-10-25 15:25:33 +01005796}
5797
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005798void LocationsBuilderARMVIXL::VisitLoadClass(HLoadClass* cls) {
5799 if (cls->NeedsAccessCheck()) {
5800 InvokeRuntimeCallingConventionARMVIXL calling_convention;
5801 CodeGenerator::CreateLoadClassLocationSummary(
5802 cls,
5803 LocationFrom(calling_convention.GetRegisterAt(0)),
5804 LocationFrom(r0),
5805 /* code_generator_supports_read_barrier */ true);
5806 return;
5807 }
Scott Wakelingfe885462016-09-22 10:24:38 +01005808
Artem Serovd4cc5b22016-11-04 11:19:09 +00005809 const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
5810 LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier)
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005811 ? LocationSummary::kCallOnSlowPath
5812 : LocationSummary::kNoCall;
5813 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
Artem Serovd4cc5b22016-11-04 11:19:09 +00005814 if (kUseBakerReadBarrier && requires_read_barrier && !cls->NeedsEnvironment()) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00005815 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Artem Serovd4cc5b22016-11-04 11:19:09 +00005816 }
5817
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005818 HLoadClass::LoadKind load_kind = cls->GetLoadKind();
5819 if (load_kind == HLoadClass::LoadKind::kReferrersClass ||
5820 load_kind == HLoadClass::LoadKind::kDexCacheViaMethod ||
5821 load_kind == HLoadClass::LoadKind::kDexCachePcRelative) {
5822 locations->SetInAt(0, Location::RequiresRegister());
5823 }
5824 locations->SetOut(Location::RequiresRegister());
5825}
5826
5827void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) {
5828 LocationSummary* locations = cls->GetLocations();
5829 if (cls->NeedsAccessCheck()) {
Andreas Gampea5b09a62016-11-17 15:21:22 -08005830 codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex().index_);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005831 codegen_->InvokeRuntime(kQuickInitializeTypeAndVerifyAccess, cls, cls->GetDexPc());
5832 CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>();
5833 return;
5834 }
5835
5836 Location out_loc = locations->Out();
5837 vixl32::Register out = OutputRegister(cls);
5838
Artem Serovd4cc5b22016-11-04 11:19:09 +00005839 const ReadBarrierOption read_barrier_option = cls->IsInBootImage()
5840 ? kWithoutReadBarrier
5841 : kCompilerReadBarrierOption;
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005842 bool generate_null_check = false;
5843 switch (cls->GetLoadKind()) {
5844 case HLoadClass::LoadKind::kReferrersClass: {
5845 DCHECK(!cls->CanCallRuntime());
5846 DCHECK(!cls->MustGenerateClinitCheck());
5847 // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
5848 vixl32::Register current_method = InputRegisterAt(cls, 0);
5849 GenerateGcRootFieldLoad(cls,
5850 out_loc,
5851 current_method,
Roland Levillain00468f32016-10-27 18:02:48 +01005852 ArtMethod::DeclaringClassOffset().Int32Value(),
Artem Serovd4cc5b22016-11-04 11:19:09 +00005853 read_barrier_option);
5854 break;
5855 }
5856 case HLoadClass::LoadKind::kBootImageLinkTimeAddress: {
5857 TODO_VIXL32(FATAL);
5858 break;
5859 }
5860 case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: {
5861 DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
5862 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
5863 codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex());
5864 codegen_->EmitMovwMovtPlaceholder(labels, out);
5865 break;
5866 }
5867 case HLoadClass::LoadKind::kBootImageAddress: {
5868 TODO_VIXL32(FATAL);
5869 break;
5870 }
Nicolas Geoffray22384ae2016-12-12 22:33:36 +00005871 case HLoadClass::LoadKind::kJitTableAddress: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00005872 TODO_VIXL32(FATAL);
5873 break;
5874 }
5875 case HLoadClass::LoadKind::kDexCachePcRelative: {
5876 vixl32::Register base_reg = InputRegisterAt(cls, 0);
5877 HArmDexCacheArraysBase* base = cls->InputAt(0)->AsArmDexCacheArraysBase();
5878 int32_t offset = cls->GetDexCacheElementOffset() - base->GetElementOffset();
5879 // /* GcRoot<mirror::Class> */ out = *(dex_cache_arrays_base + offset)
5880 GenerateGcRootFieldLoad(cls, out_loc, base_reg, offset, read_barrier_option);
5881 generate_null_check = !cls->IsInDexCache();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005882 break;
5883 }
5884 case HLoadClass::LoadKind::kDexCacheViaMethod: {
5885 // /* GcRoot<mirror::Class>[] */ out =
5886 // current_method.ptr_sized_fields_->dex_cache_resolved_types_
5887 vixl32::Register current_method = InputRegisterAt(cls, 0);
5888 const int32_t resolved_types_offset =
5889 ArtMethod::DexCacheResolvedTypesOffset(kArmPointerSize).Int32Value();
5890 GetAssembler()->LoadFromOffset(kLoadWord, out, current_method, resolved_types_offset);
5891 // /* GcRoot<mirror::Class> */ out = out[type_index]
Andreas Gampea5b09a62016-11-17 15:21:22 -08005892 size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_);
Artem Serovd4cc5b22016-11-04 11:19:09 +00005893 GenerateGcRootFieldLoad(cls, out_loc, out, offset, read_barrier_option);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005894 generate_null_check = !cls->IsInDexCache();
5895 break;
5896 }
5897 default:
5898 TODO_VIXL32(FATAL);
5899 }
5900
5901 if (generate_null_check || cls->MustGenerateClinitCheck()) {
5902 DCHECK(cls->CanCallRuntime());
5903 LoadClassSlowPathARMVIXL* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARMVIXL(
5904 cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
5905 codegen_->AddSlowPath(slow_path);
5906 if (generate_null_check) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00005907 __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005908 }
5909 if (cls->MustGenerateClinitCheck()) {
5910 GenerateClassInitializationCheck(slow_path, out);
5911 } else {
5912 __ Bind(slow_path->GetExitLabel());
5913 }
5914 }
5915}
5916
Artem Serov02d37832016-10-25 15:25:33 +01005917void LocationsBuilderARMVIXL::VisitClinitCheck(HClinitCheck* check) {
5918 LocationSummary* locations =
5919 new (GetGraph()->GetArena()) LocationSummary(check, LocationSummary::kCallOnSlowPath);
5920 locations->SetInAt(0, Location::RequiresRegister());
5921 if (check->HasUses()) {
5922 locations->SetOut(Location::SameAsFirstInput());
5923 }
5924}
5925
5926void InstructionCodeGeneratorARMVIXL::VisitClinitCheck(HClinitCheck* check) {
5927 // We assume the class is not null.
5928 LoadClassSlowPathARMVIXL* slow_path =
5929 new (GetGraph()->GetArena()) LoadClassSlowPathARMVIXL(check->GetLoadClass(),
5930 check,
5931 check->GetDexPc(),
5932 /* do_clinit */ true);
5933 codegen_->AddSlowPath(slow_path);
5934 GenerateClassInitializationCheck(slow_path, InputRegisterAt(check, 0));
5935}
5936
5937void InstructionCodeGeneratorARMVIXL::GenerateClassInitializationCheck(
5938 LoadClassSlowPathARMVIXL* slow_path, vixl32::Register class_reg) {
5939 UseScratchRegisterScope temps(GetVIXLAssembler());
5940 vixl32::Register temp = temps.Acquire();
5941 GetAssembler()->LoadFromOffset(kLoadWord,
5942 temp,
5943 class_reg,
5944 mirror::Class::StatusOffset().Int32Value());
5945 __ Cmp(temp, mirror::Class::kStatusInitialized);
5946 __ B(lt, slow_path->GetEntryLabel());
5947 // Even if the initialized flag is set, we may be in a situation where caches are not synced
5948 // properly. Therefore, we do a memory fence.
5949 __ Dmb(ISH);
5950 __ Bind(slow_path->GetExitLabel());
5951}
5952
Artem Serov02d37832016-10-25 15:25:33 +01005953HLoadString::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadStringKind(
Artem Serovd4cc5b22016-11-04 11:19:09 +00005954 HLoadString::LoadKind desired_string_load_kind) {
5955 switch (desired_string_load_kind) {
5956 case HLoadString::LoadKind::kBootImageLinkTimeAddress:
5957 // TODO(VIXL): Implement missing optimization.
5958 return HLoadString::LoadKind::kDexCacheViaMethod;
5959 case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
5960 DCHECK(GetCompilerOptions().GetCompilePic());
5961 break;
5962 case HLoadString::LoadKind::kBootImageAddress:
5963 // TODO(VIXL): Implement missing optimization.
5964 return HLoadString::LoadKind::kDexCacheViaMethod;
5965 case HLoadString::LoadKind::kBssEntry:
5966 DCHECK(!Runtime::Current()->UseJitCompilation());
5967 break;
5968 case HLoadString::LoadKind::kJitTableAddress:
5969 DCHECK(Runtime::Current()->UseJitCompilation());
5970 // TODO(VIXL): Implement missing optimization.
5971 return HLoadString::LoadKind::kDexCacheViaMethod;
5972 case HLoadString::LoadKind::kDexCacheViaMethod:
5973 break;
5974 }
5975 return desired_string_load_kind;
Artem Serov02d37832016-10-25 15:25:33 +01005976}
5977
5978void LocationsBuilderARMVIXL::VisitLoadString(HLoadString* load) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00005979 LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load);
Artem Serov02d37832016-10-25 15:25:33 +01005980 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
Artem Serov02d37832016-10-25 15:25:33 +01005981 HLoadString::LoadKind load_kind = load->GetLoadKind();
5982 if (load_kind == HLoadString::LoadKind::kDexCacheViaMethod) {
Artem Serov02d37832016-10-25 15:25:33 +01005983 locations->SetOut(LocationFrom(r0));
5984 } else {
5985 locations->SetOut(Location::RequiresRegister());
Artem Serovd4cc5b22016-11-04 11:19:09 +00005986 if (load_kind == HLoadString::LoadKind::kBssEntry) {
5987 if (!kUseReadBarrier || kUseBakerReadBarrier) {
5988 // Rely on the pResolveString and/or marking to save everything, including temps.
5989 // Note that IP may theoretically be clobbered by saving/restoring the live register
5990 // (only one thanks to the custom calling convention), so we request a different temp.
5991 locations->AddTemp(Location::RequiresRegister());
5992 RegisterSet caller_saves = RegisterSet::Empty();
5993 InvokeRuntimeCallingConventionARMVIXL calling_convention;
5994 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0)));
5995 // TODO: Add GetReturnLocation() to the calling convention so that we can DCHECK()
5996 // that the the kPrimNot result register is the same as the first argument register.
5997 locations->SetCustomSlowPathCallerSaves(caller_saves);
5998 } else {
5999 // For non-Baker read barrier we have a temp-clobbering call.
6000 }
6001 }
Artem Serov02d37832016-10-25 15:25:33 +01006002 }
6003}
6004
6005void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00006006 LocationSummary* locations = load->GetLocations();
6007 Location out_loc = locations->Out();
6008 vixl32::Register out = OutputRegister(load);
6009 HLoadString::LoadKind load_kind = load->GetLoadKind();
6010
6011 switch (load_kind) {
6012 case HLoadString::LoadKind::kBootImageLinkTimeAddress: {
6013 TODO_VIXL32(FATAL);
6014 break;
6015 }
6016 case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
6017 DCHECK(codegen_->GetCompilerOptions().IsBootImage());
6018 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
6019 codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex().index_);
6020 codegen_->EmitMovwMovtPlaceholder(labels, out);
6021 return; // No dex cache slow path.
6022 }
6023 case HLoadString::LoadKind::kBootImageAddress: {
6024 TODO_VIXL32(FATAL);
6025 break;
6026 }
6027 case HLoadString::LoadKind::kBssEntry: {
6028 DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
6029 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
6030 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
6031 codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex().index_);
6032 codegen_->EmitMovwMovtPlaceholder(labels, temp);
6033 GenerateGcRootFieldLoad(load, out_loc, temp, /* offset */ 0, kCompilerReadBarrierOption);
6034 LoadStringSlowPathARMVIXL* slow_path =
6035 new (GetGraph()->GetArena()) LoadStringSlowPathARMVIXL(load);
6036 codegen_->AddSlowPath(slow_path);
6037 __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
6038 __ Bind(slow_path->GetExitLabel());
6039 return;
6040 }
6041 case HLoadString::LoadKind::kJitTableAddress: {
6042 TODO_VIXL32(FATAL);
6043 break;
6044 }
6045 default:
6046 break;
6047 }
Artem Serov02d37832016-10-25 15:25:33 +01006048
6049 // TODO: Re-add the compiler code to do string dex cache lookup again.
6050 DCHECK_EQ(load->GetLoadKind(), HLoadString::LoadKind::kDexCacheViaMethod);
6051 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Andreas Gampe8a0128a2016-11-28 07:38:35 -08006052 __ Mov(calling_convention.GetRegisterAt(0), load->GetStringIndex().index_);
Artem Serov02d37832016-10-25 15:25:33 +01006053 codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc());
6054 CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
6055}
6056
6057static int32_t GetExceptionTlsOffset() {
6058 return Thread::ExceptionOffset<kArmPointerSize>().Int32Value();
6059}
6060
6061void LocationsBuilderARMVIXL::VisitLoadException(HLoadException* load) {
6062 LocationSummary* locations =
6063 new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kNoCall);
6064 locations->SetOut(Location::RequiresRegister());
6065}
6066
6067void InstructionCodeGeneratorARMVIXL::VisitLoadException(HLoadException* load) {
6068 vixl32::Register out = OutputRegister(load);
6069 GetAssembler()->LoadFromOffset(kLoadWord, out, tr, GetExceptionTlsOffset());
6070}
6071
6072
6073void LocationsBuilderARMVIXL::VisitClearException(HClearException* clear) {
6074 new (GetGraph()->GetArena()) LocationSummary(clear, LocationSummary::kNoCall);
6075}
6076
6077void InstructionCodeGeneratorARMVIXL::VisitClearException(HClearException* clear ATTRIBUTE_UNUSED) {
6078 UseScratchRegisterScope temps(GetVIXLAssembler());
6079 vixl32::Register temp = temps.Acquire();
6080 __ Mov(temp, 0);
6081 GetAssembler()->StoreToOffset(kStoreWord, temp, tr, GetExceptionTlsOffset());
6082}
6083
6084void LocationsBuilderARMVIXL::VisitThrow(HThrow* instruction) {
6085 LocationSummary* locations =
6086 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
6087 InvokeRuntimeCallingConventionARMVIXL calling_convention;
6088 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
6089}
6090
6091void InstructionCodeGeneratorARMVIXL::VisitThrow(HThrow* instruction) {
6092 codegen_->InvokeRuntime(kQuickDeliverException, instruction, instruction->GetDexPc());
6093 CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
6094}
6095
Artem Serov657022c2016-11-23 14:19:38 +00006096// Temp is used for read barrier.
6097static size_t NumberOfInstanceOfTemps(TypeCheckKind type_check_kind) {
6098 if (kEmitCompilerReadBarrier &&
6099 (kUseBakerReadBarrier ||
6100 type_check_kind == TypeCheckKind::kAbstractClassCheck ||
6101 type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
6102 type_check_kind == TypeCheckKind::kArrayObjectCheck)) {
6103 return 1;
6104 }
6105 return 0;
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006106}
6107
Artem Serov657022c2016-11-23 14:19:38 +00006108// Interface case has 3 temps, one for holding the number of interfaces, one for the current
6109// interface pointer, one for loading the current interface.
6110// The other checks have one temp for loading the object's class.
6111static size_t NumberOfCheckCastTemps(TypeCheckKind type_check_kind) {
6112 if (type_check_kind == TypeCheckKind::kInterfaceCheck) {
6113 return 3;
6114 }
6115 return 1 + NumberOfInstanceOfTemps(type_check_kind);
6116}
Artem Serovcfbe9132016-10-14 15:58:56 +01006117
6118void LocationsBuilderARMVIXL::VisitInstanceOf(HInstanceOf* instruction) {
6119 LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
6120 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
6121 bool baker_read_barrier_slow_path = false;
6122 switch (type_check_kind) {
6123 case TypeCheckKind::kExactCheck:
6124 case TypeCheckKind::kAbstractClassCheck:
6125 case TypeCheckKind::kClassHierarchyCheck:
6126 case TypeCheckKind::kArrayObjectCheck:
6127 call_kind =
6128 kEmitCompilerReadBarrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall;
6129 baker_read_barrier_slow_path = kUseBakerReadBarrier;
6130 break;
6131 case TypeCheckKind::kArrayCheck:
6132 case TypeCheckKind::kUnresolvedCheck:
6133 case TypeCheckKind::kInterfaceCheck:
6134 call_kind = LocationSummary::kCallOnSlowPath;
6135 break;
6136 }
6137
6138 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
6139 if (baker_read_barrier_slow_path) {
6140 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
6141 }
6142 locations->SetInAt(0, Location::RequiresRegister());
6143 locations->SetInAt(1, Location::RequiresRegister());
6144 // The "out" register is used as a temporary, so it overlaps with the inputs.
6145 // Note that TypeCheckSlowPathARM uses this register too.
6146 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
Artem Serov657022c2016-11-23 14:19:38 +00006147 locations->AddRegisterTemps(NumberOfInstanceOfTemps(type_check_kind));
Artem Serovcfbe9132016-10-14 15:58:56 +01006148}
6149
6150void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) {
6151 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
6152 LocationSummary* locations = instruction->GetLocations();
6153 Location obj_loc = locations->InAt(0);
6154 vixl32::Register obj = InputRegisterAt(instruction, 0);
6155 vixl32::Register cls = InputRegisterAt(instruction, 1);
6156 Location out_loc = locations->Out();
6157 vixl32::Register out = OutputRegister(instruction);
Artem Serov657022c2016-11-23 14:19:38 +00006158 const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind);
6159 DCHECK_LE(num_temps, 1u);
6160 Location maybe_temp_loc = (num_temps >= 1) ? locations->GetTemp(0) : Location::NoLocation();
Artem Serovcfbe9132016-10-14 15:58:56 +01006161 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
6162 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
6163 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
6164 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
6165 vixl32::Label done, zero;
6166 SlowPathCodeARMVIXL* slow_path = nullptr;
6167
6168 // Return 0 if `obj` is null.
6169 // avoid null check if we know obj is not null.
6170 if (instruction->MustDoNullCheck()) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00006171 __ CompareAndBranchIfZero(obj, &zero, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01006172 }
6173
Artem Serovcfbe9132016-10-14 15:58:56 +01006174 switch (type_check_kind) {
6175 case TypeCheckKind::kExactCheck: {
Mathieu Chartier6beced42016-11-15 15:51:31 -08006176 // /* HeapReference<Class> */ out = obj->klass_
6177 GenerateReferenceLoadTwoRegisters(instruction,
6178 out_loc,
6179 obj_loc,
6180 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00006181 maybe_temp_loc,
6182 kCompilerReadBarrierOption);
Artem Serovcfbe9132016-10-14 15:58:56 +01006183 __ Cmp(out, cls);
6184 // Classes must be equal for the instanceof to succeed.
6185 __ B(ne, &zero);
6186 __ Mov(out, 1);
6187 __ B(&done);
6188 break;
6189 }
6190
6191 case TypeCheckKind::kAbstractClassCheck: {
Mathieu Chartier6beced42016-11-15 15:51:31 -08006192 // /* HeapReference<Class> */ out = obj->klass_
6193 GenerateReferenceLoadTwoRegisters(instruction,
6194 out_loc,
6195 obj_loc,
6196 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00006197 maybe_temp_loc,
6198 kCompilerReadBarrierOption);
Artem Serovcfbe9132016-10-14 15:58:56 +01006199 // If the class is abstract, we eagerly fetch the super class of the
6200 // object to avoid doing a comparison we know will fail.
6201 vixl32::Label loop;
6202 __ Bind(&loop);
6203 // /* HeapReference<Class> */ out = out->super_class_
Artem Serov657022c2016-11-23 14:19:38 +00006204 GenerateReferenceLoadOneRegister(instruction,
6205 out_loc,
6206 super_offset,
6207 maybe_temp_loc,
6208 kCompilerReadBarrierOption);
Artem Serovcfbe9132016-10-14 15:58:56 +01006209 // If `out` is null, we use it for the result, and jump to `done`.
xueliang.zhongf51bc622016-11-04 09:23:32 +00006210 __ CompareAndBranchIfZero(out, &done, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01006211 __ Cmp(out, cls);
6212 __ B(ne, &loop);
6213 __ Mov(out, 1);
6214 if (zero.IsReferenced()) {
6215 __ B(&done);
6216 }
6217 break;
6218 }
6219
6220 case TypeCheckKind::kClassHierarchyCheck: {
Mathieu Chartier6beced42016-11-15 15:51:31 -08006221 // /* HeapReference<Class> */ out = obj->klass_
6222 GenerateReferenceLoadTwoRegisters(instruction,
6223 out_loc,
6224 obj_loc,
6225 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00006226 maybe_temp_loc,
6227 kCompilerReadBarrierOption);
Artem Serovcfbe9132016-10-14 15:58:56 +01006228 // Walk over the class hierarchy to find a match.
6229 vixl32::Label loop, success;
6230 __ Bind(&loop);
6231 __ Cmp(out, cls);
6232 __ B(eq, &success);
6233 // /* HeapReference<Class> */ out = out->super_class_
Artem Serov657022c2016-11-23 14:19:38 +00006234 GenerateReferenceLoadOneRegister(instruction,
6235 out_loc,
6236 super_offset,
6237 maybe_temp_loc,
6238 kCompilerReadBarrierOption);
xueliang.zhongf51bc622016-11-04 09:23:32 +00006239 __ CompareAndBranchIfNonZero(out, &loop);
Artem Serovcfbe9132016-10-14 15:58:56 +01006240 // If `out` is null, we use it for the result, and jump to `done`.
6241 __ B(&done);
6242 __ Bind(&success);
6243 __ Mov(out, 1);
6244 if (zero.IsReferenced()) {
6245 __ B(&done);
6246 }
6247 break;
6248 }
6249
6250 case TypeCheckKind::kArrayObjectCheck: {
Mathieu Chartier6beced42016-11-15 15:51:31 -08006251 // /* HeapReference<Class> */ out = obj->klass_
6252 GenerateReferenceLoadTwoRegisters(instruction,
6253 out_loc,
6254 obj_loc,
6255 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00006256 maybe_temp_loc,
6257 kCompilerReadBarrierOption);
Artem Serovcfbe9132016-10-14 15:58:56 +01006258 // Do an exact check.
6259 vixl32::Label exact_check;
6260 __ Cmp(out, cls);
6261 __ B(eq, &exact_check);
6262 // Otherwise, we need to check that the object's class is a non-primitive array.
6263 // /* HeapReference<Class> */ out = out->component_type_
Artem Serov657022c2016-11-23 14:19:38 +00006264 GenerateReferenceLoadOneRegister(instruction,
6265 out_loc,
6266 component_offset,
6267 maybe_temp_loc,
6268 kCompilerReadBarrierOption);
Artem Serovcfbe9132016-10-14 15:58:56 +01006269 // If `out` is null, we use it for the result, and jump to `done`.
xueliang.zhongf51bc622016-11-04 09:23:32 +00006270 __ CompareAndBranchIfZero(out, &done, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01006271 GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset);
6272 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
xueliang.zhongf51bc622016-11-04 09:23:32 +00006273 __ CompareAndBranchIfNonZero(out, &zero, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01006274 __ Bind(&exact_check);
6275 __ Mov(out, 1);
6276 __ B(&done);
6277 break;
6278 }
6279
6280 case TypeCheckKind::kArrayCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00006281 // No read barrier since the slow path will retry upon failure.
Mathieu Chartier6beced42016-11-15 15:51:31 -08006282 // /* HeapReference<Class> */ out = obj->klass_
6283 GenerateReferenceLoadTwoRegisters(instruction,
6284 out_loc,
6285 obj_loc,
6286 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00006287 maybe_temp_loc,
6288 kWithoutReadBarrier);
Artem Serovcfbe9132016-10-14 15:58:56 +01006289 __ Cmp(out, cls);
6290 DCHECK(locations->OnlyCallsOnSlowPath());
6291 slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARMVIXL(instruction,
6292 /* is_fatal */ false);
6293 codegen_->AddSlowPath(slow_path);
6294 __ B(ne, slow_path->GetEntryLabel());
6295 __ Mov(out, 1);
6296 if (zero.IsReferenced()) {
6297 __ B(&done);
6298 }
6299 break;
6300 }
6301
6302 case TypeCheckKind::kUnresolvedCheck:
6303 case TypeCheckKind::kInterfaceCheck: {
6304 // Note that we indeed only call on slow path, but we always go
6305 // into the slow path for the unresolved and interface check
6306 // cases.
6307 //
6308 // We cannot directly call the InstanceofNonTrivial runtime
6309 // entry point without resorting to a type checking slow path
6310 // here (i.e. by calling InvokeRuntime directly), as it would
6311 // require to assign fixed registers for the inputs of this
6312 // HInstanceOf instruction (following the runtime calling
6313 // convention), which might be cluttered by the potential first
6314 // read barrier emission at the beginning of this method.
6315 //
6316 // TODO: Introduce a new runtime entry point taking the object
6317 // to test (instead of its class) as argument, and let it deal
6318 // with the read barrier issues. This will let us refactor this
6319 // case of the `switch` code as it was previously (with a direct
6320 // call to the runtime not using a type checking slow path).
6321 // This should also be beneficial for the other cases above.
6322 DCHECK(locations->OnlyCallsOnSlowPath());
6323 slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARMVIXL(instruction,
6324 /* is_fatal */ false);
6325 codegen_->AddSlowPath(slow_path);
6326 __ B(slow_path->GetEntryLabel());
6327 if (zero.IsReferenced()) {
6328 __ B(&done);
6329 }
6330 break;
6331 }
6332 }
6333
6334 if (zero.IsReferenced()) {
6335 __ Bind(&zero);
6336 __ Mov(out, 0);
6337 }
6338
6339 if (done.IsReferenced()) {
6340 __ Bind(&done);
6341 }
6342
6343 if (slow_path != nullptr) {
6344 __ Bind(slow_path->GetExitLabel());
6345 }
6346}
6347
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006348void LocationsBuilderARMVIXL::VisitCheckCast(HCheckCast* instruction) {
6349 LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
6350 bool throws_into_catch = instruction->CanThrowIntoCatchBlock();
6351
6352 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
6353 switch (type_check_kind) {
6354 case TypeCheckKind::kExactCheck:
6355 case TypeCheckKind::kAbstractClassCheck:
6356 case TypeCheckKind::kClassHierarchyCheck:
6357 case TypeCheckKind::kArrayObjectCheck:
6358 call_kind = (throws_into_catch || kEmitCompilerReadBarrier) ?
6359 LocationSummary::kCallOnSlowPath :
6360 LocationSummary::kNoCall; // In fact, call on a fatal (non-returning) slow path.
6361 break;
6362 case TypeCheckKind::kArrayCheck:
6363 case TypeCheckKind::kUnresolvedCheck:
6364 case TypeCheckKind::kInterfaceCheck:
6365 call_kind = LocationSummary::kCallOnSlowPath;
6366 break;
6367 }
6368
6369 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
6370 locations->SetInAt(0, Location::RequiresRegister());
6371 locations->SetInAt(1, Location::RequiresRegister());
Artem Serov657022c2016-11-23 14:19:38 +00006372 locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind));
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006373}
6374
6375void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) {
6376 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
6377 LocationSummary* locations = instruction->GetLocations();
6378 Location obj_loc = locations->InAt(0);
6379 vixl32::Register obj = InputRegisterAt(instruction, 0);
6380 vixl32::Register cls = InputRegisterAt(instruction, 1);
6381 Location temp_loc = locations->GetTemp(0);
6382 vixl32::Register temp = RegisterFrom(temp_loc);
Artem Serov657022c2016-11-23 14:19:38 +00006383 const size_t num_temps = NumberOfCheckCastTemps(type_check_kind);
6384 DCHECK_LE(num_temps, 3u);
6385 Location maybe_temp2_loc = (num_temps >= 2) ? locations->GetTemp(1) : Location::NoLocation();
6386 Location maybe_temp3_loc = (num_temps >= 3) ? locations->GetTemp(2) : Location::NoLocation();
6387 const uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
6388 const uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
6389 const uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
6390 const uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
6391 const uint32_t iftable_offset = mirror::Class::IfTableOffset().Uint32Value();
6392 const uint32_t array_length_offset = mirror::Array::LengthOffset().Uint32Value();
6393 const uint32_t object_array_data_offset =
6394 mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value();
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006395
Artem Serov657022c2016-11-23 14:19:38 +00006396 // Always false for read barriers since we may need to go to the entrypoint for non-fatal cases
6397 // from false negatives. The false negatives may come from avoiding read barriers below. Avoiding
6398 // read barriers is done for performance and code size reasons.
6399 bool is_type_check_slow_path_fatal = false;
6400 if (!kEmitCompilerReadBarrier) {
6401 is_type_check_slow_path_fatal =
6402 (type_check_kind == TypeCheckKind::kExactCheck ||
6403 type_check_kind == TypeCheckKind::kAbstractClassCheck ||
6404 type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
6405 type_check_kind == TypeCheckKind::kArrayObjectCheck) &&
6406 !instruction->CanThrowIntoCatchBlock();
6407 }
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006408 SlowPathCodeARMVIXL* type_check_slow_path =
6409 new (GetGraph()->GetArena()) TypeCheckSlowPathARMVIXL(instruction,
6410 is_type_check_slow_path_fatal);
6411 codegen_->AddSlowPath(type_check_slow_path);
6412
6413 vixl32::Label done;
6414 // Avoid null check if we know obj is not null.
6415 if (instruction->MustDoNullCheck()) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00006416 __ CompareAndBranchIfZero(obj, &done, /* far_target */ false);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006417 }
6418
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006419 switch (type_check_kind) {
6420 case TypeCheckKind::kExactCheck:
6421 case TypeCheckKind::kArrayCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00006422 // /* HeapReference<Class> */ temp = obj->klass_
6423 GenerateReferenceLoadTwoRegisters(instruction,
6424 temp_loc,
6425 obj_loc,
6426 class_offset,
6427 maybe_temp2_loc,
6428 kWithoutReadBarrier);
6429
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006430 __ Cmp(temp, cls);
6431 // Jump to slow path for throwing the exception or doing a
6432 // more involved array check.
6433 __ B(ne, type_check_slow_path->GetEntryLabel());
6434 break;
6435 }
6436
6437 case TypeCheckKind::kAbstractClassCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00006438 // /* HeapReference<Class> */ temp = obj->klass_
6439 GenerateReferenceLoadTwoRegisters(instruction,
6440 temp_loc,
6441 obj_loc,
6442 class_offset,
6443 maybe_temp2_loc,
6444 kWithoutReadBarrier);
6445
Artem Serovcfbe9132016-10-14 15:58:56 +01006446 // If the class is abstract, we eagerly fetch the super class of the
6447 // object to avoid doing a comparison we know will fail.
6448 vixl32::Label loop;
6449 __ Bind(&loop);
6450 // /* HeapReference<Class> */ temp = temp->super_class_
Artem Serov657022c2016-11-23 14:19:38 +00006451 GenerateReferenceLoadOneRegister(instruction,
6452 temp_loc,
6453 super_offset,
6454 maybe_temp2_loc,
6455 kWithoutReadBarrier);
Artem Serovcfbe9132016-10-14 15:58:56 +01006456
6457 // If the class reference currently in `temp` is null, jump to the slow path to throw the
6458 // exception.
xueliang.zhongf51bc622016-11-04 09:23:32 +00006459 __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
Artem Serovcfbe9132016-10-14 15:58:56 +01006460
6461 // Otherwise, compare the classes.
6462 __ Cmp(temp, cls);
6463 __ B(ne, &loop);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006464 break;
6465 }
6466
6467 case TypeCheckKind::kClassHierarchyCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00006468 // /* HeapReference<Class> */ temp = obj->klass_
6469 GenerateReferenceLoadTwoRegisters(instruction,
6470 temp_loc,
6471 obj_loc,
6472 class_offset,
6473 maybe_temp2_loc,
6474 kWithoutReadBarrier);
6475
Artem Serovcfbe9132016-10-14 15:58:56 +01006476 // Walk over the class hierarchy to find a match.
6477 vixl32::Label loop;
6478 __ Bind(&loop);
6479 __ Cmp(temp, cls);
6480 __ B(eq, &done);
6481
6482 // /* HeapReference<Class> */ temp = temp->super_class_
Artem Serov657022c2016-11-23 14:19:38 +00006483 GenerateReferenceLoadOneRegister(instruction,
6484 temp_loc,
6485 super_offset,
6486 maybe_temp2_loc,
6487 kWithoutReadBarrier);
Artem Serovcfbe9132016-10-14 15:58:56 +01006488
6489 // If the class reference currently in `temp` is null, jump to the slow path to throw the
6490 // exception.
xueliang.zhongf51bc622016-11-04 09:23:32 +00006491 __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
Artem Serovcfbe9132016-10-14 15:58:56 +01006492 // Otherwise, jump to the beginning of the loop.
6493 __ B(&loop);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006494 break;
6495 }
6496
Artem Serovcfbe9132016-10-14 15:58:56 +01006497 case TypeCheckKind::kArrayObjectCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00006498 // /* HeapReference<Class> */ temp = obj->klass_
6499 GenerateReferenceLoadTwoRegisters(instruction,
6500 temp_loc,
6501 obj_loc,
6502 class_offset,
6503 maybe_temp2_loc,
6504 kWithoutReadBarrier);
6505
Artem Serovcfbe9132016-10-14 15:58:56 +01006506 // Do an exact check.
6507 __ Cmp(temp, cls);
6508 __ B(eq, &done);
6509
6510 // Otherwise, we need to check that the object's class is a non-primitive array.
6511 // /* HeapReference<Class> */ temp = temp->component_type_
Artem Serov657022c2016-11-23 14:19:38 +00006512 GenerateReferenceLoadOneRegister(instruction,
6513 temp_loc,
6514 component_offset,
6515 maybe_temp2_loc,
6516 kWithoutReadBarrier);
Artem Serovcfbe9132016-10-14 15:58:56 +01006517 // If the component type is null, jump to the slow path to throw the exception.
xueliang.zhongf51bc622016-11-04 09:23:32 +00006518 __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
Artem Serovcfbe9132016-10-14 15:58:56 +01006519 // Otherwise,the object is indeed an array, jump to label `check_non_primitive_component_type`
6520 // to further check that this component type is not a primitive type.
6521 GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, temp, temp, primitive_offset);
6522 static_assert(Primitive::kPrimNot == 0, "Expected 0 for art::Primitive::kPrimNot");
xueliang.zhongf51bc622016-11-04 09:23:32 +00006523 __ CompareAndBranchIfNonZero(temp, type_check_slow_path->GetEntryLabel());
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006524 break;
6525 }
6526
6527 case TypeCheckKind::kUnresolvedCheck:
Artem Serov657022c2016-11-23 14:19:38 +00006528 // We always go into the type check slow path for the unresolved check case.
Artem Serovcfbe9132016-10-14 15:58:56 +01006529 // We cannot directly call the CheckCast runtime entry point
6530 // without resorting to a type checking slow path here (i.e. by
6531 // calling InvokeRuntime directly), as it would require to
6532 // assign fixed registers for the inputs of this HInstanceOf
6533 // instruction (following the runtime calling convention), which
6534 // might be cluttered by the potential first read barrier
6535 // emission at the beginning of this method.
Artem Serov657022c2016-11-23 14:19:38 +00006536
Artem Serovcfbe9132016-10-14 15:58:56 +01006537 __ B(type_check_slow_path->GetEntryLabel());
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006538 break;
Artem Serov657022c2016-11-23 14:19:38 +00006539
6540 case TypeCheckKind::kInterfaceCheck: {
6541 // Avoid read barriers to improve performance of the fast path. We can not get false
6542 // positives by doing this.
6543 // /* HeapReference<Class> */ temp = obj->klass_
6544 GenerateReferenceLoadTwoRegisters(instruction,
6545 temp_loc,
6546 obj_loc,
6547 class_offset,
6548 maybe_temp2_loc,
6549 kWithoutReadBarrier);
6550
6551 // /* HeapReference<Class> */ temp = temp->iftable_
6552 GenerateReferenceLoadTwoRegisters(instruction,
6553 temp_loc,
6554 temp_loc,
6555 iftable_offset,
6556 maybe_temp2_loc,
6557 kWithoutReadBarrier);
6558 // Iftable is never null.
6559 __ Ldr(RegisterFrom(maybe_temp2_loc), MemOperand(temp, array_length_offset));
6560 // Loop through the iftable and check if any class matches.
6561 vixl32::Label start_loop;
6562 __ Bind(&start_loop);
6563 __ CompareAndBranchIfZero(RegisterFrom(maybe_temp2_loc),
6564 type_check_slow_path->GetEntryLabel());
6565 __ Ldr(RegisterFrom(maybe_temp3_loc), MemOperand(temp, object_array_data_offset));
6566 GetAssembler()->MaybeUnpoisonHeapReference(RegisterFrom(maybe_temp3_loc));
6567 // Go to next interface.
6568 __ Add(temp, temp, Operand::From(2 * kHeapReferenceSize));
6569 __ Sub(RegisterFrom(maybe_temp2_loc), RegisterFrom(maybe_temp2_loc), 2);
6570 // Compare the classes and continue the loop if they do not match.
6571 __ Cmp(cls, RegisterFrom(maybe_temp3_loc));
6572 __ B(ne, &start_loop);
6573 break;
6574 }
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006575 }
6576 __ Bind(&done);
6577
6578 __ Bind(type_check_slow_path->GetExitLabel());
6579}
6580
Artem Serov551b28f2016-10-18 19:11:30 +01006581void LocationsBuilderARMVIXL::VisitMonitorOperation(HMonitorOperation* instruction) {
6582 LocationSummary* locations =
6583 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
6584 InvokeRuntimeCallingConventionARMVIXL calling_convention;
6585 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
6586}
6587
6588void InstructionCodeGeneratorARMVIXL::VisitMonitorOperation(HMonitorOperation* instruction) {
6589 codegen_->InvokeRuntime(instruction->IsEnter() ? kQuickLockObject : kQuickUnlockObject,
6590 instruction,
6591 instruction->GetDexPc());
6592 if (instruction->IsEnter()) {
6593 CheckEntrypointTypes<kQuickLockObject, void, mirror::Object*>();
6594 } else {
6595 CheckEntrypointTypes<kQuickUnlockObject, void, mirror::Object*>();
6596 }
6597}
6598
Artem Serov02109dd2016-09-23 17:17:54 +01006599void LocationsBuilderARMVIXL::VisitAnd(HAnd* instruction) {
6600 HandleBitwiseOperation(instruction, AND);
6601}
6602
6603void LocationsBuilderARMVIXL::VisitOr(HOr* instruction) {
6604 HandleBitwiseOperation(instruction, ORR);
6605}
6606
6607void LocationsBuilderARMVIXL::VisitXor(HXor* instruction) {
6608 HandleBitwiseOperation(instruction, EOR);
6609}
6610
6611void LocationsBuilderARMVIXL::HandleBitwiseOperation(HBinaryOperation* instruction, Opcode opcode) {
6612 LocationSummary* locations =
6613 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
6614 DCHECK(instruction->GetResultType() == Primitive::kPrimInt
6615 || instruction->GetResultType() == Primitive::kPrimLong);
6616 // Note: GVN reorders commutative operations to have the constant on the right hand side.
6617 locations->SetInAt(0, Location::RequiresRegister());
6618 locations->SetInAt(1, ArmEncodableConstantOrRegister(instruction->InputAt(1), opcode));
6619 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
6620}
6621
6622void InstructionCodeGeneratorARMVIXL::VisitAnd(HAnd* instruction) {
6623 HandleBitwiseOperation(instruction);
6624}
6625
6626void InstructionCodeGeneratorARMVIXL::VisitOr(HOr* instruction) {
6627 HandleBitwiseOperation(instruction);
6628}
6629
6630void InstructionCodeGeneratorARMVIXL::VisitXor(HXor* instruction) {
6631 HandleBitwiseOperation(instruction);
6632}
6633
Artem Serov2bbc9532016-10-21 11:51:50 +01006634void LocationsBuilderARMVIXL::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) {
6635 LocationSummary* locations =
6636 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
6637 DCHECK(instruction->GetResultType() == Primitive::kPrimInt
6638 || instruction->GetResultType() == Primitive::kPrimLong);
6639
6640 locations->SetInAt(0, Location::RequiresRegister());
6641 locations->SetInAt(1, Location::RequiresRegister());
6642 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
6643}
6644
6645void InstructionCodeGeneratorARMVIXL::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) {
6646 LocationSummary* locations = instruction->GetLocations();
6647 Location first = locations->InAt(0);
6648 Location second = locations->InAt(1);
6649 Location out = locations->Out();
6650
6651 if (instruction->GetResultType() == Primitive::kPrimInt) {
6652 vixl32::Register first_reg = RegisterFrom(first);
6653 vixl32::Register second_reg = RegisterFrom(second);
6654 vixl32::Register out_reg = RegisterFrom(out);
6655
6656 switch (instruction->GetOpKind()) {
6657 case HInstruction::kAnd:
6658 __ Bic(out_reg, first_reg, second_reg);
6659 break;
6660 case HInstruction::kOr:
6661 __ Orn(out_reg, first_reg, second_reg);
6662 break;
6663 // There is no EON on arm.
6664 case HInstruction::kXor:
6665 default:
6666 LOG(FATAL) << "Unexpected instruction " << instruction->DebugName();
6667 UNREACHABLE();
6668 }
6669 return;
6670
6671 } else {
6672 DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong);
6673 vixl32::Register first_low = LowRegisterFrom(first);
6674 vixl32::Register first_high = HighRegisterFrom(first);
6675 vixl32::Register second_low = LowRegisterFrom(second);
6676 vixl32::Register second_high = HighRegisterFrom(second);
6677 vixl32::Register out_low = LowRegisterFrom(out);
6678 vixl32::Register out_high = HighRegisterFrom(out);
6679
6680 switch (instruction->GetOpKind()) {
6681 case HInstruction::kAnd:
6682 __ Bic(out_low, first_low, second_low);
6683 __ Bic(out_high, first_high, second_high);
6684 break;
6685 case HInstruction::kOr:
6686 __ Orn(out_low, first_low, second_low);
6687 __ Orn(out_high, first_high, second_high);
6688 break;
6689 // There is no EON on arm.
6690 case HInstruction::kXor:
6691 default:
6692 LOG(FATAL) << "Unexpected instruction " << instruction->DebugName();
6693 UNREACHABLE();
6694 }
6695 }
6696}
6697
Artem Serov02109dd2016-09-23 17:17:54 +01006698// TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
6699void InstructionCodeGeneratorARMVIXL::GenerateAndConst(vixl32::Register out,
6700 vixl32::Register first,
6701 uint32_t value) {
6702 // Optimize special cases for individual halfs of `and-long` (`and` is simplified earlier).
6703 if (value == 0xffffffffu) {
6704 if (!out.Is(first)) {
6705 __ Mov(out, first);
6706 }
6707 return;
6708 }
6709 if (value == 0u) {
6710 __ Mov(out, 0);
6711 return;
6712 }
6713 if (GetAssembler()->ShifterOperandCanHold(AND, value)) {
6714 __ And(out, first, value);
6715 } else {
6716 DCHECK(GetAssembler()->ShifterOperandCanHold(BIC, ~value));
6717 __ Bic(out, first, ~value);
6718 }
6719}
6720
6721// TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
6722void InstructionCodeGeneratorARMVIXL::GenerateOrrConst(vixl32::Register out,
6723 vixl32::Register first,
6724 uint32_t value) {
6725 // Optimize special cases for individual halfs of `or-long` (`or` is simplified earlier).
6726 if (value == 0u) {
6727 if (!out.Is(first)) {
6728 __ Mov(out, first);
6729 }
6730 return;
6731 }
6732 if (value == 0xffffffffu) {
6733 __ Mvn(out, 0);
6734 return;
6735 }
6736 if (GetAssembler()->ShifterOperandCanHold(ORR, value)) {
6737 __ Orr(out, first, value);
6738 } else {
6739 DCHECK(GetAssembler()->ShifterOperandCanHold(ORN, ~value));
6740 __ Orn(out, first, ~value);
6741 }
6742}
6743
6744// TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
6745void InstructionCodeGeneratorARMVIXL::GenerateEorConst(vixl32::Register out,
6746 vixl32::Register first,
6747 uint32_t value) {
6748 // Optimize special case for individual halfs of `xor-long` (`xor` is simplified earlier).
6749 if (value == 0u) {
6750 if (!out.Is(first)) {
6751 __ Mov(out, first);
6752 }
6753 return;
6754 }
6755 __ Eor(out, first, value);
6756}
6757
Anton Kirilovdda43962016-11-21 19:55:20 +00006758void InstructionCodeGeneratorARMVIXL::GenerateAddLongConst(Location out,
6759 Location first,
6760 uint64_t value) {
6761 vixl32::Register out_low = LowRegisterFrom(out);
6762 vixl32::Register out_high = HighRegisterFrom(out);
6763 vixl32::Register first_low = LowRegisterFrom(first);
6764 vixl32::Register first_high = HighRegisterFrom(first);
6765 uint32_t value_low = Low32Bits(value);
6766 uint32_t value_high = High32Bits(value);
6767 if (value_low == 0u) {
6768 if (!out_low.Is(first_low)) {
6769 __ Mov(out_low, first_low);
6770 }
6771 __ Add(out_high, first_high, value_high);
6772 return;
6773 }
6774 __ Adds(out_low, first_low, value_low);
Scott Wakelingbffdc702016-12-07 17:46:03 +00006775 if (GetAssembler()->ShifterOperandCanHold(ADC, value_high, kCcDontCare)) {
Anton Kirilovdda43962016-11-21 19:55:20 +00006776 __ Adc(out_high, first_high, value_high);
Scott Wakelingbffdc702016-12-07 17:46:03 +00006777 } else if (GetAssembler()->ShifterOperandCanHold(SBC, ~value_high, kCcDontCare)) {
Anton Kirilovdda43962016-11-21 19:55:20 +00006778 __ Sbc(out_high, first_high, ~value_high);
6779 } else {
6780 LOG(FATAL) << "Unexpected constant " << value_high;
6781 UNREACHABLE();
6782 }
6783}
6784
Artem Serov02109dd2016-09-23 17:17:54 +01006785void InstructionCodeGeneratorARMVIXL::HandleBitwiseOperation(HBinaryOperation* instruction) {
6786 LocationSummary* locations = instruction->GetLocations();
6787 Location first = locations->InAt(0);
6788 Location second = locations->InAt(1);
6789 Location out = locations->Out();
6790
6791 if (second.IsConstant()) {
6792 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
6793 uint32_t value_low = Low32Bits(value);
6794 if (instruction->GetResultType() == Primitive::kPrimInt) {
6795 vixl32::Register first_reg = InputRegisterAt(instruction, 0);
6796 vixl32::Register out_reg = OutputRegister(instruction);
6797 if (instruction->IsAnd()) {
6798 GenerateAndConst(out_reg, first_reg, value_low);
6799 } else if (instruction->IsOr()) {
6800 GenerateOrrConst(out_reg, first_reg, value_low);
6801 } else {
6802 DCHECK(instruction->IsXor());
6803 GenerateEorConst(out_reg, first_reg, value_low);
6804 }
6805 } else {
6806 DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong);
6807 uint32_t value_high = High32Bits(value);
6808 vixl32::Register first_low = LowRegisterFrom(first);
6809 vixl32::Register first_high = HighRegisterFrom(first);
6810 vixl32::Register out_low = LowRegisterFrom(out);
6811 vixl32::Register out_high = HighRegisterFrom(out);
6812 if (instruction->IsAnd()) {
6813 GenerateAndConst(out_low, first_low, value_low);
6814 GenerateAndConst(out_high, first_high, value_high);
6815 } else if (instruction->IsOr()) {
6816 GenerateOrrConst(out_low, first_low, value_low);
6817 GenerateOrrConst(out_high, first_high, value_high);
6818 } else {
6819 DCHECK(instruction->IsXor());
6820 GenerateEorConst(out_low, first_low, value_low);
6821 GenerateEorConst(out_high, first_high, value_high);
6822 }
6823 }
6824 return;
6825 }
6826
6827 if (instruction->GetResultType() == Primitive::kPrimInt) {
6828 vixl32::Register first_reg = InputRegisterAt(instruction, 0);
6829 vixl32::Register second_reg = InputRegisterAt(instruction, 1);
6830 vixl32::Register out_reg = OutputRegister(instruction);
6831 if (instruction->IsAnd()) {
6832 __ And(out_reg, first_reg, second_reg);
6833 } else if (instruction->IsOr()) {
6834 __ Orr(out_reg, first_reg, second_reg);
6835 } else {
6836 DCHECK(instruction->IsXor());
6837 __ Eor(out_reg, first_reg, second_reg);
6838 }
6839 } else {
6840 DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong);
6841 vixl32::Register first_low = LowRegisterFrom(first);
6842 vixl32::Register first_high = HighRegisterFrom(first);
6843 vixl32::Register second_low = LowRegisterFrom(second);
6844 vixl32::Register second_high = HighRegisterFrom(second);
6845 vixl32::Register out_low = LowRegisterFrom(out);
6846 vixl32::Register out_high = HighRegisterFrom(out);
6847 if (instruction->IsAnd()) {
6848 __ And(out_low, first_low, second_low);
6849 __ And(out_high, first_high, second_high);
6850 } else if (instruction->IsOr()) {
6851 __ Orr(out_low, first_low, second_low);
6852 __ Orr(out_high, first_high, second_high);
6853 } else {
6854 DCHECK(instruction->IsXor());
6855 __ Eor(out_low, first_low, second_low);
6856 __ Eor(out_high, first_high, second_high);
6857 }
6858 }
6859}
6860
Artem Serovcfbe9132016-10-14 15:58:56 +01006861void InstructionCodeGeneratorARMVIXL::GenerateReferenceLoadOneRegister(
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006862 HInstruction* instruction,
Artem Serovcfbe9132016-10-14 15:58:56 +01006863 Location out,
6864 uint32_t offset,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006865 Location maybe_temp,
6866 ReadBarrierOption read_barrier_option) {
Artem Serovcfbe9132016-10-14 15:58:56 +01006867 vixl32::Register out_reg = RegisterFrom(out);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006868 if (read_barrier_option == kWithReadBarrier) {
6869 CHECK(kEmitCompilerReadBarrier);
6870 DCHECK(maybe_temp.IsRegister()) << maybe_temp;
6871 if (kUseBakerReadBarrier) {
6872 // Load with fast path based Baker's read barrier.
6873 // /* HeapReference<Object> */ out = *(out + offset)
6874 codegen_->GenerateFieldLoadWithBakerReadBarrier(
6875 instruction, out, out_reg, offset, maybe_temp, /* needs_null_check */ false);
6876 } else {
6877 // Load with slow path based read barrier.
6878 // Save the value of `out` into `maybe_temp` before overwriting it
6879 // in the following move operation, as we will need it for the
6880 // read barrier below.
6881 __ Mov(RegisterFrom(maybe_temp), out_reg);
6882 // /* HeapReference<Object> */ out = *(out + offset)
6883 GetAssembler()->LoadFromOffset(kLoadWord, out_reg, out_reg, offset);
6884 codegen_->GenerateReadBarrierSlow(instruction, out, out, maybe_temp, offset);
6885 }
Artem Serovcfbe9132016-10-14 15:58:56 +01006886 } else {
6887 // Plain load with no read barrier.
6888 // /* HeapReference<Object> */ out = *(out + offset)
6889 GetAssembler()->LoadFromOffset(kLoadWord, out_reg, out_reg, offset);
6890 GetAssembler()->MaybeUnpoisonHeapReference(out_reg);
6891 }
6892}
6893
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006894void InstructionCodeGeneratorARMVIXL::GenerateReferenceLoadTwoRegisters(
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006895 HInstruction* instruction,
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006896 Location out,
6897 Location obj,
6898 uint32_t offset,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006899 Location maybe_temp,
6900 ReadBarrierOption read_barrier_option) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006901 vixl32::Register out_reg = RegisterFrom(out);
6902 vixl32::Register obj_reg = RegisterFrom(obj);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006903 if (read_barrier_option == kWithReadBarrier) {
6904 CHECK(kEmitCompilerReadBarrier);
6905 if (kUseBakerReadBarrier) {
6906 DCHECK(maybe_temp.IsRegister()) << maybe_temp;
6907 // Load with fast path based Baker's read barrier.
6908 // /* HeapReference<Object> */ out = *(obj + offset)
6909 codegen_->GenerateFieldLoadWithBakerReadBarrier(
6910 instruction, out, obj_reg, offset, maybe_temp, /* needs_null_check */ false);
6911 } else {
6912 // Load with slow path based read barrier.
6913 // /* HeapReference<Object> */ out = *(obj + offset)
6914 GetAssembler()->LoadFromOffset(kLoadWord, out_reg, obj_reg, offset);
6915 codegen_->GenerateReadBarrierSlow(instruction, out, out, obj, offset);
6916 }
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006917 } else {
6918 // Plain load with no read barrier.
6919 // /* HeapReference<Object> */ out = *(obj + offset)
6920 GetAssembler()->LoadFromOffset(kLoadWord, out_reg, obj_reg, offset);
6921 GetAssembler()->MaybeUnpoisonHeapReference(out_reg);
6922 }
6923}
6924
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006925void InstructionCodeGeneratorARMVIXL::GenerateGcRootFieldLoad(
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006926 HInstruction* instruction,
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006927 Location root,
6928 vixl32::Register obj,
6929 uint32_t offset,
Artem Serovd4cc5b22016-11-04 11:19:09 +00006930 ReadBarrierOption read_barrier_option) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006931 vixl32::Register root_reg = RegisterFrom(root);
Artem Serovd4cc5b22016-11-04 11:19:09 +00006932 if (read_barrier_option == kWithReadBarrier) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006933 DCHECK(kEmitCompilerReadBarrier);
6934 if (kUseBakerReadBarrier) {
6935 // Fast path implementation of art::ReadBarrier::BarrierForRoot when
6936 // Baker's read barrier are used:
6937 //
6938 // root = obj.field;
6939 // temp = Thread::Current()->pReadBarrierMarkReg ## root.reg()
6940 // if (temp != null) {
6941 // root = temp(root)
6942 // }
6943
6944 // /* GcRoot<mirror::Object> */ root = *(obj + offset)
6945 GetAssembler()->LoadFromOffset(kLoadWord, root_reg, obj, offset);
6946 static_assert(
6947 sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(GcRoot<mirror::Object>),
6948 "art::mirror::CompressedReference<mirror::Object> and art::GcRoot<mirror::Object> "
6949 "have different sizes.");
6950 static_assert(sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(int32_t),
6951 "art::mirror::CompressedReference<mirror::Object> and int32_t "
6952 "have different sizes.");
6953
6954 // Slow path marking the GC root `root`.
6955 Location temp = LocationFrom(lr);
6956 SlowPathCodeARMVIXL* slow_path =
6957 new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathARMVIXL(
6958 instruction,
6959 root,
6960 /*entrypoint*/ temp);
6961 codegen_->AddSlowPath(slow_path);
6962
6963 // temp = Thread::Current()->pReadBarrierMarkReg ## root.reg()
6964 const int32_t entry_point_offset =
6965 CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArmPointerSize>(root.reg());
6966 // Loading the entrypoint does not require a load acquire since it is only changed when
6967 // threads are suspended or running a checkpoint.
6968 GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(temp), tr, entry_point_offset);
6969 // The entrypoint is null when the GC is not marking, this prevents one load compared to
6970 // checking GetIsGcMarking.
6971 __ CompareAndBranchIfNonZero(RegisterFrom(temp), slow_path->GetEntryLabel());
6972 __ Bind(slow_path->GetExitLabel());
6973 } else {
6974 // GC root loaded through a slow path for read barriers other
6975 // than Baker's.
6976 // /* GcRoot<mirror::Object>* */ root = obj + offset
6977 __ Add(root_reg, obj, offset);
6978 // /* mirror::Object* */ root = root->Read()
6979 codegen_->GenerateReadBarrierForRootSlow(instruction, root, root);
6980 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006981 } else {
6982 // Plain GC root load with no read barrier.
6983 // /* GcRoot<mirror::Object> */ root = *(obj + offset)
6984 GetAssembler()->LoadFromOffset(kLoadWord, root_reg, obj, offset);
6985 // Note that GC roots are not affected by heap poisoning, thus we
6986 // do not have to unpoison `root_reg` here.
6987 }
6988}
6989
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006990void CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
6991 Location ref,
6992 vixl32::Register obj,
6993 uint32_t offset,
6994 Location temp,
6995 bool needs_null_check) {
6996 DCHECK(kEmitCompilerReadBarrier);
6997 DCHECK(kUseBakerReadBarrier);
6998
6999 // /* HeapReference<Object> */ ref = *(obj + offset)
7000 Location no_index = Location::NoLocation();
7001 ScaleFactor no_scale_factor = TIMES_1;
7002 GenerateReferenceLoadWithBakerReadBarrier(
7003 instruction, ref, obj, offset, no_index, no_scale_factor, temp, needs_null_check);
Roland Levillain6070e882016-11-03 17:51:58 +00007004}
7005
Anton Kirilovedb2ac32016-11-30 15:14:10 +00007006void CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction,
7007 Location ref,
7008 vixl32::Register obj,
7009 uint32_t data_offset,
7010 Location index,
7011 Location temp,
7012 bool needs_null_check) {
7013 DCHECK(kEmitCompilerReadBarrier);
7014 DCHECK(kUseBakerReadBarrier);
7015
7016 static_assert(
7017 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
7018 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
7019 // /* HeapReference<Object> */ ref =
7020 // *(obj + data_offset + index * sizeof(HeapReference<Object>))
7021 ScaleFactor scale_factor = TIMES_4;
7022 GenerateReferenceLoadWithBakerReadBarrier(
7023 instruction, ref, obj, data_offset, index, scale_factor, temp, needs_null_check);
Roland Levillain6070e882016-11-03 17:51:58 +00007024}
7025
Anton Kirilovedb2ac32016-11-30 15:14:10 +00007026void CodeGeneratorARMVIXL::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
7027 Location ref,
7028 vixl32::Register obj,
7029 uint32_t offset,
7030 Location index,
7031 ScaleFactor scale_factor,
7032 Location temp,
7033 bool needs_null_check,
7034 bool always_update_field,
7035 vixl32::Register* temp2) {
7036 DCHECK(kEmitCompilerReadBarrier);
7037 DCHECK(kUseBakerReadBarrier);
7038
7039 // In slow path based read barriers, the read barrier call is
7040 // inserted after the original load. However, in fast path based
7041 // Baker's read barriers, we need to perform the load of
7042 // mirror::Object::monitor_ *before* the original reference load.
7043 // This load-load ordering is required by the read barrier.
7044 // The fast path/slow path (for Baker's algorithm) should look like:
7045 //
7046 // uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
7047 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
7048 // HeapReference<Object> ref = *src; // Original reference load.
7049 // bool is_gray = (rb_state == ReadBarrier::GrayState());
7050 // if (is_gray) {
7051 // ref = ReadBarrier::Mark(ref); // Performed by runtime entrypoint slow path.
7052 // }
7053 //
7054 // Note: the original implementation in ReadBarrier::Barrier is
7055 // slightly more complex as it performs additional checks that we do
7056 // not do here for performance reasons.
7057
7058 vixl32::Register ref_reg = RegisterFrom(ref);
7059 vixl32::Register temp_reg = RegisterFrom(temp);
7060 uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
7061
7062 // /* int32_t */ monitor = obj->monitor_
7063 GetAssembler()->LoadFromOffset(kLoadWord, temp_reg, obj, monitor_offset);
7064 if (needs_null_check) {
7065 MaybeRecordImplicitNullCheck(instruction);
7066 }
7067 // /* LockWord */ lock_word = LockWord(monitor)
7068 static_assert(sizeof(LockWord) == sizeof(int32_t),
7069 "art::LockWord and int32_t have different sizes.");
7070
7071 // Introduce a dependency on the lock_word including the rb_state,
7072 // which shall prevent load-load reordering without using
7073 // a memory barrier (which would be more expensive).
7074 // `obj` is unchanged by this operation, but its value now depends
7075 // on `temp_reg`.
7076 __ Add(obj, obj, Operand(temp_reg, ShiftType::LSR, 32));
7077
7078 // The actual reference load.
7079 if (index.IsValid()) {
7080 // Load types involving an "index": ArrayGet,
7081 // UnsafeGetObject/UnsafeGetObjectVolatile and UnsafeCASObject
7082 // intrinsics.
7083 // /* HeapReference<Object> */ ref = *(obj + offset + (index << scale_factor))
7084 if (index.IsConstant()) {
7085 size_t computed_offset =
7086 (Int32ConstantFrom(index) << scale_factor) + offset;
7087 GetAssembler()->LoadFromOffset(kLoadWord, ref_reg, obj, computed_offset);
7088 } else {
7089 // Handle the special case of the
7090 // UnsafeGetObject/UnsafeGetObjectVolatile and UnsafeCASObject
7091 // intrinsics, which use a register pair as index ("long
7092 // offset"), of which only the low part contains data.
7093 vixl32::Register index_reg = index.IsRegisterPair()
7094 ? LowRegisterFrom(index)
7095 : RegisterFrom(index);
7096 UseScratchRegisterScope temps(GetVIXLAssembler());
7097 const vixl32::Register temp3 = temps.Acquire();
7098 __ Add(temp3, obj, Operand(index_reg, ShiftType::LSL, scale_factor));
7099 GetAssembler()->LoadFromOffset(kLoadWord, ref_reg, temp3, offset);
7100 }
7101 } else {
7102 // /* HeapReference<Object> */ ref = *(obj + offset)
7103 GetAssembler()->LoadFromOffset(kLoadWord, ref_reg, obj, offset);
7104 }
7105
7106 // Object* ref = ref_addr->AsMirrorPtr()
7107 GetAssembler()->MaybeUnpoisonHeapReference(ref_reg);
7108
7109 // Slow path marking the object `ref` when it is gray.
7110 SlowPathCodeARMVIXL* slow_path;
7111 if (always_update_field) {
7112 DCHECK(temp2 != nullptr);
7113 // ReadBarrierMarkAndUpdateFieldSlowPathARMVIXL only supports address
7114 // of the form `obj + field_offset`, where `obj` is a register and
7115 // `field_offset` is a register pair (of which only the lower half
7116 // is used). Thus `offset` and `scale_factor` above are expected
7117 // to be null in this code path.
7118 DCHECK_EQ(offset, 0u);
7119 DCHECK_EQ(scale_factor, ScaleFactor::TIMES_1);
7120 slow_path = new (GetGraph()->GetArena()) ReadBarrierMarkAndUpdateFieldSlowPathARMVIXL(
7121 instruction, ref, obj, /* field_offset */ index, temp_reg, *temp2);
7122 } else {
7123 slow_path = new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathARMVIXL(instruction, ref);
7124 }
7125 AddSlowPath(slow_path);
7126
7127 // if (rb_state == ReadBarrier::GrayState())
7128 // ref = ReadBarrier::Mark(ref);
7129 // Given the numeric representation, it's enough to check the low bit of the
7130 // rb_state. We do that by shifting the bit out of the lock word with LSRS
7131 // which can be a 16-bit instruction unlike the TST immediate.
7132 static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
7133 static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
7134 __ Lsrs(temp_reg, temp_reg, LockWord::kReadBarrierStateShift + 1);
7135 __ B(cs, slow_path->GetEntryLabel()); // Carry flag is the last bit shifted out by LSRS.
7136 __ Bind(slow_path->GetExitLabel());
Roland Levillain844e6532016-11-03 16:09:47 +00007137}
7138
Anton Kirilovedb2ac32016-11-30 15:14:10 +00007139void CodeGeneratorARMVIXL::GenerateReadBarrierSlow(HInstruction* instruction,
7140 Location out,
7141 Location ref,
7142 Location obj,
7143 uint32_t offset,
7144 Location index) {
7145 DCHECK(kEmitCompilerReadBarrier);
7146
7147 // Insert a slow path based read barrier *after* the reference load.
7148 //
7149 // If heap poisoning is enabled, the unpoisoning of the loaded
7150 // reference will be carried out by the runtime within the slow
7151 // path.
7152 //
7153 // Note that `ref` currently does not get unpoisoned (when heap
7154 // poisoning is enabled), which is alright as the `ref` argument is
7155 // not used by the artReadBarrierSlow entry point.
7156 //
7157 // TODO: Unpoison `ref` when it is used by artReadBarrierSlow.
7158 SlowPathCodeARMVIXL* slow_path = new (GetGraph()->GetArena())
7159 ReadBarrierForHeapReferenceSlowPathARMVIXL(instruction, out, ref, obj, offset, index);
7160 AddSlowPath(slow_path);
7161
7162 __ B(slow_path->GetEntryLabel());
7163 __ Bind(slow_path->GetExitLabel());
7164}
7165
7166void CodeGeneratorARMVIXL::MaybeGenerateReadBarrierSlow(HInstruction* instruction,
Artem Serov02d37832016-10-25 15:25:33 +01007167 Location out,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00007168 Location ref,
7169 Location obj,
7170 uint32_t offset,
7171 Location index) {
Artem Serov02d37832016-10-25 15:25:33 +01007172 if (kEmitCompilerReadBarrier) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00007173 // Baker's read barriers shall be handled by the fast path
7174 // (CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier).
Artem Serov02d37832016-10-25 15:25:33 +01007175 DCHECK(!kUseBakerReadBarrier);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00007176 // If heap poisoning is enabled, unpoisoning will be taken care of
7177 // by the runtime within the slow path.
7178 GenerateReadBarrierSlow(instruction, out, ref, obj, offset, index);
Artem Serov02d37832016-10-25 15:25:33 +01007179 } else if (kPoisonHeapReferences) {
7180 GetAssembler()->UnpoisonHeapReference(RegisterFrom(out));
7181 }
7182}
7183
Anton Kirilovedb2ac32016-11-30 15:14:10 +00007184void CodeGeneratorARMVIXL::GenerateReadBarrierForRootSlow(HInstruction* instruction,
7185 Location out,
7186 Location root) {
7187 DCHECK(kEmitCompilerReadBarrier);
7188
7189 // Insert a slow path based read barrier *after* the GC root load.
7190 //
7191 // Note that GC roots are not affected by heap poisoning, so we do
7192 // not need to do anything special for this here.
7193 SlowPathCodeARMVIXL* slow_path =
7194 new (GetGraph()->GetArena()) ReadBarrierForRootSlowPathARMVIXL(instruction, out, root);
7195 AddSlowPath(slow_path);
7196
7197 __ B(slow_path->GetEntryLabel());
7198 __ Bind(slow_path->GetExitLabel());
7199}
7200
Artem Serov02d37832016-10-25 15:25:33 +01007201// Check if the desired_dispatch_info is supported. If it is, return it,
7202// otherwise return a fall-back info that should be used instead.
7203HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARMVIXL::GetSupportedInvokeStaticOrDirectDispatch(
Artem Serovd4cc5b22016-11-04 11:19:09 +00007204 const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
7205 HInvokeStaticOrDirect* invoke) {
Artem Serov02d37832016-10-25 15:25:33 +01007206 // TODO(VIXL): Implement optimized code paths.
Artem Serovd4cc5b22016-11-04 11:19:09 +00007207 if (desired_dispatch_info.method_load_kind ==
7208 HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup ||
7209 desired_dispatch_info.code_ptr_location ==
7210 HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup) {
7211 return {
7212 HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod,
7213 HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
7214 0u,
7215 0u
7216 };
7217 }
7218
7219 HInvokeStaticOrDirect::DispatchInfo dispatch_info = desired_dispatch_info;
7220 // We disable pc-relative load when there is an irreducible loop, as the optimization
7221 // is incompatible with it.
7222 // TODO: Create as many ArmDexCacheArraysBase instructions as needed for methods
7223 // with irreducible loops.
7224 if (GetGraph()->HasIrreducibleLoops() &&
7225 (dispatch_info.method_load_kind ==
7226 HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative)) {
7227 dispatch_info.method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod;
7228 }
7229
7230 if (dispatch_info.code_ptr_location == HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative) {
7231 const DexFile& outer_dex_file = GetGraph()->GetDexFile();
7232 if (&outer_dex_file != invoke->GetTargetMethod().dex_file) {
7233 // Calls across dex files are more likely to exceed the available BL range,
7234 // so use absolute patch with fixup if available and kCallArtMethod otherwise.
7235 HInvokeStaticOrDirect::CodePtrLocation code_ptr_location =
7236 (desired_dispatch_info.method_load_kind ==
7237 HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup)
7238 ? HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup
7239 : HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod;
7240 return HInvokeStaticOrDirect::DispatchInfo {
7241 dispatch_info.method_load_kind,
7242 code_ptr_location,
7243 dispatch_info.method_load_data,
7244 0u
7245 };
7246 }
7247 }
7248 return dispatch_info;
Artem Serov02d37832016-10-25 15:25:33 +01007249}
7250
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007251vixl32::Register CodeGeneratorARMVIXL::GetInvokeStaticOrDirectExtraParameter(
7252 HInvokeStaticOrDirect* invoke, vixl32::Register temp) {
7253 DCHECK_EQ(invoke->InputCount(), invoke->GetNumberOfArguments() + 1u);
7254 Location location = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
7255 if (!invoke->GetLocations()->Intrinsified()) {
7256 return RegisterFrom(location);
7257 }
7258 // For intrinsics we allow any location, so it may be on the stack.
7259 if (!location.IsRegister()) {
7260 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, location.GetStackIndex());
7261 return temp;
7262 }
7263 // For register locations, check if the register was saved. If so, get it from the stack.
7264 // Note: There is a chance that the register was saved but not overwritten, so we could
7265 // save one load. However, since this is just an intrinsic slow path we prefer this
7266 // simple and more robust approach rather that trying to determine if that's the case.
7267 SlowPathCode* slow_path = GetCurrentSlowPath();
7268 DCHECK(slow_path != nullptr); // For intrinsified invokes the call is emitted on the slow path.
7269 if (slow_path->IsCoreRegisterSaved(RegisterFrom(location).GetCode())) {
7270 int stack_offset = slow_path->GetStackOffsetOfCoreRegister(RegisterFrom(location).GetCode());
7271 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, stack_offset);
7272 return temp;
7273 }
7274 return RegisterFrom(location);
7275}
7276
7277void CodeGeneratorARMVIXL::GenerateStaticOrDirectCall(
7278 HInvokeStaticOrDirect* invoke, Location temp) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00007279 // For better instruction scheduling we load the direct code pointer before the method pointer.
7280 switch (invoke->GetCodePtrLocation()) {
7281 case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
7282 // LR = code address from literal pool with link-time patch.
7283 TODO_VIXL32(FATAL);
7284 break;
7285 case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
7286 // LR = invoke->GetDirectCodePtr();
7287 __ Mov(lr, Operand::From(invoke->GetDirectCodePtr()));
7288 break;
7289 default:
7290 break;
7291 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007292
Artem Serovd4cc5b22016-11-04 11:19:09 +00007293 Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007294 switch (invoke->GetMethodLoadKind()) {
7295 case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: {
7296 uint32_t offset =
7297 GetThreadOffset<kArmPointerSize>(invoke->GetStringInitEntryPoint()).Int32Value();
7298 // temp = thread->string_init_entrypoint
Artem Serovd4cc5b22016-11-04 11:19:09 +00007299 GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(temp), tr, offset);
7300 break;
7301 }
7302 case HInvokeStaticOrDirect::MethodLoadKind::kRecursive:
7303 callee_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
7304 break;
7305 case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress:
7306 __ Mov(RegisterFrom(temp), Operand::From(invoke->GetMethodAddress()));
7307 break;
7308 case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup:
7309 TODO_VIXL32(FATAL);
7310 break;
7311 case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: {
7312 HArmDexCacheArraysBase* base =
7313 invoke->InputAt(invoke->GetSpecialInputIndex())->AsArmDexCacheArraysBase();
7314 vixl32::Register base_reg = GetInvokeStaticOrDirectExtraParameter(invoke, RegisterFrom(temp));
7315 int32_t offset = invoke->GetDexCacheArrayOffset() - base->GetElementOffset();
7316 GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(temp), base_reg, offset);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007317 break;
7318 }
7319 case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: {
7320 Location current_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
7321 vixl32::Register method_reg;
Artem Serovd4cc5b22016-11-04 11:19:09 +00007322 vixl32::Register reg = RegisterFrom(temp);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007323 if (current_method.IsRegister()) {
7324 method_reg = RegisterFrom(current_method);
7325 } else {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007326 DCHECK(invoke->GetLocations()->Intrinsified());
7327 DCHECK(!current_method.IsValid());
Artem Serovd4cc5b22016-11-04 11:19:09 +00007328 method_reg = reg;
7329 GetAssembler()->LoadFromOffset(kLoadWord, reg, sp, kCurrentMethodStackOffset);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007330 }
7331 // /* ArtMethod*[] */ temp = temp.ptr_sized_fields_->dex_cache_resolved_methods_;
7332 GetAssembler()->LoadFromOffset(
7333 kLoadWord,
Artem Serovd4cc5b22016-11-04 11:19:09 +00007334 reg,
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007335 method_reg,
7336 ArtMethod::DexCacheResolvedMethodsOffset(kArmPointerSize).Int32Value());
7337 // temp = temp[index_in_cache];
7338 // Note: Don't use invoke->GetTargetMethod() as it may point to a different dex file.
7339 uint32_t index_in_cache = invoke->GetDexMethodIndex();
7340 GetAssembler()->LoadFromOffset(
Artem Serovd4cc5b22016-11-04 11:19:09 +00007341 kLoadWord, reg, reg, CodeGenerator::GetCachePointerOffset(index_in_cache));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007342 break;
7343 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007344 }
7345
Artem Serovd4cc5b22016-11-04 11:19:09 +00007346 switch (invoke->GetCodePtrLocation()) {
7347 case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf:
7348 __ Bl(GetFrameEntryLabel());
7349 break;
7350 case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative:
7351 relative_call_patches_.emplace_back(*invoke->GetTargetMethod().dex_file,
7352 invoke->GetTargetMethod().dex_method_index);
7353 {
7354 AssemblerAccurateScope aas(GetVIXLAssembler(),
Alexandre Rames374ddf32016-11-04 10:40:49 +00007355 vixl32::kMaxInstructionSizeInBytes,
Artem Serovd4cc5b22016-11-04 11:19:09 +00007356 CodeBufferCheckScope::kMaximumSize);
7357 __ bind(&relative_call_patches_.back().label);
7358 // Arbitrarily branch to the BL itself, override at link time.
7359 __ bl(&relative_call_patches_.back().label);
7360 }
7361 break;
7362 case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
7363 case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
7364 // LR prepared above for better instruction scheduling.
7365 // LR()
Alexandre Rames374ddf32016-11-04 10:40:49 +00007366 {
7367 // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
7368 AssemblerAccurateScope aas(GetVIXLAssembler(),
7369 vixl32::k16BitT32InstructionSizeInBytes,
7370 CodeBufferCheckScope::kExactSize);
7371 __ blx(lr);
7372 }
Artem Serovd4cc5b22016-11-04 11:19:09 +00007373 break;
7374 case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod:
7375 // LR = callee_method->entry_point_from_quick_compiled_code_
7376 GetAssembler()->LoadFromOffset(
7377 kLoadWord,
7378 lr,
7379 RegisterFrom(callee_method),
7380 ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value());
Alexandre Rames374ddf32016-11-04 10:40:49 +00007381 {
7382 // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
7383 AssemblerAccurateScope aas(GetVIXLAssembler(),
7384 vixl32::k16BitT32InstructionSizeInBytes,
7385 CodeBufferCheckScope::kExactSize);
7386 // LR()
7387 __ blx(lr);
7388 }
Artem Serovd4cc5b22016-11-04 11:19:09 +00007389 break;
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007390 }
7391
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007392 DCHECK(!IsLeafMethod());
7393}
7394
7395void CodeGeneratorARMVIXL::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp_location) {
7396 vixl32::Register temp = RegisterFrom(temp_location);
7397 uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
7398 invoke->GetVTableIndex(), kArmPointerSize).Uint32Value();
7399
7400 // Use the calling convention instead of the location of the receiver, as
7401 // intrinsics may have put the receiver in a different register. In the intrinsics
7402 // slow path, the arguments have been moved to the right place, so here we are
7403 // guaranteed that the receiver is the first register of the calling convention.
7404 InvokeDexCallingConventionARMVIXL calling_convention;
7405 vixl32::Register receiver = calling_convention.GetRegisterAt(0);
7406 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
Alexandre Rames374ddf32016-11-04 10:40:49 +00007407 {
7408 // Make sure the pc is recorded immediately after the `ldr` instruction.
7409 AssemblerAccurateScope aas(GetVIXLAssembler(),
7410 vixl32::kMaxInstructionSizeInBytes,
7411 CodeBufferCheckScope::kMaximumSize);
7412 // /* HeapReference<Class> */ temp = receiver->klass_
7413 __ ldr(temp, MemOperand(receiver, class_offset));
7414 MaybeRecordImplicitNullCheck(invoke);
7415 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007416 // Instead of simply (possibly) unpoisoning `temp` here, we should
7417 // emit a read barrier for the previous class reference load.
7418 // However this is not required in practice, as this is an
7419 // intermediate/temporary reference and because the current
7420 // concurrent copying collector keeps the from-space memory
7421 // intact/accessible until the end of the marking phase (the
7422 // concurrent copying collector may not in the future).
7423 GetAssembler()->MaybeUnpoisonHeapReference(temp);
7424
7425 // temp = temp->GetMethodAt(method_offset);
7426 uint32_t entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(
7427 kArmPointerSize).Int32Value();
7428 GetAssembler()->LoadFromOffset(kLoadWord, temp, temp, method_offset);
7429 // LR = temp->GetEntryPoint();
7430 GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, entry_point);
7431 // LR();
Alexandre Rames374ddf32016-11-04 10:40:49 +00007432 // This `blx` *must* be the *last* instruction generated by this stub, so that calls to
7433 // `RecordPcInfo()` immediately following record the correct pc. Use a scope to help guarantee
7434 // that.
7435 // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
7436 AssemblerAccurateScope aas(GetVIXLAssembler(),
7437 vixl32::k16BitT32InstructionSizeInBytes,
7438 CodeBufferCheckScope::kExactSize);
7439 __ blx(lr);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007440}
7441
Artem Serovd4cc5b22016-11-04 11:19:09 +00007442CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewPcRelativeStringPatch(
7443 const DexFile& dex_file, uint32_t string_index) {
7444 return NewPcRelativePatch(dex_file, string_index, &pc_relative_string_patches_);
7445}
7446
7447CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewPcRelativeTypePatch(
7448 const DexFile& dex_file, dex::TypeIndex type_index) {
7449 return NewPcRelativePatch(dex_file, type_index.index_, &pc_relative_type_patches_);
7450}
7451
7452CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewPcRelativeDexCacheArrayPatch(
7453 const DexFile& dex_file, uint32_t element_offset) {
7454 return NewPcRelativePatch(dex_file, element_offset, &pc_relative_dex_cache_patches_);
7455}
7456
7457CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewPcRelativePatch(
7458 const DexFile& dex_file, uint32_t offset_or_index, ArenaDeque<PcRelativePatchInfo>* patches) {
7459 patches->emplace_back(dex_file, offset_or_index);
7460 return &patches->back();
7461}
7462
7463template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)>
7464inline void CodeGeneratorARMVIXL::EmitPcRelativeLinkerPatches(
7465 const ArenaDeque<PcRelativePatchInfo>& infos,
7466 ArenaVector<LinkerPatch>* linker_patches) {
7467 for (const PcRelativePatchInfo& info : infos) {
7468 const DexFile& dex_file = info.target_dex_file;
7469 size_t offset_or_index = info.offset_or_index;
7470 DCHECK(info.add_pc_label.IsBound());
7471 uint32_t add_pc_offset = dchecked_integral_cast<uint32_t>(info.add_pc_label.GetLocation());
7472 // Add MOVW patch.
7473 DCHECK(info.movw_label.IsBound());
7474 uint32_t movw_offset = dchecked_integral_cast<uint32_t>(info.movw_label.GetLocation());
7475 linker_patches->push_back(Factory(movw_offset, &dex_file, add_pc_offset, offset_or_index));
7476 // Add MOVT patch.
7477 DCHECK(info.movt_label.IsBound());
7478 uint32_t movt_offset = dchecked_integral_cast<uint32_t>(info.movt_label.GetLocation());
7479 linker_patches->push_back(Factory(movt_offset, &dex_file, add_pc_offset, offset_or_index));
7480 }
7481}
7482
7483void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
7484 DCHECK(linker_patches->empty());
7485 size_t size =
7486 relative_call_patches_.size() +
7487 /* MOVW+MOVT for each entry */ 2u * pc_relative_dex_cache_patches_.size() +
7488 /* MOVW+MOVT for each entry */ 2u * pc_relative_string_patches_.size() +
7489 /* MOVW+MOVT for each entry */ 2u * pc_relative_type_patches_.size();
7490 linker_patches->reserve(size);
7491 for (const PatchInfo<vixl32::Label>& info : relative_call_patches_) {
7492 uint32_t literal_offset = info.label.GetLocation();
7493 linker_patches->push_back(
7494 LinkerPatch::RelativeCodePatch(literal_offset, &info.dex_file, info.index));
7495 }
7496 EmitPcRelativeLinkerPatches<LinkerPatch::DexCacheArrayPatch>(pc_relative_dex_cache_patches_,
7497 linker_patches);
7498 if (!GetCompilerOptions().IsBootImage()) {
7499 EmitPcRelativeLinkerPatches<LinkerPatch::StringBssEntryPatch>(pc_relative_string_patches_,
7500 linker_patches);
7501 } else {
7502 EmitPcRelativeLinkerPatches<LinkerPatch::RelativeStringPatch>(pc_relative_string_patches_,
7503 linker_patches);
7504 }
7505 EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(pc_relative_type_patches_,
7506 linker_patches);
7507}
7508
Artem Serov2bbc9532016-10-21 11:51:50 +01007509void LocationsBuilderARMVIXL::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
7510 LocationSummary* locations =
7511 new (GetGraph()->GetArena()) LocationSummary(instr, LocationSummary::kNoCall);
7512 locations->SetInAt(HMultiplyAccumulate::kInputAccumulatorIndex,
7513 Location::RequiresRegister());
7514 locations->SetInAt(HMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister());
7515 locations->SetInAt(HMultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister());
7516 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
7517}
7518
7519void InstructionCodeGeneratorARMVIXL::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
7520 vixl32::Register res = OutputRegister(instr);
7521 vixl32::Register accumulator =
7522 InputRegisterAt(instr, HMultiplyAccumulate::kInputAccumulatorIndex);
7523 vixl32::Register mul_left =
7524 InputRegisterAt(instr, HMultiplyAccumulate::kInputMulLeftIndex);
7525 vixl32::Register mul_right =
7526 InputRegisterAt(instr, HMultiplyAccumulate::kInputMulRightIndex);
7527
7528 if (instr->GetOpKind() == HInstruction::kAdd) {
7529 __ Mla(res, mul_left, mul_right, accumulator);
7530 } else {
7531 __ Mls(res, mul_left, mul_right, accumulator);
7532 }
7533}
7534
Artem Serov551b28f2016-10-18 19:11:30 +01007535void LocationsBuilderARMVIXL::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
7536 // Nothing to do, this should be removed during prepare for register allocator.
7537 LOG(FATAL) << "Unreachable";
7538}
7539
7540void InstructionCodeGeneratorARMVIXL::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
7541 // Nothing to do, this should be removed during prepare for register allocator.
7542 LOG(FATAL) << "Unreachable";
7543}
7544
7545// Simple implementation of packed switch - generate cascaded compare/jumps.
7546void LocationsBuilderARMVIXL::VisitPackedSwitch(HPackedSwitch* switch_instr) {
7547 LocationSummary* locations =
7548 new (GetGraph()->GetArena()) LocationSummary(switch_instr, LocationSummary::kNoCall);
7549 locations->SetInAt(0, Location::RequiresRegister());
7550 if (switch_instr->GetNumEntries() > kPackedSwitchCompareJumpThreshold &&
7551 codegen_->GetAssembler()->GetVIXLAssembler()->IsUsingT32()) {
7552 locations->AddTemp(Location::RequiresRegister()); // We need a temp for the table base.
7553 if (switch_instr->GetStartValue() != 0) {
7554 locations->AddTemp(Location::RequiresRegister()); // We need a temp for the bias.
7555 }
7556 }
7557}
7558
7559// TODO(VIXL): Investigate and reach the parity with old arm codegen.
7560void InstructionCodeGeneratorARMVIXL::VisitPackedSwitch(HPackedSwitch* switch_instr) {
7561 int32_t lower_bound = switch_instr->GetStartValue();
7562 uint32_t num_entries = switch_instr->GetNumEntries();
7563 LocationSummary* locations = switch_instr->GetLocations();
7564 vixl32::Register value_reg = InputRegisterAt(switch_instr, 0);
7565 HBasicBlock* default_block = switch_instr->GetDefaultBlock();
7566
7567 if (num_entries <= kPackedSwitchCompareJumpThreshold ||
7568 !codegen_->GetAssembler()->GetVIXLAssembler()->IsUsingT32()) {
7569 // Create a series of compare/jumps.
Anton Kirilovedb2ac32016-11-30 15:14:10 +00007570 UseScratchRegisterScope temps(GetVIXLAssembler());
Artem Serov551b28f2016-10-18 19:11:30 +01007571 vixl32::Register temp_reg = temps.Acquire();
7572 // Note: It is fine for the below AddConstantSetFlags() using IP register to temporarily store
7573 // the immediate, because IP is used as the destination register. For the other
7574 // AddConstantSetFlags() and GenerateCompareWithImmediate(), the immediate values are constant,
7575 // and they can be encoded in the instruction without making use of IP register.
7576 __ Adds(temp_reg, value_reg, -lower_bound);
7577
7578 const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors();
7579 // Jump to successors[0] if value == lower_bound.
7580 __ B(eq, codegen_->GetLabelOf(successors[0]));
7581 int32_t last_index = 0;
7582 for (; num_entries - last_index > 2; last_index += 2) {
7583 __ Adds(temp_reg, temp_reg, -2);
7584 // Jump to successors[last_index + 1] if value < case_value[last_index + 2].
7585 __ B(lo, codegen_->GetLabelOf(successors[last_index + 1]));
7586 // Jump to successors[last_index + 2] if value == case_value[last_index + 2].
7587 __ B(eq, codegen_->GetLabelOf(successors[last_index + 2]));
7588 }
7589 if (num_entries - last_index == 2) {
7590 // The last missing case_value.
7591 __ Cmp(temp_reg, 1);
7592 __ B(eq, codegen_->GetLabelOf(successors[last_index + 1]));
7593 }
7594
7595 // And the default for any other value.
7596 if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) {
7597 __ B(codegen_->GetLabelOf(default_block));
7598 }
7599 } else {
7600 // Create a table lookup.
7601 vixl32::Register table_base = RegisterFrom(locations->GetTemp(0));
7602
7603 JumpTableARMVIXL* jump_table = codegen_->CreateJumpTable(switch_instr);
7604
7605 // Remove the bias.
7606 vixl32::Register key_reg;
7607 if (lower_bound != 0) {
7608 key_reg = RegisterFrom(locations->GetTemp(1));
7609 __ Sub(key_reg, value_reg, lower_bound);
7610 } else {
7611 key_reg = value_reg;
7612 }
7613
7614 // Check whether the value is in the table, jump to default block if not.
7615 __ Cmp(key_reg, num_entries - 1);
7616 __ B(hi, codegen_->GetLabelOf(default_block));
7617
Anton Kirilovedb2ac32016-11-30 15:14:10 +00007618 UseScratchRegisterScope temps(GetVIXLAssembler());
Artem Serov551b28f2016-10-18 19:11:30 +01007619 vixl32::Register jump_offset = temps.Acquire();
7620
7621 // Load jump offset from the table.
7622 __ Adr(table_base, jump_table->GetTableStartLabel());
7623 __ Ldr(jump_offset, MemOperand(table_base, key_reg, vixl32::LSL, 2));
7624
7625 // Jump to target block by branching to table_base(pc related) + offset.
7626 vixl32::Register target_address = table_base;
7627 __ Add(target_address, table_base, jump_offset);
7628 __ Bx(target_address);
Artem Serov09a940d2016-11-11 16:15:11 +00007629
7630 jump_table->EmitTable(codegen_);
Artem Serov551b28f2016-10-18 19:11:30 +01007631 }
7632}
Artem Serovd4cc5b22016-11-04 11:19:09 +00007633void LocationsBuilderARMVIXL::VisitArmDexCacheArraysBase(HArmDexCacheArraysBase* base) {
7634 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(base);
7635 locations->SetOut(Location::RequiresRegister());
7636}
7637
7638void InstructionCodeGeneratorARMVIXL::VisitArmDexCacheArraysBase(HArmDexCacheArraysBase* base) {
7639 vixl32::Register base_reg = OutputRegister(base);
7640 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
7641 codegen_->NewPcRelativeDexCacheArrayPatch(base->GetDexFile(), base->GetElementOffset());
7642 codegen_->EmitMovwMovtPlaceholder(labels, base_reg);
7643}
Artem Serov551b28f2016-10-18 19:11:30 +01007644
Artem Serov02d37832016-10-25 15:25:33 +01007645// Copy the result of a call into the given target.
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007646void CodeGeneratorARMVIXL::MoveFromReturnRegister(Location trg, Primitive::Type type) {
7647 if (!trg.IsValid()) {
7648 DCHECK_EQ(type, Primitive::kPrimVoid);
7649 return;
7650 }
7651
7652 DCHECK_NE(type, Primitive::kPrimVoid);
7653
Artem Serovd4cc5b22016-11-04 11:19:09 +00007654 Location return_loc = InvokeDexCallingConventionVisitorARMVIXL().GetReturnLocation(type);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007655 if (return_loc.Equals(trg)) {
7656 return;
7657 }
7658
7659 // TODO: Consider pairs in the parallel move resolver, then this could be nicely merged
7660 // with the last branch.
7661 if (type == Primitive::kPrimLong) {
7662 TODO_VIXL32(FATAL);
7663 } else if (type == Primitive::kPrimDouble) {
7664 TODO_VIXL32(FATAL);
7665 } else {
7666 // Let the parallel move resolver take care of all of this.
7667 HParallelMove parallel_move(GetGraph()->GetArena());
7668 parallel_move.AddMove(return_loc, trg, type, nullptr);
7669 GetMoveResolver()->EmitNativeCode(&parallel_move);
7670 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007671}
Scott Wakelingfe885462016-09-22 10:24:38 +01007672
xueliang.zhong8d2c4592016-11-23 17:05:25 +00007673void LocationsBuilderARMVIXL::VisitClassTableGet(HClassTableGet* instruction) {
7674 LocationSummary* locations =
7675 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
7676 locations->SetInAt(0, Location::RequiresRegister());
7677 locations->SetOut(Location::RequiresRegister());
Artem Serov551b28f2016-10-18 19:11:30 +01007678}
7679
xueliang.zhong8d2c4592016-11-23 17:05:25 +00007680void InstructionCodeGeneratorARMVIXL::VisitClassTableGet(HClassTableGet* instruction) {
7681 if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) {
7682 uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
7683 instruction->GetIndex(), kArmPointerSize).SizeValue();
7684 GetAssembler()->LoadFromOffset(kLoadWord,
7685 OutputRegister(instruction),
7686 InputRegisterAt(instruction, 0),
7687 method_offset);
7688 } else {
7689 uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement(
7690 instruction->GetIndex(), kArmPointerSize));
7691 GetAssembler()->LoadFromOffset(kLoadWord,
7692 OutputRegister(instruction),
7693 InputRegisterAt(instruction, 0),
7694 mirror::Class::ImtPtrOffset(kArmPointerSize).Uint32Value());
7695 GetAssembler()->LoadFromOffset(kLoadWord,
7696 OutputRegister(instruction),
7697 OutputRegister(instruction),
7698 method_offset);
7699 }
Artem Serov551b28f2016-10-18 19:11:30 +01007700}
7701
Artem Serovd4cc5b22016-11-04 11:19:09 +00007702void CodeGeneratorARMVIXL::EmitMovwMovtPlaceholder(
7703 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels,
7704 vixl32::Register out) {
7705 AssemblerAccurateScope aas(GetVIXLAssembler(),
Alexandre Rames374ddf32016-11-04 10:40:49 +00007706 3 * vixl32::kMaxInstructionSizeInBytes,
Artem Serovd4cc5b22016-11-04 11:19:09 +00007707 CodeBufferCheckScope::kMaximumSize);
7708 // TODO(VIXL): Think about using mov instead of movw.
7709 __ bind(&labels->movw_label);
7710 __ movw(out, /* placeholder */ 0u);
7711 __ bind(&labels->movt_label);
7712 __ movt(out, /* placeholder */ 0u);
7713 __ bind(&labels->add_pc_label);
7714 __ add(out, out, pc);
7715}
7716
Scott Wakelingfe885462016-09-22 10:24:38 +01007717#undef __
7718#undef QUICK_ENTRY_POINT
7719#undef TODO_VIXL32
7720
7721} // namespace arm
7722} // namespace art