blob: 9f2720e46a661938b3e323a703eba1893589dd53 [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;
Anton Kirilov644032c2016-12-06 17:51:43 +000049using helpers::InputVRegister;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010050using helpers::InputVRegisterAt;
Scott Wakelingb77051e2016-11-21 19:46:00 +000051using helpers::Int32ConstantFrom;
Anton Kirilov644032c2016-12-06 17:51:43 +000052using helpers::Int64ConstantFrom;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010053using helpers::LocationFrom;
54using helpers::LowRegisterFrom;
55using helpers::LowSRegisterFrom;
56using helpers::OutputRegister;
57using helpers::OutputSRegister;
58using helpers::OutputVRegister;
59using helpers::RegisterFrom;
60using helpers::SRegisterFrom;
Anton Kirilov644032c2016-12-06 17:51:43 +000061using helpers::Uint64ConstantFrom;
Scott Wakelingfe885462016-09-22 10:24:38 +010062
Artem Serov0fb37192016-12-06 18:13:40 +000063using vixl::ExactAssemblyScope;
64using vixl::CodeBufferCheckScope;
65
Scott Wakelingfe885462016-09-22 10:24:38 +010066using RegisterList = vixl32::RegisterList;
67
68static bool ExpectedPairLayout(Location location) {
69 // We expected this for both core and fpu register pairs.
70 return ((location.low() & 1) == 0) && (location.low() + 1 == location.high());
71}
Artem Serovd4cc5b22016-11-04 11:19:09 +000072// Use a local definition to prevent copying mistakes.
73static constexpr size_t kArmWordSize = static_cast<size_t>(kArmPointerSize);
74static constexpr size_t kArmBitsPerWord = kArmWordSize * kBitsPerByte;
Anton Kirilove28d9ae2016-10-25 18:17:23 +010075static constexpr int kCurrentMethodStackOffset = 0;
Artem Serov551b28f2016-10-18 19:11:30 +010076static constexpr uint32_t kPackedSwitchCompareJumpThreshold = 7;
Scott Wakelingfe885462016-09-22 10:24:38 +010077
78#ifdef __
79#error "ARM Codegen VIXL macro-assembler macro already defined."
80#endif
81
Scott Wakelingfe885462016-09-22 10:24:38 +010082// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
83#define __ down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler()-> // NOLINT
84#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArmPointerSize, x).Int32Value()
85
86// Marker that code is yet to be, and must, be implemented.
87#define TODO_VIXL32(level) LOG(level) << __PRETTY_FUNCTION__ << " unimplemented "
88
Scott Wakelinga7812ae2016-10-17 10:03:36 +010089// SaveLiveRegisters and RestoreLiveRegisters from SlowPathCodeARM operate on sets of S registers,
90// for each live D registers they treat two corresponding S registers as live ones.
91//
92// Two following functions (SaveContiguousSRegisterList, RestoreContiguousSRegisterList) build
93// from a list of contiguous S registers a list of contiguous D registers (processing first/last
94// S registers corner cases) and save/restore this new list treating them as D registers.
95// - decreasing code size
96// - avoiding hazards on Cortex-A57, when a pair of S registers for an actual live D register is
97// restored and then used in regular non SlowPath code as D register.
98//
99// For the following example (v means the S register is live):
100// D names: | D0 | D1 | D2 | D4 | ...
101// S names: | S0 | S1 | S2 | S3 | S4 | S5 | S6 | S7 | ...
102// Live? | | v | v | v | v | v | v | | ...
103//
104// S1 and S6 will be saved/restored independently; D registers list (D1, D2) will be processed
105// as D registers.
106//
107// TODO(VIXL): All this code should be unnecessary once the VIXL AArch32 backend provides helpers
108// for lists of floating-point registers.
109static size_t SaveContiguousSRegisterList(size_t first,
110 size_t last,
111 CodeGenerator* codegen,
112 size_t stack_offset) {
113 static_assert(kSRegSizeInBytes == kArmWordSize, "Broken assumption on reg/word sizes.");
114 static_assert(kDRegSizeInBytes == 2 * kArmWordSize, "Broken assumption on reg/word sizes.");
115 DCHECK_LE(first, last);
116 if ((first == last) && (first == 0)) {
117 __ Vstr(vixl32::SRegister(first), MemOperand(sp, stack_offset));
118 return stack_offset + kSRegSizeInBytes;
119 }
120 if (first % 2 == 1) {
121 __ Vstr(vixl32::SRegister(first++), MemOperand(sp, stack_offset));
122 stack_offset += kSRegSizeInBytes;
123 }
124
125 bool save_last = false;
126 if (last % 2 == 0) {
127 save_last = true;
128 --last;
129 }
130
131 if (first < last) {
132 vixl32::DRegister d_reg = vixl32::DRegister(first / 2);
133 DCHECK_EQ((last - first + 1) % 2, 0u);
134 size_t number_of_d_regs = (last - first + 1) / 2;
135
136 if (number_of_d_regs == 1) {
137 __ Vstr(d_reg, MemOperand(sp, stack_offset));
138 } else if (number_of_d_regs > 1) {
139 UseScratchRegisterScope temps(down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler());
140 vixl32::Register base = sp;
141 if (stack_offset != 0) {
142 base = temps.Acquire();
Scott Wakelingb77051e2016-11-21 19:46:00 +0000143 __ Add(base, sp, Operand::From(stack_offset));
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100144 }
145 __ Vstm(F64, base, NO_WRITE_BACK, DRegisterList(d_reg, number_of_d_regs));
146 }
147 stack_offset += number_of_d_regs * kDRegSizeInBytes;
148 }
149
150 if (save_last) {
151 __ Vstr(vixl32::SRegister(last + 1), MemOperand(sp, stack_offset));
152 stack_offset += kSRegSizeInBytes;
153 }
154
155 return stack_offset;
156}
157
158static size_t RestoreContiguousSRegisterList(size_t first,
159 size_t last,
160 CodeGenerator* codegen,
161 size_t stack_offset) {
162 static_assert(kSRegSizeInBytes == kArmWordSize, "Broken assumption on reg/word sizes.");
163 static_assert(kDRegSizeInBytes == 2 * kArmWordSize, "Broken assumption on reg/word sizes.");
164 DCHECK_LE(first, last);
165 if ((first == last) && (first == 0)) {
166 __ Vldr(vixl32::SRegister(first), MemOperand(sp, stack_offset));
167 return stack_offset + kSRegSizeInBytes;
168 }
169 if (first % 2 == 1) {
170 __ Vldr(vixl32::SRegister(first++), MemOperand(sp, stack_offset));
171 stack_offset += kSRegSizeInBytes;
172 }
173
174 bool restore_last = false;
175 if (last % 2 == 0) {
176 restore_last = true;
177 --last;
178 }
179
180 if (first < last) {
181 vixl32::DRegister d_reg = vixl32::DRegister(first / 2);
182 DCHECK_EQ((last - first + 1) % 2, 0u);
183 size_t number_of_d_regs = (last - first + 1) / 2;
184 if (number_of_d_regs == 1) {
185 __ Vldr(d_reg, MemOperand(sp, stack_offset));
186 } else if (number_of_d_regs > 1) {
187 UseScratchRegisterScope temps(down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler());
188 vixl32::Register base = sp;
189 if (stack_offset != 0) {
190 base = temps.Acquire();
Scott Wakelingb77051e2016-11-21 19:46:00 +0000191 __ Add(base, sp, Operand::From(stack_offset));
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100192 }
193 __ Vldm(F64, base, NO_WRITE_BACK, DRegisterList(d_reg, number_of_d_regs));
194 }
195 stack_offset += number_of_d_regs * kDRegSizeInBytes;
196 }
197
198 if (restore_last) {
199 __ Vldr(vixl32::SRegister(last + 1), MemOperand(sp, stack_offset));
200 stack_offset += kSRegSizeInBytes;
201 }
202
203 return stack_offset;
204}
205
206void SlowPathCodeARMVIXL::SaveLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
207 size_t stack_offset = codegen->GetFirstRegisterSlotInSlowPath();
208 size_t orig_offset = stack_offset;
209
210 const uint32_t core_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ true);
211 for (uint32_t i : LowToHighBits(core_spills)) {
212 // If the register holds an object, update the stack mask.
213 if (locations->RegisterContainsObject(i)) {
214 locations->SetStackBit(stack_offset / kVRegSize);
215 }
216 DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
217 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
218 saved_core_stack_offsets_[i] = stack_offset;
219 stack_offset += kArmWordSize;
220 }
221
222 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
223 arm_codegen->GetAssembler()->StoreRegisterList(core_spills, orig_offset);
224
225 uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ false);
226 orig_offset = stack_offset;
227 for (uint32_t i : LowToHighBits(fp_spills)) {
228 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
229 saved_fpu_stack_offsets_[i] = stack_offset;
230 stack_offset += kArmWordSize;
231 }
232
233 stack_offset = orig_offset;
234 while (fp_spills != 0u) {
235 uint32_t begin = CTZ(fp_spills);
236 uint32_t tmp = fp_spills + (1u << begin);
237 fp_spills &= tmp; // Clear the contiguous range of 1s.
238 uint32_t end = (tmp == 0u) ? 32u : CTZ(tmp); // CTZ(0) is undefined.
239 stack_offset = SaveContiguousSRegisterList(begin, end - 1, codegen, stack_offset);
240 }
241 DCHECK_LE(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
242}
243
244void SlowPathCodeARMVIXL::RestoreLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
245 size_t stack_offset = codegen->GetFirstRegisterSlotInSlowPath();
246 size_t orig_offset = stack_offset;
247
248 const uint32_t core_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ true);
249 for (uint32_t i : LowToHighBits(core_spills)) {
250 DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
251 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
252 stack_offset += kArmWordSize;
253 }
254
255 // TODO(VIXL): Check the coherency of stack_offset after this with a test.
256 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
257 arm_codegen->GetAssembler()->LoadRegisterList(core_spills, orig_offset);
258
259 uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ false);
260 while (fp_spills != 0u) {
261 uint32_t begin = CTZ(fp_spills);
262 uint32_t tmp = fp_spills + (1u << begin);
263 fp_spills &= tmp; // Clear the contiguous range of 1s.
264 uint32_t end = (tmp == 0u) ? 32u : CTZ(tmp); // CTZ(0) is undefined.
265 stack_offset = RestoreContiguousSRegisterList(begin, end - 1, codegen, stack_offset);
266 }
267 DCHECK_LE(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
268}
269
270class NullCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
271 public:
272 explicit NullCheckSlowPathARMVIXL(HNullCheck* instruction) : SlowPathCodeARMVIXL(instruction) {}
273
274 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
275 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
276 __ Bind(GetEntryLabel());
277 if (instruction_->CanThrowIntoCatchBlock()) {
278 // Live registers will be restored in the catch block if caught.
279 SaveLiveRegisters(codegen, instruction_->GetLocations());
280 }
281 arm_codegen->InvokeRuntime(kQuickThrowNullPointer,
282 instruction_,
283 instruction_->GetDexPc(),
284 this);
285 CheckEntrypointTypes<kQuickThrowNullPointer, void, void>();
286 }
287
288 bool IsFatal() const OVERRIDE { return true; }
289
290 const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathARMVIXL"; }
291
292 private:
293 DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARMVIXL);
294};
295
Scott Wakelingfe885462016-09-22 10:24:38 +0100296class DivZeroCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
297 public:
298 explicit DivZeroCheckSlowPathARMVIXL(HDivZeroCheck* instruction)
299 : SlowPathCodeARMVIXL(instruction) {}
300
301 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100302 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
Scott Wakelingfe885462016-09-22 10:24:38 +0100303 __ Bind(GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100304 arm_codegen->InvokeRuntime(kQuickThrowDivZero, instruction_, instruction_->GetDexPc(), this);
Scott Wakelingfe885462016-09-22 10:24:38 +0100305 CheckEntrypointTypes<kQuickThrowDivZero, void, void>();
306 }
307
308 bool IsFatal() const OVERRIDE { return true; }
309
310 const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathARMVIXL"; }
311
312 private:
313 DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathARMVIXL);
314};
315
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100316class SuspendCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
317 public:
318 SuspendCheckSlowPathARMVIXL(HSuspendCheck* instruction, HBasicBlock* successor)
319 : SlowPathCodeARMVIXL(instruction), successor_(successor) {}
320
321 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
322 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
323 __ Bind(GetEntryLabel());
324 arm_codegen->InvokeRuntime(kQuickTestSuspend, instruction_, instruction_->GetDexPc(), this);
325 CheckEntrypointTypes<kQuickTestSuspend, void, void>();
326 if (successor_ == nullptr) {
327 __ B(GetReturnLabel());
328 } else {
329 __ B(arm_codegen->GetLabelOf(successor_));
330 }
331 }
332
333 vixl32::Label* GetReturnLabel() {
334 DCHECK(successor_ == nullptr);
335 return &return_label_;
336 }
337
338 HBasicBlock* GetSuccessor() const {
339 return successor_;
340 }
341
342 const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathARMVIXL"; }
343
344 private:
345 // If not null, the block to branch to after the suspend check.
346 HBasicBlock* const successor_;
347
348 // If `successor_` is null, the label to branch to after the suspend check.
349 vixl32::Label return_label_;
350
351 DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathARMVIXL);
352};
353
Scott Wakelingc34dba72016-10-03 10:14:44 +0100354class BoundsCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
355 public:
356 explicit BoundsCheckSlowPathARMVIXL(HBoundsCheck* instruction)
357 : SlowPathCodeARMVIXL(instruction) {}
358
359 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
360 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
361 LocationSummary* locations = instruction_->GetLocations();
362
363 __ Bind(GetEntryLabel());
364 if (instruction_->CanThrowIntoCatchBlock()) {
365 // Live registers will be restored in the catch block if caught.
366 SaveLiveRegisters(codegen, instruction_->GetLocations());
367 }
368 // We're moving two locations to locations that could overlap, so we need a parallel
369 // move resolver.
370 InvokeRuntimeCallingConventionARMVIXL calling_convention;
371 codegen->EmitParallelMoves(
372 locations->InAt(0),
373 LocationFrom(calling_convention.GetRegisterAt(0)),
374 Primitive::kPrimInt,
375 locations->InAt(1),
376 LocationFrom(calling_convention.GetRegisterAt(1)),
377 Primitive::kPrimInt);
378 QuickEntrypointEnum entrypoint = instruction_->AsBoundsCheck()->IsStringCharAt()
379 ? kQuickThrowStringBounds
380 : kQuickThrowArrayBounds;
381 arm_codegen->InvokeRuntime(entrypoint, instruction_, instruction_->GetDexPc(), this);
382 CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>();
383 CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>();
384 }
385
386 bool IsFatal() const OVERRIDE { return true; }
387
388 const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathARMVIXL"; }
389
390 private:
391 DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARMVIXL);
392};
393
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100394class LoadClassSlowPathARMVIXL : public SlowPathCodeARMVIXL {
395 public:
396 LoadClassSlowPathARMVIXL(HLoadClass* cls, HInstruction* at, uint32_t dex_pc, bool do_clinit)
397 : SlowPathCodeARMVIXL(at), cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) {
398 DCHECK(at->IsLoadClass() || at->IsClinitCheck());
399 }
400
401 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
402 LocationSummary* locations = at_->GetLocations();
403
404 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
405 __ Bind(GetEntryLabel());
406 SaveLiveRegisters(codegen, locations);
407
408 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Andreas Gampea5b09a62016-11-17 15:21:22 -0800409 __ Mov(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex().index_);
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100410 QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage
411 : kQuickInitializeType;
412 arm_codegen->InvokeRuntime(entrypoint, at_, dex_pc_, this);
413 if (do_clinit_) {
414 CheckEntrypointTypes<kQuickInitializeStaticStorage, void*, uint32_t>();
415 } else {
416 CheckEntrypointTypes<kQuickInitializeType, void*, uint32_t>();
417 }
418
419 // Move the class to the desired location.
420 Location out = locations->Out();
421 if (out.IsValid()) {
422 DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
423 arm_codegen->Move32(locations->Out(), LocationFrom(r0));
424 }
425 RestoreLiveRegisters(codegen, locations);
426 __ B(GetExitLabel());
427 }
428
429 const char* GetDescription() const OVERRIDE { return "LoadClassSlowPathARMVIXL"; }
430
431 private:
432 // The class this slow path will load.
433 HLoadClass* const cls_;
434
435 // The instruction where this slow path is happening.
436 // (Might be the load class or an initialization check).
437 HInstruction* const at_;
438
439 // The dex PC of `at_`.
440 const uint32_t dex_pc_;
441
442 // Whether to initialize the class.
443 const bool do_clinit_;
444
445 DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathARMVIXL);
446};
447
Artem Serovd4cc5b22016-11-04 11:19:09 +0000448class LoadStringSlowPathARMVIXL : public SlowPathCodeARMVIXL {
449 public:
450 explicit LoadStringSlowPathARMVIXL(HLoadString* instruction)
451 : SlowPathCodeARMVIXL(instruction) {}
452
453 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
454 LocationSummary* locations = instruction_->GetLocations();
455 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
456 HLoadString* load = instruction_->AsLoadString();
457 const uint32_t string_index = load->GetStringIndex().index_;
458 vixl32::Register out = OutputRegister(load);
459 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
460 constexpr bool call_saves_everything_except_r0 = (!kUseReadBarrier || kUseBakerReadBarrier);
461
462 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
463 __ Bind(GetEntryLabel());
464 SaveLiveRegisters(codegen, locations);
465
466 InvokeRuntimeCallingConventionARMVIXL calling_convention;
467 // In the unlucky case that the `temp` is R0, we preserve the address in `out` across
468 // the kSaveEverything call (or use `out` for the address after non-kSaveEverything call).
469 bool temp_is_r0 = (temp.Is(calling_convention.GetRegisterAt(0)));
470 vixl32::Register entry_address = temp_is_r0 ? out : temp;
471 DCHECK(!entry_address.Is(calling_convention.GetRegisterAt(0)));
472 if (call_saves_everything_except_r0 && temp_is_r0) {
473 __ Mov(entry_address, temp);
474 }
475
476 __ Mov(calling_convention.GetRegisterAt(0), string_index);
477 arm_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), this);
478 CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
479
480 // Store the resolved String to the .bss entry.
481 if (call_saves_everything_except_r0) {
482 // The string entry address was preserved in `entry_address` thanks to kSaveEverything.
483 __ Str(r0, MemOperand(entry_address));
484 } else {
485 // For non-Baker read barrier, we need to re-calculate the address of the string entry.
486 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
487 arm_codegen->NewPcRelativeStringPatch(load->GetDexFile(), string_index);
488 arm_codegen->EmitMovwMovtPlaceholder(labels, out);
489 __ Str(r0, MemOperand(entry_address));
490 }
491
492 arm_codegen->Move32(locations->Out(), LocationFrom(r0));
493 RestoreLiveRegisters(codegen, locations);
494
495 __ B(GetExitLabel());
496 }
497
498 const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathARMVIXL"; }
499
500 private:
501 DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathARMVIXL);
502};
503
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100504class TypeCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
505 public:
506 TypeCheckSlowPathARMVIXL(HInstruction* instruction, bool is_fatal)
507 : SlowPathCodeARMVIXL(instruction), is_fatal_(is_fatal) {}
508
509 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
510 LocationSummary* locations = instruction_->GetLocations();
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100511 DCHECK(instruction_->IsCheckCast()
512 || !locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
513
514 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
515 __ Bind(GetEntryLabel());
516
517 if (!is_fatal_) {
Artem Serovcfbe9132016-10-14 15:58:56 +0100518 SaveLiveRegisters(codegen, locations);
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100519 }
520
521 // We're moving two locations to locations that could overlap, so we need a parallel
522 // move resolver.
523 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100524
Mathieu Chartier9fd8c602016-11-14 14:38:53 -0800525 codegen->EmitParallelMoves(locations->InAt(0),
Mathieu Chartierb99f4d62016-11-07 16:17:26 -0800526 LocationFrom(calling_convention.GetRegisterAt(0)),
527 Primitive::kPrimNot,
Mathieu Chartier9fd8c602016-11-14 14:38:53 -0800528 locations->InAt(1),
Mathieu Chartierb99f4d62016-11-07 16:17:26 -0800529 LocationFrom(calling_convention.GetRegisterAt(1)),
530 Primitive::kPrimNot);
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100531 if (instruction_->IsInstanceOf()) {
Artem Serovcfbe9132016-10-14 15:58:56 +0100532 arm_codegen->InvokeRuntime(kQuickInstanceofNonTrivial,
533 instruction_,
534 instruction_->GetDexPc(),
535 this);
Mathieu Chartier9fd8c602016-11-14 14:38:53 -0800536 CheckEntrypointTypes<kQuickInstanceofNonTrivial, size_t, mirror::Object*, mirror::Class*>();
Artem Serovcfbe9132016-10-14 15:58:56 +0100537 arm_codegen->Move32(locations->Out(), LocationFrom(r0));
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100538 } else {
539 DCHECK(instruction_->IsCheckCast());
Mathieu Chartierb99f4d62016-11-07 16:17:26 -0800540 arm_codegen->InvokeRuntime(kQuickCheckInstanceOf,
541 instruction_,
542 instruction_->GetDexPc(),
543 this);
544 CheckEntrypointTypes<kQuickCheckInstanceOf, void, mirror::Object*, mirror::Class*>();
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100545 }
546
547 if (!is_fatal_) {
Artem Serovcfbe9132016-10-14 15:58:56 +0100548 RestoreLiveRegisters(codegen, locations);
549 __ B(GetExitLabel());
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100550 }
551 }
552
553 const char* GetDescription() const OVERRIDE { return "TypeCheckSlowPathARMVIXL"; }
554
555 bool IsFatal() const OVERRIDE { return is_fatal_; }
556
557 private:
558 const bool is_fatal_;
559
560 DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathARMVIXL);
561};
562
Scott Wakelingc34dba72016-10-03 10:14:44 +0100563class DeoptimizationSlowPathARMVIXL : public SlowPathCodeARMVIXL {
564 public:
565 explicit DeoptimizationSlowPathARMVIXL(HDeoptimize* instruction)
566 : SlowPathCodeARMVIXL(instruction) {}
567
568 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
569 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
570 __ Bind(GetEntryLabel());
571 arm_codegen->InvokeRuntime(kQuickDeoptimize, instruction_, instruction_->GetDexPc(), this);
572 CheckEntrypointTypes<kQuickDeoptimize, void, void>();
573 }
574
575 const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathARMVIXL"; }
576
577 private:
578 DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathARMVIXL);
579};
580
581class ArraySetSlowPathARMVIXL : public SlowPathCodeARMVIXL {
582 public:
583 explicit ArraySetSlowPathARMVIXL(HInstruction* instruction) : SlowPathCodeARMVIXL(instruction) {}
584
585 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
586 LocationSummary* locations = instruction_->GetLocations();
587 __ Bind(GetEntryLabel());
588 SaveLiveRegisters(codegen, locations);
589
590 InvokeRuntimeCallingConventionARMVIXL calling_convention;
591 HParallelMove parallel_move(codegen->GetGraph()->GetArena());
592 parallel_move.AddMove(
593 locations->InAt(0),
594 LocationFrom(calling_convention.GetRegisterAt(0)),
595 Primitive::kPrimNot,
596 nullptr);
597 parallel_move.AddMove(
598 locations->InAt(1),
599 LocationFrom(calling_convention.GetRegisterAt(1)),
600 Primitive::kPrimInt,
601 nullptr);
602 parallel_move.AddMove(
603 locations->InAt(2),
604 LocationFrom(calling_convention.GetRegisterAt(2)),
605 Primitive::kPrimNot,
606 nullptr);
607 codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
608
609 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
610 arm_codegen->InvokeRuntime(kQuickAputObject, instruction_, instruction_->GetDexPc(), this);
611 CheckEntrypointTypes<kQuickAputObject, void, mirror::Array*, int32_t, mirror::Object*>();
612 RestoreLiveRegisters(codegen, locations);
613 __ B(GetExitLabel());
614 }
615
616 const char* GetDescription() const OVERRIDE { return "ArraySetSlowPathARMVIXL"; }
617
618 private:
619 DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathARMVIXL);
620};
621
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000622// Slow path marking an object reference `ref` during a read
623// barrier. The field `obj.field` in the object `obj` holding this
624// reference does not get updated by this slow path after marking (see
625// ReadBarrierMarkAndUpdateFieldSlowPathARM below for that).
626//
627// This means that after the execution of this slow path, `ref` will
628// always be up-to-date, but `obj.field` may not; i.e., after the
629// flip, `ref` will be a to-space reference, but `obj.field` will
630// probably still be a from-space reference (unless it gets updated by
631// another thread, or if another thread installed another object
632// reference (different from `ref`) in `obj.field`).
633class ReadBarrierMarkSlowPathARMVIXL : public SlowPathCodeARMVIXL {
634 public:
635 ReadBarrierMarkSlowPathARMVIXL(HInstruction* instruction,
636 Location ref,
637 Location entrypoint = Location::NoLocation())
638 : SlowPathCodeARMVIXL(instruction), ref_(ref), entrypoint_(entrypoint) {
639 DCHECK(kEmitCompilerReadBarrier);
640 }
641
642 const char* GetDescription() const OVERRIDE { return "ReadBarrierMarkSlowPathARMVIXL"; }
643
644 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
645 LocationSummary* locations = instruction_->GetLocations();
646 vixl32::Register ref_reg = RegisterFrom(ref_);
647 DCHECK(locations->CanCall());
648 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_reg.GetCode())) << ref_reg;
649 DCHECK(instruction_->IsInstanceFieldGet() ||
650 instruction_->IsStaticFieldGet() ||
651 instruction_->IsArrayGet() ||
652 instruction_->IsArraySet() ||
653 instruction_->IsLoadClass() ||
654 instruction_->IsLoadString() ||
655 instruction_->IsInstanceOf() ||
656 instruction_->IsCheckCast() ||
657 (instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()) ||
658 (instruction_->IsInvokeStaticOrDirect() && instruction_->GetLocations()->Intrinsified()))
659 << "Unexpected instruction in read barrier marking slow path: "
660 << instruction_->DebugName();
661 // The read barrier instrumentation of object ArrayGet
662 // instructions does not support the HIntermediateAddress
663 // instruction.
664 DCHECK(!(instruction_->IsArrayGet() &&
665 instruction_->AsArrayGet()->GetArray()->IsIntermediateAddress()));
666
667 __ Bind(GetEntryLabel());
668 // No need to save live registers; it's taken care of by the
669 // entrypoint. Also, there is no need to update the stack mask,
670 // as this runtime call will not trigger a garbage collection.
671 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
672 DCHECK(!ref_reg.Is(sp));
673 DCHECK(!ref_reg.Is(lr));
674 DCHECK(!ref_reg.Is(pc));
675 // IP is used internally by the ReadBarrierMarkRegX entry point
676 // as a temporary, it cannot be the entry point's input/output.
677 DCHECK(!ref_reg.Is(ip));
678 DCHECK(ref_reg.IsRegister()) << ref_reg;
679 // "Compact" slow path, saving two moves.
680 //
681 // Instead of using the standard runtime calling convention (input
682 // and output in R0):
683 //
684 // R0 <- ref
685 // R0 <- ReadBarrierMark(R0)
686 // ref <- R0
687 //
688 // we just use rX (the register containing `ref`) as input and output
689 // of a dedicated entrypoint:
690 //
691 // rX <- ReadBarrierMarkRegX(rX)
692 //
693 if (entrypoint_.IsValid()) {
694 arm_codegen->ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction_, this);
695 __ Blx(RegisterFrom(entrypoint_));
696 } else {
697 int32_t entry_point_offset =
698 CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArmPointerSize>(ref_reg.GetCode());
699 // This runtime call does not require a stack map.
700 arm_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
701 }
702 __ B(GetExitLabel());
703 }
704
705 private:
706 // The location (register) of the marked object reference.
707 const Location ref_;
708
709 // The location of the entrypoint if already loaded.
710 const Location entrypoint_;
711
712 DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkSlowPathARMVIXL);
713};
714
715// Slow path marking an object reference `ref` during a read barrier,
716// and if needed, atomically updating the field `obj.field` in the
717// object `obj` holding this reference after marking (contrary to
718// ReadBarrierMarkSlowPathARM above, which never tries to update
719// `obj.field`).
720//
721// This means that after the execution of this slow path, both `ref`
722// and `obj.field` will be up-to-date; i.e., after the flip, both will
723// hold the same to-space reference (unless another thread installed
724// another object reference (different from `ref`) in `obj.field`).
725class ReadBarrierMarkAndUpdateFieldSlowPathARMVIXL : public SlowPathCodeARMVIXL {
726 public:
727 ReadBarrierMarkAndUpdateFieldSlowPathARMVIXL(HInstruction* instruction,
728 Location ref,
729 vixl32::Register obj,
730 Location field_offset,
731 vixl32::Register temp1,
732 vixl32::Register temp2)
733 : SlowPathCodeARMVIXL(instruction),
734 ref_(ref),
735 obj_(obj),
736 field_offset_(field_offset),
737 temp1_(temp1),
738 temp2_(temp2) {
739 DCHECK(kEmitCompilerReadBarrier);
740 }
741
742 const char* GetDescription() const OVERRIDE {
743 return "ReadBarrierMarkAndUpdateFieldSlowPathARMVIXL";
744 }
745
746 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
747 LocationSummary* locations = instruction_->GetLocations();
748 vixl32::Register ref_reg = RegisterFrom(ref_);
749 DCHECK(locations->CanCall());
750 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_reg.GetCode())) << ref_reg;
751 // This slow path is only used by the UnsafeCASObject intrinsic.
752 DCHECK((instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()))
753 << "Unexpected instruction in read barrier marking and field updating slow path: "
754 << instruction_->DebugName();
755 DCHECK(instruction_->GetLocations()->Intrinsified());
756 DCHECK_EQ(instruction_->AsInvoke()->GetIntrinsic(), Intrinsics::kUnsafeCASObject);
757 DCHECK(field_offset_.IsRegisterPair()) << field_offset_;
758
759 __ Bind(GetEntryLabel());
760
761 // Save the old reference.
762 // Note that we cannot use IP to save the old reference, as IP is
763 // used internally by the ReadBarrierMarkRegX entry point, and we
764 // need the old reference after the call to that entry point.
765 DCHECK(!temp1_.Is(ip));
766 __ Mov(temp1_, ref_reg);
767
768 // No need to save live registers; it's taken care of by the
769 // entrypoint. Also, there is no need to update the stack mask,
770 // as this runtime call will not trigger a garbage collection.
771 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
772 DCHECK(!ref_reg.Is(sp));
773 DCHECK(!ref_reg.Is(lr));
774 DCHECK(!ref_reg.Is(pc));
775 // IP is used internally by the ReadBarrierMarkRegX entry point
776 // as a temporary, it cannot be the entry point's input/output.
777 DCHECK(!ref_reg.Is(ip));
778 DCHECK(ref_reg.IsRegister()) << ref_reg;
779 // "Compact" slow path, saving two moves.
780 //
781 // Instead of using the standard runtime calling convention (input
782 // and output in R0):
783 //
784 // R0 <- ref
785 // R0 <- ReadBarrierMark(R0)
786 // ref <- R0
787 //
788 // we just use rX (the register containing `ref`) as input and output
789 // of a dedicated entrypoint:
790 //
791 // rX <- ReadBarrierMarkRegX(rX)
792 //
793 int32_t entry_point_offset =
794 CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArmPointerSize>(ref_reg.GetCode());
795 // This runtime call does not require a stack map.
796 arm_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
797
798 // If the new reference is different from the old reference,
799 // update the field in the holder (`*(obj_ + field_offset_)`).
800 //
801 // Note that this field could also hold a different object, if
802 // another thread had concurrently changed it. In that case, the
803 // LDREX/SUBS/ITNE sequence of instructions in the compare-and-set
804 // (CAS) operation below would abort the CAS, leaving the field
805 // as-is.
806 vixl32::Label done;
807 __ Cmp(temp1_, ref_reg);
Artem Serov517d9f62016-12-12 15:51:15 +0000808 __ B(eq, &done, /* far_target */ false);
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000809
810 // Update the the holder's field atomically. This may fail if
811 // mutator updates before us, but it's OK. This is achieved
812 // using a strong compare-and-set (CAS) operation with relaxed
813 // memory synchronization ordering, where the expected value is
814 // the old reference and the desired value is the new reference.
815
816 UseScratchRegisterScope temps(arm_codegen->GetVIXLAssembler());
817 // Convenience aliases.
818 vixl32::Register base = obj_;
819 // The UnsafeCASObject intrinsic uses a register pair as field
820 // offset ("long offset"), of which only the low part contains
821 // data.
822 vixl32::Register offset = LowRegisterFrom(field_offset_);
823 vixl32::Register expected = temp1_;
824 vixl32::Register value = ref_reg;
825 vixl32::Register tmp_ptr = temps.Acquire(); // Pointer to actual memory.
826 vixl32::Register tmp = temp2_; // Value in memory.
827
828 __ Add(tmp_ptr, base, offset);
829
830 if (kPoisonHeapReferences) {
831 arm_codegen->GetAssembler()->PoisonHeapReference(expected);
832 if (value.Is(expected)) {
833 // Do not poison `value`, as it is the same register as
834 // `expected`, which has just been poisoned.
835 } else {
836 arm_codegen->GetAssembler()->PoisonHeapReference(value);
837 }
838 }
839
840 // do {
841 // tmp = [r_ptr] - expected;
842 // } while (tmp == 0 && failure([r_ptr] <- r_new_value));
843
844 vixl32::Label loop_head, exit_loop;
845 __ Bind(&loop_head);
846
847 __ Ldrex(tmp, MemOperand(tmp_ptr));
848
849 __ Subs(tmp, tmp, expected);
850
851 {
Artem Serov0fb37192016-12-06 18:13:40 +0000852 ExactAssemblyScope aas(arm_codegen->GetVIXLAssembler(),
853 2 * kMaxInstructionSizeInBytes,
854 CodeBufferCheckScope::kMaximumSize);
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000855
856 __ it(ne);
857 __ clrex(ne);
858 }
859
Artem Serov517d9f62016-12-12 15:51:15 +0000860 __ B(ne, &exit_loop, /* far_target */ false);
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000861
862 __ Strex(tmp, value, MemOperand(tmp_ptr));
863 __ Cmp(tmp, 1);
Artem Serov517d9f62016-12-12 15:51:15 +0000864 __ B(eq, &loop_head, /* far_target */ false);
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000865
866 __ Bind(&exit_loop);
867
868 if (kPoisonHeapReferences) {
869 arm_codegen->GetAssembler()->UnpoisonHeapReference(expected);
870 if (value.Is(expected)) {
871 // Do not unpoison `value`, as it is the same register as
872 // `expected`, which has just been unpoisoned.
873 } else {
874 arm_codegen->GetAssembler()->UnpoisonHeapReference(value);
875 }
876 }
877
878 __ Bind(&done);
879 __ B(GetExitLabel());
880 }
881
882 private:
883 // The location (register) of the marked object reference.
884 const Location ref_;
885 // The register containing the object holding the marked object reference field.
886 const vixl32::Register obj_;
887 // The location of the offset of the marked reference field within `obj_`.
888 Location field_offset_;
889
890 const vixl32::Register temp1_;
891 const vixl32::Register temp2_;
892
893 DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkAndUpdateFieldSlowPathARMVIXL);
894};
895
896// Slow path generating a read barrier for a heap reference.
897class ReadBarrierForHeapReferenceSlowPathARMVIXL : public SlowPathCodeARMVIXL {
898 public:
899 ReadBarrierForHeapReferenceSlowPathARMVIXL(HInstruction* instruction,
900 Location out,
901 Location ref,
902 Location obj,
903 uint32_t offset,
904 Location index)
905 : SlowPathCodeARMVIXL(instruction),
906 out_(out),
907 ref_(ref),
908 obj_(obj),
909 offset_(offset),
910 index_(index) {
911 DCHECK(kEmitCompilerReadBarrier);
912 // If `obj` is equal to `out` or `ref`, it means the initial object
913 // has been overwritten by (or after) the heap object reference load
914 // to be instrumented, e.g.:
915 //
916 // __ LoadFromOffset(kLoadWord, out, out, offset);
917 // codegen_->GenerateReadBarrierSlow(instruction, out_loc, out_loc, out_loc, offset);
918 //
919 // In that case, we have lost the information about the original
920 // object, and the emitted read barrier cannot work properly.
921 DCHECK(!obj.Equals(out)) << "obj=" << obj << " out=" << out;
922 DCHECK(!obj.Equals(ref)) << "obj=" << obj << " ref=" << ref;
923 }
924
925 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
926 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
927 LocationSummary* locations = instruction_->GetLocations();
928 vixl32::Register reg_out = RegisterFrom(out_);
929 DCHECK(locations->CanCall());
930 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out.GetCode()));
931 DCHECK(instruction_->IsInstanceFieldGet() ||
932 instruction_->IsStaticFieldGet() ||
933 instruction_->IsArrayGet() ||
934 instruction_->IsInstanceOf() ||
935 instruction_->IsCheckCast() ||
936 (instruction_->IsInvokeVirtual()) && instruction_->GetLocations()->Intrinsified())
937 << "Unexpected instruction in read barrier for heap reference slow path: "
938 << instruction_->DebugName();
939 // The read barrier instrumentation of object ArrayGet
940 // instructions does not support the HIntermediateAddress
941 // instruction.
942 DCHECK(!(instruction_->IsArrayGet() &&
943 instruction_->AsArrayGet()->GetArray()->IsIntermediateAddress()));
944
945 __ Bind(GetEntryLabel());
946 SaveLiveRegisters(codegen, locations);
947
948 // We may have to change the index's value, but as `index_` is a
949 // constant member (like other "inputs" of this slow path),
950 // introduce a copy of it, `index`.
951 Location index = index_;
952 if (index_.IsValid()) {
953 // Handle `index_` for HArrayGet and UnsafeGetObject/UnsafeGetObjectVolatile intrinsics.
954 if (instruction_->IsArrayGet()) {
955 // Compute the actual memory offset and store it in `index`.
956 vixl32::Register index_reg = RegisterFrom(index_);
957 DCHECK(locations->GetLiveRegisters()->ContainsCoreRegister(index_reg.GetCode()));
958 if (codegen->IsCoreCalleeSaveRegister(index_reg.GetCode())) {
959 // We are about to change the value of `index_reg` (see the
960 // calls to art::arm::Thumb2Assembler::Lsl and
961 // art::arm::Thumb2Assembler::AddConstant below), but it has
962 // not been saved by the previous call to
963 // art::SlowPathCode::SaveLiveRegisters, as it is a
964 // callee-save register --
965 // art::SlowPathCode::SaveLiveRegisters does not consider
966 // callee-save registers, as it has been designed with the
967 // assumption that callee-save registers are supposed to be
968 // handled by the called function. So, as a callee-save
969 // register, `index_reg` _would_ eventually be saved onto
970 // the stack, but it would be too late: we would have
971 // changed its value earlier. Therefore, we manually save
972 // it here into another freely available register,
973 // `free_reg`, chosen of course among the caller-save
974 // registers (as a callee-save `free_reg` register would
975 // exhibit the same problem).
976 //
977 // Note we could have requested a temporary register from
978 // the register allocator instead; but we prefer not to, as
979 // this is a slow path, and we know we can find a
980 // caller-save register that is available.
981 vixl32::Register free_reg = FindAvailableCallerSaveRegister(codegen);
982 __ Mov(free_reg, index_reg);
983 index_reg = free_reg;
984 index = LocationFrom(index_reg);
985 } else {
986 // The initial register stored in `index_` has already been
987 // saved in the call to art::SlowPathCode::SaveLiveRegisters
988 // (as it is not a callee-save register), so we can freely
989 // use it.
990 }
991 // Shifting the index value contained in `index_reg` by the scale
992 // factor (2) cannot overflow in practice, as the runtime is
993 // unable to allocate object arrays with a size larger than
994 // 2^26 - 1 (that is, 2^28 - 4 bytes).
995 __ Lsl(index_reg, index_reg, TIMES_4);
996 static_assert(
997 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
998 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
999 __ Add(index_reg, index_reg, offset_);
1000 } else {
1001 // In the case of the UnsafeGetObject/UnsafeGetObjectVolatile
1002 // intrinsics, `index_` is not shifted by a scale factor of 2
1003 // (as in the case of ArrayGet), as it is actually an offset
1004 // to an object field within an object.
1005 DCHECK(instruction_->IsInvoke()) << instruction_->DebugName();
1006 DCHECK(instruction_->GetLocations()->Intrinsified());
1007 DCHECK((instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObject) ||
1008 (instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile))
1009 << instruction_->AsInvoke()->GetIntrinsic();
1010 DCHECK_EQ(offset_, 0U);
1011 DCHECK(index_.IsRegisterPair());
1012 // UnsafeGet's offset location is a register pair, the low
1013 // part contains the correct offset.
1014 index = index_.ToLow();
1015 }
1016 }
1017
1018 // We're moving two or three locations to locations that could
1019 // overlap, so we need a parallel move resolver.
1020 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1021 HParallelMove parallel_move(codegen->GetGraph()->GetArena());
1022 parallel_move.AddMove(ref_,
1023 LocationFrom(calling_convention.GetRegisterAt(0)),
1024 Primitive::kPrimNot,
1025 nullptr);
1026 parallel_move.AddMove(obj_,
1027 LocationFrom(calling_convention.GetRegisterAt(1)),
1028 Primitive::kPrimNot,
1029 nullptr);
1030 if (index.IsValid()) {
1031 parallel_move.AddMove(index,
1032 LocationFrom(calling_convention.GetRegisterAt(2)),
1033 Primitive::kPrimInt,
1034 nullptr);
1035 codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
1036 } else {
1037 codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
1038 __ Mov(calling_convention.GetRegisterAt(2), offset_);
1039 }
1040 arm_codegen->InvokeRuntime(kQuickReadBarrierSlow, instruction_, instruction_->GetDexPc(), this);
1041 CheckEntrypointTypes<
1042 kQuickReadBarrierSlow, mirror::Object*, mirror::Object*, mirror::Object*, uint32_t>();
1043 arm_codegen->Move32(out_, LocationFrom(r0));
1044
1045 RestoreLiveRegisters(codegen, locations);
1046 __ B(GetExitLabel());
1047 }
1048
1049 const char* GetDescription() const OVERRIDE {
1050 return "ReadBarrierForHeapReferenceSlowPathARMVIXL";
1051 }
1052
1053 private:
1054 vixl32::Register FindAvailableCallerSaveRegister(CodeGenerator* codegen) {
1055 uint32_t ref = RegisterFrom(ref_).GetCode();
1056 uint32_t obj = RegisterFrom(obj_).GetCode();
1057 for (uint32_t i = 0, e = codegen->GetNumberOfCoreRegisters(); i < e; ++i) {
1058 if (i != ref && i != obj && !codegen->IsCoreCalleeSaveRegister(i)) {
1059 return vixl32::Register(i);
1060 }
1061 }
1062 // We shall never fail to find a free caller-save register, as
1063 // there are more than two core caller-save registers on ARM
1064 // (meaning it is possible to find one which is different from
1065 // `ref` and `obj`).
1066 DCHECK_GT(codegen->GetNumberOfCoreCallerSaveRegisters(), 2u);
1067 LOG(FATAL) << "Could not find a free caller-save register";
1068 UNREACHABLE();
1069 }
1070
1071 const Location out_;
1072 const Location ref_;
1073 const Location obj_;
1074 const uint32_t offset_;
1075 // An additional location containing an index to an array.
1076 // Only used for HArrayGet and the UnsafeGetObject &
1077 // UnsafeGetObjectVolatile intrinsics.
1078 const Location index_;
1079
1080 DISALLOW_COPY_AND_ASSIGN(ReadBarrierForHeapReferenceSlowPathARMVIXL);
1081};
1082
1083// Slow path generating a read barrier for a GC root.
1084class ReadBarrierForRootSlowPathARMVIXL : public SlowPathCodeARMVIXL {
1085 public:
1086 ReadBarrierForRootSlowPathARMVIXL(HInstruction* instruction, Location out, Location root)
1087 : SlowPathCodeARMVIXL(instruction), out_(out), root_(root) {
1088 DCHECK(kEmitCompilerReadBarrier);
1089 }
1090
1091 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
1092 LocationSummary* locations = instruction_->GetLocations();
1093 vixl32::Register reg_out = RegisterFrom(out_);
1094 DCHECK(locations->CanCall());
1095 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out.GetCode()));
1096 DCHECK(instruction_->IsLoadClass() || instruction_->IsLoadString())
1097 << "Unexpected instruction in read barrier for GC root slow path: "
1098 << instruction_->DebugName();
1099
1100 __ Bind(GetEntryLabel());
1101 SaveLiveRegisters(codegen, locations);
1102
1103 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1104 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
1105 arm_codegen->Move32(LocationFrom(calling_convention.GetRegisterAt(0)), root_);
1106 arm_codegen->InvokeRuntime(kQuickReadBarrierForRootSlow,
1107 instruction_,
1108 instruction_->GetDexPc(),
1109 this);
1110 CheckEntrypointTypes<kQuickReadBarrierForRootSlow, mirror::Object*, GcRoot<mirror::Object>*>();
1111 arm_codegen->Move32(out_, LocationFrom(r0));
1112
1113 RestoreLiveRegisters(codegen, locations);
1114 __ B(GetExitLabel());
1115 }
1116
1117 const char* GetDescription() const OVERRIDE { return "ReadBarrierForRootSlowPathARMVIXL"; }
1118
1119 private:
1120 const Location out_;
1121 const Location root_;
1122
1123 DISALLOW_COPY_AND_ASSIGN(ReadBarrierForRootSlowPathARMVIXL);
1124};
Scott Wakelingc34dba72016-10-03 10:14:44 +01001125
Scott Wakelingfe885462016-09-22 10:24:38 +01001126inline vixl32::Condition ARMCondition(IfCondition cond) {
1127 switch (cond) {
1128 case kCondEQ: return eq;
1129 case kCondNE: return ne;
1130 case kCondLT: return lt;
1131 case kCondLE: return le;
1132 case kCondGT: return gt;
1133 case kCondGE: return ge;
1134 case kCondB: return lo;
1135 case kCondBE: return ls;
1136 case kCondA: return hi;
1137 case kCondAE: return hs;
1138 }
1139 LOG(FATAL) << "Unreachable";
1140 UNREACHABLE();
1141}
1142
1143// Maps signed condition to unsigned condition.
1144inline vixl32::Condition ARMUnsignedCondition(IfCondition cond) {
1145 switch (cond) {
1146 case kCondEQ: return eq;
1147 case kCondNE: return ne;
1148 // Signed to unsigned.
1149 case kCondLT: return lo;
1150 case kCondLE: return ls;
1151 case kCondGT: return hi;
1152 case kCondGE: return hs;
1153 // Unsigned remain unchanged.
1154 case kCondB: return lo;
1155 case kCondBE: return ls;
1156 case kCondA: return hi;
1157 case kCondAE: return hs;
1158 }
1159 LOG(FATAL) << "Unreachable";
1160 UNREACHABLE();
1161}
1162
1163inline vixl32::Condition ARMFPCondition(IfCondition cond, bool gt_bias) {
1164 // The ARM condition codes can express all the necessary branches, see the
1165 // "Meaning (floating-point)" column in the table A8-1 of the ARMv7 reference manual.
1166 // There is no dex instruction or HIR that would need the missing conditions
1167 // "equal or unordered" or "not equal".
1168 switch (cond) {
1169 case kCondEQ: return eq;
1170 case kCondNE: return ne /* unordered */;
1171 case kCondLT: return gt_bias ? cc : lt /* unordered */;
1172 case kCondLE: return gt_bias ? ls : le /* unordered */;
1173 case kCondGT: return gt_bias ? hi /* unordered */ : gt;
1174 case kCondGE: return gt_bias ? cs /* unordered */ : ge;
1175 default:
1176 LOG(FATAL) << "UNREACHABLE";
1177 UNREACHABLE();
1178 }
1179}
1180
Scott Wakelingfe885462016-09-22 10:24:38 +01001181void CodeGeneratorARMVIXL::DumpCoreRegister(std::ostream& stream, int reg) const {
1182 stream << vixl32::Register(reg);
1183}
1184
1185void CodeGeneratorARMVIXL::DumpFloatingPointRegister(std::ostream& stream, int reg) const {
1186 stream << vixl32::SRegister(reg);
1187}
1188
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001189static uint32_t ComputeSRegisterListMask(const SRegisterList& regs) {
Scott Wakelingfe885462016-09-22 10:24:38 +01001190 uint32_t mask = 0;
1191 for (uint32_t i = regs.GetFirstSRegister().GetCode();
1192 i <= regs.GetLastSRegister().GetCode();
1193 ++i) {
1194 mask |= (1 << i);
1195 }
1196 return mask;
1197}
1198
Artem Serovd4cc5b22016-11-04 11:19:09 +00001199// Saves the register in the stack. Returns the size taken on stack.
1200size_t CodeGeneratorARMVIXL::SaveCoreRegister(size_t stack_index ATTRIBUTE_UNUSED,
1201 uint32_t reg_id ATTRIBUTE_UNUSED) {
1202 TODO_VIXL32(FATAL);
1203 return 0;
1204}
1205
1206// Restores the register from the stack. Returns the size taken on stack.
1207size_t CodeGeneratorARMVIXL::RestoreCoreRegister(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::SaveFloatingPointRegister(size_t stack_index ATTRIBUTE_UNUSED,
1214 uint32_t reg_id ATTRIBUTE_UNUSED) {
1215 TODO_VIXL32(FATAL);
1216 return 0;
1217}
1218
1219size_t CodeGeneratorARMVIXL::RestoreFloatingPointRegister(size_t stack_index ATTRIBUTE_UNUSED,
1220 uint32_t reg_id ATTRIBUTE_UNUSED) {
1221 TODO_VIXL32(FATAL);
1222 return 0;
Anton Kirilove28d9ae2016-10-25 18:17:23 +01001223}
1224
Scott Wakelingfe885462016-09-22 10:24:38 +01001225#undef __
1226
1227CodeGeneratorARMVIXL::CodeGeneratorARMVIXL(HGraph* graph,
1228 const ArmInstructionSetFeatures& isa_features,
1229 const CompilerOptions& compiler_options,
1230 OptimizingCompilerStats* stats)
1231 : CodeGenerator(graph,
1232 kNumberOfCoreRegisters,
1233 kNumberOfSRegisters,
1234 kNumberOfRegisterPairs,
1235 kCoreCalleeSaves.GetList(),
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001236 ComputeSRegisterListMask(kFpuCalleeSaves),
Scott Wakelingfe885462016-09-22 10:24:38 +01001237 compiler_options,
1238 stats),
1239 block_labels_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
Artem Serov551b28f2016-10-18 19:11:30 +01001240 jump_tables_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
Scott Wakelingfe885462016-09-22 10:24:38 +01001241 location_builder_(graph, this),
1242 instruction_visitor_(graph, this),
1243 move_resolver_(graph->GetArena(), this),
1244 assembler_(graph->GetArena()),
Artem Serovd4cc5b22016-11-04 11:19:09 +00001245 isa_features_(isa_features),
Artem Serovc5fcb442016-12-02 19:19:58 +00001246 uint32_literals_(std::less<uint32_t>(),
1247 graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
Artem Serovd4cc5b22016-11-04 11:19:09 +00001248 pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
Artem Serovc5fcb442016-12-02 19:19:58 +00001249 boot_image_string_patches_(StringReferenceValueComparator(),
1250 graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
Artem Serovd4cc5b22016-11-04 11:19:09 +00001251 pc_relative_string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
Artem Serovc5fcb442016-12-02 19:19:58 +00001252 boot_image_type_patches_(TypeReferenceValueComparator(),
1253 graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
1254 pc_relative_type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
1255 boot_image_address_patches_(std::less<uint32_t>(),
1256 graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
1257 jit_string_patches_(StringReferenceValueComparator(),
1258 graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
1259 jit_class_patches_(TypeReferenceValueComparator(),
1260 graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
Scott Wakelingfe885462016-09-22 10:24:38 +01001261 // Always save the LR register to mimic Quick.
1262 AddAllocatedRegister(Location::RegisterLocation(LR));
Alexandre Rames9c19bd62016-10-24 11:50:32 +01001263 // Give d14 and d15 as scratch registers to VIXL.
1264 // They are removed from the register allocator in `SetupBlockedRegisters()`.
1265 // TODO(VIXL): We need two scratch D registers for `EmitSwap` when swapping two double stack
1266 // slots. If that is sufficiently rare, and we have pressure on FP registers, we could instead
1267 // spill in `EmitSwap`. But if we actually are guaranteed to have 32 D registers, we could give
1268 // d30 and d31 to VIXL to avoid removing registers from the allocator. If that is the case, we may
1269 // also want to investigate giving those 14 other D registers to the allocator.
1270 GetVIXLAssembler()->GetScratchVRegisterList()->Combine(d14);
1271 GetVIXLAssembler()->GetScratchVRegisterList()->Combine(d15);
Scott Wakelingfe885462016-09-22 10:24:38 +01001272}
1273
Artem Serov551b28f2016-10-18 19:11:30 +01001274void JumpTableARMVIXL::EmitTable(CodeGeneratorARMVIXL* codegen) {
1275 uint32_t num_entries = switch_instr_->GetNumEntries();
1276 DCHECK_GE(num_entries, kPackedSwitchCompareJumpThreshold);
1277
1278 // We are about to use the assembler to place literals directly. Make sure we have enough
Scott Wakelingb77051e2016-11-21 19:46:00 +00001279 // underlying code buffer and we have generated a jump table of the right size, using
1280 // codegen->GetVIXLAssembler()->GetBuffer().Align();
Artem Serov0fb37192016-12-06 18:13:40 +00001281 ExactAssemblyScope aas(codegen->GetVIXLAssembler(),
1282 num_entries * sizeof(int32_t),
1283 CodeBufferCheckScope::kMaximumSize);
Artem Serov551b28f2016-10-18 19:11:30 +01001284 // TODO(VIXL): Check that using lower case bind is fine here.
1285 codegen->GetVIXLAssembler()->bind(&table_start_);
Artem Serov09a940d2016-11-11 16:15:11 +00001286 for (uint32_t i = 0; i < num_entries; i++) {
1287 codegen->GetVIXLAssembler()->place(bb_addresses_[i].get());
1288 }
1289}
1290
1291void JumpTableARMVIXL::FixTable(CodeGeneratorARMVIXL* codegen) {
1292 uint32_t num_entries = switch_instr_->GetNumEntries();
1293 DCHECK_GE(num_entries, kPackedSwitchCompareJumpThreshold);
1294
Artem Serov551b28f2016-10-18 19:11:30 +01001295 const ArenaVector<HBasicBlock*>& successors = switch_instr_->GetBlock()->GetSuccessors();
1296 for (uint32_t i = 0; i < num_entries; i++) {
1297 vixl32::Label* target_label = codegen->GetLabelOf(successors[i]);
1298 DCHECK(target_label->IsBound());
1299 int32_t jump_offset = target_label->GetLocation() - table_start_.GetLocation();
1300 // When doing BX to address we need to have lower bit set to 1 in T32.
1301 if (codegen->GetVIXLAssembler()->IsUsingT32()) {
1302 jump_offset++;
1303 }
1304 DCHECK_GT(jump_offset, std::numeric_limits<int32_t>::min());
1305 DCHECK_LE(jump_offset, std::numeric_limits<int32_t>::max());
Artem Serov09a940d2016-11-11 16:15:11 +00001306
Scott Wakelingb77051e2016-11-21 19:46:00 +00001307 bb_addresses_[i].get()->UpdateValue(jump_offset, codegen->GetVIXLAssembler()->GetBuffer());
Artem Serov551b28f2016-10-18 19:11:30 +01001308 }
1309}
1310
Artem Serov09a940d2016-11-11 16:15:11 +00001311void CodeGeneratorARMVIXL::FixJumpTables() {
Artem Serov551b28f2016-10-18 19:11:30 +01001312 for (auto&& jump_table : jump_tables_) {
Artem Serov09a940d2016-11-11 16:15:11 +00001313 jump_table->FixTable(this);
Artem Serov551b28f2016-10-18 19:11:30 +01001314 }
1315}
1316
Andreas Gampeca620d72016-11-08 08:09:33 -08001317#define __ reinterpret_cast<ArmVIXLAssembler*>(GetAssembler())->GetVIXLAssembler()-> // NOLINT
Scott Wakelingfe885462016-09-22 10:24:38 +01001318
1319void CodeGeneratorARMVIXL::Finalize(CodeAllocator* allocator) {
Artem Serov09a940d2016-11-11 16:15:11 +00001320 FixJumpTables();
Scott Wakelingfe885462016-09-22 10:24:38 +01001321 GetAssembler()->FinalizeCode();
1322 CodeGenerator::Finalize(allocator);
1323}
1324
1325void CodeGeneratorARMVIXL::SetupBlockedRegisters() const {
Scott Wakelingfe885462016-09-22 10:24:38 +01001326 // Stack register, LR and PC are always reserved.
1327 blocked_core_registers_[SP] = true;
1328 blocked_core_registers_[LR] = true;
1329 blocked_core_registers_[PC] = true;
1330
1331 // Reserve thread register.
1332 blocked_core_registers_[TR] = true;
1333
1334 // Reserve temp register.
1335 blocked_core_registers_[IP] = true;
1336
Alexandre Rames9c19bd62016-10-24 11:50:32 +01001337 // Registers s28-s31 (d14-d15) are left to VIXL for scratch registers.
1338 // (They are given to the `MacroAssembler` in `CodeGeneratorARMVIXL::CodeGeneratorARMVIXL`.)
1339 blocked_fpu_registers_[28] = true;
1340 blocked_fpu_registers_[29] = true;
1341 blocked_fpu_registers_[30] = true;
1342 blocked_fpu_registers_[31] = true;
1343
Scott Wakelingfe885462016-09-22 10:24:38 +01001344 if (GetGraph()->IsDebuggable()) {
1345 // Stubs do not save callee-save floating point registers. If the graph
1346 // is debuggable, we need to deal with these registers differently. For
1347 // now, just block them.
1348 for (uint32_t i = kFpuCalleeSaves.GetFirstSRegister().GetCode();
1349 i <= kFpuCalleeSaves.GetLastSRegister().GetCode();
1350 ++i) {
1351 blocked_fpu_registers_[i] = true;
1352 }
1353 }
Scott Wakelingfe885462016-09-22 10:24:38 +01001354}
1355
Scott Wakelingfe885462016-09-22 10:24:38 +01001356InstructionCodeGeneratorARMVIXL::InstructionCodeGeneratorARMVIXL(HGraph* graph,
1357 CodeGeneratorARMVIXL* codegen)
1358 : InstructionCodeGenerator(graph, codegen),
1359 assembler_(codegen->GetAssembler()),
1360 codegen_(codegen) {}
1361
1362void CodeGeneratorARMVIXL::ComputeSpillMask() {
1363 core_spill_mask_ = allocated_registers_.GetCoreRegisters() & core_callee_save_mask_;
1364 DCHECK_NE(core_spill_mask_, 0u) << "At least the return address register must be saved";
1365 // There is no easy instruction to restore just the PC on thumb2. We spill and
1366 // restore another arbitrary register.
1367 core_spill_mask_ |= (1 << kCoreAlwaysSpillRegister.GetCode());
1368 fpu_spill_mask_ = allocated_registers_.GetFloatingPointRegisters() & fpu_callee_save_mask_;
1369 // We use vpush and vpop for saving and restoring floating point registers, which take
1370 // a SRegister and the number of registers to save/restore after that SRegister. We
1371 // therefore update the `fpu_spill_mask_` to also contain those registers not allocated,
1372 // but in the range.
1373 if (fpu_spill_mask_ != 0) {
1374 uint32_t least_significant_bit = LeastSignificantBit(fpu_spill_mask_);
1375 uint32_t most_significant_bit = MostSignificantBit(fpu_spill_mask_);
1376 for (uint32_t i = least_significant_bit + 1 ; i < most_significant_bit; ++i) {
1377 fpu_spill_mask_ |= (1 << i);
1378 }
1379 }
1380}
1381
1382void CodeGeneratorARMVIXL::GenerateFrameEntry() {
1383 bool skip_overflow_check =
1384 IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kArm);
1385 DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks());
1386 __ Bind(&frame_entry_label_);
1387
1388 if (HasEmptyFrame()) {
1389 return;
1390 }
1391
Scott Wakelingfe885462016-09-22 10:24:38 +01001392 if (!skip_overflow_check) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001393 UseScratchRegisterScope temps(GetVIXLAssembler());
1394 vixl32::Register temp = temps.Acquire();
Anton Kirilov644032c2016-12-06 17:51:43 +00001395 __ Sub(temp, sp, Operand::From(GetStackOverflowReservedBytes(kArm)));
Scott Wakelingfe885462016-09-22 10:24:38 +01001396 // The load must immediately precede RecordPcInfo.
Artem Serov0fb37192016-12-06 18:13:40 +00001397 ExactAssemblyScope aas(GetVIXLAssembler(),
1398 vixl32::kMaxInstructionSizeInBytes,
1399 CodeBufferCheckScope::kMaximumSize);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001400 __ ldr(temp, MemOperand(temp));
1401 RecordPcInfo(nullptr, 0);
Scott Wakelingfe885462016-09-22 10:24:38 +01001402 }
1403
1404 __ Push(RegisterList(core_spill_mask_));
1405 GetAssembler()->cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(core_spill_mask_));
1406 GetAssembler()->cfi().RelOffsetForMany(DWARFReg(kMethodRegister),
1407 0,
1408 core_spill_mask_,
1409 kArmWordSize);
1410 if (fpu_spill_mask_ != 0) {
1411 uint32_t first = LeastSignificantBit(fpu_spill_mask_);
1412
1413 // Check that list is contiguous.
1414 DCHECK_EQ(fpu_spill_mask_ >> CTZ(fpu_spill_mask_), ~0u >> (32 - POPCOUNT(fpu_spill_mask_)));
1415
1416 __ Vpush(SRegisterList(vixl32::SRegister(first), POPCOUNT(fpu_spill_mask_)));
1417 GetAssembler()->cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(fpu_spill_mask_));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001418 GetAssembler()->cfi().RelOffsetForMany(DWARFReg(s0), 0, fpu_spill_mask_, kArmWordSize);
Scott Wakelingfe885462016-09-22 10:24:38 +01001419 }
Scott Wakelingbffdc702016-12-07 17:46:03 +00001420
1421 if (GetGraph()->HasShouldDeoptimizeFlag()) {
1422 UseScratchRegisterScope temps(GetVIXLAssembler());
1423 vixl32::Register temp = temps.Acquire();
1424 // Initialize should_deoptimize flag to 0.
1425 __ Mov(temp, 0);
1426 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, -kShouldDeoptimizeFlagSize);
1427 }
1428
Scott Wakelingfe885462016-09-22 10:24:38 +01001429 int adjust = GetFrameSize() - FrameEntrySpillSize();
1430 __ Sub(sp, sp, adjust);
1431 GetAssembler()->cfi().AdjustCFAOffset(adjust);
Scott Wakelingbffdc702016-12-07 17:46:03 +00001432
1433 // Save the current method if we need it. Note that we do not
1434 // do this in HCurrentMethod, as the instruction might have been removed
1435 // in the SSA graph.
1436 if (RequiresCurrentMethod()) {
1437 GetAssembler()->StoreToOffset(kStoreWord, kMethodRegister, sp, 0);
1438 }
Scott Wakelingfe885462016-09-22 10:24:38 +01001439}
1440
1441void CodeGeneratorARMVIXL::GenerateFrameExit() {
1442 if (HasEmptyFrame()) {
1443 __ Bx(lr);
1444 return;
1445 }
1446 GetAssembler()->cfi().RememberState();
1447 int adjust = GetFrameSize() - FrameEntrySpillSize();
1448 __ Add(sp, sp, adjust);
1449 GetAssembler()->cfi().AdjustCFAOffset(-adjust);
1450 if (fpu_spill_mask_ != 0) {
1451 uint32_t first = LeastSignificantBit(fpu_spill_mask_);
1452
1453 // Check that list is contiguous.
1454 DCHECK_EQ(fpu_spill_mask_ >> CTZ(fpu_spill_mask_), ~0u >> (32 - POPCOUNT(fpu_spill_mask_)));
1455
1456 __ Vpop(SRegisterList(vixl32::SRegister(first), POPCOUNT(fpu_spill_mask_)));
1457 GetAssembler()->cfi().AdjustCFAOffset(
1458 -static_cast<int>(kArmWordSize) * POPCOUNT(fpu_spill_mask_));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001459 GetAssembler()->cfi().RestoreMany(DWARFReg(vixl32::SRegister(0)), fpu_spill_mask_);
Scott Wakelingfe885462016-09-22 10:24:38 +01001460 }
1461 // Pop LR into PC to return.
1462 DCHECK_NE(core_spill_mask_ & (1 << kLrCode), 0U);
1463 uint32_t pop_mask = (core_spill_mask_ & (~(1 << kLrCode))) | 1 << kPcCode;
1464 __ Pop(RegisterList(pop_mask));
1465 GetAssembler()->cfi().RestoreState();
1466 GetAssembler()->cfi().DefCFAOffset(GetFrameSize());
1467}
1468
1469void CodeGeneratorARMVIXL::Bind(HBasicBlock* block) {
1470 __ Bind(GetLabelOf(block));
1471}
1472
Artem Serovd4cc5b22016-11-04 11:19:09 +00001473Location InvokeDexCallingConventionVisitorARMVIXL::GetNextLocation(Primitive::Type type) {
1474 switch (type) {
1475 case Primitive::kPrimBoolean:
1476 case Primitive::kPrimByte:
1477 case Primitive::kPrimChar:
1478 case Primitive::kPrimShort:
1479 case Primitive::kPrimInt:
1480 case Primitive::kPrimNot: {
1481 uint32_t index = gp_index_++;
1482 uint32_t stack_index = stack_index_++;
1483 if (index < calling_convention.GetNumberOfRegisters()) {
1484 return LocationFrom(calling_convention.GetRegisterAt(index));
1485 } else {
1486 return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index));
1487 }
1488 }
1489
1490 case Primitive::kPrimLong: {
1491 uint32_t index = gp_index_;
1492 uint32_t stack_index = stack_index_;
1493 gp_index_ += 2;
1494 stack_index_ += 2;
1495 if (index + 1 < calling_convention.GetNumberOfRegisters()) {
1496 if (calling_convention.GetRegisterAt(index).Is(r1)) {
1497 // Skip R1, and use R2_R3 instead.
1498 gp_index_++;
1499 index++;
1500 }
1501 }
1502 if (index + 1 < calling_convention.GetNumberOfRegisters()) {
1503 DCHECK_EQ(calling_convention.GetRegisterAt(index).GetCode() + 1,
1504 calling_convention.GetRegisterAt(index + 1).GetCode());
1505
1506 return LocationFrom(calling_convention.GetRegisterAt(index),
1507 calling_convention.GetRegisterAt(index + 1));
1508 } else {
1509 return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index));
1510 }
1511 }
1512
1513 case Primitive::kPrimFloat: {
1514 uint32_t stack_index = stack_index_++;
1515 if (float_index_ % 2 == 0) {
1516 float_index_ = std::max(double_index_, float_index_);
1517 }
1518 if (float_index_ < calling_convention.GetNumberOfFpuRegisters()) {
1519 return LocationFrom(calling_convention.GetFpuRegisterAt(float_index_++));
1520 } else {
1521 return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index));
1522 }
1523 }
1524
1525 case Primitive::kPrimDouble: {
1526 double_index_ = std::max(double_index_, RoundUp(float_index_, 2));
1527 uint32_t stack_index = stack_index_;
1528 stack_index_ += 2;
1529 if (double_index_ + 1 < calling_convention.GetNumberOfFpuRegisters()) {
1530 uint32_t index = double_index_;
1531 double_index_ += 2;
1532 Location result = LocationFrom(
1533 calling_convention.GetFpuRegisterAt(index),
1534 calling_convention.GetFpuRegisterAt(index + 1));
1535 DCHECK(ExpectedPairLayout(result));
1536 return result;
1537 } else {
1538 return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index));
1539 }
1540 }
1541
1542 case Primitive::kPrimVoid:
1543 LOG(FATAL) << "Unexpected parameter type " << type;
1544 break;
1545 }
1546 return Location::NoLocation();
1547}
1548
1549Location InvokeDexCallingConventionVisitorARMVIXL::GetReturnLocation(Primitive::Type type) const {
1550 switch (type) {
1551 case Primitive::kPrimBoolean:
1552 case Primitive::kPrimByte:
1553 case Primitive::kPrimChar:
1554 case Primitive::kPrimShort:
1555 case Primitive::kPrimInt:
1556 case Primitive::kPrimNot: {
1557 return LocationFrom(r0);
1558 }
1559
1560 case Primitive::kPrimFloat: {
1561 return LocationFrom(s0);
1562 }
1563
1564 case Primitive::kPrimLong: {
1565 return LocationFrom(r0, r1);
1566 }
1567
1568 case Primitive::kPrimDouble: {
1569 return LocationFrom(s0, s1);
1570 }
1571
1572 case Primitive::kPrimVoid:
1573 return Location::NoLocation();
1574 }
1575
1576 UNREACHABLE();
1577}
1578
1579Location InvokeDexCallingConventionVisitorARMVIXL::GetMethodLocation() const {
1580 return LocationFrom(kMethodRegister);
1581}
1582
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001583void CodeGeneratorARMVIXL::Move32(Location destination, Location source) {
1584 if (source.Equals(destination)) {
1585 return;
1586 }
1587 if (destination.IsRegister()) {
1588 if (source.IsRegister()) {
1589 __ Mov(RegisterFrom(destination), RegisterFrom(source));
1590 } else if (source.IsFpuRegister()) {
1591 __ Vmov(RegisterFrom(destination), SRegisterFrom(source));
1592 } else {
1593 GetAssembler()->LoadFromOffset(kLoadWord,
1594 RegisterFrom(destination),
1595 sp,
1596 source.GetStackIndex());
1597 }
1598 } else if (destination.IsFpuRegister()) {
1599 if (source.IsRegister()) {
1600 __ Vmov(SRegisterFrom(destination), RegisterFrom(source));
1601 } else if (source.IsFpuRegister()) {
1602 __ Vmov(SRegisterFrom(destination), SRegisterFrom(source));
1603 } else {
1604 GetAssembler()->LoadSFromOffset(SRegisterFrom(destination), sp, source.GetStackIndex());
1605 }
1606 } else {
1607 DCHECK(destination.IsStackSlot()) << destination;
1608 if (source.IsRegister()) {
1609 GetAssembler()->StoreToOffset(kStoreWord,
1610 RegisterFrom(source),
1611 sp,
1612 destination.GetStackIndex());
1613 } else if (source.IsFpuRegister()) {
1614 GetAssembler()->StoreSToOffset(SRegisterFrom(source), sp, destination.GetStackIndex());
1615 } else {
1616 DCHECK(source.IsStackSlot()) << source;
1617 UseScratchRegisterScope temps(GetVIXLAssembler());
1618 vixl32::Register temp = temps.Acquire();
1619 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, source.GetStackIndex());
1620 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
1621 }
1622 }
1623}
1624
Artem Serovcfbe9132016-10-14 15:58:56 +01001625void CodeGeneratorARMVIXL::MoveConstant(Location location, int32_t value) {
1626 DCHECK(location.IsRegister());
1627 __ Mov(RegisterFrom(location), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01001628}
1629
1630void CodeGeneratorARMVIXL::MoveLocation(Location dst, Location src, Primitive::Type dst_type) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001631 // TODO(VIXL): Maybe refactor to have the 'move' implementation here and use it in
1632 // `ParallelMoveResolverARMVIXL::EmitMove`, as is done in the `arm64` backend.
1633 HParallelMove move(GetGraph()->GetArena());
1634 move.AddMove(src, dst, dst_type, nullptr);
1635 GetMoveResolver()->EmitNativeCode(&move);
Scott Wakelingfe885462016-09-22 10:24:38 +01001636}
1637
Artem Serovcfbe9132016-10-14 15:58:56 +01001638void CodeGeneratorARMVIXL::AddLocationAsTemp(Location location, LocationSummary* locations) {
1639 if (location.IsRegister()) {
1640 locations->AddTemp(location);
1641 } else if (location.IsRegisterPair()) {
1642 locations->AddTemp(LocationFrom(LowRegisterFrom(location)));
1643 locations->AddTemp(LocationFrom(HighRegisterFrom(location)));
1644 } else {
1645 UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location;
1646 }
Scott Wakelingfe885462016-09-22 10:24:38 +01001647}
1648
1649void CodeGeneratorARMVIXL::InvokeRuntime(QuickEntrypointEnum entrypoint,
1650 HInstruction* instruction,
1651 uint32_t dex_pc,
1652 SlowPathCode* slow_path) {
1653 ValidateInvokeRuntime(entrypoint, instruction, slow_path);
Alexandre Rames374ddf32016-11-04 10:40:49 +00001654 __ Ldr(lr, MemOperand(tr, GetThreadOffset<kArmPointerSize>(entrypoint).Int32Value()));
1655 // Ensure the pc position is recorded immediately after the `blx` instruction.
1656 // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
Artem Serov0fb37192016-12-06 18:13:40 +00001657 ExactAssemblyScope aas(GetVIXLAssembler(),
1658 vixl32::k16BitT32InstructionSizeInBytes,
1659 CodeBufferCheckScope::kExactSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00001660 __ blx(lr);
Scott Wakelingfe885462016-09-22 10:24:38 +01001661 if (EntrypointRequiresStackMap(entrypoint)) {
1662 RecordPcInfo(instruction, dex_pc, slow_path);
1663 }
1664}
1665
1666void CodeGeneratorARMVIXL::InvokeRuntimeWithoutRecordingPcInfo(int32_t entry_point_offset,
1667 HInstruction* instruction,
1668 SlowPathCode* slow_path) {
1669 ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction, slow_path);
Alexandre Rames374ddf32016-11-04 10:40:49 +00001670 __ Ldr(lr, MemOperand(tr, entry_point_offset));
Scott Wakelingfe885462016-09-22 10:24:38 +01001671 __ Blx(lr);
1672}
1673
Scott Wakelingfe885462016-09-22 10:24:38 +01001674void InstructionCodeGeneratorARMVIXL::HandleGoto(HInstruction* got, HBasicBlock* successor) {
1675 DCHECK(!successor->IsExitBlock());
1676 HBasicBlock* block = got->GetBlock();
1677 HInstruction* previous = got->GetPrevious();
1678 HLoopInformation* info = block->GetLoopInformation();
1679
1680 if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) {
1681 codegen_->ClearSpillSlotsFromLoopPhisInStackMap(info->GetSuspendCheck());
1682 GenerateSuspendCheck(info->GetSuspendCheck(), successor);
1683 return;
1684 }
1685 if (block->IsEntryBlock() && (previous != nullptr) && previous->IsSuspendCheck()) {
1686 GenerateSuspendCheck(previous->AsSuspendCheck(), nullptr);
1687 }
1688 if (!codegen_->GoesToNextBlock(block, successor)) {
1689 __ B(codegen_->GetLabelOf(successor));
1690 }
1691}
1692
1693void LocationsBuilderARMVIXL::VisitGoto(HGoto* got) {
1694 got->SetLocations(nullptr);
1695}
1696
1697void InstructionCodeGeneratorARMVIXL::VisitGoto(HGoto* got) {
1698 HandleGoto(got, got->GetSuccessor());
1699}
1700
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001701void LocationsBuilderARMVIXL::VisitTryBoundary(HTryBoundary* try_boundary) {
1702 try_boundary->SetLocations(nullptr);
1703}
1704
1705void InstructionCodeGeneratorARMVIXL::VisitTryBoundary(HTryBoundary* try_boundary) {
1706 HBasicBlock* successor = try_boundary->GetNormalFlowSuccessor();
1707 if (!successor->IsExitBlock()) {
1708 HandleGoto(try_boundary, successor);
1709 }
1710}
1711
Scott Wakelingfe885462016-09-22 10:24:38 +01001712void LocationsBuilderARMVIXL::VisitExit(HExit* exit) {
1713 exit->SetLocations(nullptr);
1714}
1715
1716void InstructionCodeGeneratorARMVIXL::VisitExit(HExit* exit ATTRIBUTE_UNUSED) {
1717}
1718
1719void InstructionCodeGeneratorARMVIXL::GenerateVcmp(HInstruction* instruction) {
1720 Primitive::Type type = instruction->InputAt(0)->GetType();
1721 Location lhs_loc = instruction->GetLocations()->InAt(0);
1722 Location rhs_loc = instruction->GetLocations()->InAt(1);
1723 if (rhs_loc.IsConstant()) {
1724 // 0.0 is the only immediate that can be encoded directly in
1725 // a VCMP instruction.
1726 //
1727 // Both the JLS (section 15.20.1) and the JVMS (section 6.5)
1728 // specify that in a floating-point comparison, positive zero
1729 // and negative zero are considered equal, so we can use the
1730 // literal 0.0 for both cases here.
1731 //
1732 // Note however that some methods (Float.equal, Float.compare,
1733 // Float.compareTo, Double.equal, Double.compare,
1734 // Double.compareTo, Math.max, Math.min, StrictMath.max,
1735 // StrictMath.min) consider 0.0 to be (strictly) greater than
1736 // -0.0. So if we ever translate calls to these methods into a
1737 // HCompare instruction, we must handle the -0.0 case with
1738 // care here.
1739 DCHECK(rhs_loc.GetConstant()->IsArithmeticZero());
1740 if (type == Primitive::kPrimFloat) {
1741 __ Vcmp(F32, InputSRegisterAt(instruction, 0), 0.0);
1742 } else {
1743 DCHECK_EQ(type, Primitive::kPrimDouble);
Scott Wakelingc34dba72016-10-03 10:14:44 +01001744 __ Vcmp(F64, DRegisterFrom(lhs_loc), 0.0);
Scott Wakelingfe885462016-09-22 10:24:38 +01001745 }
1746 } else {
1747 if (type == Primitive::kPrimFloat) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001748 __ Vcmp(InputSRegisterAt(instruction, 0), InputSRegisterAt(instruction, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01001749 } else {
1750 DCHECK_EQ(type, Primitive::kPrimDouble);
Scott Wakelingc34dba72016-10-03 10:14:44 +01001751 __ Vcmp(DRegisterFrom(lhs_loc), DRegisterFrom(rhs_loc));
Scott Wakelingfe885462016-09-22 10:24:38 +01001752 }
1753 }
1754}
1755
1756void InstructionCodeGeneratorARMVIXL::GenerateFPJumps(HCondition* cond,
1757 vixl32::Label* true_label,
1758 vixl32::Label* false_label ATTRIBUTE_UNUSED) {
1759 // To branch on the result of the FP compare we transfer FPSCR to APSR (encoded as PC in VMRS).
1760 __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
1761 __ B(ARMFPCondition(cond->GetCondition(), cond->IsGtBias()), true_label);
1762}
1763
1764void InstructionCodeGeneratorARMVIXL::GenerateLongComparesAndJumps(HCondition* cond,
1765 vixl32::Label* true_label,
1766 vixl32::Label* false_label) {
1767 LocationSummary* locations = cond->GetLocations();
1768 Location left = locations->InAt(0);
1769 Location right = locations->InAt(1);
1770 IfCondition if_cond = cond->GetCondition();
1771
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001772 vixl32::Register left_high = HighRegisterFrom(left);
1773 vixl32::Register left_low = LowRegisterFrom(left);
Scott Wakelingfe885462016-09-22 10:24:38 +01001774 IfCondition true_high_cond = if_cond;
1775 IfCondition false_high_cond = cond->GetOppositeCondition();
1776 vixl32::Condition final_condition = ARMUnsignedCondition(if_cond); // unsigned on lower part
1777
1778 // Set the conditions for the test, remembering that == needs to be
1779 // decided using the low words.
1780 // TODO: consider avoiding jumps with temporary and CMP low+SBC high
1781 switch (if_cond) {
1782 case kCondEQ:
1783 case kCondNE:
1784 // Nothing to do.
1785 break;
1786 case kCondLT:
1787 false_high_cond = kCondGT;
1788 break;
1789 case kCondLE:
1790 true_high_cond = kCondLT;
1791 break;
1792 case kCondGT:
1793 false_high_cond = kCondLT;
1794 break;
1795 case kCondGE:
1796 true_high_cond = kCondGT;
1797 break;
1798 case kCondB:
1799 false_high_cond = kCondA;
1800 break;
1801 case kCondBE:
1802 true_high_cond = kCondB;
1803 break;
1804 case kCondA:
1805 false_high_cond = kCondB;
1806 break;
1807 case kCondAE:
1808 true_high_cond = kCondA;
1809 break;
1810 }
1811 if (right.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00001812 int64_t value = Int64ConstantFrom(right);
Scott Wakelingfe885462016-09-22 10:24:38 +01001813 int32_t val_low = Low32Bits(value);
1814 int32_t val_high = High32Bits(value);
1815
1816 __ Cmp(left_high, val_high);
1817 if (if_cond == kCondNE) {
1818 __ B(ARMCondition(true_high_cond), true_label);
1819 } else if (if_cond == kCondEQ) {
1820 __ B(ARMCondition(false_high_cond), false_label);
1821 } else {
1822 __ B(ARMCondition(true_high_cond), true_label);
1823 __ B(ARMCondition(false_high_cond), false_label);
1824 }
1825 // Must be equal high, so compare the lows.
1826 __ Cmp(left_low, val_low);
1827 } else {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001828 vixl32::Register right_high = HighRegisterFrom(right);
1829 vixl32::Register right_low = LowRegisterFrom(right);
Scott Wakelingfe885462016-09-22 10:24:38 +01001830
1831 __ Cmp(left_high, right_high);
1832 if (if_cond == kCondNE) {
1833 __ B(ARMCondition(true_high_cond), true_label);
1834 } else if (if_cond == kCondEQ) {
1835 __ B(ARMCondition(false_high_cond), false_label);
1836 } else {
1837 __ B(ARMCondition(true_high_cond), true_label);
1838 __ B(ARMCondition(false_high_cond), false_label);
1839 }
1840 // Must be equal high, so compare the lows.
1841 __ Cmp(left_low, right_low);
1842 }
1843 // The last comparison might be unsigned.
1844 // TODO: optimize cases where this is always true/false
1845 __ B(final_condition, true_label);
1846}
1847
1848void InstructionCodeGeneratorARMVIXL::GenerateCompareTestAndBranch(HCondition* condition,
1849 vixl32::Label* true_target_in,
1850 vixl32::Label* false_target_in) {
1851 // Generated branching requires both targets to be explicit. If either of the
1852 // targets is nullptr (fallthrough) use and bind `fallthrough` instead.
1853 vixl32::Label fallthrough;
1854 vixl32::Label* true_target = (true_target_in == nullptr) ? &fallthrough : true_target_in;
1855 vixl32::Label* false_target = (false_target_in == nullptr) ? &fallthrough : false_target_in;
1856
1857 Primitive::Type type = condition->InputAt(0)->GetType();
1858 switch (type) {
1859 case Primitive::kPrimLong:
1860 GenerateLongComparesAndJumps(condition, true_target, false_target);
1861 break;
1862 case Primitive::kPrimFloat:
1863 case Primitive::kPrimDouble:
1864 GenerateVcmp(condition);
1865 GenerateFPJumps(condition, true_target, false_target);
1866 break;
1867 default:
1868 LOG(FATAL) << "Unexpected compare type " << type;
1869 }
1870
1871 if (false_target != &fallthrough) {
1872 __ B(false_target);
1873 }
1874
1875 if (true_target_in == nullptr || false_target_in == nullptr) {
1876 __ Bind(&fallthrough);
1877 }
1878}
1879
1880void InstructionCodeGeneratorARMVIXL::GenerateTestAndBranch(HInstruction* instruction,
1881 size_t condition_input_index,
1882 vixl32::Label* true_target,
xueliang.zhongf51bc622016-11-04 09:23:32 +00001883 vixl32::Label* false_target,
1884 bool far_target) {
Scott Wakelingfe885462016-09-22 10:24:38 +01001885 HInstruction* cond = instruction->InputAt(condition_input_index);
1886
1887 if (true_target == nullptr && false_target == nullptr) {
1888 // Nothing to do. The code always falls through.
1889 return;
1890 } else if (cond->IsIntConstant()) {
1891 // Constant condition, statically compared against "true" (integer value 1).
1892 if (cond->AsIntConstant()->IsTrue()) {
1893 if (true_target != nullptr) {
1894 __ B(true_target);
1895 }
1896 } else {
Anton Kirilov644032c2016-12-06 17:51:43 +00001897 DCHECK(cond->AsIntConstant()->IsFalse()) << Int32ConstantFrom(cond);
Scott Wakelingfe885462016-09-22 10:24:38 +01001898 if (false_target != nullptr) {
1899 __ B(false_target);
1900 }
1901 }
1902 return;
1903 }
1904
1905 // The following code generates these patterns:
1906 // (1) true_target == nullptr && false_target != nullptr
1907 // - opposite condition true => branch to false_target
1908 // (2) true_target != nullptr && false_target == nullptr
1909 // - condition true => branch to true_target
1910 // (3) true_target != nullptr && false_target != nullptr
1911 // - condition true => branch to true_target
1912 // - branch to false_target
1913 if (IsBooleanValueOrMaterializedCondition(cond)) {
1914 // Condition has been materialized, compare the output to 0.
1915 if (kIsDebugBuild) {
1916 Location cond_val = instruction->GetLocations()->InAt(condition_input_index);
1917 DCHECK(cond_val.IsRegister());
1918 }
1919 if (true_target == nullptr) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00001920 __ CompareAndBranchIfZero(InputRegisterAt(instruction, condition_input_index),
1921 false_target,
1922 far_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01001923 } else {
xueliang.zhongf51bc622016-11-04 09:23:32 +00001924 __ CompareAndBranchIfNonZero(InputRegisterAt(instruction, condition_input_index),
1925 true_target,
1926 far_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01001927 }
1928 } else {
1929 // Condition has not been materialized. Use its inputs as the comparison and
1930 // its condition as the branch condition.
1931 HCondition* condition = cond->AsCondition();
1932
1933 // If this is a long or FP comparison that has been folded into
1934 // the HCondition, generate the comparison directly.
1935 Primitive::Type type = condition->InputAt(0)->GetType();
1936 if (type == Primitive::kPrimLong || Primitive::IsFloatingPointType(type)) {
1937 GenerateCompareTestAndBranch(condition, true_target, false_target);
1938 return;
1939 }
1940
1941 LocationSummary* locations = cond->GetLocations();
1942 DCHECK(locations->InAt(0).IsRegister());
1943 vixl32::Register left = InputRegisterAt(cond, 0);
1944 Location right = locations->InAt(1);
1945 if (right.IsRegister()) {
1946 __ Cmp(left, InputRegisterAt(cond, 1));
1947 } else {
1948 DCHECK(right.IsConstant());
1949 __ Cmp(left, CodeGenerator::GetInt32ValueOf(right.GetConstant()));
1950 }
1951 if (true_target == nullptr) {
1952 __ B(ARMCondition(condition->GetOppositeCondition()), false_target);
1953 } else {
1954 __ B(ARMCondition(condition->GetCondition()), true_target);
1955 }
1956 }
1957
1958 // If neither branch falls through (case 3), the conditional branch to `true_target`
1959 // was already emitted (case 2) and we need to emit a jump to `false_target`.
1960 if (true_target != nullptr && false_target != nullptr) {
1961 __ B(false_target);
1962 }
1963}
1964
1965void LocationsBuilderARMVIXL::VisitIf(HIf* if_instr) {
1966 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr);
1967 if (IsBooleanValueOrMaterializedCondition(if_instr->InputAt(0))) {
1968 locations->SetInAt(0, Location::RequiresRegister());
1969 }
1970}
1971
1972void InstructionCodeGeneratorARMVIXL::VisitIf(HIf* if_instr) {
1973 HBasicBlock* true_successor = if_instr->IfTrueSuccessor();
1974 HBasicBlock* false_successor = if_instr->IfFalseSuccessor();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001975 vixl32::Label* true_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), true_successor) ?
1976 nullptr : codegen_->GetLabelOf(true_successor);
1977 vixl32::Label* false_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), false_successor) ?
1978 nullptr : codegen_->GetLabelOf(false_successor);
Scott Wakelingfe885462016-09-22 10:24:38 +01001979 GenerateTestAndBranch(if_instr, /* condition_input_index */ 0, true_target, false_target);
1980}
1981
Scott Wakelingc34dba72016-10-03 10:14:44 +01001982void LocationsBuilderARMVIXL::VisitDeoptimize(HDeoptimize* deoptimize) {
1983 LocationSummary* locations = new (GetGraph()->GetArena())
1984 LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath);
1985 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
1986 if (IsBooleanValueOrMaterializedCondition(deoptimize->InputAt(0))) {
1987 locations->SetInAt(0, Location::RequiresRegister());
1988 }
1989}
1990
1991void InstructionCodeGeneratorARMVIXL::VisitDeoptimize(HDeoptimize* deoptimize) {
1992 SlowPathCodeARMVIXL* slow_path =
1993 deopt_slow_paths_.NewSlowPath<DeoptimizationSlowPathARMVIXL>(deoptimize);
1994 GenerateTestAndBranch(deoptimize,
1995 /* condition_input_index */ 0,
1996 slow_path->GetEntryLabel(),
1997 /* false_target */ nullptr);
1998}
1999
Artem Serovd4cc5b22016-11-04 11:19:09 +00002000void LocationsBuilderARMVIXL::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
2001 LocationSummary* locations = new (GetGraph()->GetArena())
2002 LocationSummary(flag, LocationSummary::kNoCall);
2003 locations->SetOut(Location::RequiresRegister());
2004}
2005
2006void InstructionCodeGeneratorARMVIXL::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
2007 GetAssembler()->LoadFromOffset(kLoadWord,
2008 OutputRegister(flag),
2009 sp,
2010 codegen_->GetStackOffsetOfShouldDeoptimizeFlag());
2011}
2012
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002013void LocationsBuilderARMVIXL::VisitSelect(HSelect* select) {
2014 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select);
2015 if (Primitive::IsFloatingPointType(select->GetType())) {
2016 locations->SetInAt(0, Location::RequiresFpuRegister());
2017 locations->SetInAt(1, Location::RequiresFpuRegister());
2018 } else {
2019 locations->SetInAt(0, Location::RequiresRegister());
2020 locations->SetInAt(1, Location::RequiresRegister());
2021 }
2022 if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) {
2023 locations->SetInAt(2, Location::RequiresRegister());
2024 }
2025 locations->SetOut(Location::SameAsFirstInput());
2026}
2027
2028void InstructionCodeGeneratorARMVIXL::VisitSelect(HSelect* select) {
2029 LocationSummary* locations = select->GetLocations();
2030 vixl32::Label false_target;
2031 GenerateTestAndBranch(select,
2032 /* condition_input_index */ 2,
2033 /* true_target */ nullptr,
xueliang.zhongf51bc622016-11-04 09:23:32 +00002034 &false_target,
2035 /* far_target */ false);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002036 codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType());
2037 __ Bind(&false_target);
2038}
2039
Artem Serov551b28f2016-10-18 19:11:30 +01002040void LocationsBuilderARMVIXL::VisitNativeDebugInfo(HNativeDebugInfo* info) {
2041 new (GetGraph()->GetArena()) LocationSummary(info);
2042}
2043
2044void InstructionCodeGeneratorARMVIXL::VisitNativeDebugInfo(HNativeDebugInfo*) {
2045 // MaybeRecordNativeDebugInfo is already called implicitly in CodeGenerator::Compile.
2046}
2047
Scott Wakelingfe885462016-09-22 10:24:38 +01002048void CodeGeneratorARMVIXL::GenerateNop() {
2049 __ Nop();
2050}
2051
2052void LocationsBuilderARMVIXL::HandleCondition(HCondition* cond) {
2053 LocationSummary* locations =
2054 new (GetGraph()->GetArena()) LocationSummary(cond, LocationSummary::kNoCall);
2055 // Handle the long/FP comparisons made in instruction simplification.
2056 switch (cond->InputAt(0)->GetType()) {
2057 case Primitive::kPrimLong:
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::kOutputOverlap);
2062 }
2063 break;
2064
Scott Wakelingfe885462016-09-22 10:24:38 +01002065 case Primitive::kPrimFloat:
2066 case Primitive::kPrimDouble:
2067 locations->SetInAt(0, Location::RequiresFpuRegister());
Artem Serov657022c2016-11-23 14:19:38 +00002068 locations->SetInAt(1, ArithmeticZeroOrFpuRegister(cond->InputAt(1)));
Scott Wakelingfe885462016-09-22 10:24:38 +01002069 if (!cond->IsEmittedAtUseSite()) {
2070 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2071 }
2072 break;
2073
2074 default:
2075 locations->SetInAt(0, Location::RequiresRegister());
2076 locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
2077 if (!cond->IsEmittedAtUseSite()) {
2078 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2079 }
2080 }
2081}
2082
2083void InstructionCodeGeneratorARMVIXL::HandleCondition(HCondition* cond) {
2084 if (cond->IsEmittedAtUseSite()) {
2085 return;
2086 }
2087
Artem Serov657022c2016-11-23 14:19:38 +00002088 Location right = cond->GetLocations()->InAt(1);
Scott Wakelingfe885462016-09-22 10:24:38 +01002089 vixl32::Register out = OutputRegister(cond);
2090 vixl32::Label true_label, false_label;
2091
2092 switch (cond->InputAt(0)->GetType()) {
2093 default: {
2094 // Integer case.
Artem Serov657022c2016-11-23 14:19:38 +00002095 if (right.IsRegister()) {
2096 __ Cmp(InputRegisterAt(cond, 0), InputOperandAt(cond, 1));
2097 } else {
2098 DCHECK(right.IsConstant());
2099 __ Cmp(InputRegisterAt(cond, 0),
2100 CodeGenerator::GetInt32ValueOf(right.GetConstant()));
2101 }
Artem Serov0fb37192016-12-06 18:13:40 +00002102 ExactAssemblyScope aas(GetVIXLAssembler(),
2103 3 * vixl32::kMaxInstructionSizeInBytes,
2104 CodeBufferCheckScope::kMaximumSize);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002105 __ ite(ARMCondition(cond->GetCondition()));
2106 __ mov(ARMCondition(cond->GetCondition()), OutputRegister(cond), 1);
2107 __ mov(ARMCondition(cond->GetOppositeCondition()), OutputRegister(cond), 0);
Scott Wakelingfe885462016-09-22 10:24:38 +01002108 return;
2109 }
2110 case Primitive::kPrimLong:
2111 GenerateLongComparesAndJumps(cond, &true_label, &false_label);
2112 break;
2113 case Primitive::kPrimFloat:
2114 case Primitive::kPrimDouble:
2115 GenerateVcmp(cond);
2116 GenerateFPJumps(cond, &true_label, &false_label);
2117 break;
2118 }
2119
2120 // Convert the jumps into the result.
2121 vixl32::Label done_label;
2122
2123 // False case: result = 0.
2124 __ Bind(&false_label);
2125 __ Mov(out, 0);
2126 __ B(&done_label);
2127
2128 // True case: result = 1.
2129 __ Bind(&true_label);
2130 __ Mov(out, 1);
2131 __ Bind(&done_label);
2132}
2133
2134void LocationsBuilderARMVIXL::VisitEqual(HEqual* comp) {
2135 HandleCondition(comp);
2136}
2137
2138void InstructionCodeGeneratorARMVIXL::VisitEqual(HEqual* comp) {
2139 HandleCondition(comp);
2140}
2141
2142void LocationsBuilderARMVIXL::VisitNotEqual(HNotEqual* comp) {
2143 HandleCondition(comp);
2144}
2145
2146void InstructionCodeGeneratorARMVIXL::VisitNotEqual(HNotEqual* comp) {
2147 HandleCondition(comp);
2148}
2149
2150void LocationsBuilderARMVIXL::VisitLessThan(HLessThan* comp) {
2151 HandleCondition(comp);
2152}
2153
2154void InstructionCodeGeneratorARMVIXL::VisitLessThan(HLessThan* comp) {
2155 HandleCondition(comp);
2156}
2157
2158void LocationsBuilderARMVIXL::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
2159 HandleCondition(comp);
2160}
2161
2162void InstructionCodeGeneratorARMVIXL::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
2163 HandleCondition(comp);
2164}
2165
2166void LocationsBuilderARMVIXL::VisitGreaterThan(HGreaterThan* comp) {
2167 HandleCondition(comp);
2168}
2169
2170void InstructionCodeGeneratorARMVIXL::VisitGreaterThan(HGreaterThan* comp) {
2171 HandleCondition(comp);
2172}
2173
2174void LocationsBuilderARMVIXL::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
2175 HandleCondition(comp);
2176}
2177
2178void InstructionCodeGeneratorARMVIXL::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
2179 HandleCondition(comp);
2180}
2181
2182void LocationsBuilderARMVIXL::VisitBelow(HBelow* comp) {
2183 HandleCondition(comp);
2184}
2185
2186void InstructionCodeGeneratorARMVIXL::VisitBelow(HBelow* comp) {
2187 HandleCondition(comp);
2188}
2189
2190void LocationsBuilderARMVIXL::VisitBelowOrEqual(HBelowOrEqual* comp) {
2191 HandleCondition(comp);
2192}
2193
2194void InstructionCodeGeneratorARMVIXL::VisitBelowOrEqual(HBelowOrEqual* comp) {
2195 HandleCondition(comp);
2196}
2197
2198void LocationsBuilderARMVIXL::VisitAbove(HAbove* comp) {
2199 HandleCondition(comp);
2200}
2201
2202void InstructionCodeGeneratorARMVIXL::VisitAbove(HAbove* comp) {
2203 HandleCondition(comp);
2204}
2205
2206void LocationsBuilderARMVIXL::VisitAboveOrEqual(HAboveOrEqual* comp) {
2207 HandleCondition(comp);
2208}
2209
2210void InstructionCodeGeneratorARMVIXL::VisitAboveOrEqual(HAboveOrEqual* comp) {
2211 HandleCondition(comp);
2212}
2213
2214void LocationsBuilderARMVIXL::VisitIntConstant(HIntConstant* constant) {
2215 LocationSummary* locations =
2216 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
2217 locations->SetOut(Location::ConstantLocation(constant));
2218}
2219
2220void InstructionCodeGeneratorARMVIXL::VisitIntConstant(HIntConstant* constant ATTRIBUTE_UNUSED) {
2221 // Will be generated at use site.
2222}
2223
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002224void LocationsBuilderARMVIXL::VisitNullConstant(HNullConstant* constant) {
2225 LocationSummary* locations =
2226 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
2227 locations->SetOut(Location::ConstantLocation(constant));
2228}
2229
2230void InstructionCodeGeneratorARMVIXL::VisitNullConstant(HNullConstant* constant ATTRIBUTE_UNUSED) {
2231 // Will be generated at use site.
2232}
2233
Scott Wakelingfe885462016-09-22 10:24:38 +01002234void LocationsBuilderARMVIXL::VisitLongConstant(HLongConstant* constant) {
2235 LocationSummary* locations =
2236 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
2237 locations->SetOut(Location::ConstantLocation(constant));
2238}
2239
2240void InstructionCodeGeneratorARMVIXL::VisitLongConstant(HLongConstant* constant ATTRIBUTE_UNUSED) {
2241 // Will be generated at use site.
2242}
2243
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01002244void LocationsBuilderARMVIXL::VisitFloatConstant(HFloatConstant* constant) {
2245 LocationSummary* locations =
2246 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
2247 locations->SetOut(Location::ConstantLocation(constant));
2248}
2249
Scott Wakelingc34dba72016-10-03 10:14:44 +01002250void InstructionCodeGeneratorARMVIXL::VisitFloatConstant(
2251 HFloatConstant* constant ATTRIBUTE_UNUSED) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01002252 // Will be generated at use site.
2253}
2254
2255void LocationsBuilderARMVIXL::VisitDoubleConstant(HDoubleConstant* constant) {
2256 LocationSummary* locations =
2257 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
2258 locations->SetOut(Location::ConstantLocation(constant));
2259}
2260
Scott Wakelingc34dba72016-10-03 10:14:44 +01002261void InstructionCodeGeneratorARMVIXL::VisitDoubleConstant(
2262 HDoubleConstant* constant ATTRIBUTE_UNUSED) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01002263 // Will be generated at use site.
2264}
2265
Scott Wakelingfe885462016-09-22 10:24:38 +01002266void LocationsBuilderARMVIXL::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
2267 memory_barrier->SetLocations(nullptr);
2268}
2269
2270void InstructionCodeGeneratorARMVIXL::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
2271 codegen_->GenerateMemoryBarrier(memory_barrier->GetBarrierKind());
2272}
2273
2274void LocationsBuilderARMVIXL::VisitReturnVoid(HReturnVoid* ret) {
2275 ret->SetLocations(nullptr);
2276}
2277
2278void InstructionCodeGeneratorARMVIXL::VisitReturnVoid(HReturnVoid* ret ATTRIBUTE_UNUSED) {
2279 codegen_->GenerateFrameExit();
2280}
2281
2282void LocationsBuilderARMVIXL::VisitReturn(HReturn* ret) {
2283 LocationSummary* locations =
2284 new (GetGraph()->GetArena()) LocationSummary(ret, LocationSummary::kNoCall);
2285 locations->SetInAt(0, parameter_visitor_.GetReturnLocation(ret->InputAt(0)->GetType()));
2286}
2287
2288void InstructionCodeGeneratorARMVIXL::VisitReturn(HReturn* ret ATTRIBUTE_UNUSED) {
2289 codegen_->GenerateFrameExit();
2290}
2291
Artem Serovcfbe9132016-10-14 15:58:56 +01002292void LocationsBuilderARMVIXL::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
2293 // The trampoline uses the same calling convention as dex calling conventions,
2294 // except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain
2295 // the method_idx.
2296 HandleInvoke(invoke);
2297}
2298
2299void InstructionCodeGeneratorARMVIXL::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
2300 codegen_->GenerateInvokeUnresolvedRuntimeCall(invoke);
2301}
2302
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002303void LocationsBuilderARMVIXL::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
2304 // Explicit clinit checks triggered by static invokes must have been pruned by
2305 // art::PrepareForRegisterAllocation.
2306 DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
2307
Anton Kirilov5ec62182016-10-13 20:16:02 +01002308 IntrinsicLocationsBuilderARMVIXL intrinsic(codegen_);
2309 if (intrinsic.TryDispatch(invoke)) {
2310 if (invoke->GetLocations()->CanCall() && invoke->HasPcRelativeDexCache()) {
2311 invoke->GetLocations()->SetInAt(invoke->GetSpecialInputIndex(), Location::Any());
2312 }
2313 return;
2314 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002315
2316 HandleInvoke(invoke);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01002317
Artem Serovd4cc5b22016-11-04 11:19:09 +00002318 // For PC-relative dex cache the invoke has an extra input, the PC-relative address base.
2319 if (invoke->HasPcRelativeDexCache()) {
2320 invoke->GetLocations()->SetInAt(invoke->GetSpecialInputIndex(), Location::RequiresRegister());
2321 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002322}
2323
Anton Kirilov5ec62182016-10-13 20:16:02 +01002324static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorARMVIXL* codegen) {
2325 if (invoke->GetLocations()->Intrinsified()) {
2326 IntrinsicCodeGeneratorARMVIXL intrinsic(codegen);
2327 intrinsic.Dispatch(invoke);
2328 return true;
2329 }
2330 return false;
2331}
2332
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002333void InstructionCodeGeneratorARMVIXL::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
2334 // Explicit clinit checks triggered by static invokes must have been pruned by
2335 // art::PrepareForRegisterAllocation.
2336 DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
2337
Anton Kirilov5ec62182016-10-13 20:16:02 +01002338 if (TryGenerateIntrinsicCode(invoke, codegen_)) {
2339 return;
2340 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002341
2342 LocationSummary* locations = invoke->GetLocations();
Artem Serovd4cc5b22016-11-04 11:19:09 +00002343 codegen_->GenerateStaticOrDirectCall(
2344 invoke, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002345 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
2346}
2347
2348void LocationsBuilderARMVIXL::HandleInvoke(HInvoke* invoke) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002349 InvokeDexCallingConventionVisitorARMVIXL calling_convention_visitor;
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002350 CodeGenerator::CreateCommonInvokeLocationSummary(invoke, &calling_convention_visitor);
2351}
2352
2353void LocationsBuilderARMVIXL::VisitInvokeVirtual(HInvokeVirtual* invoke) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01002354 IntrinsicLocationsBuilderARMVIXL intrinsic(codegen_);
2355 if (intrinsic.TryDispatch(invoke)) {
2356 return;
2357 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002358
2359 HandleInvoke(invoke);
2360}
2361
2362void InstructionCodeGeneratorARMVIXL::VisitInvokeVirtual(HInvokeVirtual* invoke) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01002363 if (TryGenerateIntrinsicCode(invoke, codegen_)) {
2364 return;
2365 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002366
2367 codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002368 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
Alexandre Rames374ddf32016-11-04 10:40:49 +00002369 DCHECK(!codegen_->IsLeafMethod());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002370}
2371
Artem Serovcfbe9132016-10-14 15:58:56 +01002372void LocationsBuilderARMVIXL::VisitInvokeInterface(HInvokeInterface* invoke) {
2373 HandleInvoke(invoke);
2374 // Add the hidden argument.
2375 invoke->GetLocations()->AddTemp(LocationFrom(r12));
2376}
2377
2378void InstructionCodeGeneratorARMVIXL::VisitInvokeInterface(HInvokeInterface* invoke) {
2379 // TODO: b/18116999, our IMTs can miss an IncompatibleClassChangeError.
2380 LocationSummary* locations = invoke->GetLocations();
2381 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
2382 vixl32::Register hidden_reg = RegisterFrom(locations->GetTemp(1));
2383 Location receiver = locations->InAt(0);
2384 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
2385
2386 DCHECK(!receiver.IsStackSlot());
2387
Alexandre Rames374ddf32016-11-04 10:40:49 +00002388 // Ensure the pc position is recorded immediately after the `ldr` instruction.
2389 {
Artem Serov0fb37192016-12-06 18:13:40 +00002390 ExactAssemblyScope aas(GetVIXLAssembler(),
2391 vixl32::kMaxInstructionSizeInBytes,
2392 CodeBufferCheckScope::kMaximumSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00002393 // /* HeapReference<Class> */ temp = receiver->klass_
2394 __ ldr(temp, MemOperand(RegisterFrom(receiver), class_offset));
2395 codegen_->MaybeRecordImplicitNullCheck(invoke);
2396 }
Artem Serovcfbe9132016-10-14 15:58:56 +01002397 // Instead of simply (possibly) unpoisoning `temp` here, we should
2398 // emit a read barrier for the previous class reference load.
2399 // However this is not required in practice, as this is an
2400 // intermediate/temporary reference and because the current
2401 // concurrent copying collector keeps the from-space memory
2402 // intact/accessible until the end of the marking phase (the
2403 // concurrent copying collector may not in the future).
2404 GetAssembler()->MaybeUnpoisonHeapReference(temp);
2405 GetAssembler()->LoadFromOffset(kLoadWord,
2406 temp,
2407 temp,
2408 mirror::Class::ImtPtrOffset(kArmPointerSize).Uint32Value());
2409 uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement(
2410 invoke->GetImtIndex(), kArmPointerSize));
2411 // temp = temp->GetImtEntryAt(method_offset);
2412 GetAssembler()->LoadFromOffset(kLoadWord, temp, temp, method_offset);
2413 uint32_t entry_point =
2414 ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value();
2415 // LR = temp->GetEntryPoint();
2416 GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, entry_point);
2417
2418 // Set the hidden (in r12) argument. It is done here, right before a BLX to prevent other
2419 // instruction from clobbering it as they might use r12 as a scratch register.
2420 DCHECK(hidden_reg.Is(r12));
Scott Wakelingb77051e2016-11-21 19:46:00 +00002421
2422 {
2423 // The VIXL macro assembler may clobber any of the scratch registers that are available to it,
2424 // so it checks if the application is using them (by passing them to the macro assembler
2425 // methods). The following application of UseScratchRegisterScope corrects VIXL's notion of
2426 // what is available, and is the opposite of the standard usage: Instead of requesting a
2427 // temporary location, it imposes an external constraint (i.e. a specific register is reserved
2428 // for the hidden argument). Note that this works even if VIXL needs a scratch register itself
2429 // (to materialize the constant), since the destination register becomes available for such use
2430 // internally for the duration of the macro instruction.
2431 UseScratchRegisterScope temps(GetVIXLAssembler());
2432 temps.Exclude(hidden_reg);
2433 __ Mov(hidden_reg, invoke->GetDexMethodIndex());
2434 }
Artem Serovcfbe9132016-10-14 15:58:56 +01002435 {
Alexandre Rames374ddf32016-11-04 10:40:49 +00002436 // Ensure the pc position is recorded immediately after the `blx` instruction.
2437 // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
Artem Serov0fb37192016-12-06 18:13:40 +00002438 ExactAssemblyScope aas(GetVIXLAssembler(),
Alexandre Rames374ddf32016-11-04 10:40:49 +00002439 vixl32::k16BitT32InstructionSizeInBytes,
2440 CodeBufferCheckScope::kExactSize);
Artem Serovcfbe9132016-10-14 15:58:56 +01002441 // LR();
2442 __ blx(lr);
Artem Serovcfbe9132016-10-14 15:58:56 +01002443 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
Alexandre Rames374ddf32016-11-04 10:40:49 +00002444 DCHECK(!codegen_->IsLeafMethod());
Artem Serovcfbe9132016-10-14 15:58:56 +01002445 }
2446}
2447
Artem Serov02109dd2016-09-23 17:17:54 +01002448void LocationsBuilderARMVIXL::VisitNeg(HNeg* neg) {
2449 LocationSummary* locations =
2450 new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall);
2451 switch (neg->GetResultType()) {
2452 case Primitive::kPrimInt: {
2453 locations->SetInAt(0, Location::RequiresRegister());
2454 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2455 break;
2456 }
2457 case Primitive::kPrimLong: {
2458 locations->SetInAt(0, Location::RequiresRegister());
2459 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
2460 break;
2461 }
2462
2463 case Primitive::kPrimFloat:
2464 case Primitive::kPrimDouble:
2465 locations->SetInAt(0, Location::RequiresFpuRegister());
2466 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2467 break;
2468
2469 default:
2470 LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
2471 }
2472}
2473
2474void InstructionCodeGeneratorARMVIXL::VisitNeg(HNeg* neg) {
2475 LocationSummary* locations = neg->GetLocations();
2476 Location out = locations->Out();
2477 Location in = locations->InAt(0);
2478 switch (neg->GetResultType()) {
2479 case Primitive::kPrimInt:
2480 __ Rsb(OutputRegister(neg), InputRegisterAt(neg, 0), 0);
2481 break;
2482
2483 case Primitive::kPrimLong:
2484 // out.lo = 0 - in.lo (and update the carry/borrow (C) flag)
2485 __ Rsbs(LowRegisterFrom(out), LowRegisterFrom(in), 0);
2486 // We cannot emit an RSC (Reverse Subtract with Carry)
2487 // instruction here, as it does not exist in the Thumb-2
2488 // instruction set. We use the following approach
2489 // using SBC and SUB instead.
2490 //
2491 // out.hi = -C
2492 __ Sbc(HighRegisterFrom(out), HighRegisterFrom(out), HighRegisterFrom(out));
2493 // out.hi = out.hi - in.hi
2494 __ Sub(HighRegisterFrom(out), HighRegisterFrom(out), HighRegisterFrom(in));
2495 break;
2496
2497 case Primitive::kPrimFloat:
2498 case Primitive::kPrimDouble:
Anton Kirilov644032c2016-12-06 17:51:43 +00002499 __ Vneg(OutputVRegister(neg), InputVRegister(neg));
Artem Serov02109dd2016-09-23 17:17:54 +01002500 break;
2501
2502 default:
2503 LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
2504 }
2505}
2506
Scott Wakelingfe885462016-09-22 10:24:38 +01002507void LocationsBuilderARMVIXL::VisitTypeConversion(HTypeConversion* conversion) {
2508 Primitive::Type result_type = conversion->GetResultType();
2509 Primitive::Type input_type = conversion->GetInputType();
2510 DCHECK_NE(result_type, input_type);
2511
2512 // The float-to-long, double-to-long and long-to-float type conversions
2513 // rely on a call to the runtime.
2514 LocationSummary::CallKind call_kind =
2515 (((input_type == Primitive::kPrimFloat || input_type == Primitive::kPrimDouble)
2516 && result_type == Primitive::kPrimLong)
2517 || (input_type == Primitive::kPrimLong && result_type == Primitive::kPrimFloat))
2518 ? LocationSummary::kCallOnMainOnly
2519 : LocationSummary::kNoCall;
2520 LocationSummary* locations =
2521 new (GetGraph()->GetArena()) LocationSummary(conversion, call_kind);
2522
2523 // The Java language does not allow treating boolean as an integral type but
2524 // our bit representation makes it safe.
2525
2526 switch (result_type) {
2527 case Primitive::kPrimByte:
2528 switch (input_type) {
2529 case Primitive::kPrimLong:
2530 // Type conversion from long to byte is a result of code transformations.
2531 case Primitive::kPrimBoolean:
2532 // Boolean input is a result of code transformations.
2533 case Primitive::kPrimShort:
2534 case Primitive::kPrimInt:
2535 case Primitive::kPrimChar:
2536 // Processing a Dex `int-to-byte' instruction.
2537 locations->SetInAt(0, Location::RequiresRegister());
2538 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2539 break;
2540
2541 default:
2542 LOG(FATAL) << "Unexpected type conversion from " << input_type
2543 << " to " << result_type;
2544 }
2545 break;
2546
2547 case Primitive::kPrimShort:
2548 switch (input_type) {
2549 case Primitive::kPrimLong:
2550 // Type conversion from long to short is a result of code transformations.
2551 case Primitive::kPrimBoolean:
2552 // Boolean input is a result of code transformations.
2553 case Primitive::kPrimByte:
2554 case Primitive::kPrimInt:
2555 case Primitive::kPrimChar:
2556 // Processing a Dex `int-to-short' instruction.
2557 locations->SetInAt(0, Location::RequiresRegister());
2558 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2559 break;
2560
2561 default:
2562 LOG(FATAL) << "Unexpected type conversion from " << input_type
2563 << " to " << result_type;
2564 }
2565 break;
2566
2567 case Primitive::kPrimInt:
2568 switch (input_type) {
2569 case Primitive::kPrimLong:
2570 // Processing a Dex `long-to-int' instruction.
2571 locations->SetInAt(0, Location::Any());
2572 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2573 break;
2574
2575 case Primitive::kPrimFloat:
2576 // Processing a Dex `float-to-int' instruction.
2577 locations->SetInAt(0, Location::RequiresFpuRegister());
2578 locations->SetOut(Location::RequiresRegister());
2579 locations->AddTemp(Location::RequiresFpuRegister());
2580 break;
2581
2582 case Primitive::kPrimDouble:
2583 // Processing a Dex `double-to-int' instruction.
2584 locations->SetInAt(0, Location::RequiresFpuRegister());
2585 locations->SetOut(Location::RequiresRegister());
2586 locations->AddTemp(Location::RequiresFpuRegister());
2587 break;
2588
2589 default:
2590 LOG(FATAL) << "Unexpected type conversion from " << input_type
2591 << " to " << result_type;
2592 }
2593 break;
2594
2595 case Primitive::kPrimLong:
2596 switch (input_type) {
2597 case Primitive::kPrimBoolean:
2598 // Boolean input is a result of code transformations.
2599 case Primitive::kPrimByte:
2600 case Primitive::kPrimShort:
2601 case Primitive::kPrimInt:
2602 case Primitive::kPrimChar:
2603 // Processing a Dex `int-to-long' instruction.
2604 locations->SetInAt(0, Location::RequiresRegister());
2605 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2606 break;
2607
2608 case Primitive::kPrimFloat: {
2609 // Processing a Dex `float-to-long' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002610 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2611 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
2612 locations->SetOut(LocationFrom(r0, r1));
Scott Wakelingfe885462016-09-22 10:24:38 +01002613 break;
2614 }
2615
2616 case Primitive::kPrimDouble: {
2617 // Processing a Dex `double-to-long' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002618 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2619 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0),
2620 calling_convention.GetFpuRegisterAt(1)));
2621 locations->SetOut(LocationFrom(r0, r1));
Scott Wakelingfe885462016-09-22 10:24:38 +01002622 break;
2623 }
2624
2625 default:
2626 LOG(FATAL) << "Unexpected type conversion from " << input_type
2627 << " to " << result_type;
2628 }
2629 break;
2630
2631 case Primitive::kPrimChar:
2632 switch (input_type) {
2633 case Primitive::kPrimLong:
2634 // Type conversion from long to char is a result of code transformations.
2635 case Primitive::kPrimBoolean:
2636 // Boolean input is a result of code transformations.
2637 case Primitive::kPrimByte:
2638 case Primitive::kPrimShort:
2639 case Primitive::kPrimInt:
2640 // Processing a Dex `int-to-char' instruction.
2641 locations->SetInAt(0, Location::RequiresRegister());
2642 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2643 break;
2644
2645 default:
2646 LOG(FATAL) << "Unexpected type conversion from " << input_type
2647 << " to " << result_type;
2648 }
2649 break;
2650
2651 case Primitive::kPrimFloat:
2652 switch (input_type) {
2653 case Primitive::kPrimBoolean:
2654 // Boolean input is a result of code transformations.
2655 case Primitive::kPrimByte:
2656 case Primitive::kPrimShort:
2657 case Primitive::kPrimInt:
2658 case Primitive::kPrimChar:
2659 // Processing a Dex `int-to-float' instruction.
2660 locations->SetInAt(0, Location::RequiresRegister());
2661 locations->SetOut(Location::RequiresFpuRegister());
2662 break;
2663
2664 case Primitive::kPrimLong: {
2665 // Processing a Dex `long-to-float' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002666 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2667 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0),
2668 calling_convention.GetRegisterAt(1)));
2669 locations->SetOut(LocationFrom(calling_convention.GetFpuRegisterAt(0)));
Scott Wakelingfe885462016-09-22 10:24:38 +01002670 break;
2671 }
2672
2673 case Primitive::kPrimDouble:
2674 // Processing a Dex `double-to-float' instruction.
2675 locations->SetInAt(0, Location::RequiresFpuRegister());
2676 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2677 break;
2678
2679 default:
2680 LOG(FATAL) << "Unexpected type conversion from " << input_type
2681 << " to " << result_type;
2682 };
2683 break;
2684
2685 case Primitive::kPrimDouble:
2686 switch (input_type) {
2687 case Primitive::kPrimBoolean:
2688 // Boolean input is a result of code transformations.
2689 case Primitive::kPrimByte:
2690 case Primitive::kPrimShort:
2691 case Primitive::kPrimInt:
2692 case Primitive::kPrimChar:
2693 // Processing a Dex `int-to-double' instruction.
2694 locations->SetInAt(0, Location::RequiresRegister());
2695 locations->SetOut(Location::RequiresFpuRegister());
2696 break;
2697
2698 case Primitive::kPrimLong:
2699 // Processing a Dex `long-to-double' instruction.
2700 locations->SetInAt(0, Location::RequiresRegister());
2701 locations->SetOut(Location::RequiresFpuRegister());
2702 locations->AddTemp(Location::RequiresFpuRegister());
2703 locations->AddTemp(Location::RequiresFpuRegister());
2704 break;
2705
2706 case Primitive::kPrimFloat:
2707 // Processing a Dex `float-to-double' instruction.
2708 locations->SetInAt(0, Location::RequiresFpuRegister());
2709 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2710 break;
2711
2712 default:
2713 LOG(FATAL) << "Unexpected type conversion from " << input_type
2714 << " to " << result_type;
2715 };
2716 break;
2717
2718 default:
2719 LOG(FATAL) << "Unexpected type conversion from " << input_type
2720 << " to " << result_type;
2721 }
2722}
2723
2724void InstructionCodeGeneratorARMVIXL::VisitTypeConversion(HTypeConversion* conversion) {
2725 LocationSummary* locations = conversion->GetLocations();
2726 Location out = locations->Out();
2727 Location in = locations->InAt(0);
2728 Primitive::Type result_type = conversion->GetResultType();
2729 Primitive::Type input_type = conversion->GetInputType();
2730 DCHECK_NE(result_type, input_type);
2731 switch (result_type) {
2732 case Primitive::kPrimByte:
2733 switch (input_type) {
2734 case Primitive::kPrimLong:
2735 // Type conversion from long to byte is a result of code transformations.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002736 __ Sbfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 8);
Scott Wakelingfe885462016-09-22 10:24:38 +01002737 break;
2738 case Primitive::kPrimBoolean:
2739 // Boolean input is a result of code transformations.
2740 case Primitive::kPrimShort:
2741 case Primitive::kPrimInt:
2742 case Primitive::kPrimChar:
2743 // Processing a Dex `int-to-byte' instruction.
2744 __ Sbfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 8);
2745 break;
2746
2747 default:
2748 LOG(FATAL) << "Unexpected type conversion from " << input_type
2749 << " to " << result_type;
2750 }
2751 break;
2752
2753 case Primitive::kPrimShort:
2754 switch (input_type) {
2755 case Primitive::kPrimLong:
2756 // Type conversion from long to short is a result of code transformations.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002757 __ Sbfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 16);
Scott Wakelingfe885462016-09-22 10:24:38 +01002758 break;
2759 case Primitive::kPrimBoolean:
2760 // Boolean input is a result of code transformations.
2761 case Primitive::kPrimByte:
2762 case Primitive::kPrimInt:
2763 case Primitive::kPrimChar:
2764 // Processing a Dex `int-to-short' instruction.
2765 __ Sbfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 16);
2766 break;
2767
2768 default:
2769 LOG(FATAL) << "Unexpected type conversion from " << input_type
2770 << " to " << result_type;
2771 }
2772 break;
2773
2774 case Primitive::kPrimInt:
2775 switch (input_type) {
2776 case Primitive::kPrimLong:
2777 // Processing a Dex `long-to-int' instruction.
2778 DCHECK(out.IsRegister());
2779 if (in.IsRegisterPair()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002780 __ Mov(OutputRegister(conversion), LowRegisterFrom(in));
Scott Wakelingfe885462016-09-22 10:24:38 +01002781 } else if (in.IsDoubleStackSlot()) {
2782 GetAssembler()->LoadFromOffset(kLoadWord,
2783 OutputRegister(conversion),
2784 sp,
2785 in.GetStackIndex());
2786 } else {
2787 DCHECK(in.IsConstant());
2788 DCHECK(in.GetConstant()->IsLongConstant());
Anton Kirilov644032c2016-12-06 17:51:43 +00002789 int32_t value = Int32ConstantFrom(in);
2790 __ Mov(OutputRegister(conversion), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01002791 }
2792 break;
2793
2794 case Primitive::kPrimFloat: {
2795 // Processing a Dex `float-to-int' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002796 vixl32::SRegister temp = LowSRegisterFrom(locations->GetTemp(0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01002797 __ Vcvt(S32, F32, temp, InputSRegisterAt(conversion, 0));
Scott Wakelingfe885462016-09-22 10:24:38 +01002798 __ Vmov(OutputRegister(conversion), temp);
2799 break;
2800 }
2801
2802 case Primitive::kPrimDouble: {
2803 // Processing a Dex `double-to-int' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002804 vixl32::SRegister temp_s = LowSRegisterFrom(locations->GetTemp(0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01002805 __ Vcvt(S32, F64, temp_s, DRegisterFrom(in));
Scott Wakelingfe885462016-09-22 10:24:38 +01002806 __ Vmov(OutputRegister(conversion), temp_s);
2807 break;
2808 }
2809
2810 default:
2811 LOG(FATAL) << "Unexpected type conversion from " << input_type
2812 << " to " << result_type;
2813 }
2814 break;
2815
2816 case Primitive::kPrimLong:
2817 switch (input_type) {
2818 case Primitive::kPrimBoolean:
2819 // Boolean input is a result of code transformations.
2820 case Primitive::kPrimByte:
2821 case Primitive::kPrimShort:
2822 case Primitive::kPrimInt:
2823 case Primitive::kPrimChar:
2824 // Processing a Dex `int-to-long' instruction.
2825 DCHECK(out.IsRegisterPair());
2826 DCHECK(in.IsRegister());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002827 __ Mov(LowRegisterFrom(out), InputRegisterAt(conversion, 0));
Scott Wakelingfe885462016-09-22 10:24:38 +01002828 // Sign extension.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002829 __ Asr(HighRegisterFrom(out), LowRegisterFrom(out), 31);
Scott Wakelingfe885462016-09-22 10:24:38 +01002830 break;
2831
2832 case Primitive::kPrimFloat:
2833 // Processing a Dex `float-to-long' instruction.
2834 codegen_->InvokeRuntime(kQuickF2l, conversion, conversion->GetDexPc());
2835 CheckEntrypointTypes<kQuickF2l, int64_t, float>();
2836 break;
2837
2838 case Primitive::kPrimDouble:
2839 // Processing a Dex `double-to-long' instruction.
2840 codegen_->InvokeRuntime(kQuickD2l, conversion, conversion->GetDexPc());
2841 CheckEntrypointTypes<kQuickD2l, int64_t, double>();
2842 break;
2843
2844 default:
2845 LOG(FATAL) << "Unexpected type conversion from " << input_type
2846 << " to " << result_type;
2847 }
2848 break;
2849
2850 case Primitive::kPrimChar:
2851 switch (input_type) {
2852 case Primitive::kPrimLong:
2853 // Type conversion from long to char is a result of code transformations.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002854 __ Ubfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 16);
Scott Wakelingfe885462016-09-22 10:24:38 +01002855 break;
2856 case Primitive::kPrimBoolean:
2857 // Boolean input is a result of code transformations.
2858 case Primitive::kPrimByte:
2859 case Primitive::kPrimShort:
2860 case Primitive::kPrimInt:
2861 // Processing a Dex `int-to-char' instruction.
2862 __ Ubfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 16);
2863 break;
2864
2865 default:
2866 LOG(FATAL) << "Unexpected type conversion from " << input_type
2867 << " to " << result_type;
2868 }
2869 break;
2870
2871 case Primitive::kPrimFloat:
2872 switch (input_type) {
2873 case Primitive::kPrimBoolean:
2874 // Boolean input is a result of code transformations.
2875 case Primitive::kPrimByte:
2876 case Primitive::kPrimShort:
2877 case Primitive::kPrimInt:
2878 case Primitive::kPrimChar: {
2879 // Processing a Dex `int-to-float' instruction.
2880 __ Vmov(OutputSRegister(conversion), InputRegisterAt(conversion, 0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01002881 __ Vcvt(F32, S32, OutputSRegister(conversion), OutputSRegister(conversion));
Scott Wakelingfe885462016-09-22 10:24:38 +01002882 break;
2883 }
2884
2885 case Primitive::kPrimLong:
2886 // Processing a Dex `long-to-float' instruction.
2887 codegen_->InvokeRuntime(kQuickL2f, conversion, conversion->GetDexPc());
2888 CheckEntrypointTypes<kQuickL2f, float, int64_t>();
2889 break;
2890
2891 case Primitive::kPrimDouble:
2892 // Processing a Dex `double-to-float' instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01002893 __ Vcvt(F32, F64, OutputSRegister(conversion), DRegisterFrom(in));
Scott Wakelingfe885462016-09-22 10:24:38 +01002894 break;
2895
2896 default:
2897 LOG(FATAL) << "Unexpected type conversion from " << input_type
2898 << " to " << result_type;
2899 };
2900 break;
2901
2902 case Primitive::kPrimDouble:
2903 switch (input_type) {
2904 case Primitive::kPrimBoolean:
2905 // Boolean input is a result of code transformations.
2906 case Primitive::kPrimByte:
2907 case Primitive::kPrimShort:
2908 case Primitive::kPrimInt:
2909 case Primitive::kPrimChar: {
2910 // Processing a Dex `int-to-double' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002911 __ Vmov(LowSRegisterFrom(out), InputRegisterAt(conversion, 0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01002912 __ Vcvt(F64, S32, DRegisterFrom(out), LowSRegisterFrom(out));
Scott Wakelingfe885462016-09-22 10:24:38 +01002913 break;
2914 }
2915
2916 case Primitive::kPrimLong: {
2917 // Processing a Dex `long-to-double' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002918 vixl32::Register low = LowRegisterFrom(in);
2919 vixl32::Register high = HighRegisterFrom(in);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002920 vixl32::SRegister out_s = LowSRegisterFrom(out);
Scott Wakelingc34dba72016-10-03 10:14:44 +01002921 vixl32::DRegister out_d = DRegisterFrom(out);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002922 vixl32::SRegister temp_s = LowSRegisterFrom(locations->GetTemp(0));
Scott Wakelingc34dba72016-10-03 10:14:44 +01002923 vixl32::DRegister temp_d = DRegisterFrom(locations->GetTemp(0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01002924 vixl32::DRegister constant_d = DRegisterFrom(locations->GetTemp(1));
Scott Wakelingfe885462016-09-22 10:24:38 +01002925
2926 // temp_d = int-to-double(high)
2927 __ Vmov(temp_s, high);
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01002928 __ Vcvt(F64, S32, temp_d, temp_s);
Scott Wakelingfe885462016-09-22 10:24:38 +01002929 // constant_d = k2Pow32EncodingForDouble
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002930 __ Vmov(constant_d, bit_cast<double, int64_t>(k2Pow32EncodingForDouble));
Scott Wakelingfe885462016-09-22 10:24:38 +01002931 // out_d = unsigned-to-double(low)
2932 __ Vmov(out_s, low);
2933 __ Vcvt(F64, U32, out_d, out_s);
2934 // out_d += temp_d * constant_d
2935 __ Vmla(F64, out_d, temp_d, constant_d);
2936 break;
2937 }
2938
2939 case Primitive::kPrimFloat:
2940 // Processing a Dex `float-to-double' instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01002941 __ Vcvt(F64, F32, DRegisterFrom(out), InputSRegisterAt(conversion, 0));
Scott Wakelingfe885462016-09-22 10:24:38 +01002942 break;
2943
2944 default:
2945 LOG(FATAL) << "Unexpected type conversion from " << input_type
2946 << " to " << result_type;
2947 };
2948 break;
2949
2950 default:
2951 LOG(FATAL) << "Unexpected type conversion from " << input_type
2952 << " to " << result_type;
2953 }
2954}
2955
2956void LocationsBuilderARMVIXL::VisitAdd(HAdd* add) {
2957 LocationSummary* locations =
2958 new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall);
2959 switch (add->GetResultType()) {
2960 case Primitive::kPrimInt: {
2961 locations->SetInAt(0, Location::RequiresRegister());
2962 locations->SetInAt(1, Location::RegisterOrConstant(add->InputAt(1)));
2963 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2964 break;
2965 }
2966
Scott Wakelingfe885462016-09-22 10:24:38 +01002967 case Primitive::kPrimLong: {
2968 locations->SetInAt(0, Location::RequiresRegister());
Anton Kirilovdda43962016-11-21 19:55:20 +00002969 locations->SetInAt(1, ArmEncodableConstantOrRegister(add->InputAt(1), ADD));
Scott Wakelingfe885462016-09-22 10:24:38 +01002970 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2971 break;
2972 }
2973
2974 case Primitive::kPrimFloat:
2975 case Primitive::kPrimDouble: {
2976 locations->SetInAt(0, Location::RequiresFpuRegister());
2977 locations->SetInAt(1, Location::RequiresFpuRegister());
2978 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2979 break;
2980 }
2981
2982 default:
2983 LOG(FATAL) << "Unexpected add type " << add->GetResultType();
2984 }
2985}
2986
2987void InstructionCodeGeneratorARMVIXL::VisitAdd(HAdd* add) {
2988 LocationSummary* locations = add->GetLocations();
2989 Location out = locations->Out();
2990 Location first = locations->InAt(0);
2991 Location second = locations->InAt(1);
2992
2993 switch (add->GetResultType()) {
2994 case Primitive::kPrimInt: {
2995 __ Add(OutputRegister(add), InputRegisterAt(add, 0), InputOperandAt(add, 1));
2996 }
2997 break;
2998
Scott Wakelingfe885462016-09-22 10:24:38 +01002999 case Primitive::kPrimLong: {
Anton Kirilovdda43962016-11-21 19:55:20 +00003000 if (second.IsConstant()) {
3001 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
3002 GenerateAddLongConst(out, first, value);
3003 } else {
3004 DCHECK(second.IsRegisterPair());
3005 __ Adds(LowRegisterFrom(out), LowRegisterFrom(first), LowRegisterFrom(second));
3006 __ Adc(HighRegisterFrom(out), HighRegisterFrom(first), HighRegisterFrom(second));
3007 }
Scott Wakelingfe885462016-09-22 10:24:38 +01003008 break;
3009 }
3010
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003011 case Primitive::kPrimFloat:
Scott Wakelingfe885462016-09-22 10:24:38 +01003012 case Primitive::kPrimDouble:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003013 __ Vadd(OutputVRegister(add), InputVRegisterAt(add, 0), InputVRegisterAt(add, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01003014 break;
3015
3016 default:
3017 LOG(FATAL) << "Unexpected add type " << add->GetResultType();
3018 }
3019}
3020
3021void LocationsBuilderARMVIXL::VisitSub(HSub* sub) {
3022 LocationSummary* locations =
3023 new (GetGraph()->GetArena()) LocationSummary(sub, LocationSummary::kNoCall);
3024 switch (sub->GetResultType()) {
3025 case Primitive::kPrimInt: {
3026 locations->SetInAt(0, Location::RequiresRegister());
3027 locations->SetInAt(1, Location::RegisterOrConstant(sub->InputAt(1)));
3028 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3029 break;
3030 }
3031
Scott Wakelingfe885462016-09-22 10:24:38 +01003032 case Primitive::kPrimLong: {
3033 locations->SetInAt(0, Location::RequiresRegister());
Anton Kirilovdda43962016-11-21 19:55:20 +00003034 locations->SetInAt(1, ArmEncodableConstantOrRegister(sub->InputAt(1), SUB));
Scott Wakelingfe885462016-09-22 10:24:38 +01003035 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3036 break;
3037 }
3038 case Primitive::kPrimFloat:
3039 case Primitive::kPrimDouble: {
3040 locations->SetInAt(0, Location::RequiresFpuRegister());
3041 locations->SetInAt(1, Location::RequiresFpuRegister());
3042 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3043 break;
3044 }
3045 default:
3046 LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
3047 }
3048}
3049
3050void InstructionCodeGeneratorARMVIXL::VisitSub(HSub* sub) {
3051 LocationSummary* locations = sub->GetLocations();
3052 Location out = locations->Out();
3053 Location first = locations->InAt(0);
3054 Location second = locations->InAt(1);
3055 switch (sub->GetResultType()) {
3056 case Primitive::kPrimInt: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003057 __ Sub(OutputRegister(sub), InputRegisterAt(sub, 0), InputOperandAt(sub, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01003058 break;
3059 }
3060
Scott Wakelingfe885462016-09-22 10:24:38 +01003061 case Primitive::kPrimLong: {
Anton Kirilovdda43962016-11-21 19:55:20 +00003062 if (second.IsConstant()) {
3063 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
3064 GenerateAddLongConst(out, first, -value);
3065 } else {
3066 DCHECK(second.IsRegisterPair());
3067 __ Subs(LowRegisterFrom(out), LowRegisterFrom(first), LowRegisterFrom(second));
3068 __ Sbc(HighRegisterFrom(out), HighRegisterFrom(first), HighRegisterFrom(second));
3069 }
Scott Wakelingfe885462016-09-22 10:24:38 +01003070 break;
3071 }
3072
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003073 case Primitive::kPrimFloat:
3074 case Primitive::kPrimDouble:
3075 __ Vsub(OutputVRegister(sub), InputVRegisterAt(sub, 0), InputVRegisterAt(sub, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01003076 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01003077
3078 default:
3079 LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
3080 }
3081}
3082
3083void LocationsBuilderARMVIXL::VisitMul(HMul* mul) {
3084 LocationSummary* locations =
3085 new (GetGraph()->GetArena()) LocationSummary(mul, LocationSummary::kNoCall);
3086 switch (mul->GetResultType()) {
3087 case Primitive::kPrimInt:
3088 case Primitive::kPrimLong: {
3089 locations->SetInAt(0, Location::RequiresRegister());
3090 locations->SetInAt(1, Location::RequiresRegister());
3091 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3092 break;
3093 }
3094
3095 case Primitive::kPrimFloat:
3096 case Primitive::kPrimDouble: {
3097 locations->SetInAt(0, Location::RequiresFpuRegister());
3098 locations->SetInAt(1, Location::RequiresFpuRegister());
3099 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3100 break;
3101 }
3102
3103 default:
3104 LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
3105 }
3106}
3107
3108void InstructionCodeGeneratorARMVIXL::VisitMul(HMul* mul) {
3109 LocationSummary* locations = mul->GetLocations();
3110 Location out = locations->Out();
3111 Location first = locations->InAt(0);
3112 Location second = locations->InAt(1);
3113 switch (mul->GetResultType()) {
3114 case Primitive::kPrimInt: {
3115 __ Mul(OutputRegister(mul), InputRegisterAt(mul, 0), InputRegisterAt(mul, 1));
3116 break;
3117 }
3118 case Primitive::kPrimLong: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003119 vixl32::Register out_hi = HighRegisterFrom(out);
3120 vixl32::Register out_lo = LowRegisterFrom(out);
3121 vixl32::Register in1_hi = HighRegisterFrom(first);
3122 vixl32::Register in1_lo = LowRegisterFrom(first);
3123 vixl32::Register in2_hi = HighRegisterFrom(second);
3124 vixl32::Register in2_lo = LowRegisterFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01003125
3126 // Extra checks to protect caused by the existence of R1_R2.
3127 // The algorithm is wrong if out.hi is either in1.lo or in2.lo:
3128 // (e.g. in1=r0_r1, in2=r2_r3 and out=r1_r2);
Anton Kirilov644032c2016-12-06 17:51:43 +00003129 DCHECK(!out_hi.Is(in1_lo));
3130 DCHECK(!out_hi.Is(in2_lo));
Scott Wakelingfe885462016-09-22 10:24:38 +01003131
3132 // input: in1 - 64 bits, in2 - 64 bits
3133 // output: out
3134 // formula: out.hi : out.lo = (in1.lo * in2.hi + in1.hi * in2.lo)* 2^32 + in1.lo * in2.lo
3135 // parts: out.hi = in1.lo * in2.hi + in1.hi * in2.lo + (in1.lo * in2.lo)[63:32]
3136 // parts: out.lo = (in1.lo * in2.lo)[31:0]
3137
3138 UseScratchRegisterScope temps(GetVIXLAssembler());
3139 vixl32::Register temp = temps.Acquire();
3140 // temp <- in1.lo * in2.hi
3141 __ Mul(temp, in1_lo, in2_hi);
3142 // out.hi <- in1.lo * in2.hi + in1.hi * in2.lo
3143 __ Mla(out_hi, in1_hi, in2_lo, temp);
3144 // out.lo <- (in1.lo * in2.lo)[31:0];
3145 __ Umull(out_lo, temp, in1_lo, in2_lo);
3146 // out.hi <- in2.hi * in1.lo + in2.lo * in1.hi + (in1.lo * in2.lo)[63:32]
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003147 __ Add(out_hi, out_hi, temp);
Scott Wakelingfe885462016-09-22 10:24:38 +01003148 break;
3149 }
3150
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003151 case Primitive::kPrimFloat:
3152 case Primitive::kPrimDouble:
3153 __ Vmul(OutputVRegister(mul), InputVRegisterAt(mul, 0), InputVRegisterAt(mul, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01003154 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01003155
3156 default:
3157 LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
3158 }
3159}
3160
Scott Wakelingfe885462016-09-22 10:24:38 +01003161void InstructionCodeGeneratorARMVIXL::DivRemOneOrMinusOne(HBinaryOperation* instruction) {
3162 DCHECK(instruction->IsDiv() || instruction->IsRem());
3163 DCHECK(instruction->GetResultType() == Primitive::kPrimInt);
3164
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003165 Location second = instruction->GetLocations()->InAt(1);
Scott Wakelingfe885462016-09-22 10:24:38 +01003166 DCHECK(second.IsConstant());
3167
3168 vixl32::Register out = OutputRegister(instruction);
3169 vixl32::Register dividend = InputRegisterAt(instruction, 0);
Anton Kirilov644032c2016-12-06 17:51:43 +00003170 int32_t imm = Int32ConstantFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01003171 DCHECK(imm == 1 || imm == -1);
3172
3173 if (instruction->IsRem()) {
3174 __ Mov(out, 0);
3175 } else {
3176 if (imm == 1) {
3177 __ Mov(out, dividend);
3178 } else {
3179 __ Rsb(out, dividend, 0);
3180 }
3181 }
3182}
3183
3184void InstructionCodeGeneratorARMVIXL::DivRemByPowerOfTwo(HBinaryOperation* instruction) {
3185 DCHECK(instruction->IsDiv() || instruction->IsRem());
3186 DCHECK(instruction->GetResultType() == Primitive::kPrimInt);
3187
3188 LocationSummary* locations = instruction->GetLocations();
3189 Location second = locations->InAt(1);
3190 DCHECK(second.IsConstant());
3191
3192 vixl32::Register out = OutputRegister(instruction);
3193 vixl32::Register dividend = InputRegisterAt(instruction, 0);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003194 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
Anton Kirilov644032c2016-12-06 17:51:43 +00003195 int32_t imm = Int32ConstantFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01003196 uint32_t abs_imm = static_cast<uint32_t>(AbsOrMin(imm));
3197 int ctz_imm = CTZ(abs_imm);
3198
3199 if (ctz_imm == 1) {
3200 __ Lsr(temp, dividend, 32 - ctz_imm);
3201 } else {
3202 __ Asr(temp, dividend, 31);
3203 __ Lsr(temp, temp, 32 - ctz_imm);
3204 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003205 __ Add(out, temp, dividend);
Scott Wakelingfe885462016-09-22 10:24:38 +01003206
3207 if (instruction->IsDiv()) {
3208 __ Asr(out, out, ctz_imm);
3209 if (imm < 0) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003210 __ Rsb(out, out, 0);
Scott Wakelingfe885462016-09-22 10:24:38 +01003211 }
3212 } else {
3213 __ Ubfx(out, out, 0, ctz_imm);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003214 __ Sub(out, out, temp);
Scott Wakelingfe885462016-09-22 10:24:38 +01003215 }
3216}
3217
3218void InstructionCodeGeneratorARMVIXL::GenerateDivRemWithAnyConstant(HBinaryOperation* instruction) {
3219 DCHECK(instruction->IsDiv() || instruction->IsRem());
3220 DCHECK(instruction->GetResultType() == Primitive::kPrimInt);
3221
3222 LocationSummary* locations = instruction->GetLocations();
3223 Location second = locations->InAt(1);
3224 DCHECK(second.IsConstant());
3225
3226 vixl32::Register out = OutputRegister(instruction);
3227 vixl32::Register dividend = InputRegisterAt(instruction, 0);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003228 vixl32::Register temp1 = RegisterFrom(locations->GetTemp(0));
3229 vixl32::Register temp2 = RegisterFrom(locations->GetTemp(1));
Scott Wakelingb77051e2016-11-21 19:46:00 +00003230 int32_t imm = Int32ConstantFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01003231
3232 int64_t magic;
3233 int shift;
3234 CalculateMagicAndShiftForDivRem(imm, false /* is_long */, &magic, &shift);
3235
Anton Kirilovdda43962016-11-21 19:55:20 +00003236 // TODO(VIXL): Change the static cast to Operand::From() after VIXL is fixed.
3237 __ Mov(temp1, static_cast<int32_t>(magic));
Scott Wakelingfe885462016-09-22 10:24:38 +01003238 __ Smull(temp2, temp1, dividend, temp1);
3239
3240 if (imm > 0 && magic < 0) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003241 __ Add(temp1, temp1, dividend);
Scott Wakelingfe885462016-09-22 10:24:38 +01003242 } else if (imm < 0 && magic > 0) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003243 __ Sub(temp1, temp1, dividend);
Scott Wakelingfe885462016-09-22 10:24:38 +01003244 }
3245
3246 if (shift != 0) {
3247 __ Asr(temp1, temp1, shift);
3248 }
3249
3250 if (instruction->IsDiv()) {
3251 __ Sub(out, temp1, Operand(temp1, vixl32::Shift(ASR), 31));
3252 } else {
3253 __ Sub(temp1, temp1, Operand(temp1, vixl32::Shift(ASR), 31));
3254 // TODO: Strength reduction for mls.
3255 __ Mov(temp2, imm);
3256 __ Mls(out, temp1, temp2, dividend);
3257 }
3258}
3259
3260void InstructionCodeGeneratorARMVIXL::GenerateDivRemConstantIntegral(
3261 HBinaryOperation* instruction) {
3262 DCHECK(instruction->IsDiv() || instruction->IsRem());
3263 DCHECK(instruction->GetResultType() == Primitive::kPrimInt);
3264
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003265 Location second = instruction->GetLocations()->InAt(1);
Scott Wakelingfe885462016-09-22 10:24:38 +01003266 DCHECK(second.IsConstant());
3267
Anton Kirilov644032c2016-12-06 17:51:43 +00003268 int32_t imm = Int32ConstantFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01003269 if (imm == 0) {
3270 // Do not generate anything. DivZeroCheck would prevent any code to be executed.
3271 } else if (imm == 1 || imm == -1) {
3272 DivRemOneOrMinusOne(instruction);
3273 } else if (IsPowerOfTwo(AbsOrMin(imm))) {
3274 DivRemByPowerOfTwo(instruction);
3275 } else {
3276 DCHECK(imm <= -2 || imm >= 2);
3277 GenerateDivRemWithAnyConstant(instruction);
3278 }
3279}
3280
3281void LocationsBuilderARMVIXL::VisitDiv(HDiv* div) {
3282 LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
3283 if (div->GetResultType() == Primitive::kPrimLong) {
3284 // pLdiv runtime call.
3285 call_kind = LocationSummary::kCallOnMainOnly;
3286 } else if (div->GetResultType() == Primitive::kPrimInt && div->InputAt(1)->IsConstant()) {
3287 // sdiv will be replaced by other instruction sequence.
3288 } else if (div->GetResultType() == Primitive::kPrimInt &&
3289 !codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
3290 // pIdivmod runtime call.
3291 call_kind = LocationSummary::kCallOnMainOnly;
3292 }
3293
3294 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(div, call_kind);
3295
3296 switch (div->GetResultType()) {
3297 case Primitive::kPrimInt: {
3298 if (div->InputAt(1)->IsConstant()) {
3299 locations->SetInAt(0, Location::RequiresRegister());
3300 locations->SetInAt(1, Location::ConstantLocation(div->InputAt(1)->AsConstant()));
3301 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Anton Kirilov644032c2016-12-06 17:51:43 +00003302 int32_t value = Int32ConstantFrom(div->InputAt(1));
Scott Wakelingfe885462016-09-22 10:24:38 +01003303 if (value == 1 || value == 0 || value == -1) {
3304 // No temp register required.
3305 } else {
3306 locations->AddTemp(Location::RequiresRegister());
3307 if (!IsPowerOfTwo(AbsOrMin(value))) {
3308 locations->AddTemp(Location::RequiresRegister());
3309 }
3310 }
3311 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
3312 locations->SetInAt(0, Location::RequiresRegister());
3313 locations->SetInAt(1, Location::RequiresRegister());
3314 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3315 } else {
Artem Serov551b28f2016-10-18 19:11:30 +01003316 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3317 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
3318 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
3319 // Note: divrem will compute both the quotient and the remainder as the pair R0 and R1, but
3320 // we only need the former.
3321 locations->SetOut(LocationFrom(r0));
Scott Wakelingfe885462016-09-22 10:24:38 +01003322 }
3323 break;
3324 }
3325 case Primitive::kPrimLong: {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01003326 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3327 locations->SetInAt(0, LocationFrom(
3328 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
3329 locations->SetInAt(1, LocationFrom(
3330 calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3)));
3331 locations->SetOut(LocationFrom(r0, r1));
Scott Wakelingfe885462016-09-22 10:24:38 +01003332 break;
3333 }
3334 case Primitive::kPrimFloat:
3335 case Primitive::kPrimDouble: {
3336 locations->SetInAt(0, Location::RequiresFpuRegister());
3337 locations->SetInAt(1, Location::RequiresFpuRegister());
3338 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3339 break;
3340 }
3341
3342 default:
3343 LOG(FATAL) << "Unexpected div type " << div->GetResultType();
3344 }
3345}
3346
3347void InstructionCodeGeneratorARMVIXL::VisitDiv(HDiv* div) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01003348 Location lhs = div->GetLocations()->InAt(0);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003349 Location rhs = div->GetLocations()->InAt(1);
Scott Wakelingfe885462016-09-22 10:24:38 +01003350
3351 switch (div->GetResultType()) {
3352 case Primitive::kPrimInt: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003353 if (rhs.IsConstant()) {
Scott Wakelingfe885462016-09-22 10:24:38 +01003354 GenerateDivRemConstantIntegral(div);
3355 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
3356 __ Sdiv(OutputRegister(div), InputRegisterAt(div, 0), InputRegisterAt(div, 1));
3357 } else {
Artem Serov551b28f2016-10-18 19:11:30 +01003358 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3359 DCHECK(calling_convention.GetRegisterAt(0).Is(RegisterFrom(lhs)));
3360 DCHECK(calling_convention.GetRegisterAt(1).Is(RegisterFrom(rhs)));
3361 DCHECK(r0.Is(OutputRegister(div)));
3362
3363 codegen_->InvokeRuntime(kQuickIdivmod, div, div->GetDexPc());
3364 CheckEntrypointTypes<kQuickIdivmod, int32_t, int32_t, int32_t>();
Scott Wakelingfe885462016-09-22 10:24:38 +01003365 }
3366 break;
3367 }
3368
3369 case Primitive::kPrimLong: {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01003370 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3371 DCHECK(calling_convention.GetRegisterAt(0).Is(LowRegisterFrom(lhs)));
3372 DCHECK(calling_convention.GetRegisterAt(1).Is(HighRegisterFrom(lhs)));
3373 DCHECK(calling_convention.GetRegisterAt(2).Is(LowRegisterFrom(rhs)));
3374 DCHECK(calling_convention.GetRegisterAt(3).Is(HighRegisterFrom(rhs)));
3375 DCHECK(LowRegisterFrom(div->GetLocations()->Out()).Is(r0));
3376 DCHECK(HighRegisterFrom(div->GetLocations()->Out()).Is(r1));
3377
3378 codegen_->InvokeRuntime(kQuickLdiv, div, div->GetDexPc());
3379 CheckEntrypointTypes<kQuickLdiv, int64_t, int64_t, int64_t>();
Scott Wakelingfe885462016-09-22 10:24:38 +01003380 break;
3381 }
3382
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003383 case Primitive::kPrimFloat:
3384 case Primitive::kPrimDouble:
3385 __ Vdiv(OutputVRegister(div), InputVRegisterAt(div, 0), InputVRegisterAt(div, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01003386 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01003387
3388 default:
3389 LOG(FATAL) << "Unexpected div type " << div->GetResultType();
3390 }
3391}
3392
Artem Serov551b28f2016-10-18 19:11:30 +01003393void LocationsBuilderARMVIXL::VisitRem(HRem* rem) {
3394 Primitive::Type type = rem->GetResultType();
3395
3396 // Most remainders are implemented in the runtime.
3397 LocationSummary::CallKind call_kind = LocationSummary::kCallOnMainOnly;
3398 if (rem->GetResultType() == Primitive::kPrimInt && rem->InputAt(1)->IsConstant()) {
3399 // sdiv will be replaced by other instruction sequence.
3400 call_kind = LocationSummary::kNoCall;
3401 } else if ((rem->GetResultType() == Primitive::kPrimInt)
3402 && codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
3403 // Have hardware divide instruction for int, do it with three instructions.
3404 call_kind = LocationSummary::kNoCall;
3405 }
3406
3407 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(rem, call_kind);
3408
3409 switch (type) {
3410 case Primitive::kPrimInt: {
3411 if (rem->InputAt(1)->IsConstant()) {
3412 locations->SetInAt(0, Location::RequiresRegister());
3413 locations->SetInAt(1, Location::ConstantLocation(rem->InputAt(1)->AsConstant()));
3414 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Anton Kirilov644032c2016-12-06 17:51:43 +00003415 int32_t value = Int32ConstantFrom(rem->InputAt(1));
Artem Serov551b28f2016-10-18 19:11:30 +01003416 if (value == 1 || value == 0 || value == -1) {
3417 // No temp register required.
3418 } else {
3419 locations->AddTemp(Location::RequiresRegister());
3420 if (!IsPowerOfTwo(AbsOrMin(value))) {
3421 locations->AddTemp(Location::RequiresRegister());
3422 }
3423 }
3424 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
3425 locations->SetInAt(0, Location::RequiresRegister());
3426 locations->SetInAt(1, Location::RequiresRegister());
3427 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3428 locations->AddTemp(Location::RequiresRegister());
3429 } else {
3430 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3431 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
3432 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
3433 // Note: divrem will compute both the quotient and the remainder as the pair R0 and R1, but
3434 // we only need the latter.
3435 locations->SetOut(LocationFrom(r1));
3436 }
3437 break;
3438 }
3439 case Primitive::kPrimLong: {
3440 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3441 locations->SetInAt(0, LocationFrom(
3442 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
3443 locations->SetInAt(1, LocationFrom(
3444 calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3)));
3445 // The runtime helper puts the output in R2,R3.
3446 locations->SetOut(LocationFrom(r2, r3));
3447 break;
3448 }
3449 case Primitive::kPrimFloat: {
3450 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3451 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
3452 locations->SetInAt(1, LocationFrom(calling_convention.GetFpuRegisterAt(1)));
3453 locations->SetOut(LocationFrom(s0));
3454 break;
3455 }
3456
3457 case Primitive::kPrimDouble: {
3458 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3459 locations->SetInAt(0, LocationFrom(
3460 calling_convention.GetFpuRegisterAt(0), calling_convention.GetFpuRegisterAt(1)));
3461 locations->SetInAt(1, LocationFrom(
3462 calling_convention.GetFpuRegisterAt(2), calling_convention.GetFpuRegisterAt(3)));
3463 locations->SetOut(LocationFrom(s0, s1));
3464 break;
3465 }
3466
3467 default:
3468 LOG(FATAL) << "Unexpected rem type " << type;
3469 }
3470}
3471
3472void InstructionCodeGeneratorARMVIXL::VisitRem(HRem* rem) {
3473 LocationSummary* locations = rem->GetLocations();
3474 Location second = locations->InAt(1);
3475
3476 Primitive::Type type = rem->GetResultType();
3477 switch (type) {
3478 case Primitive::kPrimInt: {
3479 vixl32::Register reg1 = InputRegisterAt(rem, 0);
3480 vixl32::Register out_reg = OutputRegister(rem);
3481 if (second.IsConstant()) {
3482 GenerateDivRemConstantIntegral(rem);
3483 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
3484 vixl32::Register reg2 = RegisterFrom(second);
3485 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
3486
3487 // temp = reg1 / reg2 (integer division)
3488 // dest = reg1 - temp * reg2
3489 __ Sdiv(temp, reg1, reg2);
3490 __ Mls(out_reg, temp, reg2, reg1);
3491 } else {
3492 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3493 DCHECK(reg1.Is(calling_convention.GetRegisterAt(0)));
3494 DCHECK(RegisterFrom(second).Is(calling_convention.GetRegisterAt(1)));
3495 DCHECK(out_reg.Is(r1));
3496
3497 codegen_->InvokeRuntime(kQuickIdivmod, rem, rem->GetDexPc());
3498 CheckEntrypointTypes<kQuickIdivmod, int32_t, int32_t, int32_t>();
3499 }
3500 break;
3501 }
3502
3503 case Primitive::kPrimLong: {
3504 codegen_->InvokeRuntime(kQuickLmod, rem, rem->GetDexPc());
3505 CheckEntrypointTypes<kQuickLmod, int64_t, int64_t, int64_t>();
3506 break;
3507 }
3508
3509 case Primitive::kPrimFloat: {
3510 codegen_->InvokeRuntime(kQuickFmodf, rem, rem->GetDexPc());
3511 CheckEntrypointTypes<kQuickFmodf, float, float, float>();
3512 break;
3513 }
3514
3515 case Primitive::kPrimDouble: {
3516 codegen_->InvokeRuntime(kQuickFmod, rem, rem->GetDexPc());
3517 CheckEntrypointTypes<kQuickFmod, double, double, double>();
3518 break;
3519 }
3520
3521 default:
3522 LOG(FATAL) << "Unexpected rem type " << type;
3523 }
3524}
3525
3526
Scott Wakelingfe885462016-09-22 10:24:38 +01003527void LocationsBuilderARMVIXL::VisitDivZeroCheck(HDivZeroCheck* instruction) {
Artem Serov657022c2016-11-23 14:19:38 +00003528 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
Scott Wakelingfe885462016-09-22 10:24:38 +01003529 locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0)));
Scott Wakelingfe885462016-09-22 10:24:38 +01003530}
3531
3532void InstructionCodeGeneratorARMVIXL::VisitDivZeroCheck(HDivZeroCheck* instruction) {
3533 DivZeroCheckSlowPathARMVIXL* slow_path =
3534 new (GetGraph()->GetArena()) DivZeroCheckSlowPathARMVIXL(instruction);
3535 codegen_->AddSlowPath(slow_path);
3536
3537 LocationSummary* locations = instruction->GetLocations();
3538 Location value = locations->InAt(0);
3539
3540 switch (instruction->GetType()) {
3541 case Primitive::kPrimBoolean:
3542 case Primitive::kPrimByte:
3543 case Primitive::kPrimChar:
3544 case Primitive::kPrimShort:
3545 case Primitive::kPrimInt: {
3546 if (value.IsRegister()) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00003547 __ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
Scott Wakelingfe885462016-09-22 10:24:38 +01003548 } else {
3549 DCHECK(value.IsConstant()) << value;
Anton Kirilov644032c2016-12-06 17:51:43 +00003550 if (Int32ConstantFrom(value) == 0) {
Scott Wakelingfe885462016-09-22 10:24:38 +01003551 __ B(slow_path->GetEntryLabel());
3552 }
3553 }
3554 break;
3555 }
3556 case Primitive::kPrimLong: {
3557 if (value.IsRegisterPair()) {
3558 UseScratchRegisterScope temps(GetVIXLAssembler());
3559 vixl32::Register temp = temps.Acquire();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003560 __ Orrs(temp, LowRegisterFrom(value), HighRegisterFrom(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01003561 __ B(eq, slow_path->GetEntryLabel());
3562 } else {
3563 DCHECK(value.IsConstant()) << value;
Anton Kirilov644032c2016-12-06 17:51:43 +00003564 if (Int64ConstantFrom(value) == 0) {
Scott Wakelingfe885462016-09-22 10:24:38 +01003565 __ B(slow_path->GetEntryLabel());
3566 }
3567 }
3568 break;
3569 }
3570 default:
3571 LOG(FATAL) << "Unexpected type for HDivZeroCheck " << instruction->GetType();
3572 }
3573}
3574
Artem Serov02109dd2016-09-23 17:17:54 +01003575void InstructionCodeGeneratorARMVIXL::HandleIntegerRotate(HRor* ror) {
3576 LocationSummary* locations = ror->GetLocations();
3577 vixl32::Register in = InputRegisterAt(ror, 0);
3578 Location rhs = locations->InAt(1);
3579 vixl32::Register out = OutputRegister(ror);
3580
3581 if (rhs.IsConstant()) {
3582 // Arm32 and Thumb2 assemblers require a rotation on the interval [1,31],
3583 // so map all rotations to a +ve. equivalent in that range.
3584 // (e.g. left *or* right by -2 bits == 30 bits in the same direction.)
3585 uint32_t rot = CodeGenerator::GetInt32ValueOf(rhs.GetConstant()) & 0x1F;
3586 if (rot) {
3587 // Rotate, mapping left rotations to right equivalents if necessary.
3588 // (e.g. left by 2 bits == right by 30.)
3589 __ Ror(out, in, rot);
3590 } else if (!out.Is(in)) {
3591 __ Mov(out, in);
3592 }
3593 } else {
3594 __ Ror(out, in, RegisterFrom(rhs));
3595 }
3596}
3597
3598// Gain some speed by mapping all Long rotates onto equivalent pairs of Integer
3599// rotates by swapping input regs (effectively rotating by the first 32-bits of
3600// a larger rotation) or flipping direction (thus treating larger right/left
3601// rotations as sub-word sized rotations in the other direction) as appropriate.
3602void InstructionCodeGeneratorARMVIXL::HandleLongRotate(HRor* ror) {
3603 LocationSummary* locations = ror->GetLocations();
3604 vixl32::Register in_reg_lo = LowRegisterFrom(locations->InAt(0));
3605 vixl32::Register in_reg_hi = HighRegisterFrom(locations->InAt(0));
3606 Location rhs = locations->InAt(1);
3607 vixl32::Register out_reg_lo = LowRegisterFrom(locations->Out());
3608 vixl32::Register out_reg_hi = HighRegisterFrom(locations->Out());
3609
3610 if (rhs.IsConstant()) {
3611 uint64_t rot = CodeGenerator::GetInt64ValueOf(rhs.GetConstant());
3612 // Map all rotations to +ve. equivalents on the interval [0,63].
3613 rot &= kMaxLongShiftDistance;
3614 // For rotates over a word in size, 'pre-rotate' by 32-bits to keep rotate
3615 // logic below to a simple pair of binary orr.
3616 // (e.g. 34 bits == in_reg swap + 2 bits right.)
3617 if (rot >= kArmBitsPerWord) {
3618 rot -= kArmBitsPerWord;
3619 std::swap(in_reg_hi, in_reg_lo);
3620 }
3621 // Rotate, or mov to out for zero or word size rotations.
3622 if (rot != 0u) {
Scott Wakelingb77051e2016-11-21 19:46:00 +00003623 __ Lsr(out_reg_hi, in_reg_hi, Operand::From(rot));
Artem Serov02109dd2016-09-23 17:17:54 +01003624 __ Orr(out_reg_hi, out_reg_hi, Operand(in_reg_lo, ShiftType::LSL, kArmBitsPerWord - rot));
Scott Wakelingb77051e2016-11-21 19:46:00 +00003625 __ Lsr(out_reg_lo, in_reg_lo, Operand::From(rot));
Artem Serov02109dd2016-09-23 17:17:54 +01003626 __ Orr(out_reg_lo, out_reg_lo, Operand(in_reg_hi, ShiftType::LSL, kArmBitsPerWord - rot));
3627 } else {
3628 __ Mov(out_reg_lo, in_reg_lo);
3629 __ Mov(out_reg_hi, in_reg_hi);
3630 }
3631 } else {
3632 vixl32::Register shift_right = RegisterFrom(locations->GetTemp(0));
3633 vixl32::Register shift_left = RegisterFrom(locations->GetTemp(1));
3634 vixl32::Label end;
3635 vixl32::Label shift_by_32_plus_shift_right;
3636
3637 __ And(shift_right, RegisterFrom(rhs), 0x1F);
3638 __ Lsrs(shift_left, RegisterFrom(rhs), 6);
Scott Wakelingbffdc702016-12-07 17:46:03 +00003639 __ Rsb(LeaveFlags, shift_left, shift_right, Operand::From(kArmBitsPerWord));
Artem Serov517d9f62016-12-12 15:51:15 +00003640 __ B(cc, &shift_by_32_plus_shift_right, /* far_target */ false);
Artem Serov02109dd2016-09-23 17:17:54 +01003641
3642 // out_reg_hi = (reg_hi << shift_left) | (reg_lo >> shift_right).
3643 // out_reg_lo = (reg_lo << shift_left) | (reg_hi >> shift_right).
3644 __ Lsl(out_reg_hi, in_reg_hi, shift_left);
3645 __ Lsr(out_reg_lo, in_reg_lo, shift_right);
3646 __ Add(out_reg_hi, out_reg_hi, out_reg_lo);
3647 __ Lsl(out_reg_lo, in_reg_lo, shift_left);
3648 __ Lsr(shift_left, in_reg_hi, shift_right);
3649 __ Add(out_reg_lo, out_reg_lo, shift_left);
3650 __ B(&end);
3651
3652 __ Bind(&shift_by_32_plus_shift_right); // Shift by 32+shift_right.
3653 // out_reg_hi = (reg_hi >> shift_right) | (reg_lo << shift_left).
3654 // out_reg_lo = (reg_lo >> shift_right) | (reg_hi << shift_left).
3655 __ Lsr(out_reg_hi, in_reg_hi, shift_right);
3656 __ Lsl(out_reg_lo, in_reg_lo, shift_left);
3657 __ Add(out_reg_hi, out_reg_hi, out_reg_lo);
3658 __ Lsr(out_reg_lo, in_reg_lo, shift_right);
3659 __ Lsl(shift_right, in_reg_hi, shift_left);
3660 __ Add(out_reg_lo, out_reg_lo, shift_right);
3661
3662 __ Bind(&end);
3663 }
3664}
3665
3666void LocationsBuilderARMVIXL::VisitRor(HRor* ror) {
3667 LocationSummary* locations =
3668 new (GetGraph()->GetArena()) LocationSummary(ror, LocationSummary::kNoCall);
3669 switch (ror->GetResultType()) {
3670 case Primitive::kPrimInt: {
3671 locations->SetInAt(0, Location::RequiresRegister());
3672 locations->SetInAt(1, Location::RegisterOrConstant(ror->InputAt(1)));
3673 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3674 break;
3675 }
3676 case Primitive::kPrimLong: {
3677 locations->SetInAt(0, Location::RequiresRegister());
3678 if (ror->InputAt(1)->IsConstant()) {
3679 locations->SetInAt(1, Location::ConstantLocation(ror->InputAt(1)->AsConstant()));
3680 } else {
3681 locations->SetInAt(1, Location::RequiresRegister());
3682 locations->AddTemp(Location::RequiresRegister());
3683 locations->AddTemp(Location::RequiresRegister());
3684 }
3685 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
3686 break;
3687 }
3688 default:
3689 LOG(FATAL) << "Unexpected operation type " << ror->GetResultType();
3690 }
3691}
3692
3693void InstructionCodeGeneratorARMVIXL::VisitRor(HRor* ror) {
3694 Primitive::Type type = ror->GetResultType();
3695 switch (type) {
3696 case Primitive::kPrimInt: {
3697 HandleIntegerRotate(ror);
3698 break;
3699 }
3700 case Primitive::kPrimLong: {
3701 HandleLongRotate(ror);
3702 break;
3703 }
3704 default:
3705 LOG(FATAL) << "Unexpected operation type " << type;
3706 UNREACHABLE();
3707 }
3708}
3709
Artem Serov02d37832016-10-25 15:25:33 +01003710void LocationsBuilderARMVIXL::HandleShift(HBinaryOperation* op) {
3711 DCHECK(op->IsShl() || op->IsShr() || op->IsUShr());
3712
3713 LocationSummary* locations =
3714 new (GetGraph()->GetArena()) LocationSummary(op, LocationSummary::kNoCall);
3715
3716 switch (op->GetResultType()) {
3717 case Primitive::kPrimInt: {
3718 locations->SetInAt(0, Location::RequiresRegister());
3719 if (op->InputAt(1)->IsConstant()) {
3720 locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant()));
3721 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3722 } else {
3723 locations->SetInAt(1, Location::RequiresRegister());
3724 // Make the output overlap, as it will be used to hold the masked
3725 // second input.
3726 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
3727 }
3728 break;
3729 }
3730 case Primitive::kPrimLong: {
3731 locations->SetInAt(0, Location::RequiresRegister());
3732 if (op->InputAt(1)->IsConstant()) {
3733 locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant()));
3734 // For simplicity, use kOutputOverlap even though we only require that low registers
3735 // don't clash with high registers which the register allocator currently guarantees.
3736 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
3737 } else {
3738 locations->SetInAt(1, Location::RequiresRegister());
3739 locations->AddTemp(Location::RequiresRegister());
3740 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
3741 }
3742 break;
3743 }
3744 default:
3745 LOG(FATAL) << "Unexpected operation type " << op->GetResultType();
3746 }
3747}
3748
3749void InstructionCodeGeneratorARMVIXL::HandleShift(HBinaryOperation* op) {
3750 DCHECK(op->IsShl() || op->IsShr() || op->IsUShr());
3751
3752 LocationSummary* locations = op->GetLocations();
3753 Location out = locations->Out();
3754 Location first = locations->InAt(0);
3755 Location second = locations->InAt(1);
3756
3757 Primitive::Type type = op->GetResultType();
3758 switch (type) {
3759 case Primitive::kPrimInt: {
3760 vixl32::Register out_reg = OutputRegister(op);
3761 vixl32::Register first_reg = InputRegisterAt(op, 0);
3762 if (second.IsRegister()) {
3763 vixl32::Register second_reg = RegisterFrom(second);
3764 // ARM doesn't mask the shift count so we need to do it ourselves.
3765 __ And(out_reg, second_reg, kMaxIntShiftDistance);
3766 if (op->IsShl()) {
3767 __ Lsl(out_reg, first_reg, out_reg);
3768 } else if (op->IsShr()) {
3769 __ Asr(out_reg, first_reg, out_reg);
3770 } else {
3771 __ Lsr(out_reg, first_reg, out_reg);
3772 }
3773 } else {
Anton Kirilov644032c2016-12-06 17:51:43 +00003774 int32_t cst = Int32ConstantFrom(second);
Artem Serov02d37832016-10-25 15:25:33 +01003775 uint32_t shift_value = cst & kMaxIntShiftDistance;
3776 if (shift_value == 0) { // ARM does not support shifting with 0 immediate.
3777 __ Mov(out_reg, first_reg);
3778 } else if (op->IsShl()) {
3779 __ Lsl(out_reg, first_reg, shift_value);
3780 } else if (op->IsShr()) {
3781 __ Asr(out_reg, first_reg, shift_value);
3782 } else {
3783 __ Lsr(out_reg, first_reg, shift_value);
3784 }
3785 }
3786 break;
3787 }
3788 case Primitive::kPrimLong: {
3789 vixl32::Register o_h = HighRegisterFrom(out);
3790 vixl32::Register o_l = LowRegisterFrom(out);
3791
3792 vixl32::Register high = HighRegisterFrom(first);
3793 vixl32::Register low = LowRegisterFrom(first);
3794
3795 if (second.IsRegister()) {
3796 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
3797
3798 vixl32::Register second_reg = RegisterFrom(second);
3799
3800 if (op->IsShl()) {
3801 __ And(o_l, second_reg, kMaxLongShiftDistance);
3802 // Shift the high part
3803 __ Lsl(o_h, high, o_l);
3804 // Shift the low part and `or` what overflew on the high part
Scott Wakelingb77051e2016-11-21 19:46:00 +00003805 __ Rsb(temp, o_l, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01003806 __ Lsr(temp, low, temp);
3807 __ Orr(o_h, o_h, temp);
3808 // If the shift is > 32 bits, override the high part
Scott Wakelingb77051e2016-11-21 19:46:00 +00003809 __ Subs(temp, o_l, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01003810 {
Artem Serov0fb37192016-12-06 18:13:40 +00003811 ExactAssemblyScope guard(GetVIXLAssembler(),
3812 2 * vixl32::kMaxInstructionSizeInBytes,
3813 CodeBufferCheckScope::kMaximumSize);
Artem Serov02d37832016-10-25 15:25:33 +01003814 __ it(pl);
3815 __ lsl(pl, o_h, low, temp);
3816 }
3817 // Shift the low part
3818 __ Lsl(o_l, low, o_l);
3819 } else if (op->IsShr()) {
3820 __ And(o_h, second_reg, kMaxLongShiftDistance);
3821 // Shift the low part
3822 __ Lsr(o_l, low, o_h);
3823 // Shift the high part and `or` what underflew on the low part
Scott Wakelingb77051e2016-11-21 19:46:00 +00003824 __ Rsb(temp, o_h, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01003825 __ Lsl(temp, high, temp);
3826 __ Orr(o_l, o_l, temp);
3827 // If the shift is > 32 bits, override the low part
Scott Wakelingb77051e2016-11-21 19:46:00 +00003828 __ Subs(temp, o_h, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01003829 {
Artem Serov0fb37192016-12-06 18:13:40 +00003830 ExactAssemblyScope guard(GetVIXLAssembler(),
3831 2 * vixl32::kMaxInstructionSizeInBytes,
3832 CodeBufferCheckScope::kMaximumSize);
Artem Serov02d37832016-10-25 15:25:33 +01003833 __ it(pl);
3834 __ asr(pl, o_l, high, temp);
3835 }
3836 // Shift the high part
3837 __ Asr(o_h, high, o_h);
3838 } else {
3839 __ And(o_h, second_reg, kMaxLongShiftDistance);
3840 // same as Shr except we use `Lsr`s and not `Asr`s
3841 __ Lsr(o_l, low, o_h);
Scott Wakelingb77051e2016-11-21 19:46:00 +00003842 __ Rsb(temp, o_h, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01003843 __ Lsl(temp, high, temp);
3844 __ Orr(o_l, o_l, temp);
Scott Wakelingb77051e2016-11-21 19:46:00 +00003845 __ Subs(temp, o_h, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01003846 {
Artem Serov0fb37192016-12-06 18:13:40 +00003847 ExactAssemblyScope guard(GetVIXLAssembler(),
3848 2 * vixl32::kMaxInstructionSizeInBytes,
3849 CodeBufferCheckScope::kMaximumSize);
Artem Serov02d37832016-10-25 15:25:33 +01003850 __ it(pl);
3851 __ lsr(pl, o_l, high, temp);
3852 }
3853 __ Lsr(o_h, high, o_h);
3854 }
3855 } else {
3856 // Register allocator doesn't create partial overlap.
3857 DCHECK(!o_l.Is(high));
3858 DCHECK(!o_h.Is(low));
Anton Kirilov644032c2016-12-06 17:51:43 +00003859 int32_t cst = Int32ConstantFrom(second);
Artem Serov02d37832016-10-25 15:25:33 +01003860 uint32_t shift_value = cst & kMaxLongShiftDistance;
3861 if (shift_value > 32) {
3862 if (op->IsShl()) {
3863 __ Lsl(o_h, low, shift_value - 32);
3864 __ Mov(o_l, 0);
3865 } else if (op->IsShr()) {
3866 __ Asr(o_l, high, shift_value - 32);
3867 __ Asr(o_h, high, 31);
3868 } else {
3869 __ Lsr(o_l, high, shift_value - 32);
3870 __ Mov(o_h, 0);
3871 }
3872 } else if (shift_value == 32) {
3873 if (op->IsShl()) {
3874 __ Mov(o_h, low);
3875 __ Mov(o_l, 0);
3876 } else if (op->IsShr()) {
3877 __ Mov(o_l, high);
3878 __ Asr(o_h, high, 31);
3879 } else {
3880 __ Mov(o_l, high);
3881 __ Mov(o_h, 0);
3882 }
3883 } else if (shift_value == 1) {
3884 if (op->IsShl()) {
3885 __ Lsls(o_l, low, 1);
3886 __ Adc(o_h, high, high);
3887 } else if (op->IsShr()) {
3888 __ Asrs(o_h, high, 1);
3889 __ Rrx(o_l, low);
3890 } else {
3891 __ Lsrs(o_h, high, 1);
3892 __ Rrx(o_l, low);
3893 }
3894 } else {
3895 DCHECK(2 <= shift_value && shift_value < 32) << shift_value;
3896 if (op->IsShl()) {
3897 __ Lsl(o_h, high, shift_value);
3898 __ Orr(o_h, o_h, Operand(low, ShiftType::LSR, 32 - shift_value));
3899 __ Lsl(o_l, low, shift_value);
3900 } else if (op->IsShr()) {
3901 __ Lsr(o_l, low, shift_value);
3902 __ Orr(o_l, o_l, Operand(high, ShiftType::LSL, 32 - shift_value));
3903 __ Asr(o_h, high, shift_value);
3904 } else {
3905 __ Lsr(o_l, low, shift_value);
3906 __ Orr(o_l, o_l, Operand(high, ShiftType::LSL, 32 - shift_value));
3907 __ Lsr(o_h, high, shift_value);
3908 }
3909 }
3910 }
3911 break;
3912 }
3913 default:
3914 LOG(FATAL) << "Unexpected operation type " << type;
3915 UNREACHABLE();
3916 }
3917}
3918
3919void LocationsBuilderARMVIXL::VisitShl(HShl* shl) {
3920 HandleShift(shl);
3921}
3922
3923void InstructionCodeGeneratorARMVIXL::VisitShl(HShl* shl) {
3924 HandleShift(shl);
3925}
3926
3927void LocationsBuilderARMVIXL::VisitShr(HShr* shr) {
3928 HandleShift(shr);
3929}
3930
3931void InstructionCodeGeneratorARMVIXL::VisitShr(HShr* shr) {
3932 HandleShift(shr);
3933}
3934
3935void LocationsBuilderARMVIXL::VisitUShr(HUShr* ushr) {
3936 HandleShift(ushr);
3937}
3938
3939void InstructionCodeGeneratorARMVIXL::VisitUShr(HUShr* ushr) {
3940 HandleShift(ushr);
3941}
3942
3943void LocationsBuilderARMVIXL::VisitNewInstance(HNewInstance* instruction) {
3944 LocationSummary* locations =
3945 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
3946 if (instruction->IsStringAlloc()) {
3947 locations->AddTemp(LocationFrom(kMethodRegister));
3948 } else {
3949 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3950 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
3951 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
3952 }
3953 locations->SetOut(LocationFrom(r0));
3954}
3955
3956void InstructionCodeGeneratorARMVIXL::VisitNewInstance(HNewInstance* instruction) {
3957 // Note: if heap poisoning is enabled, the entry point takes cares
3958 // of poisoning the reference.
3959 if (instruction->IsStringAlloc()) {
3960 // String is allocated through StringFactory. Call NewEmptyString entry point.
3961 vixl32::Register temp = RegisterFrom(instruction->GetLocations()->GetTemp(0));
3962 MemberOffset code_offset = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize);
3963 GetAssembler()->LoadFromOffset(kLoadWord, temp, tr, QUICK_ENTRY_POINT(pNewEmptyString));
3964 GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, code_offset.Int32Value());
Alexandre Rames374ddf32016-11-04 10:40:49 +00003965 // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
Artem Serov0fb37192016-12-06 18:13:40 +00003966 ExactAssemblyScope aas(GetVIXLAssembler(),
3967 vixl32::k16BitT32InstructionSizeInBytes,
3968 CodeBufferCheckScope::kExactSize);
Artem Serov02d37832016-10-25 15:25:33 +01003969 __ blx(lr);
3970 codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
3971 } else {
3972 codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
3973 CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, ArtMethod*>();
3974 }
3975}
3976
3977void LocationsBuilderARMVIXL::VisitNewArray(HNewArray* instruction) {
3978 LocationSummary* locations =
3979 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
3980 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3981 locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(0)));
3982 locations->SetOut(LocationFrom(r0));
3983 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(1)));
3984 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(2)));
3985}
3986
3987void InstructionCodeGeneratorARMVIXL::VisitNewArray(HNewArray* instruction) {
3988 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Andreas Gampea5b09a62016-11-17 15:21:22 -08003989 __ Mov(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex().index_);
Artem Serov02d37832016-10-25 15:25:33 +01003990 // Note: if heap poisoning is enabled, the entry point takes cares
3991 // of poisoning the reference.
3992 codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
3993 CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>();
3994}
3995
3996void LocationsBuilderARMVIXL::VisitParameterValue(HParameterValue* instruction) {
3997 LocationSummary* locations =
3998 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
3999 Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
4000 if (location.IsStackSlot()) {
4001 location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
4002 } else if (location.IsDoubleStackSlot()) {
4003 location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
4004 }
4005 locations->SetOut(location);
4006}
4007
4008void InstructionCodeGeneratorARMVIXL::VisitParameterValue(
4009 HParameterValue* instruction ATTRIBUTE_UNUSED) {
4010 // Nothing to do, the parameter is already at its location.
4011}
4012
4013void LocationsBuilderARMVIXL::VisitCurrentMethod(HCurrentMethod* instruction) {
4014 LocationSummary* locations =
4015 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
4016 locations->SetOut(LocationFrom(kMethodRegister));
4017}
4018
4019void InstructionCodeGeneratorARMVIXL::VisitCurrentMethod(
4020 HCurrentMethod* instruction ATTRIBUTE_UNUSED) {
4021 // Nothing to do, the method is already at its location.
4022}
4023
4024void LocationsBuilderARMVIXL::VisitNot(HNot* not_) {
4025 LocationSummary* locations =
4026 new (GetGraph()->GetArena()) LocationSummary(not_, LocationSummary::kNoCall);
4027 locations->SetInAt(0, Location::RequiresRegister());
4028 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4029}
4030
4031void InstructionCodeGeneratorARMVIXL::VisitNot(HNot* not_) {
4032 LocationSummary* locations = not_->GetLocations();
4033 Location out = locations->Out();
4034 Location in = locations->InAt(0);
4035 switch (not_->GetResultType()) {
4036 case Primitive::kPrimInt:
4037 __ Mvn(OutputRegister(not_), InputRegisterAt(not_, 0));
4038 break;
4039
4040 case Primitive::kPrimLong:
4041 __ Mvn(LowRegisterFrom(out), LowRegisterFrom(in));
4042 __ Mvn(HighRegisterFrom(out), HighRegisterFrom(in));
4043 break;
4044
4045 default:
4046 LOG(FATAL) << "Unimplemented type for not operation " << not_->GetResultType();
4047 }
4048}
4049
Scott Wakelingc34dba72016-10-03 10:14:44 +01004050void LocationsBuilderARMVIXL::VisitBooleanNot(HBooleanNot* bool_not) {
4051 LocationSummary* locations =
4052 new (GetGraph()->GetArena()) LocationSummary(bool_not, LocationSummary::kNoCall);
4053 locations->SetInAt(0, Location::RequiresRegister());
4054 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4055}
4056
4057void InstructionCodeGeneratorARMVIXL::VisitBooleanNot(HBooleanNot* bool_not) {
4058 __ Eor(OutputRegister(bool_not), InputRegister(bool_not), 1);
4059}
4060
Artem Serov02d37832016-10-25 15:25:33 +01004061void LocationsBuilderARMVIXL::VisitCompare(HCompare* compare) {
4062 LocationSummary* locations =
4063 new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall);
4064 switch (compare->InputAt(0)->GetType()) {
4065 case Primitive::kPrimBoolean:
4066 case Primitive::kPrimByte:
4067 case Primitive::kPrimShort:
4068 case Primitive::kPrimChar:
4069 case Primitive::kPrimInt:
4070 case Primitive::kPrimLong: {
4071 locations->SetInAt(0, Location::RequiresRegister());
4072 locations->SetInAt(1, Location::RequiresRegister());
4073 // Output overlaps because it is written before doing the low comparison.
4074 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
4075 break;
4076 }
4077 case Primitive::kPrimFloat:
4078 case Primitive::kPrimDouble: {
4079 locations->SetInAt(0, Location::RequiresFpuRegister());
4080 locations->SetInAt(1, ArithmeticZeroOrFpuRegister(compare->InputAt(1)));
4081 locations->SetOut(Location::RequiresRegister());
4082 break;
4083 }
4084 default:
4085 LOG(FATAL) << "Unexpected type for compare operation " << compare->InputAt(0)->GetType();
4086 }
4087}
4088
4089void InstructionCodeGeneratorARMVIXL::VisitCompare(HCompare* compare) {
4090 LocationSummary* locations = compare->GetLocations();
4091 vixl32::Register out = OutputRegister(compare);
4092 Location left = locations->InAt(0);
4093 Location right = locations->InAt(1);
4094
4095 vixl32::Label less, greater, done;
4096 Primitive::Type type = compare->InputAt(0)->GetType();
4097 vixl32::Condition less_cond = vixl32::Condition(kNone);
4098 switch (type) {
4099 case Primitive::kPrimBoolean:
4100 case Primitive::kPrimByte:
4101 case Primitive::kPrimShort:
4102 case Primitive::kPrimChar:
4103 case Primitive::kPrimInt: {
4104 // Emit move to `out` before the `Cmp`, as `Mov` might affect the status flags.
4105 __ Mov(out, 0);
4106 __ Cmp(RegisterFrom(left), RegisterFrom(right)); // Signed compare.
4107 less_cond = lt;
4108 break;
4109 }
4110 case Primitive::kPrimLong: {
4111 __ Cmp(HighRegisterFrom(left), HighRegisterFrom(right)); // Signed compare.
Artem Serov517d9f62016-12-12 15:51:15 +00004112 __ B(lt, &less, /* far_target */ false);
4113 __ B(gt, &greater, /* far_target */ false);
Artem Serov02d37832016-10-25 15:25:33 +01004114 // Emit move to `out` before the last `Cmp`, as `Mov` might affect the status flags.
4115 __ Mov(out, 0);
4116 __ Cmp(LowRegisterFrom(left), LowRegisterFrom(right)); // Unsigned compare.
4117 less_cond = lo;
4118 break;
4119 }
4120 case Primitive::kPrimFloat:
4121 case Primitive::kPrimDouble: {
4122 __ Mov(out, 0);
4123 GenerateVcmp(compare);
4124 // To branch on the FP compare result we transfer FPSCR to APSR (encoded as PC in VMRS).
4125 __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
4126 less_cond = ARMFPCondition(kCondLT, compare->IsGtBias());
4127 break;
4128 }
4129 default:
4130 LOG(FATAL) << "Unexpected compare type " << type;
4131 UNREACHABLE();
4132 }
4133
Artem Serov517d9f62016-12-12 15:51:15 +00004134 __ B(eq, &done, /* far_target */ false);
4135 __ B(less_cond, &less, /* far_target */ false);
Artem Serov02d37832016-10-25 15:25:33 +01004136
4137 __ Bind(&greater);
4138 __ Mov(out, 1);
4139 __ B(&done);
4140
4141 __ Bind(&less);
4142 __ Mov(out, -1);
4143
4144 __ Bind(&done);
4145}
4146
4147void LocationsBuilderARMVIXL::VisitPhi(HPhi* instruction) {
4148 LocationSummary* locations =
4149 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
4150 for (size_t i = 0, e = locations->GetInputCount(); i < e; ++i) {
4151 locations->SetInAt(i, Location::Any());
4152 }
4153 locations->SetOut(Location::Any());
4154}
4155
4156void InstructionCodeGeneratorARMVIXL::VisitPhi(HPhi* instruction ATTRIBUTE_UNUSED) {
4157 LOG(FATAL) << "Unreachable";
4158}
4159
4160void CodeGeneratorARMVIXL::GenerateMemoryBarrier(MemBarrierKind kind) {
4161 // TODO (ported from quick): revisit ARM barrier kinds.
4162 DmbOptions flavor = DmbOptions::ISH; // Quiet C++ warnings.
4163 switch (kind) {
4164 case MemBarrierKind::kAnyStore:
4165 case MemBarrierKind::kLoadAny:
4166 case MemBarrierKind::kAnyAny: {
4167 flavor = DmbOptions::ISH;
4168 break;
4169 }
4170 case MemBarrierKind::kStoreStore: {
4171 flavor = DmbOptions::ISHST;
4172 break;
4173 }
4174 default:
4175 LOG(FATAL) << "Unexpected memory barrier " << kind;
4176 }
4177 __ Dmb(flavor);
4178}
4179
4180void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicLoad(vixl32::Register addr,
4181 uint32_t offset,
4182 vixl32::Register out_lo,
4183 vixl32::Register out_hi) {
4184 UseScratchRegisterScope temps(GetVIXLAssembler());
4185 if (offset != 0) {
4186 vixl32::Register temp = temps.Acquire();
4187 __ Add(temp, addr, offset);
4188 addr = temp;
4189 }
Scott Wakelingb77051e2016-11-21 19:46:00 +00004190 __ Ldrexd(out_lo, out_hi, MemOperand(addr));
Artem Serov02d37832016-10-25 15:25:33 +01004191}
4192
4193void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicStore(vixl32::Register addr,
4194 uint32_t offset,
4195 vixl32::Register value_lo,
4196 vixl32::Register value_hi,
4197 vixl32::Register temp1,
4198 vixl32::Register temp2,
4199 HInstruction* instruction) {
4200 UseScratchRegisterScope temps(GetVIXLAssembler());
4201 vixl32::Label fail;
4202 if (offset != 0) {
4203 vixl32::Register temp = temps.Acquire();
4204 __ Add(temp, addr, offset);
4205 addr = temp;
4206 }
4207 __ Bind(&fail);
Alexandre Rames374ddf32016-11-04 10:40:49 +00004208 {
4209 // Ensure the pc position is recorded immediately after the `ldrexd` instruction.
Artem Serov0fb37192016-12-06 18:13:40 +00004210 ExactAssemblyScope aas(GetVIXLAssembler(),
4211 vixl32::kMaxInstructionSizeInBytes,
4212 CodeBufferCheckScope::kMaximumSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00004213 // We need a load followed by store. (The address used in a STREX instruction must
4214 // be the same as the address in the most recently executed LDREX instruction.)
4215 __ ldrexd(temp1, temp2, MemOperand(addr));
4216 codegen_->MaybeRecordImplicitNullCheck(instruction);
4217 }
Scott Wakelingb77051e2016-11-21 19:46:00 +00004218 __ Strexd(temp1, value_lo, value_hi, MemOperand(addr));
xueliang.zhongf51bc622016-11-04 09:23:32 +00004219 __ CompareAndBranchIfNonZero(temp1, &fail);
Artem Serov02d37832016-10-25 15:25:33 +01004220}
Artem Serov02109dd2016-09-23 17:17:54 +01004221
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004222void LocationsBuilderARMVIXL::HandleFieldSet(
4223 HInstruction* instruction, const FieldInfo& field_info) {
4224 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
4225
4226 LocationSummary* locations =
4227 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
4228 locations->SetInAt(0, Location::RequiresRegister());
4229
4230 Primitive::Type field_type = field_info.GetFieldType();
4231 if (Primitive::IsFloatingPointType(field_type)) {
4232 locations->SetInAt(1, Location::RequiresFpuRegister());
4233 } else {
4234 locations->SetInAt(1, Location::RequiresRegister());
4235 }
4236
4237 bool is_wide = field_type == Primitive::kPrimLong || field_type == Primitive::kPrimDouble;
4238 bool generate_volatile = field_info.IsVolatile()
4239 && is_wide
4240 && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
4241 bool needs_write_barrier =
4242 CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1));
4243 // Temporary registers for the write barrier.
4244 // TODO: consider renaming StoreNeedsWriteBarrier to StoreNeedsGCMark.
4245 if (needs_write_barrier) {
4246 locations->AddTemp(Location::RequiresRegister()); // Possibly used for reference poisoning too.
4247 locations->AddTemp(Location::RequiresRegister());
4248 } else if (generate_volatile) {
4249 // ARM encoding have some additional constraints for ldrexd/strexd:
4250 // - registers need to be consecutive
4251 // - the first register should be even but not R14.
4252 // We don't test for ARM yet, and the assertion makes sure that we
4253 // revisit this if we ever enable ARM encoding.
4254 DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet());
4255
4256 locations->AddTemp(Location::RequiresRegister());
4257 locations->AddTemp(Location::RequiresRegister());
4258 if (field_type == Primitive::kPrimDouble) {
4259 // For doubles we need two more registers to copy the value.
4260 locations->AddTemp(LocationFrom(r2));
4261 locations->AddTemp(LocationFrom(r3));
4262 }
4263 }
4264}
4265
4266void InstructionCodeGeneratorARMVIXL::HandleFieldSet(HInstruction* instruction,
4267 const FieldInfo& field_info,
4268 bool value_can_be_null) {
4269 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
4270
4271 LocationSummary* locations = instruction->GetLocations();
4272 vixl32::Register base = InputRegisterAt(instruction, 0);
4273 Location value = locations->InAt(1);
4274
4275 bool is_volatile = field_info.IsVolatile();
4276 bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
4277 Primitive::Type field_type = field_info.GetFieldType();
4278 uint32_t offset = field_info.GetFieldOffset().Uint32Value();
4279 bool needs_write_barrier =
4280 CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1));
4281
4282 if (is_volatile) {
4283 codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
4284 }
4285
4286 switch (field_type) {
4287 case Primitive::kPrimBoolean:
4288 case Primitive::kPrimByte: {
4289 GetAssembler()->StoreToOffset(kStoreByte, RegisterFrom(value), base, offset);
4290 break;
4291 }
4292
4293 case Primitive::kPrimShort:
4294 case Primitive::kPrimChar: {
4295 GetAssembler()->StoreToOffset(kStoreHalfword, RegisterFrom(value), base, offset);
4296 break;
4297 }
4298
4299 case Primitive::kPrimInt:
4300 case Primitive::kPrimNot: {
4301 if (kPoisonHeapReferences && needs_write_barrier) {
4302 // Note that in the case where `value` is a null reference,
4303 // we do not enter this block, as a null reference does not
4304 // need poisoning.
4305 DCHECK_EQ(field_type, Primitive::kPrimNot);
4306 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
4307 __ Mov(temp, RegisterFrom(value));
4308 GetAssembler()->PoisonHeapReference(temp);
4309 GetAssembler()->StoreToOffset(kStoreWord, temp, base, offset);
4310 } else {
4311 GetAssembler()->StoreToOffset(kStoreWord, RegisterFrom(value), base, offset);
4312 }
4313 break;
4314 }
4315
4316 case Primitive::kPrimLong: {
4317 if (is_volatile && !atomic_ldrd_strd) {
4318 GenerateWideAtomicStore(base,
4319 offset,
4320 LowRegisterFrom(value),
4321 HighRegisterFrom(value),
4322 RegisterFrom(locations->GetTemp(0)),
4323 RegisterFrom(locations->GetTemp(1)),
4324 instruction);
4325 } else {
4326 GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), base, offset);
4327 codegen_->MaybeRecordImplicitNullCheck(instruction);
4328 }
4329 break;
4330 }
4331
4332 case Primitive::kPrimFloat: {
4333 GetAssembler()->StoreSToOffset(SRegisterFrom(value), base, offset);
4334 break;
4335 }
4336
4337 case Primitive::kPrimDouble: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01004338 vixl32::DRegister value_reg = DRegisterFrom(value);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004339 if (is_volatile && !atomic_ldrd_strd) {
4340 vixl32::Register value_reg_lo = RegisterFrom(locations->GetTemp(0));
4341 vixl32::Register value_reg_hi = RegisterFrom(locations->GetTemp(1));
4342
4343 __ Vmov(value_reg_lo, value_reg_hi, value_reg);
4344
4345 GenerateWideAtomicStore(base,
4346 offset,
4347 value_reg_lo,
4348 value_reg_hi,
4349 RegisterFrom(locations->GetTemp(2)),
4350 RegisterFrom(locations->GetTemp(3)),
4351 instruction);
4352 } else {
4353 GetAssembler()->StoreDToOffset(value_reg, base, offset);
4354 codegen_->MaybeRecordImplicitNullCheck(instruction);
4355 }
4356 break;
4357 }
4358
4359 case Primitive::kPrimVoid:
4360 LOG(FATAL) << "Unreachable type " << field_type;
4361 UNREACHABLE();
4362 }
4363
4364 // Longs and doubles are handled in the switch.
4365 if (field_type != Primitive::kPrimLong && field_type != Primitive::kPrimDouble) {
Alexandre Rames374ddf32016-11-04 10:40:49 +00004366 // TODO(VIXL): Here and for other calls to `MaybeRecordImplicitNullCheck` in this method, we
4367 // should use a scope and the assembler to emit the store instruction to guarantee that we
4368 // record the pc at the correct position. But the `Assembler` does not automatically handle
4369 // unencodable offsets. Practically, everything is fine because the helper and VIXL, at the time
4370 // of writing, do generate the store instruction last.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004371 codegen_->MaybeRecordImplicitNullCheck(instruction);
4372 }
4373
4374 if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
4375 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
4376 vixl32::Register card = RegisterFrom(locations->GetTemp(1));
4377 codegen_->MarkGCCard(temp, card, base, RegisterFrom(value), value_can_be_null);
4378 }
4379
4380 if (is_volatile) {
4381 codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
4382 }
4383}
4384
Artem Serov02d37832016-10-25 15:25:33 +01004385void LocationsBuilderARMVIXL::HandleFieldGet(HInstruction* instruction,
4386 const FieldInfo& field_info) {
4387 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
4388
4389 bool object_field_get_with_read_barrier =
4390 kEmitCompilerReadBarrier && (field_info.GetFieldType() == Primitive::kPrimNot);
4391 LocationSummary* locations =
4392 new (GetGraph()->GetArena()) LocationSummary(instruction,
4393 object_field_get_with_read_barrier ?
4394 LocationSummary::kCallOnSlowPath :
4395 LocationSummary::kNoCall);
4396 if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
4397 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
4398 }
4399 locations->SetInAt(0, Location::RequiresRegister());
4400
4401 bool volatile_for_double = field_info.IsVolatile()
4402 && (field_info.GetFieldType() == Primitive::kPrimDouble)
4403 && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
4404 // The output overlaps in case of volatile long: we don't want the
4405 // code generated by GenerateWideAtomicLoad to overwrite the
4406 // object's location. Likewise, in the case of an object field get
4407 // with read barriers enabled, we do not want the load to overwrite
4408 // the object's location, as we need it to emit the read barrier.
4409 bool overlap = (field_info.IsVolatile() && (field_info.GetFieldType() == Primitive::kPrimLong)) ||
4410 object_field_get_with_read_barrier;
4411
4412 if (Primitive::IsFloatingPointType(instruction->GetType())) {
4413 locations->SetOut(Location::RequiresFpuRegister());
4414 } else {
4415 locations->SetOut(Location::RequiresRegister(),
4416 (overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap));
4417 }
4418 if (volatile_for_double) {
4419 // ARM encoding have some additional constraints for ldrexd/strexd:
4420 // - registers need to be consecutive
4421 // - the first register should be even but not R14.
4422 // We don't test for ARM yet, and the assertion makes sure that we
4423 // revisit this if we ever enable ARM encoding.
4424 DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet());
4425 locations->AddTemp(Location::RequiresRegister());
4426 locations->AddTemp(Location::RequiresRegister());
4427 } else if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
4428 // We need a temporary register for the read barrier marking slow
Artem Serovc5fcb442016-12-02 19:19:58 +00004429 // path in CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier.
Artem Serov02d37832016-10-25 15:25:33 +01004430 locations->AddTemp(Location::RequiresRegister());
4431 }
4432}
4433
4434Location LocationsBuilderARMVIXL::ArithmeticZeroOrFpuRegister(HInstruction* input) {
4435 DCHECK(Primitive::IsFloatingPointType(input->GetType())) << input->GetType();
4436 if ((input->IsFloatConstant() && (input->AsFloatConstant()->IsArithmeticZero())) ||
4437 (input->IsDoubleConstant() && (input->AsDoubleConstant()->IsArithmeticZero()))) {
4438 return Location::ConstantLocation(input->AsConstant());
4439 } else {
4440 return Location::RequiresFpuRegister();
4441 }
4442}
4443
Artem Serov02109dd2016-09-23 17:17:54 +01004444Location LocationsBuilderARMVIXL::ArmEncodableConstantOrRegister(HInstruction* constant,
4445 Opcode opcode) {
4446 DCHECK(!Primitive::IsFloatingPointType(constant->GetType()));
4447 if (constant->IsConstant() &&
4448 CanEncodeConstantAsImmediate(constant->AsConstant(), opcode)) {
4449 return Location::ConstantLocation(constant->AsConstant());
4450 }
4451 return Location::RequiresRegister();
4452}
4453
4454bool LocationsBuilderARMVIXL::CanEncodeConstantAsImmediate(HConstant* input_cst,
4455 Opcode opcode) {
4456 uint64_t value = static_cast<uint64_t>(Int64FromConstant(input_cst));
4457 if (Primitive::Is64BitType(input_cst->GetType())) {
4458 Opcode high_opcode = opcode;
4459 SetCc low_set_cc = kCcDontCare;
4460 switch (opcode) {
4461 case SUB:
4462 // Flip the operation to an ADD.
4463 value = -value;
4464 opcode = ADD;
4465 FALLTHROUGH_INTENDED;
4466 case ADD:
4467 if (Low32Bits(value) == 0u) {
4468 return CanEncodeConstantAsImmediate(High32Bits(value), opcode, kCcDontCare);
4469 }
4470 high_opcode = ADC;
4471 low_set_cc = kCcSet;
4472 break;
4473 default:
4474 break;
4475 }
4476 return CanEncodeConstantAsImmediate(Low32Bits(value), opcode, low_set_cc) &&
4477 CanEncodeConstantAsImmediate(High32Bits(value), high_opcode, kCcDontCare);
4478 } else {
4479 return CanEncodeConstantAsImmediate(Low32Bits(value), opcode);
4480 }
4481}
4482
4483// TODO(VIXL): Replace art::arm::SetCc` with `vixl32::FlagsUpdate after flags set optimization
4484// enabled.
4485bool LocationsBuilderARMVIXL::CanEncodeConstantAsImmediate(uint32_t value,
4486 Opcode opcode,
4487 SetCc set_cc) {
4488 ArmVIXLAssembler* assembler = codegen_->GetAssembler();
4489 if (assembler->ShifterOperandCanHold(opcode, value, set_cc)) {
4490 return true;
4491 }
4492 Opcode neg_opcode = kNoOperand;
4493 switch (opcode) {
4494 case AND: neg_opcode = BIC; value = ~value; break;
4495 case ORR: neg_opcode = ORN; value = ~value; break;
4496 case ADD: neg_opcode = SUB; value = -value; break;
4497 case ADC: neg_opcode = SBC; value = ~value; break;
4498 case SUB: neg_opcode = ADD; value = -value; break;
4499 case SBC: neg_opcode = ADC; value = ~value; break;
4500 default:
4501 return false;
4502 }
4503 return assembler->ShifterOperandCanHold(neg_opcode, value, set_cc);
4504}
4505
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004506void InstructionCodeGeneratorARMVIXL::HandleFieldGet(HInstruction* instruction,
4507 const FieldInfo& field_info) {
4508 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
4509
4510 LocationSummary* locations = instruction->GetLocations();
4511 vixl32::Register base = InputRegisterAt(instruction, 0);
4512 Location out = locations->Out();
4513 bool is_volatile = field_info.IsVolatile();
4514 bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
4515 Primitive::Type field_type = field_info.GetFieldType();
4516 uint32_t offset = field_info.GetFieldOffset().Uint32Value();
4517
4518 switch (field_type) {
4519 case Primitive::kPrimBoolean:
4520 GetAssembler()->LoadFromOffset(kLoadUnsignedByte, RegisterFrom(out), base, offset);
4521 break;
4522
4523 case Primitive::kPrimByte:
4524 GetAssembler()->LoadFromOffset(kLoadSignedByte, RegisterFrom(out), base, offset);
4525 break;
4526
4527 case Primitive::kPrimShort:
4528 GetAssembler()->LoadFromOffset(kLoadSignedHalfword, RegisterFrom(out), base, offset);
4529 break;
4530
4531 case Primitive::kPrimChar:
4532 GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, RegisterFrom(out), base, offset);
4533 break;
4534
4535 case Primitive::kPrimInt:
4536 GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(out), base, offset);
4537 break;
4538
4539 case Primitive::kPrimNot: {
4540 // /* HeapReference<Object> */ out = *(base + offset)
4541 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00004542 Location temp_loc = locations->GetTemp(0);
4543 // Note that a potential implicit null check is handled in this
4544 // CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier call.
4545 codegen_->GenerateFieldLoadWithBakerReadBarrier(
4546 instruction, out, base, offset, temp_loc, /* needs_null_check */ true);
4547 if (is_volatile) {
4548 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
4549 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004550 } else {
4551 GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(out), base, offset);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004552 codegen_->MaybeRecordImplicitNullCheck(instruction);
4553 if (is_volatile) {
4554 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
4555 }
4556 // If read barriers are enabled, emit read barriers other than
4557 // Baker's using a slow path (and also unpoison the loaded
4558 // reference, if heap poisoning is enabled).
4559 codegen_->MaybeGenerateReadBarrierSlow(instruction, out, out, locations->InAt(0), offset);
4560 }
4561 break;
4562 }
4563
4564 case Primitive::kPrimLong:
4565 if (is_volatile && !atomic_ldrd_strd) {
4566 GenerateWideAtomicLoad(base, offset, LowRegisterFrom(out), HighRegisterFrom(out));
4567 } else {
4568 GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out), base, offset);
4569 }
4570 break;
4571
4572 case Primitive::kPrimFloat:
4573 GetAssembler()->LoadSFromOffset(SRegisterFrom(out), base, offset);
4574 break;
4575
4576 case Primitive::kPrimDouble: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01004577 vixl32::DRegister out_dreg = DRegisterFrom(out);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004578 if (is_volatile && !atomic_ldrd_strd) {
4579 vixl32::Register lo = RegisterFrom(locations->GetTemp(0));
4580 vixl32::Register hi = RegisterFrom(locations->GetTemp(1));
4581 GenerateWideAtomicLoad(base, offset, lo, hi);
4582 // TODO(VIXL): Do we need to be immediately after the ldrexd instruction? If so we need a
4583 // scope.
4584 codegen_->MaybeRecordImplicitNullCheck(instruction);
4585 __ Vmov(out_dreg, lo, hi);
4586 } else {
4587 GetAssembler()->LoadDFromOffset(out_dreg, base, offset);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004588 codegen_->MaybeRecordImplicitNullCheck(instruction);
4589 }
4590 break;
4591 }
4592
4593 case Primitive::kPrimVoid:
4594 LOG(FATAL) << "Unreachable type " << field_type;
4595 UNREACHABLE();
4596 }
4597
4598 if (field_type == Primitive::kPrimNot || field_type == Primitive::kPrimDouble) {
4599 // Potential implicit null checks, in the case of reference or
4600 // double fields, are handled in the previous switch statement.
4601 } else {
4602 // Address cases other than reference and double that may require an implicit null check.
Alexandre Rames374ddf32016-11-04 10:40:49 +00004603 // TODO(VIXL): Here and for other calls to `MaybeRecordImplicitNullCheck` in this method, we
4604 // should use a scope and the assembler to emit the load instruction to guarantee that we
4605 // record the pc at the correct position. But the `Assembler` does not automatically handle
4606 // unencodable offsets. Practically, everything is fine because the helper and VIXL, at the time
4607 // of writing, do generate the store instruction last.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004608 codegen_->MaybeRecordImplicitNullCheck(instruction);
4609 }
4610
4611 if (is_volatile) {
4612 if (field_type == Primitive::kPrimNot) {
4613 // Memory barriers, in the case of references, are also handled
4614 // in the previous switch statement.
4615 } else {
4616 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
4617 }
4618 }
4619}
4620
4621void LocationsBuilderARMVIXL::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
4622 HandleFieldSet(instruction, instruction->GetFieldInfo());
4623}
4624
4625void InstructionCodeGeneratorARMVIXL::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
4626 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
4627}
4628
4629void LocationsBuilderARMVIXL::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
4630 HandleFieldGet(instruction, instruction->GetFieldInfo());
4631}
4632
4633void InstructionCodeGeneratorARMVIXL::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
4634 HandleFieldGet(instruction, instruction->GetFieldInfo());
4635}
4636
4637void LocationsBuilderARMVIXL::VisitStaticFieldGet(HStaticFieldGet* instruction) {
4638 HandleFieldGet(instruction, instruction->GetFieldInfo());
4639}
4640
4641void InstructionCodeGeneratorARMVIXL::VisitStaticFieldGet(HStaticFieldGet* instruction) {
4642 HandleFieldGet(instruction, instruction->GetFieldInfo());
4643}
4644
Scott Wakelingc34dba72016-10-03 10:14:44 +01004645void LocationsBuilderARMVIXL::VisitStaticFieldSet(HStaticFieldSet* instruction) {
4646 HandleFieldSet(instruction, instruction->GetFieldInfo());
4647}
4648
4649void InstructionCodeGeneratorARMVIXL::VisitStaticFieldSet(HStaticFieldSet* instruction) {
4650 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
4651}
4652
Artem Serovcfbe9132016-10-14 15:58:56 +01004653void LocationsBuilderARMVIXL::VisitUnresolvedInstanceFieldGet(
4654 HUnresolvedInstanceFieldGet* instruction) {
4655 FieldAccessCallingConventionARMVIXL calling_convention;
4656 codegen_->CreateUnresolvedFieldLocationSummary(
4657 instruction, instruction->GetFieldType(), calling_convention);
4658}
4659
4660void InstructionCodeGeneratorARMVIXL::VisitUnresolvedInstanceFieldGet(
4661 HUnresolvedInstanceFieldGet* instruction) {
4662 FieldAccessCallingConventionARMVIXL calling_convention;
4663 codegen_->GenerateUnresolvedFieldAccess(instruction,
4664 instruction->GetFieldType(),
4665 instruction->GetFieldIndex(),
4666 instruction->GetDexPc(),
4667 calling_convention);
4668}
4669
4670void LocationsBuilderARMVIXL::VisitUnresolvedInstanceFieldSet(
4671 HUnresolvedInstanceFieldSet* instruction) {
4672 FieldAccessCallingConventionARMVIXL calling_convention;
4673 codegen_->CreateUnresolvedFieldLocationSummary(
4674 instruction, instruction->GetFieldType(), calling_convention);
4675}
4676
4677void InstructionCodeGeneratorARMVIXL::VisitUnresolvedInstanceFieldSet(
4678 HUnresolvedInstanceFieldSet* instruction) {
4679 FieldAccessCallingConventionARMVIXL calling_convention;
4680 codegen_->GenerateUnresolvedFieldAccess(instruction,
4681 instruction->GetFieldType(),
4682 instruction->GetFieldIndex(),
4683 instruction->GetDexPc(),
4684 calling_convention);
4685}
4686
4687void LocationsBuilderARMVIXL::VisitUnresolvedStaticFieldGet(
4688 HUnresolvedStaticFieldGet* instruction) {
4689 FieldAccessCallingConventionARMVIXL calling_convention;
4690 codegen_->CreateUnresolvedFieldLocationSummary(
4691 instruction, instruction->GetFieldType(), calling_convention);
4692}
4693
4694void InstructionCodeGeneratorARMVIXL::VisitUnresolvedStaticFieldGet(
4695 HUnresolvedStaticFieldGet* instruction) {
4696 FieldAccessCallingConventionARMVIXL calling_convention;
4697 codegen_->GenerateUnresolvedFieldAccess(instruction,
4698 instruction->GetFieldType(),
4699 instruction->GetFieldIndex(),
4700 instruction->GetDexPc(),
4701 calling_convention);
4702}
4703
4704void LocationsBuilderARMVIXL::VisitUnresolvedStaticFieldSet(
4705 HUnresolvedStaticFieldSet* instruction) {
4706 FieldAccessCallingConventionARMVIXL calling_convention;
4707 codegen_->CreateUnresolvedFieldLocationSummary(
4708 instruction, instruction->GetFieldType(), calling_convention);
4709}
4710
4711void InstructionCodeGeneratorARMVIXL::VisitUnresolvedStaticFieldSet(
4712 HUnresolvedStaticFieldSet* instruction) {
4713 FieldAccessCallingConventionARMVIXL calling_convention;
4714 codegen_->GenerateUnresolvedFieldAccess(instruction,
4715 instruction->GetFieldType(),
4716 instruction->GetFieldIndex(),
4717 instruction->GetDexPc(),
4718 calling_convention);
4719}
4720
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004721void LocationsBuilderARMVIXL::VisitNullCheck(HNullCheck* instruction) {
Artem Serov657022c2016-11-23 14:19:38 +00004722 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004723 locations->SetInAt(0, Location::RequiresRegister());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004724}
4725
4726void CodeGeneratorARMVIXL::GenerateImplicitNullCheck(HNullCheck* instruction) {
4727 if (CanMoveNullCheckToUser(instruction)) {
4728 return;
4729 }
4730
4731 UseScratchRegisterScope temps(GetVIXLAssembler());
Alexandre Rames374ddf32016-11-04 10:40:49 +00004732 // Ensure the pc position is recorded immediately after the `ldr` instruction.
Artem Serov0fb37192016-12-06 18:13:40 +00004733 ExactAssemblyScope aas(GetVIXLAssembler(),
4734 vixl32::kMaxInstructionSizeInBytes,
4735 CodeBufferCheckScope::kMaximumSize);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004736 __ ldr(temps.Acquire(), MemOperand(InputRegisterAt(instruction, 0)));
4737 RecordPcInfo(instruction, instruction->GetDexPc());
4738}
4739
4740void CodeGeneratorARMVIXL::GenerateExplicitNullCheck(HNullCheck* instruction) {
4741 NullCheckSlowPathARMVIXL* slow_path =
4742 new (GetGraph()->GetArena()) NullCheckSlowPathARMVIXL(instruction);
4743 AddSlowPath(slow_path);
xueliang.zhongf51bc622016-11-04 09:23:32 +00004744 __ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004745}
4746
4747void InstructionCodeGeneratorARMVIXL::VisitNullCheck(HNullCheck* instruction) {
4748 codegen_->GenerateNullCheck(instruction);
4749}
4750
Scott Wakelingc34dba72016-10-03 10:14:44 +01004751static LoadOperandType GetLoadOperandType(Primitive::Type type) {
4752 switch (type) {
4753 case Primitive::kPrimNot:
4754 return kLoadWord;
4755 case Primitive::kPrimBoolean:
4756 return kLoadUnsignedByte;
4757 case Primitive::kPrimByte:
4758 return kLoadSignedByte;
4759 case Primitive::kPrimChar:
4760 return kLoadUnsignedHalfword;
4761 case Primitive::kPrimShort:
4762 return kLoadSignedHalfword;
4763 case Primitive::kPrimInt:
4764 return kLoadWord;
4765 case Primitive::kPrimLong:
4766 return kLoadWordPair;
4767 case Primitive::kPrimFloat:
4768 return kLoadSWord;
4769 case Primitive::kPrimDouble:
4770 return kLoadDWord;
4771 default:
4772 LOG(FATAL) << "Unreachable type " << type;
4773 UNREACHABLE();
4774 }
4775}
4776
4777static StoreOperandType GetStoreOperandType(Primitive::Type type) {
4778 switch (type) {
4779 case Primitive::kPrimNot:
4780 return kStoreWord;
4781 case Primitive::kPrimBoolean:
4782 case Primitive::kPrimByte:
4783 return kStoreByte;
4784 case Primitive::kPrimChar:
4785 case Primitive::kPrimShort:
4786 return kStoreHalfword;
4787 case Primitive::kPrimInt:
4788 return kStoreWord;
4789 case Primitive::kPrimLong:
4790 return kStoreWordPair;
4791 case Primitive::kPrimFloat:
4792 return kStoreSWord;
4793 case Primitive::kPrimDouble:
4794 return kStoreDWord;
4795 default:
4796 LOG(FATAL) << "Unreachable type " << type;
4797 UNREACHABLE();
4798 }
4799}
4800
4801void CodeGeneratorARMVIXL::LoadFromShiftedRegOffset(Primitive::Type type,
4802 Location out_loc,
4803 vixl32::Register base,
4804 vixl32::Register reg_index,
4805 vixl32::Condition cond) {
4806 uint32_t shift_count = Primitive::ComponentSizeShift(type);
4807 MemOperand mem_address(base, reg_index, vixl32::LSL, shift_count);
4808
4809 switch (type) {
4810 case Primitive::kPrimByte:
4811 __ Ldrsb(cond, RegisterFrom(out_loc), mem_address);
4812 break;
4813 case Primitive::kPrimBoolean:
4814 __ Ldrb(cond, RegisterFrom(out_loc), mem_address);
4815 break;
4816 case Primitive::kPrimShort:
4817 __ Ldrsh(cond, RegisterFrom(out_loc), mem_address);
4818 break;
4819 case Primitive::kPrimChar:
4820 __ Ldrh(cond, RegisterFrom(out_loc), mem_address);
4821 break;
4822 case Primitive::kPrimNot:
4823 case Primitive::kPrimInt:
4824 __ Ldr(cond, RegisterFrom(out_loc), mem_address);
4825 break;
4826 // T32 doesn't support LoadFromShiftedRegOffset mem address mode for these types.
4827 case Primitive::kPrimLong:
4828 case Primitive::kPrimFloat:
4829 case Primitive::kPrimDouble:
4830 default:
4831 LOG(FATAL) << "Unreachable type " << type;
4832 UNREACHABLE();
4833 }
4834}
4835
4836void CodeGeneratorARMVIXL::StoreToShiftedRegOffset(Primitive::Type type,
4837 Location loc,
4838 vixl32::Register base,
4839 vixl32::Register reg_index,
4840 vixl32::Condition cond) {
4841 uint32_t shift_count = Primitive::ComponentSizeShift(type);
4842 MemOperand mem_address(base, reg_index, vixl32::LSL, shift_count);
4843
4844 switch (type) {
4845 case Primitive::kPrimByte:
4846 case Primitive::kPrimBoolean:
4847 __ Strb(cond, RegisterFrom(loc), mem_address);
4848 break;
4849 case Primitive::kPrimShort:
4850 case Primitive::kPrimChar:
4851 __ Strh(cond, RegisterFrom(loc), mem_address);
4852 break;
4853 case Primitive::kPrimNot:
4854 case Primitive::kPrimInt:
4855 __ Str(cond, RegisterFrom(loc), mem_address);
4856 break;
4857 // T32 doesn't support StoreToShiftedRegOffset mem address mode for these types.
4858 case Primitive::kPrimLong:
4859 case Primitive::kPrimFloat:
4860 case Primitive::kPrimDouble:
4861 default:
4862 LOG(FATAL) << "Unreachable type " << type;
4863 UNREACHABLE();
4864 }
4865}
4866
4867void LocationsBuilderARMVIXL::VisitArrayGet(HArrayGet* instruction) {
4868 bool object_array_get_with_read_barrier =
4869 kEmitCompilerReadBarrier && (instruction->GetType() == Primitive::kPrimNot);
4870 LocationSummary* locations =
4871 new (GetGraph()->GetArena()) LocationSummary(instruction,
4872 object_array_get_with_read_barrier ?
4873 LocationSummary::kCallOnSlowPath :
4874 LocationSummary::kNoCall);
4875 if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00004876 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Scott Wakelingc34dba72016-10-03 10:14:44 +01004877 }
4878 locations->SetInAt(0, Location::RequiresRegister());
4879 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
4880 if (Primitive::IsFloatingPointType(instruction->GetType())) {
4881 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4882 } else {
4883 // The output overlaps in the case of an object array get with
4884 // read barriers enabled: we do not want the move to overwrite the
4885 // array's location, as we need it to emit the read barrier.
4886 locations->SetOut(
4887 Location::RequiresRegister(),
4888 object_array_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap);
4889 }
4890 // We need a temporary register for the read barrier marking slow
Artem Serovc5fcb442016-12-02 19:19:58 +00004891 // path in CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier.
Scott Wakelingc34dba72016-10-03 10:14:44 +01004892 // Also need for String compression feature.
4893 if ((object_array_get_with_read_barrier && kUseBakerReadBarrier)
4894 || (mirror::kUseStringCompression && instruction->IsStringCharAt())) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004895 locations->AddTemp(Location::RequiresRegister());
Scott Wakelingc34dba72016-10-03 10:14:44 +01004896 }
4897}
4898
4899void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01004900 LocationSummary* locations = instruction->GetLocations();
4901 Location obj_loc = locations->InAt(0);
4902 vixl32::Register obj = InputRegisterAt(instruction, 0);
4903 Location index = locations->InAt(1);
4904 Location out_loc = locations->Out();
4905 uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
4906 Primitive::Type type = instruction->GetType();
4907 const bool maybe_compressed_char_at = mirror::kUseStringCompression &&
4908 instruction->IsStringCharAt();
4909 HInstruction* array_instr = instruction->GetArray();
4910 bool has_intermediate_address = array_instr->IsIntermediateAddress();
Scott Wakelingc34dba72016-10-03 10:14:44 +01004911
4912 switch (type) {
4913 case Primitive::kPrimBoolean:
4914 case Primitive::kPrimByte:
4915 case Primitive::kPrimShort:
4916 case Primitive::kPrimChar:
4917 case Primitive::kPrimInt: {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01004918 vixl32::Register length;
4919 if (maybe_compressed_char_at) {
4920 length = RegisterFrom(locations->GetTemp(0));
4921 uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
4922 GetAssembler()->LoadFromOffset(kLoadWord, length, obj, count_offset);
4923 codegen_->MaybeRecordImplicitNullCheck(instruction);
4924 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01004925 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00004926 int32_t const_index = Int32ConstantFrom(index);
Scott Wakelingc34dba72016-10-03 10:14:44 +01004927 if (maybe_compressed_char_at) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004928 vixl32::Label uncompressed_load, done;
Vladimir Markofdaf0f42016-10-13 19:29:53 +01004929 __ Lsrs(length, length, 1u); // LSRS has a 16-bit encoding, TST (immediate) does not.
4930 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
4931 "Expecting 0=compressed, 1=uncompressed");
Artem Serov517d9f62016-12-12 15:51:15 +00004932 __ B(cs, &uncompressed_load, /* far_target */ false);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004933 GetAssembler()->LoadFromOffset(kLoadUnsignedByte,
4934 RegisterFrom(out_loc),
4935 obj,
4936 data_offset + const_index);
4937 __ B(&done);
4938 __ Bind(&uncompressed_load);
4939 GetAssembler()->LoadFromOffset(GetLoadOperandType(Primitive::kPrimChar),
4940 RegisterFrom(out_loc),
4941 obj,
4942 data_offset + (const_index << 1));
4943 __ Bind(&done);
Scott Wakelingc34dba72016-10-03 10:14:44 +01004944 } else {
4945 uint32_t full_offset = data_offset + (const_index << Primitive::ComponentSizeShift(type));
4946
4947 LoadOperandType load_type = GetLoadOperandType(type);
4948 GetAssembler()->LoadFromOffset(load_type, RegisterFrom(out_loc), obj, full_offset);
4949 }
4950 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00004951 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01004952 vixl32::Register temp = temps.Acquire();
4953
4954 if (has_intermediate_address) {
Artem Serov2bbc9532016-10-21 11:51:50 +01004955 // We do not need to compute the intermediate address from the array: the
4956 // input instruction has done it already. See the comment in
4957 // `TryExtractArrayAccessAddress()`.
4958 if (kIsDebugBuild) {
4959 HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
Anton Kirilov644032c2016-12-06 17:51:43 +00004960 DCHECK_EQ(Uint64ConstantFrom(tmp->GetOffset()), data_offset);
Artem Serov2bbc9532016-10-21 11:51:50 +01004961 }
4962 temp = obj;
Scott Wakelingc34dba72016-10-03 10:14:44 +01004963 } else {
4964 __ Add(temp, obj, data_offset);
4965 }
4966 if (maybe_compressed_char_at) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004967 vixl32::Label uncompressed_load, done;
Vladimir Markofdaf0f42016-10-13 19:29:53 +01004968 __ Lsrs(length, length, 1u); // LSRS has a 16-bit encoding, TST (immediate) does not.
4969 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
4970 "Expecting 0=compressed, 1=uncompressed");
Artem Serov517d9f62016-12-12 15:51:15 +00004971 __ B(cs, &uncompressed_load, /* far_target */ false);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004972 __ Ldrb(RegisterFrom(out_loc), MemOperand(temp, RegisterFrom(index), vixl32::LSL, 0));
4973 __ B(&done);
4974 __ Bind(&uncompressed_load);
4975 __ Ldrh(RegisterFrom(out_loc), MemOperand(temp, RegisterFrom(index), vixl32::LSL, 1));
4976 __ Bind(&done);
Scott Wakelingc34dba72016-10-03 10:14:44 +01004977 } else {
4978 codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index));
4979 }
4980 }
4981 break;
4982 }
4983
4984 case Primitive::kPrimNot: {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00004985 // The read barrier instrumentation of object ArrayGet
4986 // instructions does not support the HIntermediateAddress
4987 // instruction.
4988 DCHECK(!(has_intermediate_address && kEmitCompilerReadBarrier));
4989
Scott Wakelingc34dba72016-10-03 10:14:44 +01004990 static_assert(
4991 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
4992 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
4993 // /* HeapReference<Object> */ out =
4994 // *(obj + data_offset + index * sizeof(HeapReference<Object>))
4995 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00004996 Location temp = locations->GetTemp(0);
4997 // Note that a potential implicit null check is handled in this
4998 // CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier call.
4999 codegen_->GenerateArrayLoadWithBakerReadBarrier(
5000 instruction, out_loc, obj, data_offset, index, temp, /* needs_null_check */ true);
Scott Wakelingc34dba72016-10-03 10:14:44 +01005001 } else {
5002 vixl32::Register out = OutputRegister(instruction);
5003 if (index.IsConstant()) {
5004 size_t offset =
Anton Kirilov644032c2016-12-06 17:51:43 +00005005 (Int32ConstantFrom(index) << TIMES_4) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01005006 GetAssembler()->LoadFromOffset(kLoadWord, out, obj, offset);
Alexandre Rames374ddf32016-11-04 10:40:49 +00005007 // TODO(VIXL): Here and for other calls to `MaybeRecordImplicitNullCheck` in this method,
5008 // we should use a scope and the assembler to emit the load instruction to guarantee that
5009 // we record the pc at the correct position. But the `Assembler` does not automatically
5010 // handle unencodable offsets. Practically, everything is fine because the helper and
5011 // VIXL, at the time of writing, do generate the store instruction last.
Scott Wakelingc34dba72016-10-03 10:14:44 +01005012 codegen_->MaybeRecordImplicitNullCheck(instruction);
5013 // If read barriers are enabled, emit read barriers other than
5014 // Baker's using a slow path (and also unpoison the loaded
5015 // reference, if heap poisoning is enabled).
5016 codegen_->MaybeGenerateReadBarrierSlow(instruction, out_loc, out_loc, obj_loc, offset);
5017 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00005018 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01005019 vixl32::Register temp = temps.Acquire();
5020
5021 if (has_intermediate_address) {
Artem Serov2bbc9532016-10-21 11:51:50 +01005022 // We do not need to compute the intermediate address from the array: the
5023 // input instruction has done it already. See the comment in
5024 // `TryExtractArrayAccessAddress()`.
5025 if (kIsDebugBuild) {
5026 HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
Anton Kirilov644032c2016-12-06 17:51:43 +00005027 DCHECK_EQ(Uint64ConstantFrom(tmp->GetOffset()), data_offset);
Artem Serov2bbc9532016-10-21 11:51:50 +01005028 }
5029 temp = obj;
Scott Wakelingc34dba72016-10-03 10:14:44 +01005030 } else {
5031 __ Add(temp, obj, data_offset);
5032 }
5033 codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index));
Anton Kirilovedb2ac32016-11-30 15:14:10 +00005034 temps.Close();
Alexandre Rames374ddf32016-11-04 10:40:49 +00005035 // TODO(VIXL): Use a scope to ensure that we record the pc position immediately after the
5036 // load instruction. Practically, everything is fine because the helper and VIXL, at the
5037 // time of writing, do generate the store instruction last.
Scott Wakelingc34dba72016-10-03 10:14:44 +01005038 codegen_->MaybeRecordImplicitNullCheck(instruction);
5039 // If read barriers are enabled, emit read barriers other than
5040 // Baker's using a slow path (and also unpoison the loaded
5041 // reference, if heap poisoning is enabled).
5042 codegen_->MaybeGenerateReadBarrierSlow(
5043 instruction, out_loc, out_loc, obj_loc, data_offset, index);
5044 }
5045 }
5046 break;
5047 }
5048
5049 case Primitive::kPrimLong: {
5050 if (index.IsConstant()) {
5051 size_t offset =
Anton Kirilov644032c2016-12-06 17:51:43 +00005052 (Int32ConstantFrom(index) << TIMES_8) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01005053 GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out_loc), obj, offset);
5054 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00005055 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01005056 vixl32::Register temp = temps.Acquire();
5057 __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
5058 GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out_loc), temp, data_offset);
5059 }
5060 break;
5061 }
5062
5063 case Primitive::kPrimFloat: {
5064 vixl32::SRegister out = SRegisterFrom(out_loc);
5065 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00005066 size_t offset = (Int32ConstantFrom(index) << TIMES_4) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01005067 GetAssembler()->LoadSFromOffset(out, obj, offset);
5068 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00005069 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01005070 vixl32::Register temp = temps.Acquire();
5071 __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_4));
5072 GetAssembler()->LoadSFromOffset(out, temp, data_offset);
5073 }
5074 break;
5075 }
5076
5077 case Primitive::kPrimDouble: {
5078 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00005079 size_t offset = (Int32ConstantFrom(index) << TIMES_8) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01005080 GetAssembler()->LoadDFromOffset(DRegisterFrom(out_loc), obj, offset);
5081 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00005082 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01005083 vixl32::Register temp = temps.Acquire();
5084 __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
5085 GetAssembler()->LoadDFromOffset(DRegisterFrom(out_loc), temp, data_offset);
5086 }
5087 break;
5088 }
5089
5090 case Primitive::kPrimVoid:
5091 LOG(FATAL) << "Unreachable type " << type;
5092 UNREACHABLE();
5093 }
5094
5095 if (type == Primitive::kPrimNot) {
5096 // Potential implicit null checks, in the case of reference
5097 // arrays, are handled in the previous switch statement.
5098 } else if (!maybe_compressed_char_at) {
Alexandre Rames374ddf32016-11-04 10:40:49 +00005099 // TODO(VIXL): Use a scope to ensure we record the pc info immediately after
5100 // the preceding load instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01005101 codegen_->MaybeRecordImplicitNullCheck(instruction);
5102 }
5103}
5104
5105void LocationsBuilderARMVIXL::VisitArraySet(HArraySet* instruction) {
5106 Primitive::Type value_type = instruction->GetComponentType();
5107
5108 bool needs_write_barrier =
5109 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
5110 bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck();
5111
5112 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(
5113 instruction,
5114 may_need_runtime_call_for_type_check ?
5115 LocationSummary::kCallOnSlowPath :
5116 LocationSummary::kNoCall);
5117
5118 locations->SetInAt(0, Location::RequiresRegister());
5119 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
5120 if (Primitive::IsFloatingPointType(value_type)) {
5121 locations->SetInAt(2, Location::RequiresFpuRegister());
5122 } else {
5123 locations->SetInAt(2, Location::RequiresRegister());
5124 }
5125 if (needs_write_barrier) {
5126 // Temporary registers for the write barrier.
5127 locations->AddTemp(Location::RequiresRegister()); // Possibly used for ref. poisoning too.
5128 locations->AddTemp(Location::RequiresRegister());
5129 }
5130}
5131
5132void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01005133 LocationSummary* locations = instruction->GetLocations();
5134 vixl32::Register array = InputRegisterAt(instruction, 0);
5135 Location index = locations->InAt(1);
5136 Primitive::Type value_type = instruction->GetComponentType();
5137 bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck();
5138 bool needs_write_barrier =
5139 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
5140 uint32_t data_offset =
5141 mirror::Array::DataOffset(Primitive::ComponentSize(value_type)).Uint32Value();
5142 Location value_loc = locations->InAt(2);
5143 HInstruction* array_instr = instruction->GetArray();
5144 bool has_intermediate_address = array_instr->IsIntermediateAddress();
Scott Wakelingc34dba72016-10-03 10:14:44 +01005145
5146 switch (value_type) {
5147 case Primitive::kPrimBoolean:
5148 case Primitive::kPrimByte:
5149 case Primitive::kPrimShort:
5150 case Primitive::kPrimChar:
5151 case Primitive::kPrimInt: {
5152 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00005153 int32_t const_index = Int32ConstantFrom(index);
Scott Wakelingc34dba72016-10-03 10:14:44 +01005154 uint32_t full_offset =
5155 data_offset + (const_index << Primitive::ComponentSizeShift(value_type));
5156 StoreOperandType store_type = GetStoreOperandType(value_type);
5157 GetAssembler()->StoreToOffset(store_type, RegisterFrom(value_loc), array, full_offset);
5158 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00005159 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01005160 vixl32::Register temp = temps.Acquire();
5161
5162 if (has_intermediate_address) {
Artem Serov2bbc9532016-10-21 11:51:50 +01005163 // We do not need to compute the intermediate address from the array: the
5164 // input instruction has done it already. See the comment in
5165 // `TryExtractArrayAccessAddress()`.
5166 if (kIsDebugBuild) {
5167 HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
Anton Kirilov644032c2016-12-06 17:51:43 +00005168 DCHECK_EQ(Uint64ConstantFrom(tmp->GetOffset()), data_offset);
Artem Serov2bbc9532016-10-21 11:51:50 +01005169 }
5170 temp = array;
Scott Wakelingc34dba72016-10-03 10:14:44 +01005171 } else {
5172 __ Add(temp, array, data_offset);
5173 }
5174 codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
5175 }
5176 break;
5177 }
5178
5179 case Primitive::kPrimNot: {
5180 vixl32::Register value = RegisterFrom(value_loc);
5181 // TryExtractArrayAccessAddress optimization is never applied for non-primitive ArraySet.
5182 // See the comment in instruction_simplifier_shared.cc.
5183 DCHECK(!has_intermediate_address);
5184
5185 if (instruction->InputAt(2)->IsNullConstant()) {
5186 // Just setting null.
5187 if (index.IsConstant()) {
5188 size_t offset =
Anton Kirilov644032c2016-12-06 17:51:43 +00005189 (Int32ConstantFrom(index) << TIMES_4) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01005190 GetAssembler()->StoreToOffset(kStoreWord, value, array, offset);
5191 } else {
5192 DCHECK(index.IsRegister()) << index;
Anton Kirilovedb2ac32016-11-30 15:14:10 +00005193 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01005194 vixl32::Register temp = temps.Acquire();
5195 __ Add(temp, array, data_offset);
5196 codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
5197 }
Alexandre Rames374ddf32016-11-04 10:40:49 +00005198 // TODO(VIXL): Use a scope to ensure we record the pc info immediately after the preceding
5199 // store instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01005200 codegen_->MaybeRecordImplicitNullCheck(instruction);
5201 DCHECK(!needs_write_barrier);
5202 DCHECK(!may_need_runtime_call_for_type_check);
5203 break;
5204 }
5205
5206 DCHECK(needs_write_barrier);
5207 Location temp1_loc = locations->GetTemp(0);
5208 vixl32::Register temp1 = RegisterFrom(temp1_loc);
5209 Location temp2_loc = locations->GetTemp(1);
5210 vixl32::Register temp2 = RegisterFrom(temp2_loc);
5211 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
5212 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
5213 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
5214 vixl32::Label done;
5215 SlowPathCodeARMVIXL* slow_path = nullptr;
5216
5217 if (may_need_runtime_call_for_type_check) {
5218 slow_path = new (GetGraph()->GetArena()) ArraySetSlowPathARMVIXL(instruction);
5219 codegen_->AddSlowPath(slow_path);
5220 if (instruction->GetValueCanBeNull()) {
5221 vixl32::Label non_zero;
xueliang.zhongf51bc622016-11-04 09:23:32 +00005222 __ CompareAndBranchIfNonZero(value, &non_zero);
Scott Wakelingc34dba72016-10-03 10:14:44 +01005223 if (index.IsConstant()) {
5224 size_t offset =
Anton Kirilov644032c2016-12-06 17:51:43 +00005225 (Int32ConstantFrom(index) << TIMES_4) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01005226 GetAssembler()->StoreToOffset(kStoreWord, value, array, offset);
5227 } else {
5228 DCHECK(index.IsRegister()) << index;
Anton Kirilovedb2ac32016-11-30 15:14:10 +00005229 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01005230 vixl32::Register temp = temps.Acquire();
5231 __ Add(temp, array, data_offset);
5232 codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
5233 }
Alexandre Rames374ddf32016-11-04 10:40:49 +00005234 // TODO(VIXL): Use a scope to ensure we record the pc info immediately after the preceding
5235 // store instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01005236 codegen_->MaybeRecordImplicitNullCheck(instruction);
5237 __ B(&done);
5238 __ Bind(&non_zero);
5239 }
5240
5241 // Note that when read barriers are enabled, the type checks
5242 // are performed without read barriers. This is fine, even in
5243 // the case where a class object is in the from-space after
5244 // the flip, as a comparison involving such a type would not
5245 // produce a false positive; it may of course produce a false
5246 // negative, in which case we would take the ArraySet slow
5247 // path.
5248
Alexandre Rames374ddf32016-11-04 10:40:49 +00005249 {
5250 // Ensure we record the pc position immediately after the `ldr` instruction.
Artem Serov0fb37192016-12-06 18:13:40 +00005251 ExactAssemblyScope aas(GetVIXLAssembler(),
5252 vixl32::kMaxInstructionSizeInBytes,
5253 CodeBufferCheckScope::kMaximumSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00005254 // /* HeapReference<Class> */ temp1 = array->klass_
5255 __ ldr(temp1, MemOperand(array, class_offset));
5256 codegen_->MaybeRecordImplicitNullCheck(instruction);
5257 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01005258 GetAssembler()->MaybeUnpoisonHeapReference(temp1);
5259
5260 // /* HeapReference<Class> */ temp1 = temp1->component_type_
5261 GetAssembler()->LoadFromOffset(kLoadWord, temp1, temp1, component_offset);
5262 // /* HeapReference<Class> */ temp2 = value->klass_
5263 GetAssembler()->LoadFromOffset(kLoadWord, temp2, value, class_offset);
5264 // If heap poisoning is enabled, no need to unpoison `temp1`
5265 // nor `temp2`, as we are comparing two poisoned references.
5266 __ Cmp(temp1, temp2);
5267
5268 if (instruction->StaticTypeOfArrayIsObjectArray()) {
5269 vixl32::Label do_put;
Artem Serov517d9f62016-12-12 15:51:15 +00005270 __ B(eq, &do_put, /* far_target */ false);
Scott Wakelingc34dba72016-10-03 10:14:44 +01005271 // If heap poisoning is enabled, the `temp1` reference has
5272 // not been unpoisoned yet; unpoison it now.
5273 GetAssembler()->MaybeUnpoisonHeapReference(temp1);
5274
5275 // /* HeapReference<Class> */ temp1 = temp1->super_class_
5276 GetAssembler()->LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
5277 // If heap poisoning is enabled, no need to unpoison
5278 // `temp1`, as we are comparing against null below.
xueliang.zhongf51bc622016-11-04 09:23:32 +00005279 __ CompareAndBranchIfNonZero(temp1, slow_path->GetEntryLabel());
Scott Wakelingc34dba72016-10-03 10:14:44 +01005280 __ Bind(&do_put);
5281 } else {
5282 __ B(ne, slow_path->GetEntryLabel());
5283 }
5284 }
5285
5286 vixl32::Register source = value;
5287 if (kPoisonHeapReferences) {
5288 // Note that in the case where `value` is a null reference,
5289 // we do not enter this block, as a null reference does not
5290 // need poisoning.
5291 DCHECK_EQ(value_type, Primitive::kPrimNot);
5292 __ Mov(temp1, value);
5293 GetAssembler()->PoisonHeapReference(temp1);
5294 source = temp1;
5295 }
5296
5297 if (index.IsConstant()) {
5298 size_t offset =
Anton Kirilov644032c2016-12-06 17:51:43 +00005299 (Int32ConstantFrom(index) << TIMES_4) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01005300 GetAssembler()->StoreToOffset(kStoreWord, source, array, offset);
5301 } else {
5302 DCHECK(index.IsRegister()) << index;
5303
Anton Kirilovedb2ac32016-11-30 15:14:10 +00005304 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01005305 vixl32::Register temp = temps.Acquire();
5306 __ Add(temp, array, data_offset);
5307 codegen_->StoreToShiftedRegOffset(value_type,
5308 LocationFrom(source),
5309 temp,
5310 RegisterFrom(index));
5311 }
5312
5313 if (!may_need_runtime_call_for_type_check) {
Alexandre Rames374ddf32016-11-04 10:40:49 +00005314 // TODO(VIXL): Ensure we record the pc position immediately after the preceding store
5315 // instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01005316 codegen_->MaybeRecordImplicitNullCheck(instruction);
5317 }
5318
5319 codegen_->MarkGCCard(temp1, temp2, array, value, instruction->GetValueCanBeNull());
5320
5321 if (done.IsReferenced()) {
5322 __ Bind(&done);
5323 }
5324
5325 if (slow_path != nullptr) {
5326 __ Bind(slow_path->GetExitLabel());
5327 }
5328
5329 break;
5330 }
5331
5332 case Primitive::kPrimLong: {
5333 Location value = locations->InAt(2);
5334 if (index.IsConstant()) {
5335 size_t offset =
Anton Kirilov644032c2016-12-06 17:51:43 +00005336 (Int32ConstantFrom(index) << TIMES_8) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01005337 GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(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_8));
5342 GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), temp, data_offset);
5343 }
5344 break;
5345 }
5346
5347 case Primitive::kPrimFloat: {
5348 Location value = locations->InAt(2);
5349 DCHECK(value.IsFpuRegister());
5350 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00005351 size_t offset = (Int32ConstantFrom(index) << TIMES_4) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01005352 GetAssembler()->StoreSToOffset(SRegisterFrom(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_4));
5357 GetAssembler()->StoreSToOffset(SRegisterFrom(value), temp, data_offset);
5358 }
5359 break;
5360 }
5361
5362 case Primitive::kPrimDouble: {
5363 Location value = locations->InAt(2);
5364 DCHECK(value.IsFpuRegisterPair());
5365 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00005366 size_t offset = (Int32ConstantFrom(index) << TIMES_8) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01005367 GetAssembler()->StoreDToOffset(DRegisterFrom(value), array, offset);
5368 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00005369 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01005370 vixl32::Register temp = temps.Acquire();
5371 __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
5372 GetAssembler()->StoreDToOffset(DRegisterFrom(value), temp, data_offset);
5373 }
5374 break;
5375 }
5376
5377 case Primitive::kPrimVoid:
5378 LOG(FATAL) << "Unreachable type " << value_type;
5379 UNREACHABLE();
5380 }
5381
5382 // Objects are handled in the switch.
5383 if (value_type != Primitive::kPrimNot) {
Alexandre Rames374ddf32016-11-04 10:40:49 +00005384 // TODO(VIXL): Ensure we record the pc position immediately after the preceding store
5385 // instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01005386 codegen_->MaybeRecordImplicitNullCheck(instruction);
5387 }
5388}
5389
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005390void LocationsBuilderARMVIXL::VisitArrayLength(HArrayLength* instruction) {
5391 LocationSummary* locations =
5392 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
5393 locations->SetInAt(0, Location::RequiresRegister());
5394 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5395}
5396
5397void InstructionCodeGeneratorARMVIXL::VisitArrayLength(HArrayLength* instruction) {
5398 uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction);
5399 vixl32::Register obj = InputRegisterAt(instruction, 0);
5400 vixl32::Register out = OutputRegister(instruction);
Alexandre Rames374ddf32016-11-04 10:40:49 +00005401 {
Artem Serov0fb37192016-12-06 18:13:40 +00005402 ExactAssemblyScope aas(GetVIXLAssembler(),
5403 vixl32::kMaxInstructionSizeInBytes,
5404 CodeBufferCheckScope::kMaximumSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00005405 __ ldr(out, MemOperand(obj, offset));
5406 codegen_->MaybeRecordImplicitNullCheck(instruction);
5407 }
Anton Kirilove28d9ae2016-10-25 18:17:23 +01005408 // Mask out compression flag from String's array length.
5409 if (mirror::kUseStringCompression && instruction->IsStringLength()) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01005410 __ Lsr(out, out, 1u);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01005411 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005412}
5413
Artem Serov2bbc9532016-10-21 11:51:50 +01005414void LocationsBuilderARMVIXL::VisitIntermediateAddress(HIntermediateAddress* instruction) {
Artem Serov2bbc9532016-10-21 11:51:50 +01005415 LocationSummary* locations =
5416 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
5417
5418 locations->SetInAt(0, Location::RequiresRegister());
5419 locations->SetInAt(1, Location::RegisterOrConstant(instruction->GetOffset()));
5420 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5421}
5422
5423void InstructionCodeGeneratorARMVIXL::VisitIntermediateAddress(HIntermediateAddress* instruction) {
5424 vixl32::Register out = OutputRegister(instruction);
5425 vixl32::Register first = InputRegisterAt(instruction, 0);
5426 Location second = instruction->GetLocations()->InAt(1);
5427
Artem Serov2bbc9532016-10-21 11:51:50 +01005428 if (second.IsRegister()) {
5429 __ Add(out, first, RegisterFrom(second));
5430 } else {
Anton Kirilov644032c2016-12-06 17:51:43 +00005431 __ Add(out, first, Int32ConstantFrom(second));
Artem Serov2bbc9532016-10-21 11:51:50 +01005432 }
5433}
5434
Scott Wakelingc34dba72016-10-03 10:14:44 +01005435void LocationsBuilderARMVIXL::VisitBoundsCheck(HBoundsCheck* instruction) {
5436 RegisterSet caller_saves = RegisterSet::Empty();
5437 InvokeRuntimeCallingConventionARMVIXL calling_convention;
5438 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0)));
5439 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(1)));
5440 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction, caller_saves);
5441 locations->SetInAt(0, Location::RequiresRegister());
5442 locations->SetInAt(1, Location::RequiresRegister());
5443}
5444
5445void InstructionCodeGeneratorARMVIXL::VisitBoundsCheck(HBoundsCheck* instruction) {
5446 SlowPathCodeARMVIXL* slow_path =
5447 new (GetGraph()->GetArena()) BoundsCheckSlowPathARMVIXL(instruction);
5448 codegen_->AddSlowPath(slow_path);
5449
5450 vixl32::Register index = InputRegisterAt(instruction, 0);
5451 vixl32::Register length = InputRegisterAt(instruction, 1);
5452
5453 __ Cmp(index, length);
5454 __ B(hs, slow_path->GetEntryLabel());
5455}
5456
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005457void CodeGeneratorARMVIXL::MarkGCCard(vixl32::Register temp,
5458 vixl32::Register card,
5459 vixl32::Register object,
5460 vixl32::Register value,
5461 bool can_be_null) {
5462 vixl32::Label is_null;
5463 if (can_be_null) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00005464 __ CompareAndBranchIfZero(value, &is_null);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005465 }
5466 GetAssembler()->LoadFromOffset(
5467 kLoadWord, card, tr, Thread::CardTableOffset<kArmPointerSize>().Int32Value());
Scott Wakelingb77051e2016-11-21 19:46:00 +00005468 __ Lsr(temp, object, Operand::From(gc::accounting::CardTable::kCardShift));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005469 __ Strb(card, MemOperand(card, temp));
5470 if (can_be_null) {
5471 __ Bind(&is_null);
5472 }
5473}
5474
Scott Wakelingfe885462016-09-22 10:24:38 +01005475void LocationsBuilderARMVIXL::VisitParallelMove(HParallelMove* instruction ATTRIBUTE_UNUSED) {
5476 LOG(FATAL) << "Unreachable";
5477}
5478
5479void InstructionCodeGeneratorARMVIXL::VisitParallelMove(HParallelMove* instruction) {
5480 codegen_->GetMoveResolver()->EmitNativeCode(instruction);
5481}
5482
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005483void LocationsBuilderARMVIXL::VisitSuspendCheck(HSuspendCheck* instruction) {
Artem Serov657022c2016-11-23 14:19:38 +00005484 LocationSummary* locations =
5485 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
5486 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005487}
5488
5489void InstructionCodeGeneratorARMVIXL::VisitSuspendCheck(HSuspendCheck* instruction) {
5490 HBasicBlock* block = instruction->GetBlock();
5491 if (block->GetLoopInformation() != nullptr) {
5492 DCHECK(block->GetLoopInformation()->GetSuspendCheck() == instruction);
5493 // The back edge will generate the suspend check.
5494 return;
5495 }
5496 if (block->IsEntryBlock() && instruction->GetNext()->IsGoto()) {
5497 // The goto will generate the suspend check.
5498 return;
5499 }
5500 GenerateSuspendCheck(instruction, nullptr);
5501}
5502
5503void InstructionCodeGeneratorARMVIXL::GenerateSuspendCheck(HSuspendCheck* instruction,
5504 HBasicBlock* successor) {
5505 SuspendCheckSlowPathARMVIXL* slow_path =
5506 down_cast<SuspendCheckSlowPathARMVIXL*>(instruction->GetSlowPath());
5507 if (slow_path == nullptr) {
5508 slow_path = new (GetGraph()->GetArena()) SuspendCheckSlowPathARMVIXL(instruction, successor);
5509 instruction->SetSlowPath(slow_path);
5510 codegen_->AddSlowPath(slow_path);
5511 if (successor != nullptr) {
5512 DCHECK(successor->IsLoopHeader());
5513 codegen_->ClearSpillSlotsFromLoopPhisInStackMap(instruction);
5514 }
5515 } else {
5516 DCHECK_EQ(slow_path->GetSuccessor(), successor);
5517 }
5518
Anton Kirilovedb2ac32016-11-30 15:14:10 +00005519 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005520 vixl32::Register temp = temps.Acquire();
5521 GetAssembler()->LoadFromOffset(
5522 kLoadUnsignedHalfword, temp, tr, Thread::ThreadFlagsOffset<kArmPointerSize>().Int32Value());
5523 if (successor == nullptr) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00005524 __ CompareAndBranchIfNonZero(temp, slow_path->GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005525 __ Bind(slow_path->GetReturnLabel());
5526 } else {
xueliang.zhongf51bc622016-11-04 09:23:32 +00005527 __ CompareAndBranchIfZero(temp, codegen_->GetLabelOf(successor));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005528 __ B(slow_path->GetEntryLabel());
5529 }
5530}
5531
Scott Wakelingfe885462016-09-22 10:24:38 +01005532ArmVIXLAssembler* ParallelMoveResolverARMVIXL::GetAssembler() const {
5533 return codegen_->GetAssembler();
5534}
5535
5536void ParallelMoveResolverARMVIXL::EmitMove(size_t index) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005537 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
Scott Wakelingfe885462016-09-22 10:24:38 +01005538 MoveOperands* move = moves_[index];
5539 Location source = move->GetSource();
5540 Location destination = move->GetDestination();
5541
5542 if (source.IsRegister()) {
5543 if (destination.IsRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005544 __ Mov(RegisterFrom(destination), RegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01005545 } else if (destination.IsFpuRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005546 __ Vmov(SRegisterFrom(destination), RegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01005547 } else {
5548 DCHECK(destination.IsStackSlot());
5549 GetAssembler()->StoreToOffset(kStoreWord,
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005550 RegisterFrom(source),
Scott Wakelingfe885462016-09-22 10:24:38 +01005551 sp,
5552 destination.GetStackIndex());
5553 }
5554 } else if (source.IsStackSlot()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005555 if (destination.IsRegister()) {
5556 GetAssembler()->LoadFromOffset(kLoadWord,
5557 RegisterFrom(destination),
5558 sp,
5559 source.GetStackIndex());
5560 } else if (destination.IsFpuRegister()) {
5561 GetAssembler()->LoadSFromOffset(SRegisterFrom(destination), sp, source.GetStackIndex());
5562 } else {
5563 DCHECK(destination.IsStackSlot());
5564 vixl32::Register temp = temps.Acquire();
5565 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, source.GetStackIndex());
5566 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
5567 }
Scott Wakelingfe885462016-09-22 10:24:38 +01005568 } else if (source.IsFpuRegister()) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01005569 if (destination.IsRegister()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01005570 __ Vmov(RegisterFrom(destination), SRegisterFrom(source));
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01005571 } else if (destination.IsFpuRegister()) {
5572 __ Vmov(SRegisterFrom(destination), SRegisterFrom(source));
5573 } else {
5574 DCHECK(destination.IsStackSlot());
5575 GetAssembler()->StoreSToOffset(SRegisterFrom(source), sp, destination.GetStackIndex());
5576 }
Scott Wakelingfe885462016-09-22 10:24:38 +01005577 } else if (source.IsDoubleStackSlot()) {
Alexandre Rames9c19bd62016-10-24 11:50:32 +01005578 if (destination.IsDoubleStackSlot()) {
5579 vixl32::DRegister temp = temps.AcquireD();
5580 GetAssembler()->LoadDFromOffset(temp, sp, source.GetStackIndex());
5581 GetAssembler()->StoreDToOffset(temp, sp, destination.GetStackIndex());
5582 } else if (destination.IsRegisterPair()) {
5583 DCHECK(ExpectedPairLayout(destination));
5584 GetAssembler()->LoadFromOffset(
5585 kLoadWordPair, LowRegisterFrom(destination), sp, source.GetStackIndex());
5586 } else {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01005587 DCHECK(destination.IsFpuRegisterPair()) << destination;
5588 GetAssembler()->LoadDFromOffset(DRegisterFrom(destination), sp, source.GetStackIndex());
Alexandre Rames9c19bd62016-10-24 11:50:32 +01005589 }
Scott Wakelingfe885462016-09-22 10:24:38 +01005590 } else if (source.IsRegisterPair()) {
5591 if (destination.IsRegisterPair()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005592 __ Mov(LowRegisterFrom(destination), LowRegisterFrom(source));
5593 __ Mov(HighRegisterFrom(destination), HighRegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01005594 } else if (destination.IsFpuRegisterPair()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01005595 __ Vmov(DRegisterFrom(destination), LowRegisterFrom(source), HighRegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01005596 } else {
5597 DCHECK(destination.IsDoubleStackSlot()) << destination;
5598 DCHECK(ExpectedPairLayout(source));
5599 GetAssembler()->StoreToOffset(kStoreWordPair,
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005600 LowRegisterFrom(source),
Scott Wakelingfe885462016-09-22 10:24:38 +01005601 sp,
5602 destination.GetStackIndex());
5603 }
5604 } else if (source.IsFpuRegisterPair()) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01005605 if (destination.IsRegisterPair()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01005606 __ Vmov(LowRegisterFrom(destination), HighRegisterFrom(destination), DRegisterFrom(source));
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01005607 } else if (destination.IsFpuRegisterPair()) {
5608 __ Vmov(DRegisterFrom(destination), DRegisterFrom(source));
5609 } else {
5610 DCHECK(destination.IsDoubleStackSlot()) << destination;
5611 GetAssembler()->StoreDToOffset(DRegisterFrom(source), sp, destination.GetStackIndex());
5612 }
Scott Wakelingfe885462016-09-22 10:24:38 +01005613 } else {
5614 DCHECK(source.IsConstant()) << source;
5615 HConstant* constant = source.GetConstant();
5616 if (constant->IsIntConstant() || constant->IsNullConstant()) {
5617 int32_t value = CodeGenerator::GetInt32ValueOf(constant);
5618 if (destination.IsRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005619 __ Mov(RegisterFrom(destination), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01005620 } else {
5621 DCHECK(destination.IsStackSlot());
Scott Wakelingfe885462016-09-22 10:24:38 +01005622 vixl32::Register temp = temps.Acquire();
5623 __ Mov(temp, value);
5624 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
5625 }
5626 } else if (constant->IsLongConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00005627 int64_t value = Int64ConstantFrom(source);
Scott Wakelingfe885462016-09-22 10:24:38 +01005628 if (destination.IsRegisterPair()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005629 __ Mov(LowRegisterFrom(destination), Low32Bits(value));
5630 __ Mov(HighRegisterFrom(destination), High32Bits(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01005631 } else {
5632 DCHECK(destination.IsDoubleStackSlot()) << destination;
Scott Wakelingfe885462016-09-22 10:24:38 +01005633 vixl32::Register temp = temps.Acquire();
5634 __ Mov(temp, Low32Bits(value));
5635 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
5636 __ Mov(temp, High32Bits(value));
5637 GetAssembler()->StoreToOffset(kStoreWord,
5638 temp,
5639 sp,
5640 destination.GetHighStackIndex(kArmWordSize));
5641 }
5642 } else if (constant->IsDoubleConstant()) {
5643 double value = constant->AsDoubleConstant()->GetValue();
5644 if (destination.IsFpuRegisterPair()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01005645 __ Vmov(DRegisterFrom(destination), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01005646 } else {
5647 DCHECK(destination.IsDoubleStackSlot()) << destination;
5648 uint64_t int_value = bit_cast<uint64_t, double>(value);
Scott Wakelingfe885462016-09-22 10:24:38 +01005649 vixl32::Register temp = temps.Acquire();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005650 __ Mov(temp, Low32Bits(int_value));
Scott Wakelingfe885462016-09-22 10:24:38 +01005651 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005652 __ Mov(temp, High32Bits(int_value));
Scott Wakelingfe885462016-09-22 10:24:38 +01005653 GetAssembler()->StoreToOffset(kStoreWord,
5654 temp,
5655 sp,
5656 destination.GetHighStackIndex(kArmWordSize));
5657 }
5658 } else {
5659 DCHECK(constant->IsFloatConstant()) << constant->DebugName();
5660 float value = constant->AsFloatConstant()->GetValue();
5661 if (destination.IsFpuRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005662 __ Vmov(SRegisterFrom(destination), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01005663 } else {
5664 DCHECK(destination.IsStackSlot());
Scott Wakelingfe885462016-09-22 10:24:38 +01005665 vixl32::Register temp = temps.Acquire();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005666 __ Mov(temp, bit_cast<int32_t, float>(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01005667 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
5668 }
5669 }
5670 }
5671}
5672
Alexandre Rames9c19bd62016-10-24 11:50:32 +01005673void ParallelMoveResolverARMVIXL::Exchange(vixl32::Register reg, int mem) {
5674 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
5675 vixl32::Register temp = temps.Acquire();
5676 __ Mov(temp, reg);
5677 GetAssembler()->LoadFromOffset(kLoadWord, reg, sp, mem);
5678 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, mem);
Scott Wakelingfe885462016-09-22 10:24:38 +01005679}
5680
Alexandre Rames9c19bd62016-10-24 11:50:32 +01005681void ParallelMoveResolverARMVIXL::Exchange(int mem1, int mem2) {
5682 // TODO(VIXL32): Double check the performance of this implementation.
5683 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
Artem Serov4593f7d2016-12-29 16:21:49 +00005684 vixl32::SRegister temp_1 = temps.AcquireS();
5685 vixl32::SRegister temp_2 = temps.AcquireS();
Alexandre Rames9c19bd62016-10-24 11:50:32 +01005686
Artem Serov4593f7d2016-12-29 16:21:49 +00005687 __ Vldr(temp_1, MemOperand(sp, mem1));
5688 __ Vldr(temp_2, MemOperand(sp, mem2));
5689 __ Vstr(temp_1, MemOperand(sp, mem2));
5690 __ Vstr(temp_2, MemOperand(sp, mem1));
Scott Wakelingfe885462016-09-22 10:24:38 +01005691}
5692
Alexandre Rames9c19bd62016-10-24 11:50:32 +01005693void ParallelMoveResolverARMVIXL::EmitSwap(size_t index) {
5694 MoveOperands* move = moves_[index];
5695 Location source = move->GetSource();
5696 Location destination = move->GetDestination();
5697 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
5698
5699 if (source.IsRegister() && destination.IsRegister()) {
5700 vixl32::Register temp = temps.Acquire();
5701 DCHECK(!RegisterFrom(source).Is(temp));
5702 DCHECK(!RegisterFrom(destination).Is(temp));
5703 __ Mov(temp, RegisterFrom(destination));
5704 __ Mov(RegisterFrom(destination), RegisterFrom(source));
5705 __ Mov(RegisterFrom(source), temp);
5706 } else if (source.IsRegister() && destination.IsStackSlot()) {
5707 Exchange(RegisterFrom(source), destination.GetStackIndex());
5708 } else if (source.IsStackSlot() && destination.IsRegister()) {
5709 Exchange(RegisterFrom(destination), source.GetStackIndex());
5710 } else if (source.IsStackSlot() && destination.IsStackSlot()) {
Anton Kirilovdda43962016-11-21 19:55:20 +00005711 Exchange(source.GetStackIndex(), destination.GetStackIndex());
Alexandre Rames9c19bd62016-10-24 11:50:32 +01005712 } else if (source.IsFpuRegister() && destination.IsFpuRegister()) {
Anton Kirilovdda43962016-11-21 19:55:20 +00005713 vixl32::SRegister temp = temps.AcquireS();
5714 __ Vmov(temp, SRegisterFrom(source));
5715 __ Vmov(SRegisterFrom(source), SRegisterFrom(destination));
5716 __ Vmov(SRegisterFrom(destination), temp);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01005717 } else if (source.IsRegisterPair() && destination.IsRegisterPair()) {
5718 vixl32::DRegister temp = temps.AcquireD();
5719 __ Vmov(temp, LowRegisterFrom(source), HighRegisterFrom(source));
5720 __ Mov(LowRegisterFrom(source), LowRegisterFrom(destination));
5721 __ Mov(HighRegisterFrom(source), HighRegisterFrom(destination));
5722 __ Vmov(LowRegisterFrom(destination), HighRegisterFrom(destination), temp);
5723 } else if (source.IsRegisterPair() || destination.IsRegisterPair()) {
5724 vixl32::Register low_reg = LowRegisterFrom(source.IsRegisterPair() ? source : destination);
5725 int mem = source.IsRegisterPair() ? destination.GetStackIndex() : source.GetStackIndex();
5726 DCHECK(ExpectedPairLayout(source.IsRegisterPair() ? source : destination));
5727 vixl32::DRegister temp = temps.AcquireD();
5728 __ Vmov(temp, low_reg, vixl32::Register(low_reg.GetCode() + 1));
5729 GetAssembler()->LoadFromOffset(kLoadWordPair, low_reg, sp, mem);
5730 GetAssembler()->StoreDToOffset(temp, sp, mem);
5731 } else if (source.IsFpuRegisterPair() && destination.IsFpuRegisterPair()) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01005732 vixl32::DRegister first = DRegisterFrom(source);
5733 vixl32::DRegister second = DRegisterFrom(destination);
5734 vixl32::DRegister temp = temps.AcquireD();
5735 __ Vmov(temp, first);
5736 __ Vmov(first, second);
5737 __ Vmov(second, temp);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01005738 } else if (source.IsFpuRegisterPair() || destination.IsFpuRegisterPair()) {
Anton Kirilovdda43962016-11-21 19:55:20 +00005739 vixl32::DRegister reg = source.IsFpuRegisterPair()
5740 ? DRegisterFrom(source)
5741 : DRegisterFrom(destination);
5742 int mem = source.IsFpuRegisterPair()
5743 ? destination.GetStackIndex()
5744 : source.GetStackIndex();
5745 vixl32::DRegister temp = temps.AcquireD();
5746 __ Vmov(temp, reg);
5747 GetAssembler()->LoadDFromOffset(reg, sp, mem);
5748 GetAssembler()->StoreDToOffset(temp, sp, mem);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01005749 } else if (source.IsFpuRegister() || destination.IsFpuRegister()) {
Anton Kirilovdda43962016-11-21 19:55:20 +00005750 vixl32::SRegister reg = source.IsFpuRegister()
5751 ? SRegisterFrom(source)
5752 : SRegisterFrom(destination);
5753 int mem = source.IsFpuRegister()
5754 ? destination.GetStackIndex()
5755 : source.GetStackIndex();
5756 vixl32::Register temp = temps.Acquire();
5757 __ Vmov(temp, reg);
5758 GetAssembler()->LoadSFromOffset(reg, sp, mem);
5759 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, mem);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01005760 } else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) {
5761 vixl32::DRegister temp1 = temps.AcquireD();
5762 vixl32::DRegister temp2 = temps.AcquireD();
5763 __ Vldr(temp1, MemOperand(sp, source.GetStackIndex()));
5764 __ Vldr(temp2, MemOperand(sp, destination.GetStackIndex()));
5765 __ Vstr(temp1, MemOperand(sp, destination.GetStackIndex()));
5766 __ Vstr(temp2, MemOperand(sp, source.GetStackIndex()));
5767 } else {
5768 LOG(FATAL) << "Unimplemented" << source << " <-> " << destination;
5769 }
Scott Wakelingfe885462016-09-22 10:24:38 +01005770}
5771
5772void ParallelMoveResolverARMVIXL::SpillScratch(int reg ATTRIBUTE_UNUSED) {
5773 TODO_VIXL32(FATAL);
5774}
5775
5776void ParallelMoveResolverARMVIXL::RestoreScratch(int reg ATTRIBUTE_UNUSED) {
5777 TODO_VIXL32(FATAL);
5778}
5779
Artem Serov02d37832016-10-25 15:25:33 +01005780HLoadClass::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadClassKind(
Artem Serovd4cc5b22016-11-04 11:19:09 +00005781 HLoadClass::LoadKind desired_class_load_kind) {
5782 switch (desired_class_load_kind) {
5783 case HLoadClass::LoadKind::kReferrersClass:
5784 break;
5785 case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
Artem Serovc5fcb442016-12-02 19:19:58 +00005786 DCHECK(!GetCompilerOptions().GetCompilePic());
5787 break;
Artem Serovd4cc5b22016-11-04 11:19:09 +00005788 case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
5789 DCHECK(GetCompilerOptions().GetCompilePic());
5790 break;
5791 case HLoadClass::LoadKind::kBootImageAddress:
Artem Serovc5fcb442016-12-02 19:19:58 +00005792 break;
Nicolas Geoffray22384ae2016-12-12 22:33:36 +00005793 case HLoadClass::LoadKind::kJitTableAddress:
Artem Serovc5fcb442016-12-02 19:19:58 +00005794 break;
Artem Serovd4cc5b22016-11-04 11:19:09 +00005795 case HLoadClass::LoadKind::kDexCachePcRelative:
5796 DCHECK(!Runtime::Current()->UseJitCompilation());
5797 // We disable pc-relative load when there is an irreducible loop, as the optimization
5798 // is incompatible with it.
5799 // TODO: Create as many ArmDexCacheArraysBase instructions as needed for methods
5800 // with irreducible loops.
5801 if (GetGraph()->HasIrreducibleLoops()) {
5802 return HLoadClass::LoadKind::kDexCacheViaMethod;
5803 }
5804 break;
5805 case HLoadClass::LoadKind::kDexCacheViaMethod:
5806 break;
5807 }
5808 return desired_class_load_kind;
Artem Serov02d37832016-10-25 15:25:33 +01005809}
5810
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005811void LocationsBuilderARMVIXL::VisitLoadClass(HLoadClass* cls) {
5812 if (cls->NeedsAccessCheck()) {
5813 InvokeRuntimeCallingConventionARMVIXL calling_convention;
5814 CodeGenerator::CreateLoadClassLocationSummary(
5815 cls,
5816 LocationFrom(calling_convention.GetRegisterAt(0)),
5817 LocationFrom(r0),
5818 /* code_generator_supports_read_barrier */ true);
5819 return;
5820 }
Scott Wakelingfe885462016-09-22 10:24:38 +01005821
Artem Serovd4cc5b22016-11-04 11:19:09 +00005822 const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
5823 LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier)
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005824 ? LocationSummary::kCallOnSlowPath
5825 : LocationSummary::kNoCall;
5826 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
Artem Serovd4cc5b22016-11-04 11:19:09 +00005827 if (kUseBakerReadBarrier && requires_read_barrier && !cls->NeedsEnvironment()) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00005828 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Artem Serovd4cc5b22016-11-04 11:19:09 +00005829 }
5830
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005831 HLoadClass::LoadKind load_kind = cls->GetLoadKind();
5832 if (load_kind == HLoadClass::LoadKind::kReferrersClass ||
5833 load_kind == HLoadClass::LoadKind::kDexCacheViaMethod ||
5834 load_kind == HLoadClass::LoadKind::kDexCachePcRelative) {
5835 locations->SetInAt(0, Location::RequiresRegister());
5836 }
5837 locations->SetOut(Location::RequiresRegister());
5838}
5839
5840void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) {
5841 LocationSummary* locations = cls->GetLocations();
5842 if (cls->NeedsAccessCheck()) {
Andreas Gampea5b09a62016-11-17 15:21:22 -08005843 codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex().index_);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005844 codegen_->InvokeRuntime(kQuickInitializeTypeAndVerifyAccess, cls, cls->GetDexPc());
5845 CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>();
5846 return;
5847 }
5848
5849 Location out_loc = locations->Out();
5850 vixl32::Register out = OutputRegister(cls);
5851
Artem Serovd4cc5b22016-11-04 11:19:09 +00005852 const ReadBarrierOption read_barrier_option = cls->IsInBootImage()
5853 ? kWithoutReadBarrier
5854 : kCompilerReadBarrierOption;
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005855 bool generate_null_check = false;
5856 switch (cls->GetLoadKind()) {
5857 case HLoadClass::LoadKind::kReferrersClass: {
5858 DCHECK(!cls->CanCallRuntime());
5859 DCHECK(!cls->MustGenerateClinitCheck());
5860 // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
5861 vixl32::Register current_method = InputRegisterAt(cls, 0);
5862 GenerateGcRootFieldLoad(cls,
5863 out_loc,
5864 current_method,
Roland Levillain00468f32016-10-27 18:02:48 +01005865 ArtMethod::DeclaringClassOffset().Int32Value(),
Artem Serovd4cc5b22016-11-04 11:19:09 +00005866 read_barrier_option);
5867 break;
5868 }
5869 case HLoadClass::LoadKind::kBootImageLinkTimeAddress: {
Artem Serovc5fcb442016-12-02 19:19:58 +00005870 DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
5871 __ Ldr(out, codegen_->DeduplicateBootImageTypeLiteral(cls->GetDexFile(),
5872 cls->GetTypeIndex()));
Artem Serovd4cc5b22016-11-04 11:19:09 +00005873 break;
5874 }
5875 case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: {
5876 DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
5877 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
5878 codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex());
5879 codegen_->EmitMovwMovtPlaceholder(labels, out);
5880 break;
5881 }
5882 case HLoadClass::LoadKind::kBootImageAddress: {
Artem Serovc5fcb442016-12-02 19:19:58 +00005883 DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
5884 DCHECK_NE(cls->GetAddress(), 0u);
5885 uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress());
5886 __ Ldr(out, codegen_->DeduplicateBootImageAddressLiteral(address));
Artem Serovd4cc5b22016-11-04 11:19:09 +00005887 break;
5888 }
Nicolas Geoffray22384ae2016-12-12 22:33:36 +00005889 case HLoadClass::LoadKind::kJitTableAddress: {
Artem Serovc5fcb442016-12-02 19:19:58 +00005890 __ Ldr(out, codegen_->DeduplicateJitClassLiteral(cls->GetDexFile(),
5891 cls->GetTypeIndex(),
5892 cls->GetAddress()));
5893 // /* GcRoot<mirror::Class> */ out = *out
5894 GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption);
Artem Serovd4cc5b22016-11-04 11:19:09 +00005895 break;
5896 }
5897 case HLoadClass::LoadKind::kDexCachePcRelative: {
5898 vixl32::Register base_reg = InputRegisterAt(cls, 0);
5899 HArmDexCacheArraysBase* base = cls->InputAt(0)->AsArmDexCacheArraysBase();
5900 int32_t offset = cls->GetDexCacheElementOffset() - base->GetElementOffset();
5901 // /* GcRoot<mirror::Class> */ out = *(dex_cache_arrays_base + offset)
5902 GenerateGcRootFieldLoad(cls, out_loc, base_reg, offset, read_barrier_option);
5903 generate_null_check = !cls->IsInDexCache();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005904 break;
5905 }
5906 case HLoadClass::LoadKind::kDexCacheViaMethod: {
5907 // /* GcRoot<mirror::Class>[] */ out =
5908 // current_method.ptr_sized_fields_->dex_cache_resolved_types_
5909 vixl32::Register current_method = InputRegisterAt(cls, 0);
5910 const int32_t resolved_types_offset =
5911 ArtMethod::DexCacheResolvedTypesOffset(kArmPointerSize).Int32Value();
5912 GetAssembler()->LoadFromOffset(kLoadWord, out, current_method, resolved_types_offset);
5913 // /* GcRoot<mirror::Class> */ out = out[type_index]
Andreas Gampea5b09a62016-11-17 15:21:22 -08005914 size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_);
Artem Serovd4cc5b22016-11-04 11:19:09 +00005915 GenerateGcRootFieldLoad(cls, out_loc, out, offset, read_barrier_option);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005916 generate_null_check = !cls->IsInDexCache();
5917 break;
5918 }
5919 default:
5920 TODO_VIXL32(FATAL);
5921 }
5922
5923 if (generate_null_check || cls->MustGenerateClinitCheck()) {
5924 DCHECK(cls->CanCallRuntime());
5925 LoadClassSlowPathARMVIXL* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARMVIXL(
5926 cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
5927 codegen_->AddSlowPath(slow_path);
5928 if (generate_null_check) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00005929 __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005930 }
5931 if (cls->MustGenerateClinitCheck()) {
5932 GenerateClassInitializationCheck(slow_path, out);
5933 } else {
5934 __ Bind(slow_path->GetExitLabel());
5935 }
5936 }
5937}
5938
Artem Serov02d37832016-10-25 15:25:33 +01005939void LocationsBuilderARMVIXL::VisitClinitCheck(HClinitCheck* check) {
5940 LocationSummary* locations =
5941 new (GetGraph()->GetArena()) LocationSummary(check, LocationSummary::kCallOnSlowPath);
5942 locations->SetInAt(0, Location::RequiresRegister());
5943 if (check->HasUses()) {
5944 locations->SetOut(Location::SameAsFirstInput());
5945 }
5946}
5947
5948void InstructionCodeGeneratorARMVIXL::VisitClinitCheck(HClinitCheck* check) {
5949 // We assume the class is not null.
5950 LoadClassSlowPathARMVIXL* slow_path =
5951 new (GetGraph()->GetArena()) LoadClassSlowPathARMVIXL(check->GetLoadClass(),
5952 check,
5953 check->GetDexPc(),
5954 /* do_clinit */ true);
5955 codegen_->AddSlowPath(slow_path);
5956 GenerateClassInitializationCheck(slow_path, InputRegisterAt(check, 0));
5957}
5958
5959void InstructionCodeGeneratorARMVIXL::GenerateClassInitializationCheck(
5960 LoadClassSlowPathARMVIXL* slow_path, vixl32::Register class_reg) {
5961 UseScratchRegisterScope temps(GetVIXLAssembler());
5962 vixl32::Register temp = temps.Acquire();
5963 GetAssembler()->LoadFromOffset(kLoadWord,
5964 temp,
5965 class_reg,
5966 mirror::Class::StatusOffset().Int32Value());
5967 __ Cmp(temp, mirror::Class::kStatusInitialized);
5968 __ B(lt, slow_path->GetEntryLabel());
5969 // Even if the initialized flag is set, we may be in a situation where caches are not synced
5970 // properly. Therefore, we do a memory fence.
5971 __ Dmb(ISH);
5972 __ Bind(slow_path->GetExitLabel());
5973}
5974
Artem Serov02d37832016-10-25 15:25:33 +01005975HLoadString::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadStringKind(
Artem Serovd4cc5b22016-11-04 11:19:09 +00005976 HLoadString::LoadKind desired_string_load_kind) {
5977 switch (desired_string_load_kind) {
5978 case HLoadString::LoadKind::kBootImageLinkTimeAddress:
Artem Serovc5fcb442016-12-02 19:19:58 +00005979 DCHECK(!GetCompilerOptions().GetCompilePic());
5980 break;
Artem Serovd4cc5b22016-11-04 11:19:09 +00005981 case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
5982 DCHECK(GetCompilerOptions().GetCompilePic());
5983 break;
5984 case HLoadString::LoadKind::kBootImageAddress:
Artem Serovc5fcb442016-12-02 19:19:58 +00005985 break;
Artem Serovd4cc5b22016-11-04 11:19:09 +00005986 case HLoadString::LoadKind::kBssEntry:
5987 DCHECK(!Runtime::Current()->UseJitCompilation());
5988 break;
5989 case HLoadString::LoadKind::kJitTableAddress:
5990 DCHECK(Runtime::Current()->UseJitCompilation());
Artem Serovc5fcb442016-12-02 19:19:58 +00005991 break;
Artem Serovd4cc5b22016-11-04 11:19:09 +00005992 case HLoadString::LoadKind::kDexCacheViaMethod:
5993 break;
5994 }
5995 return desired_string_load_kind;
Artem Serov02d37832016-10-25 15:25:33 +01005996}
5997
5998void LocationsBuilderARMVIXL::VisitLoadString(HLoadString* load) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00005999 LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load);
Artem Serov02d37832016-10-25 15:25:33 +01006000 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
Artem Serov02d37832016-10-25 15:25:33 +01006001 HLoadString::LoadKind load_kind = load->GetLoadKind();
6002 if (load_kind == HLoadString::LoadKind::kDexCacheViaMethod) {
Artem Serov02d37832016-10-25 15:25:33 +01006003 locations->SetOut(LocationFrom(r0));
6004 } else {
6005 locations->SetOut(Location::RequiresRegister());
Artem Serovd4cc5b22016-11-04 11:19:09 +00006006 if (load_kind == HLoadString::LoadKind::kBssEntry) {
6007 if (!kUseReadBarrier || kUseBakerReadBarrier) {
6008 // Rely on the pResolveString and/or marking to save everything, including temps.
6009 // Note that IP may theoretically be clobbered by saving/restoring the live register
6010 // (only one thanks to the custom calling convention), so we request a different temp.
6011 locations->AddTemp(Location::RequiresRegister());
6012 RegisterSet caller_saves = RegisterSet::Empty();
6013 InvokeRuntimeCallingConventionARMVIXL calling_convention;
6014 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0)));
6015 // TODO: Add GetReturnLocation() to the calling convention so that we can DCHECK()
6016 // that the the kPrimNot result register is the same as the first argument register.
6017 locations->SetCustomSlowPathCallerSaves(caller_saves);
6018 } else {
6019 // For non-Baker read barrier we have a temp-clobbering call.
6020 }
6021 }
Artem Serov02d37832016-10-25 15:25:33 +01006022 }
6023}
6024
6025void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00006026 LocationSummary* locations = load->GetLocations();
6027 Location out_loc = locations->Out();
6028 vixl32::Register out = OutputRegister(load);
6029 HLoadString::LoadKind load_kind = load->GetLoadKind();
6030
6031 switch (load_kind) {
6032 case HLoadString::LoadKind::kBootImageLinkTimeAddress: {
Artem Serovc5fcb442016-12-02 19:19:58 +00006033 __ Ldr(out, codegen_->DeduplicateBootImageStringLiteral(load->GetDexFile(),
6034 load->GetStringIndex()));
6035 return; // No dex cache slow path.
Artem Serovd4cc5b22016-11-04 11:19:09 +00006036 }
6037 case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
6038 DCHECK(codegen_->GetCompilerOptions().IsBootImage());
6039 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
6040 codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex().index_);
6041 codegen_->EmitMovwMovtPlaceholder(labels, out);
6042 return; // No dex cache slow path.
6043 }
6044 case HLoadString::LoadKind::kBootImageAddress: {
Artem Serovc5fcb442016-12-02 19:19:58 +00006045 DCHECK_NE(load->GetAddress(), 0u);
6046 uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress());
6047 __ Ldr(out, codegen_->DeduplicateBootImageAddressLiteral(address));
6048 return; // No dex cache slow path.
Artem Serovd4cc5b22016-11-04 11:19:09 +00006049 }
6050 case HLoadString::LoadKind::kBssEntry: {
6051 DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
6052 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
6053 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
6054 codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex().index_);
6055 codegen_->EmitMovwMovtPlaceholder(labels, temp);
6056 GenerateGcRootFieldLoad(load, out_loc, temp, /* offset */ 0, kCompilerReadBarrierOption);
6057 LoadStringSlowPathARMVIXL* slow_path =
6058 new (GetGraph()->GetArena()) LoadStringSlowPathARMVIXL(load);
6059 codegen_->AddSlowPath(slow_path);
6060 __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
6061 __ Bind(slow_path->GetExitLabel());
6062 return;
6063 }
6064 case HLoadString::LoadKind::kJitTableAddress: {
Artem Serovc5fcb442016-12-02 19:19:58 +00006065 __ Ldr(out, codegen_->DeduplicateJitStringLiteral(load->GetDexFile(),
6066 load->GetStringIndex()));
6067 // /* GcRoot<mirror::String> */ out = *out
6068 GenerateGcRootFieldLoad(load, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption);
6069 return;
Artem Serovd4cc5b22016-11-04 11:19:09 +00006070 }
6071 default:
6072 break;
6073 }
Artem Serov02d37832016-10-25 15:25:33 +01006074
6075 // TODO: Re-add the compiler code to do string dex cache lookup again.
6076 DCHECK_EQ(load->GetLoadKind(), HLoadString::LoadKind::kDexCacheViaMethod);
6077 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Andreas Gampe8a0128a2016-11-28 07:38:35 -08006078 __ Mov(calling_convention.GetRegisterAt(0), load->GetStringIndex().index_);
Artem Serov02d37832016-10-25 15:25:33 +01006079 codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc());
6080 CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
6081}
6082
6083static int32_t GetExceptionTlsOffset() {
6084 return Thread::ExceptionOffset<kArmPointerSize>().Int32Value();
6085}
6086
6087void LocationsBuilderARMVIXL::VisitLoadException(HLoadException* load) {
6088 LocationSummary* locations =
6089 new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kNoCall);
6090 locations->SetOut(Location::RequiresRegister());
6091}
6092
6093void InstructionCodeGeneratorARMVIXL::VisitLoadException(HLoadException* load) {
6094 vixl32::Register out = OutputRegister(load);
6095 GetAssembler()->LoadFromOffset(kLoadWord, out, tr, GetExceptionTlsOffset());
6096}
6097
6098
6099void LocationsBuilderARMVIXL::VisitClearException(HClearException* clear) {
6100 new (GetGraph()->GetArena()) LocationSummary(clear, LocationSummary::kNoCall);
6101}
6102
6103void InstructionCodeGeneratorARMVIXL::VisitClearException(HClearException* clear ATTRIBUTE_UNUSED) {
6104 UseScratchRegisterScope temps(GetVIXLAssembler());
6105 vixl32::Register temp = temps.Acquire();
6106 __ Mov(temp, 0);
6107 GetAssembler()->StoreToOffset(kStoreWord, temp, tr, GetExceptionTlsOffset());
6108}
6109
6110void LocationsBuilderARMVIXL::VisitThrow(HThrow* instruction) {
6111 LocationSummary* locations =
6112 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
6113 InvokeRuntimeCallingConventionARMVIXL calling_convention;
6114 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
6115}
6116
6117void InstructionCodeGeneratorARMVIXL::VisitThrow(HThrow* instruction) {
6118 codegen_->InvokeRuntime(kQuickDeliverException, instruction, instruction->GetDexPc());
6119 CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
6120}
6121
Artem Serov657022c2016-11-23 14:19:38 +00006122// Temp is used for read barrier.
6123static size_t NumberOfInstanceOfTemps(TypeCheckKind type_check_kind) {
6124 if (kEmitCompilerReadBarrier &&
6125 (kUseBakerReadBarrier ||
6126 type_check_kind == TypeCheckKind::kAbstractClassCheck ||
6127 type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
6128 type_check_kind == TypeCheckKind::kArrayObjectCheck)) {
6129 return 1;
6130 }
6131 return 0;
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006132}
6133
Artem Serov657022c2016-11-23 14:19:38 +00006134// Interface case has 3 temps, one for holding the number of interfaces, one for the current
6135// interface pointer, one for loading the current interface.
6136// The other checks have one temp for loading the object's class.
6137static size_t NumberOfCheckCastTemps(TypeCheckKind type_check_kind) {
6138 if (type_check_kind == TypeCheckKind::kInterfaceCheck) {
6139 return 3;
6140 }
6141 return 1 + NumberOfInstanceOfTemps(type_check_kind);
6142}
Artem Serovcfbe9132016-10-14 15:58:56 +01006143
6144void LocationsBuilderARMVIXL::VisitInstanceOf(HInstanceOf* instruction) {
6145 LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
6146 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
6147 bool baker_read_barrier_slow_path = false;
6148 switch (type_check_kind) {
6149 case TypeCheckKind::kExactCheck:
6150 case TypeCheckKind::kAbstractClassCheck:
6151 case TypeCheckKind::kClassHierarchyCheck:
6152 case TypeCheckKind::kArrayObjectCheck:
6153 call_kind =
6154 kEmitCompilerReadBarrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall;
6155 baker_read_barrier_slow_path = kUseBakerReadBarrier;
6156 break;
6157 case TypeCheckKind::kArrayCheck:
6158 case TypeCheckKind::kUnresolvedCheck:
6159 case TypeCheckKind::kInterfaceCheck:
6160 call_kind = LocationSummary::kCallOnSlowPath;
6161 break;
6162 }
6163
6164 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
6165 if (baker_read_barrier_slow_path) {
6166 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
6167 }
6168 locations->SetInAt(0, Location::RequiresRegister());
6169 locations->SetInAt(1, Location::RequiresRegister());
6170 // The "out" register is used as a temporary, so it overlaps with the inputs.
6171 // Note that TypeCheckSlowPathARM uses this register too.
6172 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
Artem Serov657022c2016-11-23 14:19:38 +00006173 locations->AddRegisterTemps(NumberOfInstanceOfTemps(type_check_kind));
Artem Serovcfbe9132016-10-14 15:58:56 +01006174}
6175
6176void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) {
6177 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
6178 LocationSummary* locations = instruction->GetLocations();
6179 Location obj_loc = locations->InAt(0);
6180 vixl32::Register obj = InputRegisterAt(instruction, 0);
6181 vixl32::Register cls = InputRegisterAt(instruction, 1);
6182 Location out_loc = locations->Out();
6183 vixl32::Register out = OutputRegister(instruction);
Artem Serov657022c2016-11-23 14:19:38 +00006184 const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind);
6185 DCHECK_LE(num_temps, 1u);
6186 Location maybe_temp_loc = (num_temps >= 1) ? locations->GetTemp(0) : Location::NoLocation();
Artem Serovcfbe9132016-10-14 15:58:56 +01006187 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
6188 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
6189 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
6190 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
6191 vixl32::Label done, zero;
6192 SlowPathCodeARMVIXL* slow_path = nullptr;
6193
6194 // Return 0 if `obj` is null.
6195 // avoid null check if we know obj is not null.
6196 if (instruction->MustDoNullCheck()) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00006197 __ CompareAndBranchIfZero(obj, &zero, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01006198 }
6199
Artem Serovcfbe9132016-10-14 15:58:56 +01006200 switch (type_check_kind) {
6201 case TypeCheckKind::kExactCheck: {
Mathieu Chartier6beced42016-11-15 15:51:31 -08006202 // /* HeapReference<Class> */ out = obj->klass_
6203 GenerateReferenceLoadTwoRegisters(instruction,
6204 out_loc,
6205 obj_loc,
6206 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00006207 maybe_temp_loc,
6208 kCompilerReadBarrierOption);
Artem Serovcfbe9132016-10-14 15:58:56 +01006209 __ Cmp(out, cls);
6210 // Classes must be equal for the instanceof to succeed.
Artem Serov517d9f62016-12-12 15:51:15 +00006211 __ B(ne, &zero, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01006212 __ Mov(out, 1);
6213 __ B(&done);
6214 break;
6215 }
6216
6217 case TypeCheckKind::kAbstractClassCheck: {
Mathieu Chartier6beced42016-11-15 15:51:31 -08006218 // /* HeapReference<Class> */ out = obj->klass_
6219 GenerateReferenceLoadTwoRegisters(instruction,
6220 out_loc,
6221 obj_loc,
6222 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00006223 maybe_temp_loc,
6224 kCompilerReadBarrierOption);
Artem Serovcfbe9132016-10-14 15:58:56 +01006225 // If the class is abstract, we eagerly fetch the super class of the
6226 // object to avoid doing a comparison we know will fail.
6227 vixl32::Label loop;
6228 __ Bind(&loop);
6229 // /* HeapReference<Class> */ out = out->super_class_
Artem Serov657022c2016-11-23 14:19:38 +00006230 GenerateReferenceLoadOneRegister(instruction,
6231 out_loc,
6232 super_offset,
6233 maybe_temp_loc,
6234 kCompilerReadBarrierOption);
Artem Serovcfbe9132016-10-14 15:58:56 +01006235 // If `out` is null, we use it for the result, and jump to `done`.
xueliang.zhongf51bc622016-11-04 09:23:32 +00006236 __ CompareAndBranchIfZero(out, &done, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01006237 __ Cmp(out, cls);
Artem Serov517d9f62016-12-12 15:51:15 +00006238 __ B(ne, &loop, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01006239 __ Mov(out, 1);
6240 if (zero.IsReferenced()) {
6241 __ B(&done);
6242 }
6243 break;
6244 }
6245
6246 case TypeCheckKind::kClassHierarchyCheck: {
Mathieu Chartier6beced42016-11-15 15:51:31 -08006247 // /* HeapReference<Class> */ out = obj->klass_
6248 GenerateReferenceLoadTwoRegisters(instruction,
6249 out_loc,
6250 obj_loc,
6251 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00006252 maybe_temp_loc,
6253 kCompilerReadBarrierOption);
Artem Serovcfbe9132016-10-14 15:58:56 +01006254 // Walk over the class hierarchy to find a match.
6255 vixl32::Label loop, success;
6256 __ Bind(&loop);
6257 __ Cmp(out, cls);
Artem Serov517d9f62016-12-12 15:51:15 +00006258 __ B(eq, &success, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01006259 // /* HeapReference<Class> */ out = out->super_class_
Artem Serov657022c2016-11-23 14:19:38 +00006260 GenerateReferenceLoadOneRegister(instruction,
6261 out_loc,
6262 super_offset,
6263 maybe_temp_loc,
6264 kCompilerReadBarrierOption);
xueliang.zhongf51bc622016-11-04 09:23:32 +00006265 __ CompareAndBranchIfNonZero(out, &loop);
Artem Serovcfbe9132016-10-14 15:58:56 +01006266 // If `out` is null, we use it for the result, and jump to `done`.
6267 __ B(&done);
6268 __ Bind(&success);
6269 __ Mov(out, 1);
6270 if (zero.IsReferenced()) {
6271 __ B(&done);
6272 }
6273 break;
6274 }
6275
6276 case TypeCheckKind::kArrayObjectCheck: {
Mathieu Chartier6beced42016-11-15 15:51:31 -08006277 // /* HeapReference<Class> */ out = obj->klass_
6278 GenerateReferenceLoadTwoRegisters(instruction,
6279 out_loc,
6280 obj_loc,
6281 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00006282 maybe_temp_loc,
6283 kCompilerReadBarrierOption);
Artem Serovcfbe9132016-10-14 15:58:56 +01006284 // Do an exact check.
6285 vixl32::Label exact_check;
6286 __ Cmp(out, cls);
Artem Serov517d9f62016-12-12 15:51:15 +00006287 __ B(eq, &exact_check, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01006288 // Otherwise, we need to check that the object's class is a non-primitive array.
6289 // /* HeapReference<Class> */ out = out->component_type_
Artem Serov657022c2016-11-23 14:19:38 +00006290 GenerateReferenceLoadOneRegister(instruction,
6291 out_loc,
6292 component_offset,
6293 maybe_temp_loc,
6294 kCompilerReadBarrierOption);
Artem Serovcfbe9132016-10-14 15:58:56 +01006295 // If `out` is null, we use it for the result, and jump to `done`.
xueliang.zhongf51bc622016-11-04 09:23:32 +00006296 __ CompareAndBranchIfZero(out, &done, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01006297 GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset);
6298 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
xueliang.zhongf51bc622016-11-04 09:23:32 +00006299 __ CompareAndBranchIfNonZero(out, &zero, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01006300 __ Bind(&exact_check);
6301 __ Mov(out, 1);
6302 __ B(&done);
6303 break;
6304 }
6305
6306 case TypeCheckKind::kArrayCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00006307 // No read barrier since the slow path will retry upon failure.
Mathieu Chartier6beced42016-11-15 15:51:31 -08006308 // /* HeapReference<Class> */ out = obj->klass_
6309 GenerateReferenceLoadTwoRegisters(instruction,
6310 out_loc,
6311 obj_loc,
6312 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00006313 maybe_temp_loc,
6314 kWithoutReadBarrier);
Artem Serovcfbe9132016-10-14 15:58:56 +01006315 __ Cmp(out, cls);
6316 DCHECK(locations->OnlyCallsOnSlowPath());
6317 slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARMVIXL(instruction,
6318 /* is_fatal */ false);
6319 codegen_->AddSlowPath(slow_path);
6320 __ B(ne, slow_path->GetEntryLabel());
6321 __ Mov(out, 1);
6322 if (zero.IsReferenced()) {
6323 __ B(&done);
6324 }
6325 break;
6326 }
6327
6328 case TypeCheckKind::kUnresolvedCheck:
6329 case TypeCheckKind::kInterfaceCheck: {
6330 // Note that we indeed only call on slow path, but we always go
6331 // into the slow path for the unresolved and interface check
6332 // cases.
6333 //
6334 // We cannot directly call the InstanceofNonTrivial runtime
6335 // entry point without resorting to a type checking slow path
6336 // here (i.e. by calling InvokeRuntime directly), as it would
6337 // require to assign fixed registers for the inputs of this
6338 // HInstanceOf instruction (following the runtime calling
6339 // convention), which might be cluttered by the potential first
6340 // read barrier emission at the beginning of this method.
6341 //
6342 // TODO: Introduce a new runtime entry point taking the object
6343 // to test (instead of its class) as argument, and let it deal
6344 // with the read barrier issues. This will let us refactor this
6345 // case of the `switch` code as it was previously (with a direct
6346 // call to the runtime not using a type checking slow path).
6347 // This should also be beneficial for the other cases above.
6348 DCHECK(locations->OnlyCallsOnSlowPath());
6349 slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARMVIXL(instruction,
6350 /* is_fatal */ false);
6351 codegen_->AddSlowPath(slow_path);
6352 __ B(slow_path->GetEntryLabel());
6353 if (zero.IsReferenced()) {
6354 __ B(&done);
6355 }
6356 break;
6357 }
6358 }
6359
6360 if (zero.IsReferenced()) {
6361 __ Bind(&zero);
6362 __ Mov(out, 0);
6363 }
6364
6365 if (done.IsReferenced()) {
6366 __ Bind(&done);
6367 }
6368
6369 if (slow_path != nullptr) {
6370 __ Bind(slow_path->GetExitLabel());
6371 }
6372}
6373
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006374void LocationsBuilderARMVIXL::VisitCheckCast(HCheckCast* instruction) {
6375 LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
6376 bool throws_into_catch = instruction->CanThrowIntoCatchBlock();
6377
6378 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
6379 switch (type_check_kind) {
6380 case TypeCheckKind::kExactCheck:
6381 case TypeCheckKind::kAbstractClassCheck:
6382 case TypeCheckKind::kClassHierarchyCheck:
6383 case TypeCheckKind::kArrayObjectCheck:
6384 call_kind = (throws_into_catch || kEmitCompilerReadBarrier) ?
6385 LocationSummary::kCallOnSlowPath :
6386 LocationSummary::kNoCall; // In fact, call on a fatal (non-returning) slow path.
6387 break;
6388 case TypeCheckKind::kArrayCheck:
6389 case TypeCheckKind::kUnresolvedCheck:
6390 case TypeCheckKind::kInterfaceCheck:
6391 call_kind = LocationSummary::kCallOnSlowPath;
6392 break;
6393 }
6394
6395 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
6396 locations->SetInAt(0, Location::RequiresRegister());
6397 locations->SetInAt(1, Location::RequiresRegister());
Artem Serov657022c2016-11-23 14:19:38 +00006398 locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind));
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006399}
6400
6401void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) {
6402 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
6403 LocationSummary* locations = instruction->GetLocations();
6404 Location obj_loc = locations->InAt(0);
6405 vixl32::Register obj = InputRegisterAt(instruction, 0);
6406 vixl32::Register cls = InputRegisterAt(instruction, 1);
6407 Location temp_loc = locations->GetTemp(0);
6408 vixl32::Register temp = RegisterFrom(temp_loc);
Artem Serov657022c2016-11-23 14:19:38 +00006409 const size_t num_temps = NumberOfCheckCastTemps(type_check_kind);
6410 DCHECK_LE(num_temps, 3u);
6411 Location maybe_temp2_loc = (num_temps >= 2) ? locations->GetTemp(1) : Location::NoLocation();
6412 Location maybe_temp3_loc = (num_temps >= 3) ? locations->GetTemp(2) : Location::NoLocation();
6413 const uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
6414 const uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
6415 const uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
6416 const uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
6417 const uint32_t iftable_offset = mirror::Class::IfTableOffset().Uint32Value();
6418 const uint32_t array_length_offset = mirror::Array::LengthOffset().Uint32Value();
6419 const uint32_t object_array_data_offset =
6420 mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value();
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006421
Artem Serov657022c2016-11-23 14:19:38 +00006422 // Always false for read barriers since we may need to go to the entrypoint for non-fatal cases
6423 // from false negatives. The false negatives may come from avoiding read barriers below. Avoiding
6424 // read barriers is done for performance and code size reasons.
6425 bool is_type_check_slow_path_fatal = false;
6426 if (!kEmitCompilerReadBarrier) {
6427 is_type_check_slow_path_fatal =
6428 (type_check_kind == TypeCheckKind::kExactCheck ||
6429 type_check_kind == TypeCheckKind::kAbstractClassCheck ||
6430 type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
6431 type_check_kind == TypeCheckKind::kArrayObjectCheck) &&
6432 !instruction->CanThrowIntoCatchBlock();
6433 }
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006434 SlowPathCodeARMVIXL* type_check_slow_path =
6435 new (GetGraph()->GetArena()) TypeCheckSlowPathARMVIXL(instruction,
6436 is_type_check_slow_path_fatal);
6437 codegen_->AddSlowPath(type_check_slow_path);
6438
6439 vixl32::Label done;
6440 // Avoid null check if we know obj is not null.
6441 if (instruction->MustDoNullCheck()) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00006442 __ CompareAndBranchIfZero(obj, &done, /* far_target */ false);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006443 }
6444
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006445 switch (type_check_kind) {
6446 case TypeCheckKind::kExactCheck:
6447 case TypeCheckKind::kArrayCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00006448 // /* HeapReference<Class> */ temp = obj->klass_
6449 GenerateReferenceLoadTwoRegisters(instruction,
6450 temp_loc,
6451 obj_loc,
6452 class_offset,
6453 maybe_temp2_loc,
6454 kWithoutReadBarrier);
6455
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006456 __ Cmp(temp, cls);
6457 // Jump to slow path for throwing the exception or doing a
6458 // more involved array check.
6459 __ B(ne, type_check_slow_path->GetEntryLabel());
6460 break;
6461 }
6462
6463 case TypeCheckKind::kAbstractClassCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00006464 // /* HeapReference<Class> */ temp = obj->klass_
6465 GenerateReferenceLoadTwoRegisters(instruction,
6466 temp_loc,
6467 obj_loc,
6468 class_offset,
6469 maybe_temp2_loc,
6470 kWithoutReadBarrier);
6471
Artem Serovcfbe9132016-10-14 15:58:56 +01006472 // If the class is abstract, we eagerly fetch the super class of the
6473 // object to avoid doing a comparison we know will fail.
6474 vixl32::Label loop;
6475 __ Bind(&loop);
6476 // /* HeapReference<Class> */ temp = temp->super_class_
Artem Serov657022c2016-11-23 14:19:38 +00006477 GenerateReferenceLoadOneRegister(instruction,
6478 temp_loc,
6479 super_offset,
6480 maybe_temp2_loc,
6481 kWithoutReadBarrier);
Artem Serovcfbe9132016-10-14 15:58:56 +01006482
6483 // If the class reference currently in `temp` is null, jump to the slow path to throw the
6484 // exception.
xueliang.zhongf51bc622016-11-04 09:23:32 +00006485 __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
Artem Serovcfbe9132016-10-14 15:58:56 +01006486
6487 // Otherwise, compare the classes.
6488 __ Cmp(temp, cls);
Artem Serov517d9f62016-12-12 15:51:15 +00006489 __ B(ne, &loop, /* far_target */ false);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006490 break;
6491 }
6492
6493 case TypeCheckKind::kClassHierarchyCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00006494 // /* HeapReference<Class> */ temp = obj->klass_
6495 GenerateReferenceLoadTwoRegisters(instruction,
6496 temp_loc,
6497 obj_loc,
6498 class_offset,
6499 maybe_temp2_loc,
6500 kWithoutReadBarrier);
6501
Artem Serovcfbe9132016-10-14 15:58:56 +01006502 // Walk over the class hierarchy to find a match.
6503 vixl32::Label loop;
6504 __ Bind(&loop);
6505 __ Cmp(temp, cls);
Artem Serov517d9f62016-12-12 15:51:15 +00006506 __ B(eq, &done, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01006507
6508 // /* HeapReference<Class> */ temp = temp->super_class_
Artem Serov657022c2016-11-23 14:19:38 +00006509 GenerateReferenceLoadOneRegister(instruction,
6510 temp_loc,
6511 super_offset,
6512 maybe_temp2_loc,
6513 kWithoutReadBarrier);
Artem Serovcfbe9132016-10-14 15:58:56 +01006514
6515 // If the class reference currently in `temp` is null, jump to the slow path to throw the
6516 // exception.
xueliang.zhongf51bc622016-11-04 09:23:32 +00006517 __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
Artem Serovcfbe9132016-10-14 15:58:56 +01006518 // Otherwise, jump to the beginning of the loop.
6519 __ B(&loop);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006520 break;
6521 }
6522
Artem Serovcfbe9132016-10-14 15:58:56 +01006523 case TypeCheckKind::kArrayObjectCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00006524 // /* HeapReference<Class> */ temp = obj->klass_
6525 GenerateReferenceLoadTwoRegisters(instruction,
6526 temp_loc,
6527 obj_loc,
6528 class_offset,
6529 maybe_temp2_loc,
6530 kWithoutReadBarrier);
6531
Artem Serovcfbe9132016-10-14 15:58:56 +01006532 // Do an exact check.
6533 __ Cmp(temp, cls);
Artem Serov517d9f62016-12-12 15:51:15 +00006534 __ B(eq, &done, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01006535
6536 // Otherwise, we need to check that the object's class is a non-primitive array.
6537 // /* HeapReference<Class> */ temp = temp->component_type_
Artem Serov657022c2016-11-23 14:19:38 +00006538 GenerateReferenceLoadOneRegister(instruction,
6539 temp_loc,
6540 component_offset,
6541 maybe_temp2_loc,
6542 kWithoutReadBarrier);
Artem Serovcfbe9132016-10-14 15:58:56 +01006543 // If the component type is null, jump to the slow path to throw the exception.
xueliang.zhongf51bc622016-11-04 09:23:32 +00006544 __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
Artem Serovcfbe9132016-10-14 15:58:56 +01006545 // Otherwise,the object is indeed an array, jump to label `check_non_primitive_component_type`
6546 // to further check that this component type is not a primitive type.
6547 GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, temp, temp, primitive_offset);
6548 static_assert(Primitive::kPrimNot == 0, "Expected 0 for art::Primitive::kPrimNot");
xueliang.zhongf51bc622016-11-04 09:23:32 +00006549 __ CompareAndBranchIfNonZero(temp, type_check_slow_path->GetEntryLabel());
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006550 break;
6551 }
6552
6553 case TypeCheckKind::kUnresolvedCheck:
Artem Serov657022c2016-11-23 14:19:38 +00006554 // We always go into the type check slow path for the unresolved check case.
Artem Serovcfbe9132016-10-14 15:58:56 +01006555 // We cannot directly call the CheckCast runtime entry point
6556 // without resorting to a type checking slow path here (i.e. by
6557 // calling InvokeRuntime directly), as it would require to
6558 // assign fixed registers for the inputs of this HInstanceOf
6559 // instruction (following the runtime calling convention), which
6560 // might be cluttered by the potential first read barrier
6561 // emission at the beginning of this method.
Artem Serov657022c2016-11-23 14:19:38 +00006562
Artem Serovcfbe9132016-10-14 15:58:56 +01006563 __ B(type_check_slow_path->GetEntryLabel());
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006564 break;
Artem Serov657022c2016-11-23 14:19:38 +00006565
6566 case TypeCheckKind::kInterfaceCheck: {
6567 // Avoid read barriers to improve performance of the fast path. We can not get false
6568 // positives by doing this.
6569 // /* HeapReference<Class> */ temp = obj->klass_
6570 GenerateReferenceLoadTwoRegisters(instruction,
6571 temp_loc,
6572 obj_loc,
6573 class_offset,
6574 maybe_temp2_loc,
6575 kWithoutReadBarrier);
6576
6577 // /* HeapReference<Class> */ temp = temp->iftable_
6578 GenerateReferenceLoadTwoRegisters(instruction,
6579 temp_loc,
6580 temp_loc,
6581 iftable_offset,
6582 maybe_temp2_loc,
6583 kWithoutReadBarrier);
6584 // Iftable is never null.
6585 __ Ldr(RegisterFrom(maybe_temp2_loc), MemOperand(temp, array_length_offset));
6586 // Loop through the iftable and check if any class matches.
6587 vixl32::Label start_loop;
6588 __ Bind(&start_loop);
6589 __ CompareAndBranchIfZero(RegisterFrom(maybe_temp2_loc),
6590 type_check_slow_path->GetEntryLabel());
6591 __ Ldr(RegisterFrom(maybe_temp3_loc), MemOperand(temp, object_array_data_offset));
6592 GetAssembler()->MaybeUnpoisonHeapReference(RegisterFrom(maybe_temp3_loc));
6593 // Go to next interface.
6594 __ Add(temp, temp, Operand::From(2 * kHeapReferenceSize));
6595 __ Sub(RegisterFrom(maybe_temp2_loc), RegisterFrom(maybe_temp2_loc), 2);
6596 // Compare the classes and continue the loop if they do not match.
6597 __ Cmp(cls, RegisterFrom(maybe_temp3_loc));
Artem Serov517d9f62016-12-12 15:51:15 +00006598 __ B(ne, &start_loop, /* far_target */ false);
Artem Serov657022c2016-11-23 14:19:38 +00006599 break;
6600 }
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006601 }
6602 __ Bind(&done);
6603
6604 __ Bind(type_check_slow_path->GetExitLabel());
6605}
6606
Artem Serov551b28f2016-10-18 19:11:30 +01006607void LocationsBuilderARMVIXL::VisitMonitorOperation(HMonitorOperation* instruction) {
6608 LocationSummary* locations =
6609 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
6610 InvokeRuntimeCallingConventionARMVIXL calling_convention;
6611 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
6612}
6613
6614void InstructionCodeGeneratorARMVIXL::VisitMonitorOperation(HMonitorOperation* instruction) {
6615 codegen_->InvokeRuntime(instruction->IsEnter() ? kQuickLockObject : kQuickUnlockObject,
6616 instruction,
6617 instruction->GetDexPc());
6618 if (instruction->IsEnter()) {
6619 CheckEntrypointTypes<kQuickLockObject, void, mirror::Object*>();
6620 } else {
6621 CheckEntrypointTypes<kQuickUnlockObject, void, mirror::Object*>();
6622 }
6623}
6624
Artem Serov02109dd2016-09-23 17:17:54 +01006625void LocationsBuilderARMVIXL::VisitAnd(HAnd* instruction) {
6626 HandleBitwiseOperation(instruction, AND);
6627}
6628
6629void LocationsBuilderARMVIXL::VisitOr(HOr* instruction) {
6630 HandleBitwiseOperation(instruction, ORR);
6631}
6632
6633void LocationsBuilderARMVIXL::VisitXor(HXor* instruction) {
6634 HandleBitwiseOperation(instruction, EOR);
6635}
6636
6637void LocationsBuilderARMVIXL::HandleBitwiseOperation(HBinaryOperation* instruction, Opcode opcode) {
6638 LocationSummary* locations =
6639 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
6640 DCHECK(instruction->GetResultType() == Primitive::kPrimInt
6641 || instruction->GetResultType() == Primitive::kPrimLong);
6642 // Note: GVN reorders commutative operations to have the constant on the right hand side.
6643 locations->SetInAt(0, Location::RequiresRegister());
6644 locations->SetInAt(1, ArmEncodableConstantOrRegister(instruction->InputAt(1), opcode));
6645 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
6646}
6647
6648void InstructionCodeGeneratorARMVIXL::VisitAnd(HAnd* instruction) {
6649 HandleBitwiseOperation(instruction);
6650}
6651
6652void InstructionCodeGeneratorARMVIXL::VisitOr(HOr* instruction) {
6653 HandleBitwiseOperation(instruction);
6654}
6655
6656void InstructionCodeGeneratorARMVIXL::VisitXor(HXor* instruction) {
6657 HandleBitwiseOperation(instruction);
6658}
6659
Artem Serov2bbc9532016-10-21 11:51:50 +01006660void LocationsBuilderARMVIXL::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) {
6661 LocationSummary* locations =
6662 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
6663 DCHECK(instruction->GetResultType() == Primitive::kPrimInt
6664 || instruction->GetResultType() == Primitive::kPrimLong);
6665
6666 locations->SetInAt(0, Location::RequiresRegister());
6667 locations->SetInAt(1, Location::RequiresRegister());
6668 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
6669}
6670
6671void InstructionCodeGeneratorARMVIXL::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) {
6672 LocationSummary* locations = instruction->GetLocations();
6673 Location first = locations->InAt(0);
6674 Location second = locations->InAt(1);
6675 Location out = locations->Out();
6676
6677 if (instruction->GetResultType() == Primitive::kPrimInt) {
6678 vixl32::Register first_reg = RegisterFrom(first);
6679 vixl32::Register second_reg = RegisterFrom(second);
6680 vixl32::Register out_reg = RegisterFrom(out);
6681
6682 switch (instruction->GetOpKind()) {
6683 case HInstruction::kAnd:
6684 __ Bic(out_reg, first_reg, second_reg);
6685 break;
6686 case HInstruction::kOr:
6687 __ Orn(out_reg, first_reg, second_reg);
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 return;
6696
6697 } else {
6698 DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong);
6699 vixl32::Register first_low = LowRegisterFrom(first);
6700 vixl32::Register first_high = HighRegisterFrom(first);
6701 vixl32::Register second_low = LowRegisterFrom(second);
6702 vixl32::Register second_high = HighRegisterFrom(second);
6703 vixl32::Register out_low = LowRegisterFrom(out);
6704 vixl32::Register out_high = HighRegisterFrom(out);
6705
6706 switch (instruction->GetOpKind()) {
6707 case HInstruction::kAnd:
6708 __ Bic(out_low, first_low, second_low);
6709 __ Bic(out_high, first_high, second_high);
6710 break;
6711 case HInstruction::kOr:
6712 __ Orn(out_low, first_low, second_low);
6713 __ Orn(out_high, first_high, second_high);
6714 break;
6715 // There is no EON on arm.
6716 case HInstruction::kXor:
6717 default:
6718 LOG(FATAL) << "Unexpected instruction " << instruction->DebugName();
6719 UNREACHABLE();
6720 }
6721 }
6722}
6723
Artem Serov02109dd2016-09-23 17:17:54 +01006724// TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
6725void InstructionCodeGeneratorARMVIXL::GenerateAndConst(vixl32::Register out,
6726 vixl32::Register first,
6727 uint32_t value) {
6728 // Optimize special cases for individual halfs of `and-long` (`and` is simplified earlier).
6729 if (value == 0xffffffffu) {
6730 if (!out.Is(first)) {
6731 __ Mov(out, first);
6732 }
6733 return;
6734 }
6735 if (value == 0u) {
6736 __ Mov(out, 0);
6737 return;
6738 }
6739 if (GetAssembler()->ShifterOperandCanHold(AND, value)) {
6740 __ And(out, first, value);
6741 } else {
6742 DCHECK(GetAssembler()->ShifterOperandCanHold(BIC, ~value));
6743 __ Bic(out, first, ~value);
6744 }
6745}
6746
6747// TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
6748void InstructionCodeGeneratorARMVIXL::GenerateOrrConst(vixl32::Register out,
6749 vixl32::Register first,
6750 uint32_t value) {
6751 // Optimize special cases for individual halfs of `or-long` (`or` is simplified earlier).
6752 if (value == 0u) {
6753 if (!out.Is(first)) {
6754 __ Mov(out, first);
6755 }
6756 return;
6757 }
6758 if (value == 0xffffffffu) {
6759 __ Mvn(out, 0);
6760 return;
6761 }
6762 if (GetAssembler()->ShifterOperandCanHold(ORR, value)) {
6763 __ Orr(out, first, value);
6764 } else {
6765 DCHECK(GetAssembler()->ShifterOperandCanHold(ORN, ~value));
6766 __ Orn(out, first, ~value);
6767 }
6768}
6769
6770// TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
6771void InstructionCodeGeneratorARMVIXL::GenerateEorConst(vixl32::Register out,
6772 vixl32::Register first,
6773 uint32_t value) {
6774 // Optimize special case for individual halfs of `xor-long` (`xor` is simplified earlier).
6775 if (value == 0u) {
6776 if (!out.Is(first)) {
6777 __ Mov(out, first);
6778 }
6779 return;
6780 }
6781 __ Eor(out, first, value);
6782}
6783
Anton Kirilovdda43962016-11-21 19:55:20 +00006784void InstructionCodeGeneratorARMVIXL::GenerateAddLongConst(Location out,
6785 Location first,
6786 uint64_t value) {
6787 vixl32::Register out_low = LowRegisterFrom(out);
6788 vixl32::Register out_high = HighRegisterFrom(out);
6789 vixl32::Register first_low = LowRegisterFrom(first);
6790 vixl32::Register first_high = HighRegisterFrom(first);
6791 uint32_t value_low = Low32Bits(value);
6792 uint32_t value_high = High32Bits(value);
6793 if (value_low == 0u) {
6794 if (!out_low.Is(first_low)) {
6795 __ Mov(out_low, first_low);
6796 }
6797 __ Add(out_high, first_high, value_high);
6798 return;
6799 }
6800 __ Adds(out_low, first_low, value_low);
Scott Wakelingbffdc702016-12-07 17:46:03 +00006801 if (GetAssembler()->ShifterOperandCanHold(ADC, value_high, kCcDontCare)) {
Anton Kirilovdda43962016-11-21 19:55:20 +00006802 __ Adc(out_high, first_high, value_high);
Scott Wakelingbffdc702016-12-07 17:46:03 +00006803 } else if (GetAssembler()->ShifterOperandCanHold(SBC, ~value_high, kCcDontCare)) {
Anton Kirilovdda43962016-11-21 19:55:20 +00006804 __ Sbc(out_high, first_high, ~value_high);
6805 } else {
6806 LOG(FATAL) << "Unexpected constant " << value_high;
6807 UNREACHABLE();
6808 }
6809}
6810
Artem Serov02109dd2016-09-23 17:17:54 +01006811void InstructionCodeGeneratorARMVIXL::HandleBitwiseOperation(HBinaryOperation* instruction) {
6812 LocationSummary* locations = instruction->GetLocations();
6813 Location first = locations->InAt(0);
6814 Location second = locations->InAt(1);
6815 Location out = locations->Out();
6816
6817 if (second.IsConstant()) {
6818 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
6819 uint32_t value_low = Low32Bits(value);
6820 if (instruction->GetResultType() == Primitive::kPrimInt) {
6821 vixl32::Register first_reg = InputRegisterAt(instruction, 0);
6822 vixl32::Register out_reg = OutputRegister(instruction);
6823 if (instruction->IsAnd()) {
6824 GenerateAndConst(out_reg, first_reg, value_low);
6825 } else if (instruction->IsOr()) {
6826 GenerateOrrConst(out_reg, first_reg, value_low);
6827 } else {
6828 DCHECK(instruction->IsXor());
6829 GenerateEorConst(out_reg, first_reg, value_low);
6830 }
6831 } else {
6832 DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong);
6833 uint32_t value_high = High32Bits(value);
6834 vixl32::Register first_low = LowRegisterFrom(first);
6835 vixl32::Register first_high = HighRegisterFrom(first);
6836 vixl32::Register out_low = LowRegisterFrom(out);
6837 vixl32::Register out_high = HighRegisterFrom(out);
6838 if (instruction->IsAnd()) {
6839 GenerateAndConst(out_low, first_low, value_low);
6840 GenerateAndConst(out_high, first_high, value_high);
6841 } else if (instruction->IsOr()) {
6842 GenerateOrrConst(out_low, first_low, value_low);
6843 GenerateOrrConst(out_high, first_high, value_high);
6844 } else {
6845 DCHECK(instruction->IsXor());
6846 GenerateEorConst(out_low, first_low, value_low);
6847 GenerateEorConst(out_high, first_high, value_high);
6848 }
6849 }
6850 return;
6851 }
6852
6853 if (instruction->GetResultType() == Primitive::kPrimInt) {
6854 vixl32::Register first_reg = InputRegisterAt(instruction, 0);
6855 vixl32::Register second_reg = InputRegisterAt(instruction, 1);
6856 vixl32::Register out_reg = OutputRegister(instruction);
6857 if (instruction->IsAnd()) {
6858 __ And(out_reg, first_reg, second_reg);
6859 } else if (instruction->IsOr()) {
6860 __ Orr(out_reg, first_reg, second_reg);
6861 } else {
6862 DCHECK(instruction->IsXor());
6863 __ Eor(out_reg, first_reg, second_reg);
6864 }
6865 } else {
6866 DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong);
6867 vixl32::Register first_low = LowRegisterFrom(first);
6868 vixl32::Register first_high = HighRegisterFrom(first);
6869 vixl32::Register second_low = LowRegisterFrom(second);
6870 vixl32::Register second_high = HighRegisterFrom(second);
6871 vixl32::Register out_low = LowRegisterFrom(out);
6872 vixl32::Register out_high = HighRegisterFrom(out);
6873 if (instruction->IsAnd()) {
6874 __ And(out_low, first_low, second_low);
6875 __ And(out_high, first_high, second_high);
6876 } else if (instruction->IsOr()) {
6877 __ Orr(out_low, first_low, second_low);
6878 __ Orr(out_high, first_high, second_high);
6879 } else {
6880 DCHECK(instruction->IsXor());
6881 __ Eor(out_low, first_low, second_low);
6882 __ Eor(out_high, first_high, second_high);
6883 }
6884 }
6885}
6886
Artem Serovcfbe9132016-10-14 15:58:56 +01006887void InstructionCodeGeneratorARMVIXL::GenerateReferenceLoadOneRegister(
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006888 HInstruction* instruction,
Artem Serovcfbe9132016-10-14 15:58:56 +01006889 Location out,
6890 uint32_t offset,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006891 Location maybe_temp,
6892 ReadBarrierOption read_barrier_option) {
Artem Serovcfbe9132016-10-14 15:58:56 +01006893 vixl32::Register out_reg = RegisterFrom(out);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006894 if (read_barrier_option == kWithReadBarrier) {
6895 CHECK(kEmitCompilerReadBarrier);
6896 DCHECK(maybe_temp.IsRegister()) << maybe_temp;
6897 if (kUseBakerReadBarrier) {
6898 // Load with fast path based Baker's read barrier.
6899 // /* HeapReference<Object> */ out = *(out + offset)
6900 codegen_->GenerateFieldLoadWithBakerReadBarrier(
6901 instruction, out, out_reg, offset, maybe_temp, /* needs_null_check */ false);
6902 } else {
6903 // Load with slow path based read barrier.
6904 // Save the value of `out` into `maybe_temp` before overwriting it
6905 // in the following move operation, as we will need it for the
6906 // read barrier below.
6907 __ Mov(RegisterFrom(maybe_temp), out_reg);
6908 // /* HeapReference<Object> */ out = *(out + offset)
6909 GetAssembler()->LoadFromOffset(kLoadWord, out_reg, out_reg, offset);
6910 codegen_->GenerateReadBarrierSlow(instruction, out, out, maybe_temp, offset);
6911 }
Artem Serovcfbe9132016-10-14 15:58:56 +01006912 } else {
6913 // Plain load with no read barrier.
6914 // /* HeapReference<Object> */ out = *(out + offset)
6915 GetAssembler()->LoadFromOffset(kLoadWord, out_reg, out_reg, offset);
6916 GetAssembler()->MaybeUnpoisonHeapReference(out_reg);
6917 }
6918}
6919
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006920void InstructionCodeGeneratorARMVIXL::GenerateReferenceLoadTwoRegisters(
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006921 HInstruction* instruction,
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006922 Location out,
6923 Location obj,
6924 uint32_t offset,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006925 Location maybe_temp,
6926 ReadBarrierOption read_barrier_option) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006927 vixl32::Register out_reg = RegisterFrom(out);
6928 vixl32::Register obj_reg = RegisterFrom(obj);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006929 if (read_barrier_option == kWithReadBarrier) {
6930 CHECK(kEmitCompilerReadBarrier);
6931 if (kUseBakerReadBarrier) {
6932 DCHECK(maybe_temp.IsRegister()) << maybe_temp;
6933 // Load with fast path based Baker's read barrier.
6934 // /* HeapReference<Object> */ out = *(obj + offset)
6935 codegen_->GenerateFieldLoadWithBakerReadBarrier(
6936 instruction, out, obj_reg, offset, maybe_temp, /* needs_null_check */ false);
6937 } else {
6938 // Load with slow path based read barrier.
6939 // /* HeapReference<Object> */ out = *(obj + offset)
6940 GetAssembler()->LoadFromOffset(kLoadWord, out_reg, obj_reg, offset);
6941 codegen_->GenerateReadBarrierSlow(instruction, out, out, obj, offset);
6942 }
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006943 } else {
6944 // Plain load with no read barrier.
6945 // /* HeapReference<Object> */ out = *(obj + offset)
6946 GetAssembler()->LoadFromOffset(kLoadWord, out_reg, obj_reg, offset);
6947 GetAssembler()->MaybeUnpoisonHeapReference(out_reg);
6948 }
6949}
6950
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006951void InstructionCodeGeneratorARMVIXL::GenerateGcRootFieldLoad(
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006952 HInstruction* instruction,
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006953 Location root,
6954 vixl32::Register obj,
6955 uint32_t offset,
Artem Serovd4cc5b22016-11-04 11:19:09 +00006956 ReadBarrierOption read_barrier_option) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006957 vixl32::Register root_reg = RegisterFrom(root);
Artem Serovd4cc5b22016-11-04 11:19:09 +00006958 if (read_barrier_option == kWithReadBarrier) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006959 DCHECK(kEmitCompilerReadBarrier);
6960 if (kUseBakerReadBarrier) {
6961 // Fast path implementation of art::ReadBarrier::BarrierForRoot when
6962 // Baker's read barrier are used:
6963 //
6964 // root = obj.field;
6965 // temp = Thread::Current()->pReadBarrierMarkReg ## root.reg()
6966 // if (temp != null) {
6967 // root = temp(root)
6968 // }
6969
6970 // /* GcRoot<mirror::Object> */ root = *(obj + offset)
6971 GetAssembler()->LoadFromOffset(kLoadWord, root_reg, obj, offset);
6972 static_assert(
6973 sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(GcRoot<mirror::Object>),
6974 "art::mirror::CompressedReference<mirror::Object> and art::GcRoot<mirror::Object> "
6975 "have different sizes.");
6976 static_assert(sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(int32_t),
6977 "art::mirror::CompressedReference<mirror::Object> and int32_t "
6978 "have different sizes.");
6979
6980 // Slow path marking the GC root `root`.
6981 Location temp = LocationFrom(lr);
6982 SlowPathCodeARMVIXL* slow_path =
6983 new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathARMVIXL(
6984 instruction,
6985 root,
6986 /*entrypoint*/ temp);
6987 codegen_->AddSlowPath(slow_path);
6988
6989 // temp = Thread::Current()->pReadBarrierMarkReg ## root.reg()
6990 const int32_t entry_point_offset =
6991 CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArmPointerSize>(root.reg());
6992 // Loading the entrypoint does not require a load acquire since it is only changed when
6993 // threads are suspended or running a checkpoint.
6994 GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(temp), tr, entry_point_offset);
6995 // The entrypoint is null when the GC is not marking, this prevents one load compared to
6996 // checking GetIsGcMarking.
6997 __ CompareAndBranchIfNonZero(RegisterFrom(temp), slow_path->GetEntryLabel());
6998 __ Bind(slow_path->GetExitLabel());
6999 } else {
7000 // GC root loaded through a slow path for read barriers other
7001 // than Baker's.
7002 // /* GcRoot<mirror::Object>* */ root = obj + offset
7003 __ Add(root_reg, obj, offset);
7004 // /* mirror::Object* */ root = root->Read()
7005 codegen_->GenerateReadBarrierForRootSlow(instruction, root, root);
7006 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007007 } else {
7008 // Plain GC root load with no read barrier.
7009 // /* GcRoot<mirror::Object> */ root = *(obj + offset)
7010 GetAssembler()->LoadFromOffset(kLoadWord, root_reg, obj, offset);
7011 // Note that GC roots are not affected by heap poisoning, thus we
7012 // do not have to unpoison `root_reg` here.
7013 }
7014}
7015
Anton Kirilovedb2ac32016-11-30 15:14:10 +00007016void CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
7017 Location ref,
7018 vixl32::Register obj,
7019 uint32_t offset,
7020 Location temp,
7021 bool needs_null_check) {
7022 DCHECK(kEmitCompilerReadBarrier);
7023 DCHECK(kUseBakerReadBarrier);
7024
7025 // /* HeapReference<Object> */ ref = *(obj + offset)
7026 Location no_index = Location::NoLocation();
7027 ScaleFactor no_scale_factor = TIMES_1;
7028 GenerateReferenceLoadWithBakerReadBarrier(
7029 instruction, ref, obj, offset, no_index, no_scale_factor, temp, needs_null_check);
Roland Levillain6070e882016-11-03 17:51:58 +00007030}
7031
Anton Kirilovedb2ac32016-11-30 15:14:10 +00007032void CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction,
7033 Location ref,
7034 vixl32::Register obj,
7035 uint32_t data_offset,
7036 Location index,
7037 Location temp,
7038 bool needs_null_check) {
7039 DCHECK(kEmitCompilerReadBarrier);
7040 DCHECK(kUseBakerReadBarrier);
7041
7042 static_assert(
7043 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
7044 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
7045 // /* HeapReference<Object> */ ref =
7046 // *(obj + data_offset + index * sizeof(HeapReference<Object>))
7047 ScaleFactor scale_factor = TIMES_4;
7048 GenerateReferenceLoadWithBakerReadBarrier(
7049 instruction, ref, obj, data_offset, index, scale_factor, temp, needs_null_check);
Roland Levillain6070e882016-11-03 17:51:58 +00007050}
7051
Anton Kirilovedb2ac32016-11-30 15:14:10 +00007052void CodeGeneratorARMVIXL::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
7053 Location ref,
7054 vixl32::Register obj,
7055 uint32_t offset,
7056 Location index,
7057 ScaleFactor scale_factor,
7058 Location temp,
7059 bool needs_null_check,
7060 bool always_update_field,
7061 vixl32::Register* temp2) {
7062 DCHECK(kEmitCompilerReadBarrier);
7063 DCHECK(kUseBakerReadBarrier);
7064
7065 // In slow path based read barriers, the read barrier call is
7066 // inserted after the original load. However, in fast path based
7067 // Baker's read barriers, we need to perform the load of
7068 // mirror::Object::monitor_ *before* the original reference load.
7069 // This load-load ordering is required by the read barrier.
7070 // The fast path/slow path (for Baker's algorithm) should look like:
7071 //
7072 // uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
7073 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
7074 // HeapReference<Object> ref = *src; // Original reference load.
7075 // bool is_gray = (rb_state == ReadBarrier::GrayState());
7076 // if (is_gray) {
7077 // ref = ReadBarrier::Mark(ref); // Performed by runtime entrypoint slow path.
7078 // }
7079 //
7080 // Note: the original implementation in ReadBarrier::Barrier is
7081 // slightly more complex as it performs additional checks that we do
7082 // not do here for performance reasons.
7083
7084 vixl32::Register ref_reg = RegisterFrom(ref);
7085 vixl32::Register temp_reg = RegisterFrom(temp);
7086 uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
7087
7088 // /* int32_t */ monitor = obj->monitor_
7089 GetAssembler()->LoadFromOffset(kLoadWord, temp_reg, obj, monitor_offset);
7090 if (needs_null_check) {
7091 MaybeRecordImplicitNullCheck(instruction);
7092 }
7093 // /* LockWord */ lock_word = LockWord(monitor)
7094 static_assert(sizeof(LockWord) == sizeof(int32_t),
7095 "art::LockWord and int32_t have different sizes.");
7096
7097 // Introduce a dependency on the lock_word including the rb_state,
7098 // which shall prevent load-load reordering without using
7099 // a memory barrier (which would be more expensive).
7100 // `obj` is unchanged by this operation, but its value now depends
7101 // on `temp_reg`.
7102 __ Add(obj, obj, Operand(temp_reg, ShiftType::LSR, 32));
7103
7104 // The actual reference load.
7105 if (index.IsValid()) {
7106 // Load types involving an "index": ArrayGet,
7107 // UnsafeGetObject/UnsafeGetObjectVolatile and UnsafeCASObject
7108 // intrinsics.
7109 // /* HeapReference<Object> */ ref = *(obj + offset + (index << scale_factor))
7110 if (index.IsConstant()) {
7111 size_t computed_offset =
7112 (Int32ConstantFrom(index) << scale_factor) + offset;
7113 GetAssembler()->LoadFromOffset(kLoadWord, ref_reg, obj, computed_offset);
7114 } else {
7115 // Handle the special case of the
7116 // UnsafeGetObject/UnsafeGetObjectVolatile and UnsafeCASObject
7117 // intrinsics, which use a register pair as index ("long
7118 // offset"), of which only the low part contains data.
7119 vixl32::Register index_reg = index.IsRegisterPair()
7120 ? LowRegisterFrom(index)
7121 : RegisterFrom(index);
7122 UseScratchRegisterScope temps(GetVIXLAssembler());
7123 const vixl32::Register temp3 = temps.Acquire();
7124 __ Add(temp3, obj, Operand(index_reg, ShiftType::LSL, scale_factor));
7125 GetAssembler()->LoadFromOffset(kLoadWord, ref_reg, temp3, offset);
7126 }
7127 } else {
7128 // /* HeapReference<Object> */ ref = *(obj + offset)
7129 GetAssembler()->LoadFromOffset(kLoadWord, ref_reg, obj, offset);
7130 }
7131
7132 // Object* ref = ref_addr->AsMirrorPtr()
7133 GetAssembler()->MaybeUnpoisonHeapReference(ref_reg);
7134
7135 // Slow path marking the object `ref` when it is gray.
7136 SlowPathCodeARMVIXL* slow_path;
7137 if (always_update_field) {
7138 DCHECK(temp2 != nullptr);
7139 // ReadBarrierMarkAndUpdateFieldSlowPathARMVIXL only supports address
7140 // of the form `obj + field_offset`, where `obj` is a register and
7141 // `field_offset` is a register pair (of which only the lower half
7142 // is used). Thus `offset` and `scale_factor` above are expected
7143 // to be null in this code path.
7144 DCHECK_EQ(offset, 0u);
7145 DCHECK_EQ(scale_factor, ScaleFactor::TIMES_1);
7146 slow_path = new (GetGraph()->GetArena()) ReadBarrierMarkAndUpdateFieldSlowPathARMVIXL(
7147 instruction, ref, obj, /* field_offset */ index, temp_reg, *temp2);
7148 } else {
7149 slow_path = new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathARMVIXL(instruction, ref);
7150 }
7151 AddSlowPath(slow_path);
7152
7153 // if (rb_state == ReadBarrier::GrayState())
7154 // ref = ReadBarrier::Mark(ref);
7155 // Given the numeric representation, it's enough to check the low bit of the
7156 // rb_state. We do that by shifting the bit out of the lock word with LSRS
7157 // which can be a 16-bit instruction unlike the TST immediate.
7158 static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
7159 static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
7160 __ Lsrs(temp_reg, temp_reg, LockWord::kReadBarrierStateShift + 1);
7161 __ B(cs, slow_path->GetEntryLabel()); // Carry flag is the last bit shifted out by LSRS.
7162 __ Bind(slow_path->GetExitLabel());
Roland Levillain844e6532016-11-03 16:09:47 +00007163}
7164
Anton Kirilovedb2ac32016-11-30 15:14:10 +00007165void CodeGeneratorARMVIXL::GenerateReadBarrierSlow(HInstruction* instruction,
7166 Location out,
7167 Location ref,
7168 Location obj,
7169 uint32_t offset,
7170 Location index) {
7171 DCHECK(kEmitCompilerReadBarrier);
7172
7173 // Insert a slow path based read barrier *after* the reference load.
7174 //
7175 // If heap poisoning is enabled, the unpoisoning of the loaded
7176 // reference will be carried out by the runtime within the slow
7177 // path.
7178 //
7179 // Note that `ref` currently does not get unpoisoned (when heap
7180 // poisoning is enabled), which is alright as the `ref` argument is
7181 // not used by the artReadBarrierSlow entry point.
7182 //
7183 // TODO: Unpoison `ref` when it is used by artReadBarrierSlow.
7184 SlowPathCodeARMVIXL* slow_path = new (GetGraph()->GetArena())
7185 ReadBarrierForHeapReferenceSlowPathARMVIXL(instruction, out, ref, obj, offset, index);
7186 AddSlowPath(slow_path);
7187
7188 __ B(slow_path->GetEntryLabel());
7189 __ Bind(slow_path->GetExitLabel());
7190}
7191
7192void CodeGeneratorARMVIXL::MaybeGenerateReadBarrierSlow(HInstruction* instruction,
Artem Serov02d37832016-10-25 15:25:33 +01007193 Location out,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00007194 Location ref,
7195 Location obj,
7196 uint32_t offset,
7197 Location index) {
Artem Serov02d37832016-10-25 15:25:33 +01007198 if (kEmitCompilerReadBarrier) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00007199 // Baker's read barriers shall be handled by the fast path
7200 // (CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier).
Artem Serov02d37832016-10-25 15:25:33 +01007201 DCHECK(!kUseBakerReadBarrier);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00007202 // If heap poisoning is enabled, unpoisoning will be taken care of
7203 // by the runtime within the slow path.
7204 GenerateReadBarrierSlow(instruction, out, ref, obj, offset, index);
Artem Serov02d37832016-10-25 15:25:33 +01007205 } else if (kPoisonHeapReferences) {
7206 GetAssembler()->UnpoisonHeapReference(RegisterFrom(out));
7207 }
7208}
7209
Anton Kirilovedb2ac32016-11-30 15:14:10 +00007210void CodeGeneratorARMVIXL::GenerateReadBarrierForRootSlow(HInstruction* instruction,
7211 Location out,
7212 Location root) {
7213 DCHECK(kEmitCompilerReadBarrier);
7214
7215 // Insert a slow path based read barrier *after* the GC root load.
7216 //
7217 // Note that GC roots are not affected by heap poisoning, so we do
7218 // not need to do anything special for this here.
7219 SlowPathCodeARMVIXL* slow_path =
7220 new (GetGraph()->GetArena()) ReadBarrierForRootSlowPathARMVIXL(instruction, out, root);
7221 AddSlowPath(slow_path);
7222
7223 __ B(slow_path->GetEntryLabel());
7224 __ Bind(slow_path->GetExitLabel());
7225}
7226
Artem Serov02d37832016-10-25 15:25:33 +01007227// Check if the desired_dispatch_info is supported. If it is, return it,
7228// otherwise return a fall-back info that should be used instead.
7229HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARMVIXL::GetSupportedInvokeStaticOrDirectDispatch(
Artem Serovd4cc5b22016-11-04 11:19:09 +00007230 const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
Nicolas Geoffrayc1a42cf2016-12-18 15:52:36 +00007231 HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00007232 HInvokeStaticOrDirect::DispatchInfo dispatch_info = desired_dispatch_info;
7233 // We disable pc-relative load when there is an irreducible loop, as the optimization
7234 // is incompatible with it.
7235 // TODO: Create as many ArmDexCacheArraysBase instructions as needed for methods
7236 // with irreducible loops.
7237 if (GetGraph()->HasIrreducibleLoops() &&
7238 (dispatch_info.method_load_kind ==
7239 HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative)) {
7240 dispatch_info.method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod;
7241 }
7242
Artem Serovd4cc5b22016-11-04 11:19:09 +00007243 return dispatch_info;
Artem Serov02d37832016-10-25 15:25:33 +01007244}
7245
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007246vixl32::Register CodeGeneratorARMVIXL::GetInvokeStaticOrDirectExtraParameter(
7247 HInvokeStaticOrDirect* invoke, vixl32::Register temp) {
7248 DCHECK_EQ(invoke->InputCount(), invoke->GetNumberOfArguments() + 1u);
7249 Location location = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
7250 if (!invoke->GetLocations()->Intrinsified()) {
7251 return RegisterFrom(location);
7252 }
7253 // For intrinsics we allow any location, so it may be on the stack.
7254 if (!location.IsRegister()) {
7255 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, location.GetStackIndex());
7256 return temp;
7257 }
7258 // For register locations, check if the register was saved. If so, get it from the stack.
7259 // Note: There is a chance that the register was saved but not overwritten, so we could
7260 // save one load. However, since this is just an intrinsic slow path we prefer this
7261 // simple and more robust approach rather that trying to determine if that's the case.
7262 SlowPathCode* slow_path = GetCurrentSlowPath();
7263 DCHECK(slow_path != nullptr); // For intrinsified invokes the call is emitted on the slow path.
7264 if (slow_path->IsCoreRegisterSaved(RegisterFrom(location).GetCode())) {
7265 int stack_offset = slow_path->GetStackOffsetOfCoreRegister(RegisterFrom(location).GetCode());
7266 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, stack_offset);
7267 return temp;
7268 }
7269 return RegisterFrom(location);
7270}
7271
TatWai Chongd8c052a2016-11-02 16:12:48 +08007272Location CodeGeneratorARMVIXL::GenerateCalleeMethodStaticOrDirectCall(
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007273 HInvokeStaticOrDirect* invoke, Location temp) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00007274 Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007275 switch (invoke->GetMethodLoadKind()) {
7276 case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: {
7277 uint32_t offset =
7278 GetThreadOffset<kArmPointerSize>(invoke->GetStringInitEntryPoint()).Int32Value();
7279 // temp = thread->string_init_entrypoint
Artem Serovd4cc5b22016-11-04 11:19:09 +00007280 GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(temp), tr, offset);
7281 break;
7282 }
7283 case HInvokeStaticOrDirect::MethodLoadKind::kRecursive:
7284 callee_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
7285 break;
7286 case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress:
7287 __ Mov(RegisterFrom(temp), Operand::From(invoke->GetMethodAddress()));
7288 break;
Artem Serovd4cc5b22016-11-04 11:19:09 +00007289 case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: {
7290 HArmDexCacheArraysBase* base =
7291 invoke->InputAt(invoke->GetSpecialInputIndex())->AsArmDexCacheArraysBase();
7292 vixl32::Register base_reg = GetInvokeStaticOrDirectExtraParameter(invoke, RegisterFrom(temp));
7293 int32_t offset = invoke->GetDexCacheArrayOffset() - base->GetElementOffset();
7294 GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(temp), base_reg, offset);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007295 break;
7296 }
7297 case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: {
7298 Location current_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
7299 vixl32::Register method_reg;
Artem Serovd4cc5b22016-11-04 11:19:09 +00007300 vixl32::Register reg = RegisterFrom(temp);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007301 if (current_method.IsRegister()) {
7302 method_reg = RegisterFrom(current_method);
7303 } else {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007304 DCHECK(invoke->GetLocations()->Intrinsified());
7305 DCHECK(!current_method.IsValid());
Artem Serovd4cc5b22016-11-04 11:19:09 +00007306 method_reg = reg;
7307 GetAssembler()->LoadFromOffset(kLoadWord, reg, sp, kCurrentMethodStackOffset);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007308 }
7309 // /* ArtMethod*[] */ temp = temp.ptr_sized_fields_->dex_cache_resolved_methods_;
7310 GetAssembler()->LoadFromOffset(
7311 kLoadWord,
Artem Serovd4cc5b22016-11-04 11:19:09 +00007312 reg,
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007313 method_reg,
7314 ArtMethod::DexCacheResolvedMethodsOffset(kArmPointerSize).Int32Value());
7315 // temp = temp[index_in_cache];
7316 // Note: Don't use invoke->GetTargetMethod() as it may point to a different dex file.
7317 uint32_t index_in_cache = invoke->GetDexMethodIndex();
7318 GetAssembler()->LoadFromOffset(
Artem Serovd4cc5b22016-11-04 11:19:09 +00007319 kLoadWord, reg, reg, CodeGenerator::GetCachePointerOffset(index_in_cache));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007320 break;
7321 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007322 }
TatWai Chongd8c052a2016-11-02 16:12:48 +08007323 return callee_method;
7324}
7325
7326void CodeGeneratorARMVIXL::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke,
7327 Location temp) {
7328 Location callee_method = GenerateCalleeMethodStaticOrDirectCall(invoke, temp);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007329
Artem Serovd4cc5b22016-11-04 11:19:09 +00007330 switch (invoke->GetCodePtrLocation()) {
7331 case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf:
7332 __ Bl(GetFrameEntryLabel());
7333 break;
Artem Serovd4cc5b22016-11-04 11:19:09 +00007334 case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod:
7335 // LR = callee_method->entry_point_from_quick_compiled_code_
7336 GetAssembler()->LoadFromOffset(
7337 kLoadWord,
7338 lr,
7339 RegisterFrom(callee_method),
7340 ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value());
Alexandre Rames374ddf32016-11-04 10:40:49 +00007341 {
7342 // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
Artem Serov0fb37192016-12-06 18:13:40 +00007343 ExactAssemblyScope aas(GetVIXLAssembler(),
7344 vixl32::k16BitT32InstructionSizeInBytes,
7345 CodeBufferCheckScope::kExactSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00007346 // LR()
7347 __ blx(lr);
7348 }
Artem Serovd4cc5b22016-11-04 11:19:09 +00007349 break;
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007350 }
7351
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007352 DCHECK(!IsLeafMethod());
7353}
7354
7355void CodeGeneratorARMVIXL::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp_location) {
7356 vixl32::Register temp = RegisterFrom(temp_location);
7357 uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
7358 invoke->GetVTableIndex(), kArmPointerSize).Uint32Value();
7359
7360 // Use the calling convention instead of the location of the receiver, as
7361 // intrinsics may have put the receiver in a different register. In the intrinsics
7362 // slow path, the arguments have been moved to the right place, so here we are
7363 // guaranteed that the receiver is the first register of the calling convention.
7364 InvokeDexCallingConventionARMVIXL calling_convention;
7365 vixl32::Register receiver = calling_convention.GetRegisterAt(0);
7366 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
Alexandre Rames374ddf32016-11-04 10:40:49 +00007367 {
7368 // Make sure the pc is recorded immediately after the `ldr` instruction.
Artem Serov0fb37192016-12-06 18:13:40 +00007369 ExactAssemblyScope aas(GetVIXLAssembler(),
7370 vixl32::kMaxInstructionSizeInBytes,
7371 CodeBufferCheckScope::kMaximumSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00007372 // /* HeapReference<Class> */ temp = receiver->klass_
7373 __ ldr(temp, MemOperand(receiver, class_offset));
7374 MaybeRecordImplicitNullCheck(invoke);
7375 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007376 // Instead of simply (possibly) unpoisoning `temp` here, we should
7377 // emit a read barrier for the previous class reference load.
7378 // However this is not required in practice, as this is an
7379 // intermediate/temporary reference and because the current
7380 // concurrent copying collector keeps the from-space memory
7381 // intact/accessible until the end of the marking phase (the
7382 // concurrent copying collector may not in the future).
7383 GetAssembler()->MaybeUnpoisonHeapReference(temp);
7384
7385 // temp = temp->GetMethodAt(method_offset);
7386 uint32_t entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(
7387 kArmPointerSize).Int32Value();
7388 GetAssembler()->LoadFromOffset(kLoadWord, temp, temp, method_offset);
7389 // LR = temp->GetEntryPoint();
7390 GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, entry_point);
7391 // LR();
Alexandre Rames374ddf32016-11-04 10:40:49 +00007392 // This `blx` *must* be the *last* instruction generated by this stub, so that calls to
7393 // `RecordPcInfo()` immediately following record the correct pc. Use a scope to help guarantee
7394 // that.
7395 // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
Artem Serov0fb37192016-12-06 18:13:40 +00007396 ExactAssemblyScope aas(GetVIXLAssembler(),
7397 vixl32::k16BitT32InstructionSizeInBytes,
7398 CodeBufferCheckScope::kExactSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00007399 __ blx(lr);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007400}
7401
Artem Serovd4cc5b22016-11-04 11:19:09 +00007402CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewPcRelativeStringPatch(
7403 const DexFile& dex_file, uint32_t string_index) {
7404 return NewPcRelativePatch(dex_file, string_index, &pc_relative_string_patches_);
7405}
7406
7407CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewPcRelativeTypePatch(
7408 const DexFile& dex_file, dex::TypeIndex type_index) {
7409 return NewPcRelativePatch(dex_file, type_index.index_, &pc_relative_type_patches_);
7410}
7411
7412CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewPcRelativeDexCacheArrayPatch(
7413 const DexFile& dex_file, uint32_t element_offset) {
7414 return NewPcRelativePatch(dex_file, element_offset, &pc_relative_dex_cache_patches_);
7415}
7416
7417CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewPcRelativePatch(
7418 const DexFile& dex_file, uint32_t offset_or_index, ArenaDeque<PcRelativePatchInfo>* patches) {
7419 patches->emplace_back(dex_file, offset_or_index);
7420 return &patches->back();
7421}
7422
Artem Serovc5fcb442016-12-02 19:19:58 +00007423VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateBootImageStringLiteral(
7424 const DexFile& dex_file,
7425 dex::StringIndex string_index) {
7426 return boot_image_string_patches_.GetOrCreate(
7427 StringReference(&dex_file, string_index),
7428 [this]() {
7429 return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u);
7430 });
7431}
7432
7433VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateBootImageTypeLiteral(
7434 const DexFile& dex_file,
7435 dex::TypeIndex type_index) {
7436 return boot_image_type_patches_.GetOrCreate(
7437 TypeReference(&dex_file, type_index),
7438 [this]() {
7439 return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u);
7440 });
7441}
7442
7443VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateBootImageAddressLiteral(uint32_t address) {
7444 bool needs_patch = GetCompilerOptions().GetIncludePatchInformation();
7445 Uint32ToLiteralMap* map = needs_patch ? &boot_image_address_patches_ : &uint32_literals_;
7446 return DeduplicateUint32Literal(dchecked_integral_cast<uint32_t>(address), map);
7447}
7448
7449VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateDexCacheAddressLiteral(uint32_t address) {
7450 return DeduplicateUint32Literal(address, &uint32_literals_);
7451}
7452
7453VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateJitStringLiteral(const DexFile& dex_file,
7454 dex::StringIndex string_index) {
7455 jit_string_roots_.Overwrite(StringReference(&dex_file, string_index), /* placeholder */ 0u);
7456 return jit_string_patches_.GetOrCreate(
7457 StringReference(&dex_file, string_index),
7458 [this]() {
7459 return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u);
7460 });
7461}
7462
7463VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateJitClassLiteral(const DexFile& dex_file,
7464 dex::TypeIndex type_index,
7465 uint64_t address) {
7466 jit_class_roots_.Overwrite(TypeReference(&dex_file, type_index), address);
7467 return jit_class_patches_.GetOrCreate(
7468 TypeReference(&dex_file, type_index),
7469 [this]() {
7470 return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u);
7471 });
7472}
7473
Artem Serovd4cc5b22016-11-04 11:19:09 +00007474template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)>
7475inline void CodeGeneratorARMVIXL::EmitPcRelativeLinkerPatches(
7476 const ArenaDeque<PcRelativePatchInfo>& infos,
7477 ArenaVector<LinkerPatch>* linker_patches) {
7478 for (const PcRelativePatchInfo& info : infos) {
7479 const DexFile& dex_file = info.target_dex_file;
7480 size_t offset_or_index = info.offset_or_index;
7481 DCHECK(info.add_pc_label.IsBound());
7482 uint32_t add_pc_offset = dchecked_integral_cast<uint32_t>(info.add_pc_label.GetLocation());
7483 // Add MOVW patch.
7484 DCHECK(info.movw_label.IsBound());
7485 uint32_t movw_offset = dchecked_integral_cast<uint32_t>(info.movw_label.GetLocation());
7486 linker_patches->push_back(Factory(movw_offset, &dex_file, add_pc_offset, offset_or_index));
7487 // Add MOVT patch.
7488 DCHECK(info.movt_label.IsBound());
7489 uint32_t movt_offset = dchecked_integral_cast<uint32_t>(info.movt_label.GetLocation());
7490 linker_patches->push_back(Factory(movt_offset, &dex_file, add_pc_offset, offset_or_index));
7491 }
7492}
7493
7494void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
7495 DCHECK(linker_patches->empty());
7496 size_t size =
Artem Serovd4cc5b22016-11-04 11:19:09 +00007497 /* MOVW+MOVT for each entry */ 2u * pc_relative_dex_cache_patches_.size() +
Artem Serovc5fcb442016-12-02 19:19:58 +00007498 boot_image_string_patches_.size() +
Artem Serovd4cc5b22016-11-04 11:19:09 +00007499 /* MOVW+MOVT for each entry */ 2u * pc_relative_string_patches_.size() +
Artem Serovc5fcb442016-12-02 19:19:58 +00007500 boot_image_type_patches_.size() +
7501 /* MOVW+MOVT for each entry */ 2u * pc_relative_type_patches_.size() +
7502 boot_image_address_patches_.size();
Artem Serovd4cc5b22016-11-04 11:19:09 +00007503 linker_patches->reserve(size);
Artem Serovd4cc5b22016-11-04 11:19:09 +00007504 EmitPcRelativeLinkerPatches<LinkerPatch::DexCacheArrayPatch>(pc_relative_dex_cache_patches_,
7505 linker_patches);
Artem Serovc5fcb442016-12-02 19:19:58 +00007506 for (const auto& entry : boot_image_string_patches_) {
7507 const StringReference& target_string = entry.first;
7508 VIXLUInt32Literal* literal = entry.second;
7509 DCHECK(literal->IsBound());
7510 uint32_t literal_offset = literal->GetLocation();
7511 linker_patches->push_back(LinkerPatch::StringPatch(literal_offset,
7512 target_string.dex_file,
7513 target_string.string_index.index_));
7514 }
Artem Serovd4cc5b22016-11-04 11:19:09 +00007515 if (!GetCompilerOptions().IsBootImage()) {
7516 EmitPcRelativeLinkerPatches<LinkerPatch::StringBssEntryPatch>(pc_relative_string_patches_,
7517 linker_patches);
7518 } else {
7519 EmitPcRelativeLinkerPatches<LinkerPatch::RelativeStringPatch>(pc_relative_string_patches_,
7520 linker_patches);
7521 }
Artem Serovc5fcb442016-12-02 19:19:58 +00007522 for (const auto& entry : boot_image_type_patches_) {
7523 const TypeReference& target_type = entry.first;
7524 VIXLUInt32Literal* literal = entry.second;
7525 DCHECK(literal->IsBound());
7526 uint32_t literal_offset = literal->GetLocation();
7527 linker_patches->push_back(LinkerPatch::TypePatch(literal_offset,
7528 target_type.dex_file,
7529 target_type.type_index.index_));
7530 }
Artem Serovd4cc5b22016-11-04 11:19:09 +00007531 EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(pc_relative_type_patches_,
7532 linker_patches);
Artem Serovc5fcb442016-12-02 19:19:58 +00007533 for (const auto& entry : boot_image_address_patches_) {
7534 DCHECK(GetCompilerOptions().GetIncludePatchInformation());
7535 VIXLUInt32Literal* literal = entry.second;
7536 DCHECK(literal->IsBound());
7537 uint32_t literal_offset = literal->GetLocation();
7538 linker_patches->push_back(LinkerPatch::RecordPosition(literal_offset));
7539 }
7540}
7541
7542VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateUint32Literal(
7543 uint32_t value,
7544 Uint32ToLiteralMap* map) {
7545 return map->GetOrCreate(
7546 value,
7547 [this, value]() {
7548 return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ value);
7549 });
7550}
7551
7552VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateMethodLiteral(
7553 MethodReference target_method,
7554 MethodToLiteralMap* map) {
7555 return map->GetOrCreate(
7556 target_method,
7557 [this]() {
7558 return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u);
7559 });
7560}
7561
Artem Serov2bbc9532016-10-21 11:51:50 +01007562void LocationsBuilderARMVIXL::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
7563 LocationSummary* locations =
7564 new (GetGraph()->GetArena()) LocationSummary(instr, LocationSummary::kNoCall);
7565 locations->SetInAt(HMultiplyAccumulate::kInputAccumulatorIndex,
7566 Location::RequiresRegister());
7567 locations->SetInAt(HMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister());
7568 locations->SetInAt(HMultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister());
7569 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
7570}
7571
7572void InstructionCodeGeneratorARMVIXL::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
7573 vixl32::Register res = OutputRegister(instr);
7574 vixl32::Register accumulator =
7575 InputRegisterAt(instr, HMultiplyAccumulate::kInputAccumulatorIndex);
7576 vixl32::Register mul_left =
7577 InputRegisterAt(instr, HMultiplyAccumulate::kInputMulLeftIndex);
7578 vixl32::Register mul_right =
7579 InputRegisterAt(instr, HMultiplyAccumulate::kInputMulRightIndex);
7580
7581 if (instr->GetOpKind() == HInstruction::kAdd) {
7582 __ Mla(res, mul_left, mul_right, accumulator);
7583 } else {
7584 __ Mls(res, mul_left, mul_right, accumulator);
7585 }
7586}
7587
Artem Serov551b28f2016-10-18 19:11:30 +01007588void LocationsBuilderARMVIXL::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
7589 // Nothing to do, this should be removed during prepare for register allocator.
7590 LOG(FATAL) << "Unreachable";
7591}
7592
7593void InstructionCodeGeneratorARMVIXL::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
7594 // Nothing to do, this should be removed during prepare for register allocator.
7595 LOG(FATAL) << "Unreachable";
7596}
7597
7598// Simple implementation of packed switch - generate cascaded compare/jumps.
7599void LocationsBuilderARMVIXL::VisitPackedSwitch(HPackedSwitch* switch_instr) {
7600 LocationSummary* locations =
7601 new (GetGraph()->GetArena()) LocationSummary(switch_instr, LocationSummary::kNoCall);
7602 locations->SetInAt(0, Location::RequiresRegister());
7603 if (switch_instr->GetNumEntries() > kPackedSwitchCompareJumpThreshold &&
7604 codegen_->GetAssembler()->GetVIXLAssembler()->IsUsingT32()) {
7605 locations->AddTemp(Location::RequiresRegister()); // We need a temp for the table base.
7606 if (switch_instr->GetStartValue() != 0) {
7607 locations->AddTemp(Location::RequiresRegister()); // We need a temp for the bias.
7608 }
7609 }
7610}
7611
7612// TODO(VIXL): Investigate and reach the parity with old arm codegen.
7613void InstructionCodeGeneratorARMVIXL::VisitPackedSwitch(HPackedSwitch* switch_instr) {
7614 int32_t lower_bound = switch_instr->GetStartValue();
7615 uint32_t num_entries = switch_instr->GetNumEntries();
7616 LocationSummary* locations = switch_instr->GetLocations();
7617 vixl32::Register value_reg = InputRegisterAt(switch_instr, 0);
7618 HBasicBlock* default_block = switch_instr->GetDefaultBlock();
7619
7620 if (num_entries <= kPackedSwitchCompareJumpThreshold ||
7621 !codegen_->GetAssembler()->GetVIXLAssembler()->IsUsingT32()) {
7622 // Create a series of compare/jumps.
Anton Kirilovedb2ac32016-11-30 15:14:10 +00007623 UseScratchRegisterScope temps(GetVIXLAssembler());
Artem Serov551b28f2016-10-18 19:11:30 +01007624 vixl32::Register temp_reg = temps.Acquire();
7625 // Note: It is fine for the below AddConstantSetFlags() using IP register to temporarily store
7626 // the immediate, because IP is used as the destination register. For the other
7627 // AddConstantSetFlags() and GenerateCompareWithImmediate(), the immediate values are constant,
7628 // and they can be encoded in the instruction without making use of IP register.
7629 __ Adds(temp_reg, value_reg, -lower_bound);
7630
7631 const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors();
7632 // Jump to successors[0] if value == lower_bound.
7633 __ B(eq, codegen_->GetLabelOf(successors[0]));
7634 int32_t last_index = 0;
7635 for (; num_entries - last_index > 2; last_index += 2) {
7636 __ Adds(temp_reg, temp_reg, -2);
7637 // Jump to successors[last_index + 1] if value < case_value[last_index + 2].
7638 __ B(lo, codegen_->GetLabelOf(successors[last_index + 1]));
7639 // Jump to successors[last_index + 2] if value == case_value[last_index + 2].
7640 __ B(eq, codegen_->GetLabelOf(successors[last_index + 2]));
7641 }
7642 if (num_entries - last_index == 2) {
7643 // The last missing case_value.
7644 __ Cmp(temp_reg, 1);
7645 __ B(eq, codegen_->GetLabelOf(successors[last_index + 1]));
7646 }
7647
7648 // And the default for any other value.
7649 if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) {
7650 __ B(codegen_->GetLabelOf(default_block));
7651 }
7652 } else {
7653 // Create a table lookup.
7654 vixl32::Register table_base = RegisterFrom(locations->GetTemp(0));
7655
7656 JumpTableARMVIXL* jump_table = codegen_->CreateJumpTable(switch_instr);
7657
7658 // Remove the bias.
7659 vixl32::Register key_reg;
7660 if (lower_bound != 0) {
7661 key_reg = RegisterFrom(locations->GetTemp(1));
7662 __ Sub(key_reg, value_reg, lower_bound);
7663 } else {
7664 key_reg = value_reg;
7665 }
7666
7667 // Check whether the value is in the table, jump to default block if not.
7668 __ Cmp(key_reg, num_entries - 1);
7669 __ B(hi, codegen_->GetLabelOf(default_block));
7670
Anton Kirilovedb2ac32016-11-30 15:14:10 +00007671 UseScratchRegisterScope temps(GetVIXLAssembler());
Artem Serov551b28f2016-10-18 19:11:30 +01007672 vixl32::Register jump_offset = temps.Acquire();
7673
7674 // Load jump offset from the table.
7675 __ Adr(table_base, jump_table->GetTableStartLabel());
7676 __ Ldr(jump_offset, MemOperand(table_base, key_reg, vixl32::LSL, 2));
7677
7678 // Jump to target block by branching to table_base(pc related) + offset.
7679 vixl32::Register target_address = table_base;
7680 __ Add(target_address, table_base, jump_offset);
7681 __ Bx(target_address);
Artem Serov09a940d2016-11-11 16:15:11 +00007682
7683 jump_table->EmitTable(codegen_);
Artem Serov551b28f2016-10-18 19:11:30 +01007684 }
7685}
Artem Serovd4cc5b22016-11-04 11:19:09 +00007686void LocationsBuilderARMVIXL::VisitArmDexCacheArraysBase(HArmDexCacheArraysBase* base) {
7687 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(base);
7688 locations->SetOut(Location::RequiresRegister());
7689}
7690
7691void InstructionCodeGeneratorARMVIXL::VisitArmDexCacheArraysBase(HArmDexCacheArraysBase* base) {
7692 vixl32::Register base_reg = OutputRegister(base);
7693 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
7694 codegen_->NewPcRelativeDexCacheArrayPatch(base->GetDexFile(), base->GetElementOffset());
7695 codegen_->EmitMovwMovtPlaceholder(labels, base_reg);
7696}
Artem Serov551b28f2016-10-18 19:11:30 +01007697
Artem Serov02d37832016-10-25 15:25:33 +01007698// Copy the result of a call into the given target.
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007699void CodeGeneratorARMVIXL::MoveFromReturnRegister(Location trg, Primitive::Type type) {
7700 if (!trg.IsValid()) {
7701 DCHECK_EQ(type, Primitive::kPrimVoid);
7702 return;
7703 }
7704
7705 DCHECK_NE(type, Primitive::kPrimVoid);
7706
Artem Serovd4cc5b22016-11-04 11:19:09 +00007707 Location return_loc = InvokeDexCallingConventionVisitorARMVIXL().GetReturnLocation(type);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007708 if (return_loc.Equals(trg)) {
7709 return;
7710 }
7711
7712 // TODO: Consider pairs in the parallel move resolver, then this could be nicely merged
7713 // with the last branch.
7714 if (type == Primitive::kPrimLong) {
7715 TODO_VIXL32(FATAL);
7716 } else if (type == Primitive::kPrimDouble) {
7717 TODO_VIXL32(FATAL);
7718 } else {
7719 // Let the parallel move resolver take care of all of this.
7720 HParallelMove parallel_move(GetGraph()->GetArena());
7721 parallel_move.AddMove(return_loc, trg, type, nullptr);
7722 GetMoveResolver()->EmitNativeCode(&parallel_move);
7723 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007724}
Scott Wakelingfe885462016-09-22 10:24:38 +01007725
xueliang.zhong8d2c4592016-11-23 17:05:25 +00007726void LocationsBuilderARMVIXL::VisitClassTableGet(HClassTableGet* instruction) {
7727 LocationSummary* locations =
7728 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
7729 locations->SetInAt(0, Location::RequiresRegister());
7730 locations->SetOut(Location::RequiresRegister());
Artem Serov551b28f2016-10-18 19:11:30 +01007731}
7732
xueliang.zhong8d2c4592016-11-23 17:05:25 +00007733void InstructionCodeGeneratorARMVIXL::VisitClassTableGet(HClassTableGet* instruction) {
7734 if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) {
7735 uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
7736 instruction->GetIndex(), kArmPointerSize).SizeValue();
7737 GetAssembler()->LoadFromOffset(kLoadWord,
7738 OutputRegister(instruction),
7739 InputRegisterAt(instruction, 0),
7740 method_offset);
7741 } else {
7742 uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement(
7743 instruction->GetIndex(), kArmPointerSize));
7744 GetAssembler()->LoadFromOffset(kLoadWord,
7745 OutputRegister(instruction),
7746 InputRegisterAt(instruction, 0),
7747 mirror::Class::ImtPtrOffset(kArmPointerSize).Uint32Value());
7748 GetAssembler()->LoadFromOffset(kLoadWord,
7749 OutputRegister(instruction),
7750 OutputRegister(instruction),
7751 method_offset);
7752 }
Artem Serov551b28f2016-10-18 19:11:30 +01007753}
7754
Artem Serovc5fcb442016-12-02 19:19:58 +00007755static void PatchJitRootUse(uint8_t* code,
7756 const uint8_t* roots_data,
7757 VIXLUInt32Literal* literal,
7758 uint64_t index_in_table) {
7759 DCHECK(literal->IsBound());
7760 uint32_t literal_offset = literal->GetLocation();
7761 uintptr_t address =
7762 reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>);
7763 uint8_t* data = code + literal_offset;
7764 reinterpret_cast<uint32_t*>(data)[0] = dchecked_integral_cast<uint32_t>(address);
7765}
7766
7767void CodeGeneratorARMVIXL::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) {
7768 for (const auto& entry : jit_string_patches_) {
7769 const auto& it = jit_string_roots_.find(entry.first);
7770 DCHECK(it != jit_string_roots_.end());
7771 PatchJitRootUse(code, roots_data, entry.second, it->second);
7772 }
7773 for (const auto& entry : jit_class_patches_) {
7774 const auto& it = jit_class_roots_.find(entry.first);
7775 DCHECK(it != jit_class_roots_.end());
7776 PatchJitRootUse(code, roots_data, entry.second, it->second);
7777 }
7778}
7779
Artem Serovd4cc5b22016-11-04 11:19:09 +00007780void CodeGeneratorARMVIXL::EmitMovwMovtPlaceholder(
7781 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels,
7782 vixl32::Register out) {
Artem Serov0fb37192016-12-06 18:13:40 +00007783 ExactAssemblyScope aas(GetVIXLAssembler(),
7784 3 * vixl32::kMaxInstructionSizeInBytes,
7785 CodeBufferCheckScope::kMaximumSize);
Artem Serovd4cc5b22016-11-04 11:19:09 +00007786 // TODO(VIXL): Think about using mov instead of movw.
7787 __ bind(&labels->movw_label);
7788 __ movw(out, /* placeholder */ 0u);
7789 __ bind(&labels->movt_label);
7790 __ movt(out, /* placeholder */ 0u);
7791 __ bind(&labels->add_pc_label);
7792 __ add(out, out, pc);
7793}
7794
Scott Wakelingfe885462016-09-22 10:24:38 +01007795#undef __
7796#undef QUICK_ENTRY_POINT
7797#undef TODO_VIXL32
7798
7799} // namespace arm
7800} // namespace art