blob: 2b9e0febe85fd88a3046fb08f9b093244bdd1b11 [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
Vladimir Markoeee1c0e2017-04-21 17:58:41 +010019#include "arch/arm/asm_support_arm.h"
Scott Wakelingfe885462016-09-22 10:24:38 +010020#include "arch/arm/instruction_set_features_arm.h"
21#include "art_method.h"
Andreas Gampe5678db52017-06-08 14:11:18 -070022#include "base/bit_utils.h"
23#include "base/bit_utils_iterator.h"
Vladimir Marko94ec2db2017-09-06 17:21:03 +010024#include "class_table.h"
Scott Wakelingfe885462016-09-22 10:24:38 +010025#include "code_generator_utils.h"
26#include "common_arm.h"
27#include "compiled_method.h"
28#include "entrypoints/quick/quick_entrypoints.h"
29#include "gc/accounting/card_table.h"
Andreas Gampe09659c22017-09-18 18:23:32 -070030#include "heap_poisoning.h"
Anton Kirilov5ec62182016-10-13 20:16:02 +010031#include "intrinsics_arm_vixl.h"
Vladimir Markoeee1c0e2017-04-21 17:58:41 +010032#include "linker/arm/relative_patcher_thumb2.h"
Vladimir Markod8dbc8d2017-09-20 13:37:47 +010033#include "linker/linker_patch.h"
Scott Wakelingfe885462016-09-22 10:24:38 +010034#include "mirror/array-inl.h"
35#include "mirror/class-inl.h"
36#include "thread.h"
37#include "utils/arm/assembler_arm_vixl.h"
38#include "utils/arm/managed_register_arm.h"
39#include "utils/assembler.h"
40#include "utils/stack_checks.h"
41
42namespace art {
43namespace arm {
44
45namespace vixl32 = vixl::aarch32;
46using namespace vixl32; // NOLINT(build/namespaces)
47
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +010048using helpers::DRegisterFrom;
Scott Wakelingfe885462016-09-22 10:24:38 +010049using helpers::DWARFReg;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010050using helpers::HighDRegisterFrom;
51using helpers::HighRegisterFrom;
Donghui Bai426b49c2016-11-08 14:55:38 +080052using helpers::InputDRegisterAt;
Scott Wakelingfe885462016-09-22 10:24:38 +010053using helpers::InputOperandAt;
Scott Wakelingc34dba72016-10-03 10:14:44 +010054using helpers::InputRegister;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010055using helpers::InputRegisterAt;
Scott Wakelingfe885462016-09-22 10:24:38 +010056using helpers::InputSRegisterAt;
Anton Kirilov644032c2016-12-06 17:51:43 +000057using helpers::InputVRegister;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010058using helpers::InputVRegisterAt;
Scott Wakelingb77051e2016-11-21 19:46:00 +000059using helpers::Int32ConstantFrom;
Anton Kirilov644032c2016-12-06 17:51:43 +000060using helpers::Int64ConstantFrom;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010061using helpers::LocationFrom;
62using helpers::LowRegisterFrom;
63using helpers::LowSRegisterFrom;
Donghui Bai426b49c2016-11-08 14:55:38 +080064using helpers::OperandFrom;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010065using helpers::OutputRegister;
66using helpers::OutputSRegister;
67using helpers::OutputVRegister;
68using helpers::RegisterFrom;
69using helpers::SRegisterFrom;
Anton Kirilov644032c2016-12-06 17:51:43 +000070using helpers::Uint64ConstantFrom;
Scott Wakelingfe885462016-09-22 10:24:38 +010071
Artem Serov0fb37192016-12-06 18:13:40 +000072using vixl::ExactAssemblyScope;
73using vixl::CodeBufferCheckScope;
74
Scott Wakelingfe885462016-09-22 10:24:38 +010075using RegisterList = vixl32::RegisterList;
76
77static bool ExpectedPairLayout(Location location) {
78 // We expected this for both core and fpu register pairs.
79 return ((location.low() & 1) == 0) && (location.low() + 1 == location.high());
80}
Artem Serovd4cc5b22016-11-04 11:19:09 +000081// Use a local definition to prevent copying mistakes.
82static constexpr size_t kArmWordSize = static_cast<size_t>(kArmPointerSize);
83static constexpr size_t kArmBitsPerWord = kArmWordSize * kBitsPerByte;
Artem Serov551b28f2016-10-18 19:11:30 +010084static constexpr uint32_t kPackedSwitchCompareJumpThreshold = 7;
Scott Wakelingfe885462016-09-22 10:24:38 +010085
Vladimir Markoeee1c0e2017-04-21 17:58:41 +010086// Reference load (except object array loads) is using LDR Rt, [Rn, #offset] which can handle
87// offset < 4KiB. For offsets >= 4KiB, the load shall be emitted as two or more instructions.
88// For the Baker read barrier implementation using link-generated thunks we need to split
89// the offset explicitly.
90constexpr uint32_t kReferenceLoadMinFarOffset = 4 * KB;
91
92// Flags controlling the use of link-time generated thunks for Baker read barriers.
93constexpr bool kBakerReadBarrierLinkTimeThunksEnableForFields = true;
94constexpr bool kBakerReadBarrierLinkTimeThunksEnableForArrays = true;
95constexpr bool kBakerReadBarrierLinkTimeThunksEnableForGcRoots = true;
96
97// The reserved entrypoint register for link-time generated thunks.
98const vixl32::Register kBakerCcEntrypointRegister = r4;
99
Roland Levillain5daa4952017-07-03 17:23:56 +0100100// Using a base helps identify when we hit Marking Register check breakpoints.
101constexpr int kMarkingRegisterCheckBreakCodeBaseCode = 0x10;
102
Scott Wakelingfe885462016-09-22 10:24:38 +0100103#ifdef __
104#error "ARM Codegen VIXL macro-assembler macro already defined."
105#endif
106
Scott Wakelingfe885462016-09-22 10:24:38 +0100107// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
108#define __ down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler()-> // NOLINT
109#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArmPointerSize, x).Int32Value()
110
111// Marker that code is yet to be, and must, be implemented.
112#define TODO_VIXL32(level) LOG(level) << __PRETTY_FUNCTION__ << " unimplemented "
113
Vladimir Markoeee1c0e2017-04-21 17:58:41 +0100114static inline void ExcludeIPAndBakerCcEntrypointRegister(UseScratchRegisterScope* temps,
115 HInstruction* instruction) {
116 DCHECK(temps->IsAvailable(ip));
117 temps->Exclude(ip);
118 DCHECK(!temps->IsAvailable(kBakerCcEntrypointRegister));
119 DCHECK_EQ(kBakerCcEntrypointRegister.GetCode(),
120 linker::Thumb2RelativePatcher::kBakerCcEntrypointRegister);
121 DCHECK_NE(instruction->GetLocations()->GetTempCount(), 0u);
122 DCHECK(RegisterFrom(instruction->GetLocations()->GetTemp(
123 instruction->GetLocations()->GetTempCount() - 1u)).Is(kBakerCcEntrypointRegister));
124}
125
126static inline void EmitPlaceholderBne(CodeGeneratorARMVIXL* codegen, vixl32::Label* patch_label) {
127 ExactAssemblyScope eas(codegen->GetVIXLAssembler(), kMaxInstructionSizeInBytes);
128 __ bind(patch_label);
129 vixl32::Label placeholder_label;
130 __ b(ne, EncodingSize(Wide), &placeholder_label); // Placeholder, patched at link-time.
131 __ bind(&placeholder_label);
132}
133
Vladimir Marko88abba22017-05-03 17:09:25 +0100134static inline bool CanEmitNarrowLdr(vixl32::Register rt, vixl32::Register rn, uint32_t offset) {
135 return rt.IsLow() && rn.IsLow() && offset < 32u;
136}
137
Vladimir Markoeee1c0e2017-04-21 17:58:41 +0100138class EmitAdrCode {
139 public:
140 EmitAdrCode(ArmVIXLMacroAssembler* assembler, vixl32::Register rd, vixl32::Label* label)
141 : assembler_(assembler), rd_(rd), label_(label) {
142 ExactAssemblyScope aas(assembler, kMaxInstructionSizeInBytes);
143 adr_location_ = assembler->GetCursorOffset();
144 assembler->adr(EncodingSize(Wide), rd, label);
145 }
146
147 ~EmitAdrCode() {
148 DCHECK(label_->IsBound());
149 // The ADR emitted by the assembler does not set the Thumb mode bit we need.
150 // TODO: Maybe extend VIXL to allow ADR for return address?
151 uint8_t* raw_adr = assembler_->GetBuffer()->GetOffsetAddress<uint8_t*>(adr_location_);
152 // Expecting ADR encoding T3 with `(offset & 1) == 0`.
153 DCHECK_EQ(raw_adr[1] & 0xfbu, 0xf2u); // Check bits 24-31, except 26.
154 DCHECK_EQ(raw_adr[0] & 0xffu, 0x0fu); // Check bits 16-23.
155 DCHECK_EQ(raw_adr[3] & 0x8fu, rd_.GetCode()); // Check bits 8-11 and 15.
156 DCHECK_EQ(raw_adr[2] & 0x01u, 0x00u); // Check bit 0, i.e. the `offset & 1`.
157 // Add the Thumb mode bit.
158 raw_adr[2] |= 0x01u;
159 }
160
161 private:
162 ArmVIXLMacroAssembler* const assembler_;
163 vixl32::Register rd_;
164 vixl32::Label* const label_;
165 int32_t adr_location_;
166};
167
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100168// SaveLiveRegisters and RestoreLiveRegisters from SlowPathCodeARM operate on sets of S registers,
169// for each live D registers they treat two corresponding S registers as live ones.
170//
171// Two following functions (SaveContiguousSRegisterList, RestoreContiguousSRegisterList) build
172// from a list of contiguous S registers a list of contiguous D registers (processing first/last
173// S registers corner cases) and save/restore this new list treating them as D registers.
174// - decreasing code size
175// - avoiding hazards on Cortex-A57, when a pair of S registers for an actual live D register is
176// restored and then used in regular non SlowPath code as D register.
177//
178// For the following example (v means the S register is live):
179// D names: | D0 | D1 | D2 | D4 | ...
180// S names: | S0 | S1 | S2 | S3 | S4 | S5 | S6 | S7 | ...
181// Live? | | v | v | v | v | v | v | | ...
182//
183// S1 and S6 will be saved/restored independently; D registers list (D1, D2) will be processed
184// as D registers.
185//
186// TODO(VIXL): All this code should be unnecessary once the VIXL AArch32 backend provides helpers
187// for lists of floating-point registers.
188static size_t SaveContiguousSRegisterList(size_t first,
189 size_t last,
190 CodeGenerator* codegen,
191 size_t stack_offset) {
192 static_assert(kSRegSizeInBytes == kArmWordSize, "Broken assumption on reg/word sizes.");
193 static_assert(kDRegSizeInBytes == 2 * kArmWordSize, "Broken assumption on reg/word sizes.");
194 DCHECK_LE(first, last);
195 if ((first == last) && (first == 0)) {
196 __ Vstr(vixl32::SRegister(first), MemOperand(sp, stack_offset));
197 return stack_offset + kSRegSizeInBytes;
198 }
199 if (first % 2 == 1) {
200 __ Vstr(vixl32::SRegister(first++), MemOperand(sp, stack_offset));
201 stack_offset += kSRegSizeInBytes;
202 }
203
204 bool save_last = false;
205 if (last % 2 == 0) {
206 save_last = true;
207 --last;
208 }
209
210 if (first < last) {
211 vixl32::DRegister d_reg = vixl32::DRegister(first / 2);
212 DCHECK_EQ((last - first + 1) % 2, 0u);
213 size_t number_of_d_regs = (last - first + 1) / 2;
214
215 if (number_of_d_regs == 1) {
216 __ Vstr(d_reg, MemOperand(sp, stack_offset));
217 } else if (number_of_d_regs > 1) {
218 UseScratchRegisterScope temps(down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler());
219 vixl32::Register base = sp;
220 if (stack_offset != 0) {
221 base = temps.Acquire();
Scott Wakelingb77051e2016-11-21 19:46:00 +0000222 __ Add(base, sp, Operand::From(stack_offset));
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100223 }
224 __ Vstm(F64, base, NO_WRITE_BACK, DRegisterList(d_reg, number_of_d_regs));
225 }
226 stack_offset += number_of_d_regs * kDRegSizeInBytes;
227 }
228
229 if (save_last) {
230 __ Vstr(vixl32::SRegister(last + 1), MemOperand(sp, stack_offset));
231 stack_offset += kSRegSizeInBytes;
232 }
233
234 return stack_offset;
235}
236
237static size_t RestoreContiguousSRegisterList(size_t first,
238 size_t last,
239 CodeGenerator* codegen,
240 size_t stack_offset) {
241 static_assert(kSRegSizeInBytes == kArmWordSize, "Broken assumption on reg/word sizes.");
242 static_assert(kDRegSizeInBytes == 2 * kArmWordSize, "Broken assumption on reg/word sizes.");
243 DCHECK_LE(first, last);
244 if ((first == last) && (first == 0)) {
245 __ Vldr(vixl32::SRegister(first), MemOperand(sp, stack_offset));
246 return stack_offset + kSRegSizeInBytes;
247 }
248 if (first % 2 == 1) {
249 __ Vldr(vixl32::SRegister(first++), MemOperand(sp, stack_offset));
250 stack_offset += kSRegSizeInBytes;
251 }
252
253 bool restore_last = false;
254 if (last % 2 == 0) {
255 restore_last = true;
256 --last;
257 }
258
259 if (first < last) {
260 vixl32::DRegister d_reg = vixl32::DRegister(first / 2);
261 DCHECK_EQ((last - first + 1) % 2, 0u);
262 size_t number_of_d_regs = (last - first + 1) / 2;
263 if (number_of_d_regs == 1) {
264 __ Vldr(d_reg, MemOperand(sp, stack_offset));
265 } else if (number_of_d_regs > 1) {
266 UseScratchRegisterScope temps(down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler());
267 vixl32::Register base = sp;
268 if (stack_offset != 0) {
269 base = temps.Acquire();
Scott Wakelingb77051e2016-11-21 19:46:00 +0000270 __ Add(base, sp, Operand::From(stack_offset));
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100271 }
272 __ Vldm(F64, base, NO_WRITE_BACK, DRegisterList(d_reg, number_of_d_regs));
273 }
274 stack_offset += number_of_d_regs * kDRegSizeInBytes;
275 }
276
277 if (restore_last) {
278 __ Vldr(vixl32::SRegister(last + 1), MemOperand(sp, stack_offset));
279 stack_offset += kSRegSizeInBytes;
280 }
281
282 return stack_offset;
283}
284
285void SlowPathCodeARMVIXL::SaveLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
286 size_t stack_offset = codegen->GetFirstRegisterSlotInSlowPath();
287 size_t orig_offset = stack_offset;
288
289 const uint32_t core_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ true);
290 for (uint32_t i : LowToHighBits(core_spills)) {
291 // If the register holds an object, update the stack mask.
292 if (locations->RegisterContainsObject(i)) {
293 locations->SetStackBit(stack_offset / kVRegSize);
294 }
295 DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
296 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
297 saved_core_stack_offsets_[i] = stack_offset;
298 stack_offset += kArmWordSize;
299 }
300
301 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
302 arm_codegen->GetAssembler()->StoreRegisterList(core_spills, orig_offset);
303
304 uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ false);
305 orig_offset = stack_offset;
306 for (uint32_t i : LowToHighBits(fp_spills)) {
307 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
308 saved_fpu_stack_offsets_[i] = stack_offset;
309 stack_offset += kArmWordSize;
310 }
311
312 stack_offset = orig_offset;
313 while (fp_spills != 0u) {
314 uint32_t begin = CTZ(fp_spills);
315 uint32_t tmp = fp_spills + (1u << begin);
316 fp_spills &= tmp; // Clear the contiguous range of 1s.
317 uint32_t end = (tmp == 0u) ? 32u : CTZ(tmp); // CTZ(0) is undefined.
318 stack_offset = SaveContiguousSRegisterList(begin, end - 1, codegen, stack_offset);
319 }
320 DCHECK_LE(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
321}
322
323void SlowPathCodeARMVIXL::RestoreLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
324 size_t stack_offset = codegen->GetFirstRegisterSlotInSlowPath();
325 size_t orig_offset = stack_offset;
326
327 const uint32_t core_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ true);
328 for (uint32_t i : LowToHighBits(core_spills)) {
329 DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
330 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
331 stack_offset += kArmWordSize;
332 }
333
334 // TODO(VIXL): Check the coherency of stack_offset after this with a test.
335 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
336 arm_codegen->GetAssembler()->LoadRegisterList(core_spills, orig_offset);
337
338 uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ false);
339 while (fp_spills != 0u) {
340 uint32_t begin = CTZ(fp_spills);
341 uint32_t tmp = fp_spills + (1u << begin);
342 fp_spills &= tmp; // Clear the contiguous range of 1s.
343 uint32_t end = (tmp == 0u) ? 32u : CTZ(tmp); // CTZ(0) is undefined.
344 stack_offset = RestoreContiguousSRegisterList(begin, end - 1, codegen, stack_offset);
345 }
346 DCHECK_LE(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
347}
348
349class NullCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
350 public:
351 explicit NullCheckSlowPathARMVIXL(HNullCheck* instruction) : SlowPathCodeARMVIXL(instruction) {}
352
353 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
354 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
355 __ Bind(GetEntryLabel());
356 if (instruction_->CanThrowIntoCatchBlock()) {
357 // Live registers will be restored in the catch block if caught.
358 SaveLiveRegisters(codegen, instruction_->GetLocations());
359 }
360 arm_codegen->InvokeRuntime(kQuickThrowNullPointer,
361 instruction_,
362 instruction_->GetDexPc(),
363 this);
364 CheckEntrypointTypes<kQuickThrowNullPointer, void, void>();
365 }
366
367 bool IsFatal() const OVERRIDE { return true; }
368
369 const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathARMVIXL"; }
370
371 private:
372 DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARMVIXL);
373};
374
Scott Wakelingfe885462016-09-22 10:24:38 +0100375class DivZeroCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
376 public:
377 explicit DivZeroCheckSlowPathARMVIXL(HDivZeroCheck* instruction)
378 : SlowPathCodeARMVIXL(instruction) {}
379
380 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100381 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
Scott Wakelingfe885462016-09-22 10:24:38 +0100382 __ Bind(GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100383 arm_codegen->InvokeRuntime(kQuickThrowDivZero, instruction_, instruction_->GetDexPc(), this);
Scott Wakelingfe885462016-09-22 10:24:38 +0100384 CheckEntrypointTypes<kQuickThrowDivZero, void, void>();
385 }
386
387 bool IsFatal() const OVERRIDE { return true; }
388
389 const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathARMVIXL"; }
390
391 private:
392 DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathARMVIXL);
393};
394
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100395class SuspendCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
396 public:
397 SuspendCheckSlowPathARMVIXL(HSuspendCheck* instruction, HBasicBlock* successor)
398 : SlowPathCodeARMVIXL(instruction), successor_(successor) {}
399
400 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
401 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
402 __ Bind(GetEntryLabel());
403 arm_codegen->InvokeRuntime(kQuickTestSuspend, instruction_, instruction_->GetDexPc(), this);
404 CheckEntrypointTypes<kQuickTestSuspend, void, void>();
405 if (successor_ == nullptr) {
406 __ B(GetReturnLabel());
407 } else {
408 __ B(arm_codegen->GetLabelOf(successor_));
409 }
410 }
411
412 vixl32::Label* GetReturnLabel() {
413 DCHECK(successor_ == nullptr);
414 return &return_label_;
415 }
416
417 HBasicBlock* GetSuccessor() const {
418 return successor_;
419 }
420
421 const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathARMVIXL"; }
422
423 private:
424 // If not null, the block to branch to after the suspend check.
425 HBasicBlock* const successor_;
426
427 // If `successor_` is null, the label to branch to after the suspend check.
428 vixl32::Label return_label_;
429
430 DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathARMVIXL);
431};
432
Scott Wakelingc34dba72016-10-03 10:14:44 +0100433class BoundsCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
434 public:
435 explicit BoundsCheckSlowPathARMVIXL(HBoundsCheck* instruction)
436 : SlowPathCodeARMVIXL(instruction) {}
437
438 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
439 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
440 LocationSummary* locations = instruction_->GetLocations();
441
442 __ Bind(GetEntryLabel());
443 if (instruction_->CanThrowIntoCatchBlock()) {
444 // Live registers will be restored in the catch block if caught.
445 SaveLiveRegisters(codegen, instruction_->GetLocations());
446 }
447 // We're moving two locations to locations that could overlap, so we need a parallel
448 // move resolver.
449 InvokeRuntimeCallingConventionARMVIXL calling_convention;
450 codegen->EmitParallelMoves(
451 locations->InAt(0),
452 LocationFrom(calling_convention.GetRegisterAt(0)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100453 DataType::Type::kInt32,
Scott Wakelingc34dba72016-10-03 10:14:44 +0100454 locations->InAt(1),
455 LocationFrom(calling_convention.GetRegisterAt(1)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100456 DataType::Type::kInt32);
Scott Wakelingc34dba72016-10-03 10:14:44 +0100457 QuickEntrypointEnum entrypoint = instruction_->AsBoundsCheck()->IsStringCharAt()
458 ? kQuickThrowStringBounds
459 : kQuickThrowArrayBounds;
460 arm_codegen->InvokeRuntime(entrypoint, instruction_, instruction_->GetDexPc(), this);
461 CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>();
462 CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>();
463 }
464
465 bool IsFatal() const OVERRIDE { return true; }
466
467 const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathARMVIXL"; }
468
469 private:
470 DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARMVIXL);
471};
472
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100473class LoadClassSlowPathARMVIXL : public SlowPathCodeARMVIXL {
474 public:
475 LoadClassSlowPathARMVIXL(HLoadClass* cls, HInstruction* at, uint32_t dex_pc, bool do_clinit)
Vladimir Marko6bec91c2017-01-09 15:03:12 +0000476 : SlowPathCodeARMVIXL(at), cls_(cls), dex_pc_(dex_pc), do_clinit_(do_clinit) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100477 DCHECK(at->IsLoadClass() || at->IsClinitCheck());
478 }
479
480 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
Vladimir Marko6bec91c2017-01-09 15:03:12 +0000481 LocationSummary* locations = instruction_->GetLocations();
Vladimir Markoea4c1262017-02-06 19:59:33 +0000482 Location out = locations->Out();
483 constexpr bool call_saves_everything_except_r0 = (!kUseReadBarrier || kUseBakerReadBarrier);
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100484
485 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
486 __ Bind(GetEntryLabel());
487 SaveLiveRegisters(codegen, locations);
488
489 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Vladimir Markoea4c1262017-02-06 19:59:33 +0000490 // For HLoadClass/kBssEntry/kSaveEverything, make sure we preserve the address of the entry.
491 DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_);
492 bool is_load_class_bss_entry =
493 (cls_ == instruction_) && (cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry);
494 vixl32::Register entry_address;
495 if (is_load_class_bss_entry && call_saves_everything_except_r0) {
496 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
497 // In the unlucky case that the `temp` is R0, we preserve the address in `out` across
498 // the kSaveEverything call.
499 bool temp_is_r0 = temp.Is(calling_convention.GetRegisterAt(0));
500 entry_address = temp_is_r0 ? RegisterFrom(out) : temp;
501 DCHECK(!entry_address.Is(calling_convention.GetRegisterAt(0)));
502 if (temp_is_r0) {
503 __ Mov(entry_address, temp);
504 }
505 }
Vladimir Marko6bec91c2017-01-09 15:03:12 +0000506 dex::TypeIndex type_index = cls_->GetTypeIndex();
507 __ Mov(calling_convention.GetRegisterAt(0), type_index.index_);
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100508 QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage
509 : kQuickInitializeType;
Vladimir Marko6bec91c2017-01-09 15:03:12 +0000510 arm_codegen->InvokeRuntime(entrypoint, instruction_, dex_pc_, this);
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100511 if (do_clinit_) {
512 CheckEntrypointTypes<kQuickInitializeStaticStorage, void*, uint32_t>();
513 } else {
514 CheckEntrypointTypes<kQuickInitializeType, void*, uint32_t>();
515 }
516
Vladimir Markoea4c1262017-02-06 19:59:33 +0000517 // For HLoadClass/kBssEntry, store the resolved Class to the BSS entry.
518 if (is_load_class_bss_entry) {
519 if (call_saves_everything_except_r0) {
520 // The class entry address was preserved in `entry_address` thanks to kSaveEverything.
521 __ Str(r0, MemOperand(entry_address));
522 } else {
523 // For non-Baker read barrier, we need to re-calculate the address of the string entry.
524 UseScratchRegisterScope temps(
525 down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler());
526 vixl32::Register temp = temps.Acquire();
527 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
528 arm_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index);
529 arm_codegen->EmitMovwMovtPlaceholder(labels, temp);
530 __ Str(r0, MemOperand(temp));
531 }
532 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100533 // Move the class to the desired location.
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100534 if (out.IsValid()) {
535 DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
536 arm_codegen->Move32(locations->Out(), LocationFrom(r0));
537 }
538 RestoreLiveRegisters(codegen, locations);
539 __ B(GetExitLabel());
540 }
541
542 const char* GetDescription() const OVERRIDE { return "LoadClassSlowPathARMVIXL"; }
543
544 private:
545 // The class this slow path will load.
546 HLoadClass* const cls_;
547
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100548 // The dex PC of `at_`.
549 const uint32_t dex_pc_;
550
551 // Whether to initialize the class.
552 const bool do_clinit_;
553
554 DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathARMVIXL);
555};
556
Artem Serovd4cc5b22016-11-04 11:19:09 +0000557class LoadStringSlowPathARMVIXL : public SlowPathCodeARMVIXL {
558 public:
559 explicit LoadStringSlowPathARMVIXL(HLoadString* instruction)
560 : SlowPathCodeARMVIXL(instruction) {}
561
562 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
Vladimir Markoea4c1262017-02-06 19:59:33 +0000563 DCHECK(instruction_->IsLoadString());
564 DCHECK_EQ(instruction_->AsLoadString()->GetLoadKind(), HLoadString::LoadKind::kBssEntry);
Artem Serovd4cc5b22016-11-04 11:19:09 +0000565 LocationSummary* locations = instruction_->GetLocations();
566 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
567 HLoadString* load = instruction_->AsLoadString();
Vladimir Marko6bec91c2017-01-09 15:03:12 +0000568 const dex::StringIndex string_index = load->GetStringIndex();
Artem Serovd4cc5b22016-11-04 11:19:09 +0000569 vixl32::Register out = OutputRegister(load);
Artem Serovd4cc5b22016-11-04 11:19:09 +0000570 constexpr bool call_saves_everything_except_r0 = (!kUseReadBarrier || kUseBakerReadBarrier);
571
572 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
573 __ Bind(GetEntryLabel());
574 SaveLiveRegisters(codegen, locations);
575
576 InvokeRuntimeCallingConventionARMVIXL calling_convention;
577 // In the unlucky case that the `temp` is R0, we preserve the address in `out` across
Vladimir Markoea4c1262017-02-06 19:59:33 +0000578 // the kSaveEverything call.
579 vixl32::Register entry_address;
580 if (call_saves_everything_except_r0) {
581 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
582 bool temp_is_r0 = (temp.Is(calling_convention.GetRegisterAt(0)));
583 entry_address = temp_is_r0 ? out : temp;
584 DCHECK(!entry_address.Is(calling_convention.GetRegisterAt(0)));
585 if (temp_is_r0) {
586 __ Mov(entry_address, temp);
587 }
Artem Serovd4cc5b22016-11-04 11:19:09 +0000588 }
589
Vladimir Marko6bec91c2017-01-09 15:03:12 +0000590 __ Mov(calling_convention.GetRegisterAt(0), string_index.index_);
Artem Serovd4cc5b22016-11-04 11:19:09 +0000591 arm_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), this);
592 CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
593
594 // Store the resolved String to the .bss entry.
595 if (call_saves_everything_except_r0) {
596 // The string entry address was preserved in `entry_address` thanks to kSaveEverything.
597 __ Str(r0, MemOperand(entry_address));
598 } else {
599 // For non-Baker read barrier, we need to re-calculate the address of the string entry.
Vladimir Markoea4c1262017-02-06 19:59:33 +0000600 UseScratchRegisterScope temps(
601 down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler());
602 vixl32::Register temp = temps.Acquire();
Artem Serovd4cc5b22016-11-04 11:19:09 +0000603 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +0100604 arm_codegen->NewStringBssEntryPatch(load->GetDexFile(), string_index);
Vladimir Markoea4c1262017-02-06 19:59:33 +0000605 arm_codegen->EmitMovwMovtPlaceholder(labels, temp);
606 __ Str(r0, MemOperand(temp));
Artem Serovd4cc5b22016-11-04 11:19:09 +0000607 }
608
609 arm_codegen->Move32(locations->Out(), LocationFrom(r0));
610 RestoreLiveRegisters(codegen, locations);
611
612 __ B(GetExitLabel());
613 }
614
615 const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathARMVIXL"; }
616
617 private:
618 DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathARMVIXL);
619};
620
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100621class TypeCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
622 public:
623 TypeCheckSlowPathARMVIXL(HInstruction* instruction, bool is_fatal)
624 : SlowPathCodeARMVIXL(instruction), is_fatal_(is_fatal) {}
625
626 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
627 LocationSummary* locations = instruction_->GetLocations();
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100628 DCHECK(instruction_->IsCheckCast()
629 || !locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
630
631 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
632 __ Bind(GetEntryLabel());
633
634 if (!is_fatal_) {
Artem Serovcfbe9132016-10-14 15:58:56 +0100635 SaveLiveRegisters(codegen, locations);
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100636 }
637
638 // We're moving two locations to locations that could overlap, so we need a parallel
639 // move resolver.
640 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100641
Mathieu Chartier9fd8c602016-11-14 14:38:53 -0800642 codegen->EmitParallelMoves(locations->InAt(0),
Mathieu Chartierb99f4d62016-11-07 16:17:26 -0800643 LocationFrom(calling_convention.GetRegisterAt(0)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100644 DataType::Type::kReference,
Mathieu Chartier9fd8c602016-11-14 14:38:53 -0800645 locations->InAt(1),
Mathieu Chartierb99f4d62016-11-07 16:17:26 -0800646 LocationFrom(calling_convention.GetRegisterAt(1)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100647 DataType::Type::kReference);
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100648 if (instruction_->IsInstanceOf()) {
Artem Serovcfbe9132016-10-14 15:58:56 +0100649 arm_codegen->InvokeRuntime(kQuickInstanceofNonTrivial,
650 instruction_,
651 instruction_->GetDexPc(),
652 this);
Mathieu Chartier9fd8c602016-11-14 14:38:53 -0800653 CheckEntrypointTypes<kQuickInstanceofNonTrivial, size_t, mirror::Object*, mirror::Class*>();
Artem Serovcfbe9132016-10-14 15:58:56 +0100654 arm_codegen->Move32(locations->Out(), LocationFrom(r0));
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100655 } else {
656 DCHECK(instruction_->IsCheckCast());
Mathieu Chartierb99f4d62016-11-07 16:17:26 -0800657 arm_codegen->InvokeRuntime(kQuickCheckInstanceOf,
658 instruction_,
659 instruction_->GetDexPc(),
660 this);
661 CheckEntrypointTypes<kQuickCheckInstanceOf, void, mirror::Object*, mirror::Class*>();
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100662 }
663
664 if (!is_fatal_) {
Artem Serovcfbe9132016-10-14 15:58:56 +0100665 RestoreLiveRegisters(codegen, locations);
666 __ B(GetExitLabel());
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100667 }
668 }
669
670 const char* GetDescription() const OVERRIDE { return "TypeCheckSlowPathARMVIXL"; }
671
672 bool IsFatal() const OVERRIDE { return is_fatal_; }
673
674 private:
675 const bool is_fatal_;
676
677 DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathARMVIXL);
678};
679
Scott Wakelingc34dba72016-10-03 10:14:44 +0100680class DeoptimizationSlowPathARMVIXL : public SlowPathCodeARMVIXL {
681 public:
682 explicit DeoptimizationSlowPathARMVIXL(HDeoptimize* instruction)
683 : SlowPathCodeARMVIXL(instruction) {}
684
685 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
686 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
687 __ Bind(GetEntryLabel());
Nicolas Geoffray4e92c3c2017-05-08 09:34:26 +0100688 LocationSummary* locations = instruction_->GetLocations();
689 SaveLiveRegisters(codegen, locations);
690 InvokeRuntimeCallingConventionARMVIXL calling_convention;
691 __ Mov(calling_convention.GetRegisterAt(0),
692 static_cast<uint32_t>(instruction_->AsDeoptimize()->GetDeoptimizationKind()));
693
Scott Wakelingc34dba72016-10-03 10:14:44 +0100694 arm_codegen->InvokeRuntime(kQuickDeoptimize, instruction_, instruction_->GetDexPc(), this);
Nicolas Geoffray4e92c3c2017-05-08 09:34:26 +0100695 CheckEntrypointTypes<kQuickDeoptimize, void, DeoptimizationKind>();
Scott Wakelingc34dba72016-10-03 10:14:44 +0100696 }
697
698 const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathARMVIXL"; }
699
700 private:
701 DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathARMVIXL);
702};
703
704class ArraySetSlowPathARMVIXL : public SlowPathCodeARMVIXL {
705 public:
706 explicit ArraySetSlowPathARMVIXL(HInstruction* instruction) : SlowPathCodeARMVIXL(instruction) {}
707
708 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
709 LocationSummary* locations = instruction_->GetLocations();
710 __ Bind(GetEntryLabel());
711 SaveLiveRegisters(codegen, locations);
712
713 InvokeRuntimeCallingConventionARMVIXL calling_convention;
714 HParallelMove parallel_move(codegen->GetGraph()->GetArena());
715 parallel_move.AddMove(
716 locations->InAt(0),
717 LocationFrom(calling_convention.GetRegisterAt(0)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100718 DataType::Type::kReference,
Scott Wakelingc34dba72016-10-03 10:14:44 +0100719 nullptr);
720 parallel_move.AddMove(
721 locations->InAt(1),
722 LocationFrom(calling_convention.GetRegisterAt(1)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100723 DataType::Type::kInt32,
Scott Wakelingc34dba72016-10-03 10:14:44 +0100724 nullptr);
725 parallel_move.AddMove(
726 locations->InAt(2),
727 LocationFrom(calling_convention.GetRegisterAt(2)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100728 DataType::Type::kReference,
Scott Wakelingc34dba72016-10-03 10:14:44 +0100729 nullptr);
730 codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
731
732 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
733 arm_codegen->InvokeRuntime(kQuickAputObject, instruction_, instruction_->GetDexPc(), this);
734 CheckEntrypointTypes<kQuickAputObject, void, mirror::Array*, int32_t, mirror::Object*>();
735 RestoreLiveRegisters(codegen, locations);
736 __ B(GetExitLabel());
737 }
738
739 const char* GetDescription() const OVERRIDE { return "ArraySetSlowPathARMVIXL"; }
740
741 private:
742 DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathARMVIXL);
743};
744
Roland Levillain54f869e2017-03-06 13:54:11 +0000745// Abstract base class for read barrier slow paths marking a reference
746// `ref`.
Roland Levillain27b1f9c2017-01-17 16:56:34 +0000747//
Roland Levillain54f869e2017-03-06 13:54:11 +0000748// Argument `entrypoint` must be a register location holding the read
Roland Levillain6d729a72017-06-30 18:34:01 +0100749// barrier marking runtime entry point to be invoked or an empty
750// location; in the latter case, the read barrier marking runtime
751// entry point will be loaded by the slow path code itself.
Roland Levillain54f869e2017-03-06 13:54:11 +0000752class ReadBarrierMarkSlowPathBaseARMVIXL : public SlowPathCodeARMVIXL {
753 protected:
754 ReadBarrierMarkSlowPathBaseARMVIXL(HInstruction* instruction, Location ref, Location entrypoint)
Roland Levillain27b1f9c2017-01-17 16:56:34 +0000755 : SlowPathCodeARMVIXL(instruction), ref_(ref), entrypoint_(entrypoint) {
756 DCHECK(kEmitCompilerReadBarrier);
757 }
758
Roland Levillain54f869e2017-03-06 13:54:11 +0000759 const char* GetDescription() const OVERRIDE { return "ReadBarrierMarkSlowPathBaseARMVIXL"; }
Roland Levillain27b1f9c2017-01-17 16:56:34 +0000760
Roland Levillain54f869e2017-03-06 13:54:11 +0000761 // Generate assembly code calling the read barrier marking runtime
762 // entry point (ReadBarrierMarkRegX).
763 void GenerateReadBarrierMarkRuntimeCall(CodeGenerator* codegen) {
Roland Levillain27b1f9c2017-01-17 16:56:34 +0000764 vixl32::Register ref_reg = RegisterFrom(ref_);
Roland Levillain47b3ab22017-02-27 14:31:35 +0000765
Roland Levillain47b3ab22017-02-27 14:31:35 +0000766 // No need to save live registers; it's taken care of by the
767 // entrypoint. Also, there is no need to update the stack mask,
768 // as this runtime call will not trigger a garbage collection.
769 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
770 DCHECK(!ref_reg.Is(sp));
771 DCHECK(!ref_reg.Is(lr));
772 DCHECK(!ref_reg.Is(pc));
773 // IP is used internally by the ReadBarrierMarkRegX entry point
774 // as a temporary, it cannot be the entry point's input/output.
775 DCHECK(!ref_reg.Is(ip));
776 DCHECK(ref_reg.IsRegister()) << ref_reg;
777 // "Compact" slow path, saving two moves.
778 //
779 // Instead of using the standard runtime calling convention (input
780 // and output in R0):
781 //
782 // R0 <- ref
783 // R0 <- ReadBarrierMark(R0)
784 // ref <- R0
785 //
786 // we just use rX (the register containing `ref`) as input and output
787 // of a dedicated entrypoint:
788 //
789 // rX <- ReadBarrierMarkRegX(rX)
790 //
791 if (entrypoint_.IsValid()) {
792 arm_codegen->ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction_, this);
793 __ Blx(RegisterFrom(entrypoint_));
794 } else {
Roland Levillain54f869e2017-03-06 13:54:11 +0000795 // Entrypoint is not already loaded, load from the thread.
Roland Levillain47b3ab22017-02-27 14:31:35 +0000796 int32_t entry_point_offset =
Roland Levillain97c46462017-05-11 14:04:03 +0100797 Thread::ReadBarrierMarkEntryPointsOffset<kArmPointerSize>(ref_reg.GetCode());
Roland Levillain47b3ab22017-02-27 14:31:35 +0000798 // This runtime call does not require a stack map.
799 arm_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
800 }
Roland Levillain47b3ab22017-02-27 14:31:35 +0000801 }
802
Roland Levillain47b3ab22017-02-27 14:31:35 +0000803 // The location (register) of the marked object reference.
804 const Location ref_;
805
806 // The location of the entrypoint if already loaded.
807 const Location entrypoint_;
808
Roland Levillain54f869e2017-03-06 13:54:11 +0000809 private:
810 DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkSlowPathBaseARMVIXL);
Roland Levillain47b3ab22017-02-27 14:31:35 +0000811};
812
Scott Wakelingc34dba72016-10-03 10:14:44 +0100813// Slow path marking an object reference `ref` during a read
814// barrier. The field `obj.field` in the object `obj` holding this
Roland Levillain54f869e2017-03-06 13:54:11 +0000815// reference does not get updated by this slow path after marking.
Roland Levillain47b3ab22017-02-27 14:31:35 +0000816//
Scott Wakelingc34dba72016-10-03 10:14:44 +0100817// This means that after the execution of this slow path, `ref` will
Roland Levillain27b1f9c2017-01-17 16:56:34 +0000818// always be up-to-date, but `obj.field` may not; i.e., after the
819// flip, `ref` will be a to-space reference, but `obj.field` will
820// probably still be a from-space reference (unless it gets updated by
821// another thread, or if another thread installed another object
822// reference (different from `ref`) in `obj.field`).
Roland Levillainba650a42017-03-06 13:52:32 +0000823//
Roland Levillain6d729a72017-06-30 18:34:01 +0100824// Argument `entrypoint` must be a register location holding the read
825// barrier marking runtime entry point to be invoked or an empty
826// location; in the latter case, the read barrier marking runtime
827// entry point will be loaded by the slow path code itself.
Roland Levillain54f869e2017-03-06 13:54:11 +0000828class ReadBarrierMarkSlowPathARMVIXL : public ReadBarrierMarkSlowPathBaseARMVIXL {
Roland Levillain47b3ab22017-02-27 14:31:35 +0000829 public:
Roland Levillain27b1f9c2017-01-17 16:56:34 +0000830 ReadBarrierMarkSlowPathARMVIXL(HInstruction* instruction,
831 Location ref,
832 Location entrypoint = Location::NoLocation())
Roland Levillain54f869e2017-03-06 13:54:11 +0000833 : ReadBarrierMarkSlowPathBaseARMVIXL(instruction, ref, entrypoint) {
Roland Levillain47b3ab22017-02-27 14:31:35 +0000834 DCHECK(kEmitCompilerReadBarrier);
835 }
836
Roland Levillain47b3ab22017-02-27 14:31:35 +0000837 const char* GetDescription() const OVERRIDE { return "ReadBarrierMarkSlowPathARMVIXL"; }
838
839 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
840 LocationSummary* locations = instruction_->GetLocations();
Roland Levillain54f869e2017-03-06 13:54:11 +0000841 DCHECK(locations->CanCall());
842 DCHECK(ref_.IsRegister()) << ref_;
843 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_.reg())) << ref_.reg();
844 DCHECK(instruction_->IsLoadClass() || instruction_->IsLoadString())
845 << "Unexpected instruction in read barrier marking slow path: "
846 << instruction_->DebugName();
847
848 __ Bind(GetEntryLabel());
849 GenerateReadBarrierMarkRuntimeCall(codegen);
850 __ B(GetExitLabel());
851 }
852
853 private:
854 DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkSlowPathARMVIXL);
855};
856
857// Slow path loading `obj`'s lock word, loading a reference from
858// object `*(obj + offset + (index << scale_factor))` into `ref`, and
859// marking `ref` if `obj` is gray according to the lock word (Baker
860// read barrier). The field `obj.field` in the object `obj` holding
861// this reference does not get updated by this slow path after marking
862// (see LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARMVIXL
863// below for that).
864//
865// This means that after the execution of this slow path, `ref` will
866// always be up-to-date, but `obj.field` may not; i.e., after the
867// flip, `ref` will be a to-space reference, but `obj.field` will
868// probably still be a from-space reference (unless it gets updated by
869// another thread, or if another thread installed another object
870// reference (different from `ref`) in `obj.field`).
871//
872// Argument `entrypoint` must be a register location holding the read
Roland Levillain6d729a72017-06-30 18:34:01 +0100873// barrier marking runtime entry point to be invoked or an empty
874// location; in the latter case, the read barrier marking runtime
875// entry point will be loaded by the slow path code itself.
Roland Levillain54f869e2017-03-06 13:54:11 +0000876class LoadReferenceWithBakerReadBarrierSlowPathARMVIXL : public ReadBarrierMarkSlowPathBaseARMVIXL {
877 public:
878 LoadReferenceWithBakerReadBarrierSlowPathARMVIXL(HInstruction* instruction,
879 Location ref,
880 vixl32::Register obj,
881 uint32_t offset,
882 Location index,
883 ScaleFactor scale_factor,
884 bool needs_null_check,
885 vixl32::Register temp,
Roland Levillain6d729a72017-06-30 18:34:01 +0100886 Location entrypoint = Location::NoLocation())
Roland Levillain54f869e2017-03-06 13:54:11 +0000887 : ReadBarrierMarkSlowPathBaseARMVIXL(instruction, ref, entrypoint),
888 obj_(obj),
889 offset_(offset),
890 index_(index),
891 scale_factor_(scale_factor),
892 needs_null_check_(needs_null_check),
893 temp_(temp) {
894 DCHECK(kEmitCompilerReadBarrier);
895 DCHECK(kUseBakerReadBarrier);
896 }
897
Roland Levillain47b3ab22017-02-27 14:31:35 +0000898 const char* GetDescription() const OVERRIDE {
Roland Levillain54f869e2017-03-06 13:54:11 +0000899 return "LoadReferenceWithBakerReadBarrierSlowPathARMVIXL";
Roland Levillain47b3ab22017-02-27 14:31:35 +0000900 }
901
902 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
903 LocationSummary* locations = instruction_->GetLocations();
904 vixl32::Register ref_reg = RegisterFrom(ref_);
905 DCHECK(locations->CanCall());
906 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_reg.GetCode())) << ref_reg;
Roland Levillain47b3ab22017-02-27 14:31:35 +0000907 DCHECK(instruction_->IsInstanceFieldGet() ||
908 instruction_->IsStaticFieldGet() ||
909 instruction_->IsArrayGet() ||
910 instruction_->IsArraySet() ||
Roland Levillain47b3ab22017-02-27 14:31:35 +0000911 instruction_->IsInstanceOf() ||
912 instruction_->IsCheckCast() ||
913 (instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()) ||
914 (instruction_->IsInvokeStaticOrDirect() && instruction_->GetLocations()->Intrinsified()))
915 << "Unexpected instruction in read barrier marking slow path: "
916 << instruction_->DebugName();
917 // The read barrier instrumentation of object ArrayGet
918 // instructions does not support the HIntermediateAddress
919 // instruction.
920 DCHECK(!(instruction_->IsArrayGet() &&
921 instruction_->AsArrayGet()->GetArray()->IsIntermediateAddress()));
922
Roland Levillain54f869e2017-03-06 13:54:11 +0000923 // Temporary register `temp_`, used to store the lock word, must
924 // not be IP, as we may use it to emit the reference load (in the
925 // call to GenerateRawReferenceLoad below), and we need the lock
926 // word to still be in `temp_` after the reference load.
927 DCHECK(!temp_.Is(ip));
928
Roland Levillain47b3ab22017-02-27 14:31:35 +0000929 __ Bind(GetEntryLabel());
Roland Levillain54f869e2017-03-06 13:54:11 +0000930
931 // When using MaybeGenerateReadBarrierSlow, the read barrier call is
932 // inserted after the original load. However, in fast path based
933 // Baker's read barriers, we need to perform the load of
934 // mirror::Object::monitor_ *before* the original reference load.
935 // This load-load ordering is required by the read barrier.
Roland Levillainff487002017-03-07 16:50:01 +0000936 // The slow path (for Baker's algorithm) should look like:
Roland Levillain54f869e2017-03-06 13:54:11 +0000937 //
938 // uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
939 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
940 // HeapReference<mirror::Object> ref = *src; // Original reference load.
941 // bool is_gray = (rb_state == ReadBarrier::GrayState());
942 // if (is_gray) {
943 // ref = entrypoint(ref); // ref = ReadBarrier::Mark(ref); // Runtime entry point call.
944 // }
945 //
946 // Note: the original implementation in ReadBarrier::Barrier is
947 // slightly more complex as it performs additional checks that we do
948 // not do here for performance reasons.
949
Roland Levillain47b3ab22017-02-27 14:31:35 +0000950 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
Roland Levillain54f869e2017-03-06 13:54:11 +0000951
952 // /* int32_t */ monitor = obj->monitor_
953 uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
954 arm_codegen->GetAssembler()->LoadFromOffset(kLoadWord, temp_, obj_, monitor_offset);
955 if (needs_null_check_) {
956 codegen->MaybeRecordImplicitNullCheck(instruction_);
Roland Levillain47b3ab22017-02-27 14:31:35 +0000957 }
Roland Levillain54f869e2017-03-06 13:54:11 +0000958 // /* LockWord */ lock_word = LockWord(monitor)
959 static_assert(sizeof(LockWord) == sizeof(int32_t),
960 "art::LockWord and int32_t have different sizes.");
961
962 // Introduce a dependency on the lock_word including the rb_state,
963 // which shall prevent load-load reordering without using
964 // a memory barrier (which would be more expensive).
965 // `obj` is unchanged by this operation, but its value now depends
966 // on `temp`.
967 __ Add(obj_, obj_, Operand(temp_, ShiftType::LSR, 32));
968
969 // The actual reference load.
970 // A possible implicit null check has already been handled above.
971 arm_codegen->GenerateRawReferenceLoad(
972 instruction_, ref_, obj_, offset_, index_, scale_factor_, /* needs_null_check */ false);
973
974 // Mark the object `ref` when `obj` is gray.
975 //
976 // if (rb_state == ReadBarrier::GrayState())
977 // ref = ReadBarrier::Mark(ref);
978 //
979 // Given the numeric representation, it's enough to check the low bit of the
980 // rb_state. We do that by shifting the bit out of the lock word with LSRS
981 // which can be a 16-bit instruction unlike the TST immediate.
982 static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
983 static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
984 __ Lsrs(temp_, temp_, LockWord::kReadBarrierStateShift + 1);
985 __ B(cc, GetExitLabel()); // Carry flag is the last bit shifted out by LSRS.
986 GenerateReadBarrierMarkRuntimeCall(codegen);
987
Roland Levillain47b3ab22017-02-27 14:31:35 +0000988 __ B(GetExitLabel());
989 }
990
991 private:
Roland Levillain54f869e2017-03-06 13:54:11 +0000992 // The register containing the object holding the marked object reference field.
993 vixl32::Register obj_;
994 // The offset, index and scale factor to access the reference in `obj_`.
995 uint32_t offset_;
996 Location index_;
997 ScaleFactor scale_factor_;
998 // Is a null check required?
999 bool needs_null_check_;
1000 // A temporary register used to hold the lock word of `obj_`.
1001 vixl32::Register temp_;
Roland Levillain47b3ab22017-02-27 14:31:35 +00001002
Roland Levillain54f869e2017-03-06 13:54:11 +00001003 DISALLOW_COPY_AND_ASSIGN(LoadReferenceWithBakerReadBarrierSlowPathARMVIXL);
Roland Levillain47b3ab22017-02-27 14:31:35 +00001004};
1005
Roland Levillain54f869e2017-03-06 13:54:11 +00001006// Slow path loading `obj`'s lock word, loading a reference from
1007// object `*(obj + offset + (index << scale_factor))` into `ref`, and
1008// marking `ref` if `obj` is gray according to the lock word (Baker
1009// read barrier). If needed, this slow path also atomically updates
1010// the field `obj.field` in the object `obj` holding this reference
1011// after marking (contrary to
1012// LoadReferenceWithBakerReadBarrierSlowPathARMVIXL above, which never
1013// tries to update `obj.field`).
Roland Levillain47b3ab22017-02-27 14:31:35 +00001014//
1015// This means that after the execution of this slow path, both `ref`
1016// and `obj.field` will be up-to-date; i.e., after the flip, both will
1017// hold the same to-space reference (unless another thread installed
1018// another object reference (different from `ref`) in `obj.field`).
Roland Levillainba650a42017-03-06 13:52:32 +00001019//
Roland Levillain54f869e2017-03-06 13:54:11 +00001020// Argument `entrypoint` must be a register location holding the read
Roland Levillain6d729a72017-06-30 18:34:01 +01001021// barrier marking runtime entry point to be invoked or an empty
1022// location; in the latter case, the read barrier marking runtime
1023// entry point will be loaded by the slow path code itself.
Roland Levillain54f869e2017-03-06 13:54:11 +00001024class LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARMVIXL
1025 : public ReadBarrierMarkSlowPathBaseARMVIXL {
Roland Levillain47b3ab22017-02-27 14:31:35 +00001026 public:
Roland Levillain6d729a72017-06-30 18:34:01 +01001027 LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARMVIXL(
1028 HInstruction* instruction,
1029 Location ref,
1030 vixl32::Register obj,
1031 uint32_t offset,
1032 Location index,
1033 ScaleFactor scale_factor,
1034 bool needs_null_check,
1035 vixl32::Register temp1,
1036 vixl32::Register temp2,
1037 Location entrypoint = Location::NoLocation())
Roland Levillain54f869e2017-03-06 13:54:11 +00001038 : ReadBarrierMarkSlowPathBaseARMVIXL(instruction, ref, entrypoint),
Roland Levillain47b3ab22017-02-27 14:31:35 +00001039 obj_(obj),
Roland Levillain54f869e2017-03-06 13:54:11 +00001040 offset_(offset),
1041 index_(index),
1042 scale_factor_(scale_factor),
1043 needs_null_check_(needs_null_check),
Roland Levillain47b3ab22017-02-27 14:31:35 +00001044 temp1_(temp1),
Roland Levillain54f869e2017-03-06 13:54:11 +00001045 temp2_(temp2) {
Roland Levillain47b3ab22017-02-27 14:31:35 +00001046 DCHECK(kEmitCompilerReadBarrier);
Roland Levillain54f869e2017-03-06 13:54:11 +00001047 DCHECK(kUseBakerReadBarrier);
Roland Levillain47b3ab22017-02-27 14:31:35 +00001048 }
1049
1050 const char* GetDescription() const OVERRIDE {
Roland Levillain54f869e2017-03-06 13:54:11 +00001051 return "LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARMVIXL";
Roland Levillain47b3ab22017-02-27 14:31:35 +00001052 }
1053
1054 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
1055 LocationSummary* locations = instruction_->GetLocations();
1056 vixl32::Register ref_reg = RegisterFrom(ref_);
1057 DCHECK(locations->CanCall());
1058 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_reg.GetCode())) << ref_reg;
Roland Levillain54f869e2017-03-06 13:54:11 +00001059 DCHECK_NE(ref_.reg(), LocationFrom(temp1_).reg());
1060
1061 // This slow path is only used by the UnsafeCASObject intrinsic at the moment.
Roland Levillain47b3ab22017-02-27 14:31:35 +00001062 DCHECK((instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()))
1063 << "Unexpected instruction in read barrier marking and field updating slow path: "
1064 << instruction_->DebugName();
1065 DCHECK(instruction_->GetLocations()->Intrinsified());
1066 DCHECK_EQ(instruction_->AsInvoke()->GetIntrinsic(), Intrinsics::kUnsafeCASObject);
Roland Levillain54f869e2017-03-06 13:54:11 +00001067 DCHECK_EQ(offset_, 0u);
1068 DCHECK_EQ(scale_factor_, ScaleFactor::TIMES_1);
1069 Location field_offset = index_;
1070 DCHECK(field_offset.IsRegisterPair()) << field_offset;
1071
1072 // Temporary register `temp1_`, used to store the lock word, must
1073 // not be IP, as we may use it to emit the reference load (in the
1074 // call to GenerateRawReferenceLoad below), and we need the lock
1075 // word to still be in `temp1_` after the reference load.
1076 DCHECK(!temp1_.Is(ip));
Roland Levillain47b3ab22017-02-27 14:31:35 +00001077
1078 __ Bind(GetEntryLabel());
1079
Roland Levillainff487002017-03-07 16:50:01 +00001080 // The implementation is similar to LoadReferenceWithBakerReadBarrierSlowPathARMVIXL's:
1081 //
1082 // uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
1083 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
1084 // HeapReference<mirror::Object> ref = *src; // Original reference load.
1085 // bool is_gray = (rb_state == ReadBarrier::GrayState());
1086 // if (is_gray) {
1087 // old_ref = ref;
1088 // ref = entrypoint(ref); // ref = ReadBarrier::Mark(ref); // Runtime entry point call.
1089 // compareAndSwapObject(obj, field_offset, old_ref, ref);
1090 // }
1091
Roland Levillain54f869e2017-03-06 13:54:11 +00001092 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
1093
1094 // /* int32_t */ monitor = obj->monitor_
1095 uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
1096 arm_codegen->GetAssembler()->LoadFromOffset(kLoadWord, temp1_, obj_, monitor_offset);
1097 if (needs_null_check_) {
1098 codegen->MaybeRecordImplicitNullCheck(instruction_);
1099 }
1100 // /* LockWord */ lock_word = LockWord(monitor)
1101 static_assert(sizeof(LockWord) == sizeof(int32_t),
1102 "art::LockWord and int32_t have different sizes.");
1103
1104 // Introduce a dependency on the lock_word including the rb_state,
1105 // which shall prevent load-load reordering without using
1106 // a memory barrier (which would be more expensive).
1107 // `obj` is unchanged by this operation, but its value now depends
1108 // on `temp`.
1109 __ Add(obj_, obj_, Operand(temp1_, ShiftType::LSR, 32));
1110
1111 // The actual reference load.
1112 // A possible implicit null check has already been handled above.
1113 arm_codegen->GenerateRawReferenceLoad(
1114 instruction_, ref_, obj_, offset_, index_, scale_factor_, /* needs_null_check */ false);
1115
1116 // Mark the object `ref` when `obj` is gray.
1117 //
1118 // if (rb_state == ReadBarrier::GrayState())
1119 // ref = ReadBarrier::Mark(ref);
1120 //
1121 // Given the numeric representation, it's enough to check the low bit of the
1122 // rb_state. We do that by shifting the bit out of the lock word with LSRS
1123 // which can be a 16-bit instruction unlike the TST immediate.
1124 static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
1125 static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
1126 __ Lsrs(temp1_, temp1_, LockWord::kReadBarrierStateShift + 1);
1127 __ B(cc, GetExitLabel()); // Carry flag is the last bit shifted out by LSRS.
1128
1129 // Save the old value of the reference before marking it.
Roland Levillain47b3ab22017-02-27 14:31:35 +00001130 // Note that we cannot use IP to save the old reference, as IP is
1131 // used internally by the ReadBarrierMarkRegX entry point, and we
1132 // need the old reference after the call to that entry point.
1133 DCHECK(!temp1_.Is(ip));
1134 __ Mov(temp1_, ref_reg);
Roland Levillain27b1f9c2017-01-17 16:56:34 +00001135
Roland Levillain54f869e2017-03-06 13:54:11 +00001136 GenerateReadBarrierMarkRuntimeCall(codegen);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001137
1138 // If the new reference is different from the old reference,
Roland Levillain54f869e2017-03-06 13:54:11 +00001139 // update the field in the holder (`*(obj_ + field_offset)`).
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001140 //
1141 // Note that this field could also hold a different object, if
1142 // another thread had concurrently changed it. In that case, the
1143 // LDREX/SUBS/ITNE sequence of instructions in the compare-and-set
1144 // (CAS) operation below would abort the CAS, leaving the field
1145 // as-is.
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001146 __ Cmp(temp1_, ref_reg);
Roland Levillain54f869e2017-03-06 13:54:11 +00001147 __ B(eq, GetExitLabel());
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001148
1149 // Update the the holder's field atomically. This may fail if
1150 // mutator updates before us, but it's OK. This is achieved
1151 // using a strong compare-and-set (CAS) operation with relaxed
1152 // memory synchronization ordering, where the expected value is
1153 // the old reference and the desired value is the new reference.
1154
1155 UseScratchRegisterScope temps(arm_codegen->GetVIXLAssembler());
1156 // Convenience aliases.
1157 vixl32::Register base = obj_;
1158 // The UnsafeCASObject intrinsic uses a register pair as field
1159 // offset ("long offset"), of which only the low part contains
1160 // data.
Roland Levillain54f869e2017-03-06 13:54:11 +00001161 vixl32::Register offset = LowRegisterFrom(field_offset);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001162 vixl32::Register expected = temp1_;
1163 vixl32::Register value = ref_reg;
1164 vixl32::Register tmp_ptr = temps.Acquire(); // Pointer to actual memory.
1165 vixl32::Register tmp = temp2_; // Value in memory.
1166
1167 __ Add(tmp_ptr, base, offset);
1168
1169 if (kPoisonHeapReferences) {
1170 arm_codegen->GetAssembler()->PoisonHeapReference(expected);
1171 if (value.Is(expected)) {
1172 // Do not poison `value`, as it is the same register as
1173 // `expected`, which has just been poisoned.
1174 } else {
1175 arm_codegen->GetAssembler()->PoisonHeapReference(value);
1176 }
1177 }
1178
1179 // do {
1180 // tmp = [r_ptr] - expected;
1181 // } while (tmp == 0 && failure([r_ptr] <- r_new_value));
1182
1183 vixl32::Label loop_head, exit_loop;
1184 __ Bind(&loop_head);
1185
1186 __ Ldrex(tmp, MemOperand(tmp_ptr));
1187
1188 __ Subs(tmp, tmp, expected);
1189
1190 {
Artem Serov0fb37192016-12-06 18:13:40 +00001191 ExactAssemblyScope aas(arm_codegen->GetVIXLAssembler(),
1192 2 * kMaxInstructionSizeInBytes,
1193 CodeBufferCheckScope::kMaximumSize);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001194
1195 __ it(ne);
1196 __ clrex(ne);
1197 }
1198
Artem Serov517d9f62016-12-12 15:51:15 +00001199 __ B(ne, &exit_loop, /* far_target */ false);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001200
1201 __ Strex(tmp, value, MemOperand(tmp_ptr));
1202 __ Cmp(tmp, 1);
Artem Serov517d9f62016-12-12 15:51:15 +00001203 __ B(eq, &loop_head, /* far_target */ false);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001204
1205 __ Bind(&exit_loop);
1206
1207 if (kPoisonHeapReferences) {
1208 arm_codegen->GetAssembler()->UnpoisonHeapReference(expected);
1209 if (value.Is(expected)) {
1210 // Do not unpoison `value`, as it is the same register as
1211 // `expected`, which has just been unpoisoned.
1212 } else {
1213 arm_codegen->GetAssembler()->UnpoisonHeapReference(value);
1214 }
1215 }
1216
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001217 __ B(GetExitLabel());
1218 }
1219
1220 private:
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001221 // The register containing the object holding the marked object reference field.
1222 const vixl32::Register obj_;
Roland Levillain54f869e2017-03-06 13:54:11 +00001223 // The offset, index and scale factor to access the reference in `obj_`.
1224 uint32_t offset_;
1225 Location index_;
1226 ScaleFactor scale_factor_;
1227 // Is a null check required?
1228 bool needs_null_check_;
1229 // A temporary register used to hold the lock word of `obj_`; and
1230 // also to hold the original reference value, when the reference is
1231 // marked.
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001232 const vixl32::Register temp1_;
Roland Levillain54f869e2017-03-06 13:54:11 +00001233 // A temporary register used in the implementation of the CAS, to
1234 // update the object's reference field.
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001235 const vixl32::Register temp2_;
1236
Roland Levillain54f869e2017-03-06 13:54:11 +00001237 DISALLOW_COPY_AND_ASSIGN(LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARMVIXL);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001238};
1239
1240// Slow path generating a read barrier for a heap reference.
1241class ReadBarrierForHeapReferenceSlowPathARMVIXL : public SlowPathCodeARMVIXL {
1242 public:
1243 ReadBarrierForHeapReferenceSlowPathARMVIXL(HInstruction* instruction,
1244 Location out,
1245 Location ref,
1246 Location obj,
1247 uint32_t offset,
1248 Location index)
1249 : SlowPathCodeARMVIXL(instruction),
1250 out_(out),
1251 ref_(ref),
1252 obj_(obj),
1253 offset_(offset),
1254 index_(index) {
1255 DCHECK(kEmitCompilerReadBarrier);
1256 // If `obj` is equal to `out` or `ref`, it means the initial object
1257 // has been overwritten by (or after) the heap object reference load
1258 // to be instrumented, e.g.:
1259 //
1260 // __ LoadFromOffset(kLoadWord, out, out, offset);
1261 // codegen_->GenerateReadBarrierSlow(instruction, out_loc, out_loc, out_loc, offset);
1262 //
1263 // In that case, we have lost the information about the original
1264 // object, and the emitted read barrier cannot work properly.
1265 DCHECK(!obj.Equals(out)) << "obj=" << obj << " out=" << out;
1266 DCHECK(!obj.Equals(ref)) << "obj=" << obj << " ref=" << ref;
1267 }
1268
1269 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
1270 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
1271 LocationSummary* locations = instruction_->GetLocations();
1272 vixl32::Register reg_out = RegisterFrom(out_);
1273 DCHECK(locations->CanCall());
1274 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out.GetCode()));
1275 DCHECK(instruction_->IsInstanceFieldGet() ||
1276 instruction_->IsStaticFieldGet() ||
1277 instruction_->IsArrayGet() ||
1278 instruction_->IsInstanceOf() ||
1279 instruction_->IsCheckCast() ||
Andreas Gamped9911ee2017-03-27 13:27:24 -07001280 (instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()))
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001281 << "Unexpected instruction in read barrier for heap reference slow path: "
1282 << instruction_->DebugName();
1283 // The read barrier instrumentation of object ArrayGet
1284 // instructions does not support the HIntermediateAddress
1285 // instruction.
1286 DCHECK(!(instruction_->IsArrayGet() &&
1287 instruction_->AsArrayGet()->GetArray()->IsIntermediateAddress()));
1288
1289 __ Bind(GetEntryLabel());
1290 SaveLiveRegisters(codegen, locations);
1291
1292 // We may have to change the index's value, but as `index_` is a
1293 // constant member (like other "inputs" of this slow path),
1294 // introduce a copy of it, `index`.
1295 Location index = index_;
1296 if (index_.IsValid()) {
1297 // Handle `index_` for HArrayGet and UnsafeGetObject/UnsafeGetObjectVolatile intrinsics.
1298 if (instruction_->IsArrayGet()) {
1299 // Compute the actual memory offset and store it in `index`.
1300 vixl32::Register index_reg = RegisterFrom(index_);
1301 DCHECK(locations->GetLiveRegisters()->ContainsCoreRegister(index_reg.GetCode()));
1302 if (codegen->IsCoreCalleeSaveRegister(index_reg.GetCode())) {
1303 // We are about to change the value of `index_reg` (see the
Roland Levillain9983e302017-07-14 14:34:22 +01001304 // calls to art::arm::ArmVIXLMacroAssembler::Lsl and
1305 // art::arm::ArmVIXLMacroAssembler::Add below), but it has
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001306 // not been saved by the previous call to
1307 // art::SlowPathCode::SaveLiveRegisters, as it is a
1308 // callee-save register --
1309 // art::SlowPathCode::SaveLiveRegisters does not consider
1310 // callee-save registers, as it has been designed with the
1311 // assumption that callee-save registers are supposed to be
1312 // handled by the called function. So, as a callee-save
1313 // register, `index_reg` _would_ eventually be saved onto
1314 // the stack, but it would be too late: we would have
1315 // changed its value earlier. Therefore, we manually save
1316 // it here into another freely available register,
1317 // `free_reg`, chosen of course among the caller-save
1318 // registers (as a callee-save `free_reg` register would
1319 // exhibit the same problem).
1320 //
1321 // Note we could have requested a temporary register from
1322 // the register allocator instead; but we prefer not to, as
1323 // this is a slow path, and we know we can find a
1324 // caller-save register that is available.
1325 vixl32::Register free_reg = FindAvailableCallerSaveRegister(codegen);
1326 __ Mov(free_reg, index_reg);
1327 index_reg = free_reg;
1328 index = LocationFrom(index_reg);
1329 } else {
1330 // The initial register stored in `index_` has already been
1331 // saved in the call to art::SlowPathCode::SaveLiveRegisters
1332 // (as it is not a callee-save register), so we can freely
1333 // use it.
1334 }
1335 // Shifting the index value contained in `index_reg` by the scale
1336 // factor (2) cannot overflow in practice, as the runtime is
1337 // unable to allocate object arrays with a size larger than
1338 // 2^26 - 1 (that is, 2^28 - 4 bytes).
1339 __ Lsl(index_reg, index_reg, TIMES_4);
1340 static_assert(
1341 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
1342 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
1343 __ Add(index_reg, index_reg, offset_);
1344 } else {
1345 // In the case of the UnsafeGetObject/UnsafeGetObjectVolatile
1346 // intrinsics, `index_` is not shifted by a scale factor of 2
1347 // (as in the case of ArrayGet), as it is actually an offset
1348 // to an object field within an object.
1349 DCHECK(instruction_->IsInvoke()) << instruction_->DebugName();
1350 DCHECK(instruction_->GetLocations()->Intrinsified());
1351 DCHECK((instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObject) ||
1352 (instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile))
1353 << instruction_->AsInvoke()->GetIntrinsic();
1354 DCHECK_EQ(offset_, 0U);
1355 DCHECK(index_.IsRegisterPair());
1356 // UnsafeGet's offset location is a register pair, the low
1357 // part contains the correct offset.
1358 index = index_.ToLow();
1359 }
1360 }
1361
1362 // We're moving two or three locations to locations that could
1363 // overlap, so we need a parallel move resolver.
1364 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1365 HParallelMove parallel_move(codegen->GetGraph()->GetArena());
1366 parallel_move.AddMove(ref_,
1367 LocationFrom(calling_convention.GetRegisterAt(0)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001368 DataType::Type::kReference,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001369 nullptr);
1370 parallel_move.AddMove(obj_,
1371 LocationFrom(calling_convention.GetRegisterAt(1)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001372 DataType::Type::kReference,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001373 nullptr);
1374 if (index.IsValid()) {
1375 parallel_move.AddMove(index,
1376 LocationFrom(calling_convention.GetRegisterAt(2)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001377 DataType::Type::kInt32,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001378 nullptr);
1379 codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
1380 } else {
1381 codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
1382 __ Mov(calling_convention.GetRegisterAt(2), offset_);
1383 }
1384 arm_codegen->InvokeRuntime(kQuickReadBarrierSlow, instruction_, instruction_->GetDexPc(), this);
1385 CheckEntrypointTypes<
1386 kQuickReadBarrierSlow, mirror::Object*, mirror::Object*, mirror::Object*, uint32_t>();
1387 arm_codegen->Move32(out_, LocationFrom(r0));
1388
1389 RestoreLiveRegisters(codegen, locations);
1390 __ B(GetExitLabel());
1391 }
1392
1393 const char* GetDescription() const OVERRIDE {
1394 return "ReadBarrierForHeapReferenceSlowPathARMVIXL";
1395 }
1396
1397 private:
1398 vixl32::Register FindAvailableCallerSaveRegister(CodeGenerator* codegen) {
1399 uint32_t ref = RegisterFrom(ref_).GetCode();
1400 uint32_t obj = RegisterFrom(obj_).GetCode();
1401 for (uint32_t i = 0, e = codegen->GetNumberOfCoreRegisters(); i < e; ++i) {
1402 if (i != ref && i != obj && !codegen->IsCoreCalleeSaveRegister(i)) {
1403 return vixl32::Register(i);
1404 }
1405 }
1406 // We shall never fail to find a free caller-save register, as
1407 // there are more than two core caller-save registers on ARM
1408 // (meaning it is possible to find one which is different from
1409 // `ref` and `obj`).
1410 DCHECK_GT(codegen->GetNumberOfCoreCallerSaveRegisters(), 2u);
1411 LOG(FATAL) << "Could not find a free caller-save register";
1412 UNREACHABLE();
1413 }
1414
1415 const Location out_;
1416 const Location ref_;
1417 const Location obj_;
1418 const uint32_t offset_;
1419 // An additional location containing an index to an array.
1420 // Only used for HArrayGet and the UnsafeGetObject &
1421 // UnsafeGetObjectVolatile intrinsics.
1422 const Location index_;
1423
1424 DISALLOW_COPY_AND_ASSIGN(ReadBarrierForHeapReferenceSlowPathARMVIXL);
1425};
1426
1427// Slow path generating a read barrier for a GC root.
1428class ReadBarrierForRootSlowPathARMVIXL : public SlowPathCodeARMVIXL {
1429 public:
1430 ReadBarrierForRootSlowPathARMVIXL(HInstruction* instruction, Location out, Location root)
1431 : SlowPathCodeARMVIXL(instruction), out_(out), root_(root) {
1432 DCHECK(kEmitCompilerReadBarrier);
1433 }
1434
1435 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
1436 LocationSummary* locations = instruction_->GetLocations();
1437 vixl32::Register reg_out = RegisterFrom(out_);
1438 DCHECK(locations->CanCall());
1439 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out.GetCode()));
1440 DCHECK(instruction_->IsLoadClass() || instruction_->IsLoadString())
1441 << "Unexpected instruction in read barrier for GC root slow path: "
1442 << instruction_->DebugName();
1443
1444 __ Bind(GetEntryLabel());
1445 SaveLiveRegisters(codegen, locations);
1446
1447 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1448 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
1449 arm_codegen->Move32(LocationFrom(calling_convention.GetRegisterAt(0)), root_);
1450 arm_codegen->InvokeRuntime(kQuickReadBarrierForRootSlow,
1451 instruction_,
1452 instruction_->GetDexPc(),
1453 this);
1454 CheckEntrypointTypes<kQuickReadBarrierForRootSlow, mirror::Object*, GcRoot<mirror::Object>*>();
1455 arm_codegen->Move32(out_, LocationFrom(r0));
1456
1457 RestoreLiveRegisters(codegen, locations);
1458 __ B(GetExitLabel());
1459 }
1460
1461 const char* GetDescription() const OVERRIDE { return "ReadBarrierForRootSlowPathARMVIXL"; }
1462
1463 private:
1464 const Location out_;
1465 const Location root_;
1466
1467 DISALLOW_COPY_AND_ASSIGN(ReadBarrierForRootSlowPathARMVIXL);
1468};
Scott Wakelingc34dba72016-10-03 10:14:44 +01001469
Scott Wakelingfe885462016-09-22 10:24:38 +01001470inline vixl32::Condition ARMCondition(IfCondition cond) {
1471 switch (cond) {
1472 case kCondEQ: return eq;
1473 case kCondNE: return ne;
1474 case kCondLT: return lt;
1475 case kCondLE: return le;
1476 case kCondGT: return gt;
1477 case kCondGE: return ge;
1478 case kCondB: return lo;
1479 case kCondBE: return ls;
1480 case kCondA: return hi;
1481 case kCondAE: return hs;
1482 }
1483 LOG(FATAL) << "Unreachable";
1484 UNREACHABLE();
1485}
1486
1487// Maps signed condition to unsigned condition.
1488inline vixl32::Condition ARMUnsignedCondition(IfCondition cond) {
1489 switch (cond) {
1490 case kCondEQ: return eq;
1491 case kCondNE: return ne;
1492 // Signed to unsigned.
1493 case kCondLT: return lo;
1494 case kCondLE: return ls;
1495 case kCondGT: return hi;
1496 case kCondGE: return hs;
1497 // Unsigned remain unchanged.
1498 case kCondB: return lo;
1499 case kCondBE: return ls;
1500 case kCondA: return hi;
1501 case kCondAE: return hs;
1502 }
1503 LOG(FATAL) << "Unreachable";
1504 UNREACHABLE();
1505}
1506
1507inline vixl32::Condition ARMFPCondition(IfCondition cond, bool gt_bias) {
1508 // The ARM condition codes can express all the necessary branches, see the
1509 // "Meaning (floating-point)" column in the table A8-1 of the ARMv7 reference manual.
1510 // There is no dex instruction or HIR that would need the missing conditions
1511 // "equal or unordered" or "not equal".
1512 switch (cond) {
1513 case kCondEQ: return eq;
1514 case kCondNE: return ne /* unordered */;
1515 case kCondLT: return gt_bias ? cc : lt /* unordered */;
1516 case kCondLE: return gt_bias ? ls : le /* unordered */;
1517 case kCondGT: return gt_bias ? hi /* unordered */ : gt;
1518 case kCondGE: return gt_bias ? cs /* unordered */ : ge;
1519 default:
1520 LOG(FATAL) << "UNREACHABLE";
1521 UNREACHABLE();
1522 }
1523}
1524
Anton Kirilov74234da2017-01-13 14:42:47 +00001525inline ShiftType ShiftFromOpKind(HDataProcWithShifterOp::OpKind op_kind) {
1526 switch (op_kind) {
1527 case HDataProcWithShifterOp::kASR: return ShiftType::ASR;
1528 case HDataProcWithShifterOp::kLSL: return ShiftType::LSL;
1529 case HDataProcWithShifterOp::kLSR: return ShiftType::LSR;
1530 default:
1531 LOG(FATAL) << "Unexpected op kind " << op_kind;
1532 UNREACHABLE();
1533 }
1534}
1535
Scott Wakelingfe885462016-09-22 10:24:38 +01001536void CodeGeneratorARMVIXL::DumpCoreRegister(std::ostream& stream, int reg) const {
1537 stream << vixl32::Register(reg);
1538}
1539
1540void CodeGeneratorARMVIXL::DumpFloatingPointRegister(std::ostream& stream, int reg) const {
1541 stream << vixl32::SRegister(reg);
1542}
1543
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001544static uint32_t ComputeSRegisterListMask(const SRegisterList& regs) {
Scott Wakelingfe885462016-09-22 10:24:38 +01001545 uint32_t mask = 0;
1546 for (uint32_t i = regs.GetFirstSRegister().GetCode();
1547 i <= regs.GetLastSRegister().GetCode();
1548 ++i) {
1549 mask |= (1 << i);
1550 }
1551 return mask;
1552}
1553
Artem Serovd4cc5b22016-11-04 11:19:09 +00001554// Saves the register in the stack. Returns the size taken on stack.
1555size_t CodeGeneratorARMVIXL::SaveCoreRegister(size_t stack_index ATTRIBUTE_UNUSED,
1556 uint32_t reg_id ATTRIBUTE_UNUSED) {
1557 TODO_VIXL32(FATAL);
1558 return 0;
1559}
1560
1561// Restores the register from the stack. Returns the size taken on stack.
1562size_t CodeGeneratorARMVIXL::RestoreCoreRegister(size_t stack_index ATTRIBUTE_UNUSED,
1563 uint32_t reg_id ATTRIBUTE_UNUSED) {
1564 TODO_VIXL32(FATAL);
1565 return 0;
1566}
1567
1568size_t CodeGeneratorARMVIXL::SaveFloatingPointRegister(size_t stack_index ATTRIBUTE_UNUSED,
1569 uint32_t reg_id ATTRIBUTE_UNUSED) {
1570 TODO_VIXL32(FATAL);
1571 return 0;
1572}
1573
1574size_t CodeGeneratorARMVIXL::RestoreFloatingPointRegister(size_t stack_index ATTRIBUTE_UNUSED,
1575 uint32_t reg_id ATTRIBUTE_UNUSED) {
1576 TODO_VIXL32(FATAL);
1577 return 0;
Anton Kirilove28d9ae2016-10-25 18:17:23 +01001578}
1579
Anton Kirilov74234da2017-01-13 14:42:47 +00001580static void GenerateDataProcInstruction(HInstruction::InstructionKind kind,
1581 vixl32::Register out,
1582 vixl32::Register first,
1583 const Operand& second,
1584 CodeGeneratorARMVIXL* codegen) {
1585 if (second.IsImmediate() && second.GetImmediate() == 0) {
1586 const Operand in = kind == HInstruction::kAnd
1587 ? Operand(0)
1588 : Operand(first);
1589
1590 __ Mov(out, in);
1591 } else {
1592 switch (kind) {
1593 case HInstruction::kAdd:
1594 __ Add(out, first, second);
1595 break;
1596 case HInstruction::kAnd:
1597 __ And(out, first, second);
1598 break;
1599 case HInstruction::kOr:
1600 __ Orr(out, first, second);
1601 break;
1602 case HInstruction::kSub:
1603 __ Sub(out, first, second);
1604 break;
1605 case HInstruction::kXor:
1606 __ Eor(out, first, second);
1607 break;
1608 default:
1609 LOG(FATAL) << "Unexpected instruction kind: " << kind;
1610 UNREACHABLE();
1611 }
1612 }
1613}
1614
1615static void GenerateDataProc(HInstruction::InstructionKind kind,
1616 const Location& out,
1617 const Location& first,
1618 const Operand& second_lo,
1619 const Operand& second_hi,
1620 CodeGeneratorARMVIXL* codegen) {
1621 const vixl32::Register first_hi = HighRegisterFrom(first);
1622 const vixl32::Register first_lo = LowRegisterFrom(first);
1623 const vixl32::Register out_hi = HighRegisterFrom(out);
1624 const vixl32::Register out_lo = LowRegisterFrom(out);
1625
1626 if (kind == HInstruction::kAdd) {
1627 __ Adds(out_lo, first_lo, second_lo);
1628 __ Adc(out_hi, first_hi, second_hi);
1629 } else if (kind == HInstruction::kSub) {
1630 __ Subs(out_lo, first_lo, second_lo);
1631 __ Sbc(out_hi, first_hi, second_hi);
1632 } else {
1633 GenerateDataProcInstruction(kind, out_lo, first_lo, second_lo, codegen);
1634 GenerateDataProcInstruction(kind, out_hi, first_hi, second_hi, codegen);
1635 }
1636}
1637
1638static Operand GetShifterOperand(vixl32::Register rm, ShiftType shift, uint32_t shift_imm) {
1639 return shift_imm == 0 ? Operand(rm) : Operand(rm, shift, shift_imm);
1640}
1641
1642static void GenerateLongDataProc(HDataProcWithShifterOp* instruction,
1643 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001644 DCHECK_EQ(instruction->GetType(), DataType::Type::kInt64);
Anton Kirilov74234da2017-01-13 14:42:47 +00001645 DCHECK(HDataProcWithShifterOp::IsShiftOp(instruction->GetOpKind()));
1646
1647 const LocationSummary* const locations = instruction->GetLocations();
1648 const uint32_t shift_value = instruction->GetShiftAmount();
1649 const HInstruction::InstructionKind kind = instruction->GetInstrKind();
1650 const Location first = locations->InAt(0);
1651 const Location second = locations->InAt(1);
1652 const Location out = locations->Out();
1653 const vixl32::Register first_hi = HighRegisterFrom(first);
1654 const vixl32::Register first_lo = LowRegisterFrom(first);
1655 const vixl32::Register out_hi = HighRegisterFrom(out);
1656 const vixl32::Register out_lo = LowRegisterFrom(out);
1657 const vixl32::Register second_hi = HighRegisterFrom(second);
1658 const vixl32::Register second_lo = LowRegisterFrom(second);
1659 const ShiftType shift = ShiftFromOpKind(instruction->GetOpKind());
1660
1661 if (shift_value >= 32) {
1662 if (shift == ShiftType::LSL) {
1663 GenerateDataProcInstruction(kind,
1664 out_hi,
1665 first_hi,
1666 Operand(second_lo, ShiftType::LSL, shift_value - 32),
1667 codegen);
1668 GenerateDataProcInstruction(kind, out_lo, first_lo, 0, codegen);
1669 } else if (shift == ShiftType::ASR) {
1670 GenerateDataProc(kind,
1671 out,
1672 first,
1673 GetShifterOperand(second_hi, ShiftType::ASR, shift_value - 32),
1674 Operand(second_hi, ShiftType::ASR, 31),
1675 codegen);
1676 } else {
1677 DCHECK_EQ(shift, ShiftType::LSR);
1678 GenerateDataProc(kind,
1679 out,
1680 first,
1681 GetShifterOperand(second_hi, ShiftType::LSR, shift_value - 32),
1682 0,
1683 codegen);
1684 }
1685 } else {
1686 DCHECK_GT(shift_value, 1U);
1687 DCHECK_LT(shift_value, 32U);
1688
1689 UseScratchRegisterScope temps(codegen->GetVIXLAssembler());
1690
1691 if (shift == ShiftType::LSL) {
1692 // We are not doing this for HInstruction::kAdd because the output will require
1693 // Location::kOutputOverlap; not applicable to other cases.
1694 if (kind == HInstruction::kOr || kind == HInstruction::kXor) {
1695 GenerateDataProcInstruction(kind,
1696 out_hi,
1697 first_hi,
1698 Operand(second_hi, ShiftType::LSL, shift_value),
1699 codegen);
1700 GenerateDataProcInstruction(kind,
1701 out_hi,
1702 out_hi,
1703 Operand(second_lo, ShiftType::LSR, 32 - shift_value),
1704 codegen);
1705 GenerateDataProcInstruction(kind,
1706 out_lo,
1707 first_lo,
1708 Operand(second_lo, ShiftType::LSL, shift_value),
1709 codegen);
1710 } else {
1711 const vixl32::Register temp = temps.Acquire();
1712
1713 __ Lsl(temp, second_hi, shift_value);
1714 __ Orr(temp, temp, Operand(second_lo, ShiftType::LSR, 32 - shift_value));
1715 GenerateDataProc(kind,
1716 out,
1717 first,
1718 Operand(second_lo, ShiftType::LSL, shift_value),
1719 temp,
1720 codegen);
1721 }
1722 } else {
1723 DCHECK(shift == ShiftType::ASR || shift == ShiftType::LSR);
1724
1725 // We are not doing this for HInstruction::kAdd because the output will require
1726 // Location::kOutputOverlap; not applicable to other cases.
1727 if (kind == HInstruction::kOr || kind == HInstruction::kXor) {
1728 GenerateDataProcInstruction(kind,
1729 out_lo,
1730 first_lo,
1731 Operand(second_lo, ShiftType::LSR, shift_value),
1732 codegen);
1733 GenerateDataProcInstruction(kind,
1734 out_lo,
1735 out_lo,
1736 Operand(second_hi, ShiftType::LSL, 32 - shift_value),
1737 codegen);
1738 GenerateDataProcInstruction(kind,
1739 out_hi,
1740 first_hi,
1741 Operand(second_hi, shift, shift_value),
1742 codegen);
1743 } else {
1744 const vixl32::Register temp = temps.Acquire();
1745
1746 __ Lsr(temp, second_lo, shift_value);
1747 __ Orr(temp, temp, Operand(second_hi, ShiftType::LSL, 32 - shift_value));
1748 GenerateDataProc(kind,
1749 out,
1750 first,
1751 temp,
1752 Operand(second_hi, shift, shift_value),
1753 codegen);
1754 }
1755 }
1756 }
1757}
1758
Donghui Bai426b49c2016-11-08 14:55:38 +08001759static void GenerateVcmp(HInstruction* instruction, CodeGeneratorARMVIXL* codegen) {
1760 const Location rhs_loc = instruction->GetLocations()->InAt(1);
1761 if (rhs_loc.IsConstant()) {
1762 // 0.0 is the only immediate that can be encoded directly in
1763 // a VCMP instruction.
1764 //
1765 // Both the JLS (section 15.20.1) and the JVMS (section 6.5)
1766 // specify that in a floating-point comparison, positive zero
1767 // and negative zero are considered equal, so we can use the
1768 // literal 0.0 for both cases here.
1769 //
1770 // Note however that some methods (Float.equal, Float.compare,
1771 // Float.compareTo, Double.equal, Double.compare,
1772 // Double.compareTo, Math.max, Math.min, StrictMath.max,
1773 // StrictMath.min) consider 0.0 to be (strictly) greater than
1774 // -0.0. So if we ever translate calls to these methods into a
1775 // HCompare instruction, we must handle the -0.0 case with
1776 // care here.
1777 DCHECK(rhs_loc.GetConstant()->IsArithmeticZero());
1778
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001779 const DataType::Type type = instruction->InputAt(0)->GetType();
Donghui Bai426b49c2016-11-08 14:55:38 +08001780
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001781 if (type == DataType::Type::kFloat32) {
Donghui Bai426b49c2016-11-08 14:55:38 +08001782 __ Vcmp(F32, InputSRegisterAt(instruction, 0), 0.0);
1783 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001784 DCHECK_EQ(type, DataType::Type::kFloat64);
Donghui Bai426b49c2016-11-08 14:55:38 +08001785 __ Vcmp(F64, InputDRegisterAt(instruction, 0), 0.0);
1786 }
1787 } else {
1788 __ Vcmp(InputVRegisterAt(instruction, 0), InputVRegisterAt(instruction, 1));
1789 }
1790}
1791
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001792static int64_t AdjustConstantForCondition(int64_t value,
1793 IfCondition* condition,
1794 IfCondition* opposite) {
1795 if (value == 1) {
1796 if (*condition == kCondB) {
1797 value = 0;
1798 *condition = kCondEQ;
1799 *opposite = kCondNE;
1800 } else if (*condition == kCondAE) {
1801 value = 0;
1802 *condition = kCondNE;
1803 *opposite = kCondEQ;
1804 }
1805 } else if (value == -1) {
1806 if (*condition == kCondGT) {
1807 value = 0;
1808 *condition = kCondGE;
1809 *opposite = kCondLT;
1810 } else if (*condition == kCondLE) {
1811 value = 0;
1812 *condition = kCondLT;
1813 *opposite = kCondGE;
1814 }
1815 }
1816
1817 return value;
1818}
1819
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001820static std::pair<vixl32::Condition, vixl32::Condition> GenerateLongTestConstant(
1821 HCondition* condition,
1822 bool invert,
1823 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001824 DCHECK_EQ(condition->GetLeft()->GetType(), DataType::Type::kInt64);
Donghui Bai426b49c2016-11-08 14:55:38 +08001825
1826 const LocationSummary* const locations = condition->GetLocations();
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001827 IfCondition cond = condition->GetCondition();
1828 IfCondition opposite = condition->GetOppositeCondition();
1829
1830 if (invert) {
1831 std::swap(cond, opposite);
1832 }
1833
1834 std::pair<vixl32::Condition, vixl32::Condition> ret(eq, ne);
Donghui Bai426b49c2016-11-08 14:55:38 +08001835 const Location left = locations->InAt(0);
1836 const Location right = locations->InAt(1);
1837
1838 DCHECK(right.IsConstant());
1839
1840 const vixl32::Register left_high = HighRegisterFrom(left);
1841 const vixl32::Register left_low = LowRegisterFrom(left);
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001842 int64_t value = AdjustConstantForCondition(Int64ConstantFrom(right), &cond, &opposite);
1843 UseScratchRegisterScope temps(codegen->GetVIXLAssembler());
1844
1845 // Comparisons against 0 are common enough to deserve special attention.
1846 if (value == 0) {
1847 switch (cond) {
1848 case kCondNE:
1849 // x > 0 iff x != 0 when the comparison is unsigned.
1850 case kCondA:
1851 ret = std::make_pair(ne, eq);
1852 FALLTHROUGH_INTENDED;
1853 case kCondEQ:
1854 // x <= 0 iff x == 0 when the comparison is unsigned.
1855 case kCondBE:
1856 __ Orrs(temps.Acquire(), left_low, left_high);
1857 return ret;
1858 case kCondLT:
1859 case kCondGE:
1860 __ Cmp(left_high, 0);
1861 return std::make_pair(ARMCondition(cond), ARMCondition(opposite));
1862 // Trivially true or false.
1863 case kCondB:
1864 ret = std::make_pair(ne, eq);
1865 FALLTHROUGH_INTENDED;
1866 case kCondAE:
1867 __ Cmp(left_low, left_low);
1868 return ret;
1869 default:
1870 break;
1871 }
1872 }
Donghui Bai426b49c2016-11-08 14:55:38 +08001873
1874 switch (cond) {
1875 case kCondEQ:
1876 case kCondNE:
1877 case kCondB:
1878 case kCondBE:
1879 case kCondA:
1880 case kCondAE: {
Anton Kirilov23b752b2017-07-20 14:40:44 +01001881 const uint32_t value_low = Low32Bits(value);
1882 Operand operand_low(value_low);
1883
Donghui Bai426b49c2016-11-08 14:55:38 +08001884 __ Cmp(left_high, High32Bits(value));
1885
Anton Kirilov23b752b2017-07-20 14:40:44 +01001886 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
1887 // we must ensure that the operands corresponding to the least significant
1888 // halves of the inputs fit into a 16-bit CMP encoding.
1889 if (!left_low.IsLow() || !IsUint<8>(value_low)) {
1890 operand_low = Operand(temps.Acquire());
1891 __ Mov(LeaveFlags, operand_low.GetBaseRegister(), value_low);
1892 }
1893
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001894 // We use the scope because of the IT block that follows.
Donghui Bai426b49c2016-11-08 14:55:38 +08001895 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
1896 2 * vixl32::k16BitT32InstructionSizeInBytes,
1897 CodeBufferCheckScope::kExactSize);
1898
1899 __ it(eq);
Anton Kirilov23b752b2017-07-20 14:40:44 +01001900 __ cmp(eq, left_low, operand_low);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001901 ret = std::make_pair(ARMUnsignedCondition(cond), ARMUnsignedCondition(opposite));
Donghui Bai426b49c2016-11-08 14:55:38 +08001902 break;
1903 }
1904 case kCondLE:
1905 case kCondGT:
1906 // Trivially true or false.
1907 if (value == std::numeric_limits<int64_t>::max()) {
1908 __ Cmp(left_low, left_low);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001909 ret = cond == kCondLE ? std::make_pair(eq, ne) : std::make_pair(ne, eq);
Donghui Bai426b49c2016-11-08 14:55:38 +08001910 break;
1911 }
1912
1913 if (cond == kCondLE) {
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001914 DCHECK_EQ(opposite, kCondGT);
Donghui Bai426b49c2016-11-08 14:55:38 +08001915 cond = kCondLT;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001916 opposite = kCondGE;
Donghui Bai426b49c2016-11-08 14:55:38 +08001917 } else {
1918 DCHECK_EQ(cond, kCondGT);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001919 DCHECK_EQ(opposite, kCondLE);
Donghui Bai426b49c2016-11-08 14:55:38 +08001920 cond = kCondGE;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001921 opposite = kCondLT;
Donghui Bai426b49c2016-11-08 14:55:38 +08001922 }
1923
1924 value++;
1925 FALLTHROUGH_INTENDED;
1926 case kCondGE:
1927 case kCondLT: {
Donghui Bai426b49c2016-11-08 14:55:38 +08001928 __ Cmp(left_low, Low32Bits(value));
1929 __ Sbcs(temps.Acquire(), left_high, High32Bits(value));
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001930 ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite));
Donghui Bai426b49c2016-11-08 14:55:38 +08001931 break;
1932 }
1933 default:
1934 LOG(FATAL) << "Unreachable";
1935 UNREACHABLE();
1936 }
1937
1938 return ret;
1939}
1940
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001941static std::pair<vixl32::Condition, vixl32::Condition> GenerateLongTest(
1942 HCondition* condition,
1943 bool invert,
1944 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001945 DCHECK_EQ(condition->GetLeft()->GetType(), DataType::Type::kInt64);
Donghui Bai426b49c2016-11-08 14:55:38 +08001946
1947 const LocationSummary* const locations = condition->GetLocations();
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001948 IfCondition cond = condition->GetCondition();
1949 IfCondition opposite = condition->GetOppositeCondition();
1950
1951 if (invert) {
1952 std::swap(cond, opposite);
1953 }
1954
1955 std::pair<vixl32::Condition, vixl32::Condition> ret(eq, ne);
Donghui Bai426b49c2016-11-08 14:55:38 +08001956 Location left = locations->InAt(0);
1957 Location right = locations->InAt(1);
1958
1959 DCHECK(right.IsRegisterPair());
1960
1961 switch (cond) {
1962 case kCondEQ:
1963 case kCondNE:
1964 case kCondB:
1965 case kCondBE:
1966 case kCondA:
1967 case kCondAE: {
1968 __ Cmp(HighRegisterFrom(left), HighRegisterFrom(right));
1969
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001970 // We use the scope because of the IT block that follows.
Donghui Bai426b49c2016-11-08 14:55:38 +08001971 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
1972 2 * vixl32::k16BitT32InstructionSizeInBytes,
1973 CodeBufferCheckScope::kExactSize);
1974
1975 __ it(eq);
1976 __ cmp(eq, LowRegisterFrom(left), LowRegisterFrom(right));
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001977 ret = std::make_pair(ARMUnsignedCondition(cond), ARMUnsignedCondition(opposite));
Donghui Bai426b49c2016-11-08 14:55:38 +08001978 break;
1979 }
1980 case kCondLE:
1981 case kCondGT:
1982 if (cond == kCondLE) {
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001983 DCHECK_EQ(opposite, kCondGT);
Donghui Bai426b49c2016-11-08 14:55:38 +08001984 cond = kCondGE;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001985 opposite = kCondLT;
Donghui Bai426b49c2016-11-08 14:55:38 +08001986 } else {
1987 DCHECK_EQ(cond, kCondGT);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001988 DCHECK_EQ(opposite, kCondLE);
Donghui Bai426b49c2016-11-08 14:55:38 +08001989 cond = kCondLT;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001990 opposite = kCondGE;
Donghui Bai426b49c2016-11-08 14:55:38 +08001991 }
1992
1993 std::swap(left, right);
1994 FALLTHROUGH_INTENDED;
1995 case kCondGE:
1996 case kCondLT: {
1997 UseScratchRegisterScope temps(codegen->GetVIXLAssembler());
1998
1999 __ Cmp(LowRegisterFrom(left), LowRegisterFrom(right));
2000 __ Sbcs(temps.Acquire(), HighRegisterFrom(left), HighRegisterFrom(right));
Anton Kirilov217b2ce2017-03-16 11:47:12 +00002001 ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite));
Donghui Bai426b49c2016-11-08 14:55:38 +08002002 break;
2003 }
2004 default:
2005 LOG(FATAL) << "Unreachable";
2006 UNREACHABLE();
2007 }
2008
2009 return ret;
2010}
2011
Anton Kirilov217b2ce2017-03-16 11:47:12 +00002012static std::pair<vixl32::Condition, vixl32::Condition> GenerateTest(HCondition* condition,
2013 bool invert,
2014 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002015 const DataType::Type type = condition->GetLeft()->GetType();
Anton Kirilov217b2ce2017-03-16 11:47:12 +00002016 IfCondition cond = condition->GetCondition();
2017 IfCondition opposite = condition->GetOppositeCondition();
2018 std::pair<vixl32::Condition, vixl32::Condition> ret(eq, ne);
Donghui Bai426b49c2016-11-08 14:55:38 +08002019
Anton Kirilov217b2ce2017-03-16 11:47:12 +00002020 if (invert) {
2021 std::swap(cond, opposite);
2022 }
Donghui Bai426b49c2016-11-08 14:55:38 +08002023
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002024 if (type == DataType::Type::kInt64) {
Anton Kirilov217b2ce2017-03-16 11:47:12 +00002025 ret = condition->GetLocations()->InAt(1).IsConstant()
2026 ? GenerateLongTestConstant(condition, invert, codegen)
2027 : GenerateLongTest(condition, invert, codegen);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002028 } else if (DataType::IsFloatingPointType(type)) {
Anton Kirilov217b2ce2017-03-16 11:47:12 +00002029 GenerateVcmp(condition, codegen);
2030 __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
2031 ret = std::make_pair(ARMFPCondition(cond, condition->IsGtBias()),
2032 ARMFPCondition(opposite, condition->IsGtBias()));
Donghui Bai426b49c2016-11-08 14:55:38 +08002033 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002034 DCHECK(DataType::IsIntegralType(type) || type == DataType::Type::kReference) << type;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00002035 __ Cmp(InputRegisterAt(condition, 0), InputOperandAt(condition, 1));
2036 ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite));
Donghui Bai426b49c2016-11-08 14:55:38 +08002037 }
2038
2039 return ret;
2040}
2041
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002042static void GenerateConditionGeneric(HCondition* cond, CodeGeneratorARMVIXL* codegen) {
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002043 const vixl32::Register out = OutputRegister(cond);
2044 const auto condition = GenerateTest(cond, false, codegen);
2045
2046 __ Mov(LeaveFlags, out, 0);
2047
2048 if (out.IsLow()) {
2049 // We use the scope because of the IT block that follows.
2050 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
2051 2 * vixl32::k16BitT32InstructionSizeInBytes,
2052 CodeBufferCheckScope::kExactSize);
2053
2054 __ it(condition.first);
2055 __ mov(condition.first, out, 1);
2056 } else {
2057 vixl32::Label done_label;
2058 vixl32::Label* const final_label = codegen->GetFinalLabel(cond, &done_label);
2059
2060 __ B(condition.second, final_label, /* far_target */ false);
2061 __ Mov(out, 1);
2062
2063 if (done_label.IsReferenced()) {
2064 __ Bind(&done_label);
2065 }
2066 }
2067}
2068
2069static void GenerateEqualLong(HCondition* cond, CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002070 DCHECK_EQ(cond->GetLeft()->GetType(), DataType::Type::kInt64);
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002071
2072 const LocationSummary* const locations = cond->GetLocations();
2073 IfCondition condition = cond->GetCondition();
2074 const vixl32::Register out = OutputRegister(cond);
2075 const Location left = locations->InAt(0);
2076 const Location right = locations->InAt(1);
2077 vixl32::Register left_high = HighRegisterFrom(left);
2078 vixl32::Register left_low = LowRegisterFrom(left);
2079 vixl32::Register temp;
2080 UseScratchRegisterScope temps(codegen->GetVIXLAssembler());
2081
2082 if (right.IsConstant()) {
2083 IfCondition opposite = cond->GetOppositeCondition();
2084 const int64_t value = AdjustConstantForCondition(Int64ConstantFrom(right),
2085 &condition,
2086 &opposite);
2087 Operand right_high = High32Bits(value);
2088 Operand right_low = Low32Bits(value);
2089
2090 // The output uses Location::kNoOutputOverlap.
2091 if (out.Is(left_high)) {
2092 std::swap(left_low, left_high);
2093 std::swap(right_low, right_high);
2094 }
2095
2096 __ Sub(out, left_low, right_low);
2097 temp = temps.Acquire();
2098 __ Sub(temp, left_high, right_high);
2099 } else {
2100 DCHECK(right.IsRegisterPair());
2101 temp = temps.Acquire();
2102 __ Sub(temp, left_high, HighRegisterFrom(right));
2103 __ Sub(out, left_low, LowRegisterFrom(right));
2104 }
2105
2106 // Need to check after calling AdjustConstantForCondition().
2107 DCHECK(condition == kCondEQ || condition == kCondNE) << condition;
2108
2109 if (condition == kCondNE && out.IsLow()) {
2110 __ Orrs(out, out, temp);
2111
2112 // We use the scope because of the IT block that follows.
2113 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
2114 2 * vixl32::k16BitT32InstructionSizeInBytes,
2115 CodeBufferCheckScope::kExactSize);
2116
2117 __ it(ne);
2118 __ mov(ne, out, 1);
2119 } else {
2120 __ Orr(out, out, temp);
2121 codegen->GenerateConditionWithZero(condition, out, out, temp);
2122 }
2123}
2124
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002125static void GenerateConditionLong(HCondition* cond, CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002126 DCHECK_EQ(cond->GetLeft()->GetType(), DataType::Type::kInt64);
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002127
2128 const LocationSummary* const locations = cond->GetLocations();
2129 IfCondition condition = cond->GetCondition();
2130 const vixl32::Register out = OutputRegister(cond);
2131 const Location left = locations->InAt(0);
2132 const Location right = locations->InAt(1);
2133
2134 if (right.IsConstant()) {
2135 IfCondition opposite = cond->GetOppositeCondition();
2136
2137 // Comparisons against 0 are common enough to deserve special attention.
2138 if (AdjustConstantForCondition(Int64ConstantFrom(right), &condition, &opposite) == 0) {
2139 switch (condition) {
2140 case kCondNE:
2141 case kCondA:
2142 if (out.IsLow()) {
2143 // We only care if both input registers are 0 or not.
2144 __ Orrs(out, LowRegisterFrom(left), HighRegisterFrom(left));
2145
2146 // We use the scope because of the IT block that follows.
2147 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
2148 2 * vixl32::k16BitT32InstructionSizeInBytes,
2149 CodeBufferCheckScope::kExactSize);
2150
2151 __ it(ne);
2152 __ mov(ne, out, 1);
2153 return;
2154 }
2155
2156 FALLTHROUGH_INTENDED;
2157 case kCondEQ:
2158 case kCondBE:
2159 // We only care if both input registers are 0 or not.
2160 __ Orr(out, LowRegisterFrom(left), HighRegisterFrom(left));
2161 codegen->GenerateConditionWithZero(condition, out, out);
2162 return;
2163 case kCondLT:
2164 case kCondGE:
2165 // We only care about the sign bit.
2166 FALLTHROUGH_INTENDED;
2167 case kCondAE:
2168 case kCondB:
2169 codegen->GenerateConditionWithZero(condition, out, HighRegisterFrom(left));
2170 return;
2171 case kCondLE:
2172 case kCondGT:
2173 default:
2174 break;
2175 }
2176 }
2177 }
2178
Anton Kirilov23b752b2017-07-20 14:40:44 +01002179 // If `out` is a low register, then the GenerateConditionGeneric()
2180 // function generates a shorter code sequence that is still branchless.
2181 if ((condition == kCondEQ || condition == kCondNE) && !out.IsLow()) {
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002182 GenerateEqualLong(cond, codegen);
2183 return;
2184 }
2185
Anton Kirilov23b752b2017-07-20 14:40:44 +01002186 GenerateConditionGeneric(cond, codegen);
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002187}
2188
Roland Levillain6d729a72017-06-30 18:34:01 +01002189static void GenerateConditionIntegralOrNonPrimitive(HCondition* cond,
2190 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002191 const DataType::Type type = cond->GetLeft()->GetType();
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002192
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002193 DCHECK(DataType::IsIntegralType(type) || type == DataType::Type::kReference) << type;
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002194
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002195 if (type == DataType::Type::kInt64) {
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002196 GenerateConditionLong(cond, codegen);
2197 return;
2198 }
2199
2200 IfCondition condition = cond->GetCondition();
2201 vixl32::Register in = InputRegisterAt(cond, 0);
2202 const vixl32::Register out = OutputRegister(cond);
2203 const Location right = cond->GetLocations()->InAt(1);
2204 int64_t value;
2205
2206 if (right.IsConstant()) {
2207 IfCondition opposite = cond->GetOppositeCondition();
2208
2209 value = AdjustConstantForCondition(Int64ConstantFrom(right), &condition, &opposite);
2210
2211 // Comparisons against 0 are common enough to deserve special attention.
2212 if (value == 0) {
2213 switch (condition) {
2214 case kCondNE:
2215 case kCondA:
2216 if (out.IsLow() && out.Is(in)) {
2217 __ Cmp(out, 0);
2218
2219 // We use the scope because of the IT block that follows.
2220 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
2221 2 * vixl32::k16BitT32InstructionSizeInBytes,
2222 CodeBufferCheckScope::kExactSize);
2223
2224 __ it(ne);
2225 __ mov(ne, out, 1);
2226 return;
2227 }
2228
2229 FALLTHROUGH_INTENDED;
2230 case kCondEQ:
2231 case kCondBE:
2232 case kCondLT:
2233 case kCondGE:
2234 case kCondAE:
2235 case kCondB:
2236 codegen->GenerateConditionWithZero(condition, out, in);
2237 return;
2238 case kCondLE:
2239 case kCondGT:
2240 default:
2241 break;
2242 }
2243 }
2244 }
2245
2246 if (condition == kCondEQ || condition == kCondNE) {
2247 Operand operand(0);
2248
2249 if (right.IsConstant()) {
2250 operand = Operand::From(value);
2251 } else if (out.Is(RegisterFrom(right))) {
2252 // Avoid 32-bit instructions if possible.
2253 operand = InputOperandAt(cond, 0);
2254 in = RegisterFrom(right);
2255 } else {
2256 operand = InputOperandAt(cond, 1);
2257 }
2258
2259 if (condition == kCondNE && out.IsLow()) {
2260 __ Subs(out, in, operand);
2261
2262 // We use the scope because of the IT block that follows.
2263 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
2264 2 * vixl32::k16BitT32InstructionSizeInBytes,
2265 CodeBufferCheckScope::kExactSize);
2266
2267 __ it(ne);
2268 __ mov(ne, out, 1);
2269 } else {
2270 __ Sub(out, in, operand);
2271 codegen->GenerateConditionWithZero(condition, out, out);
2272 }
2273
2274 return;
2275 }
2276
2277 GenerateConditionGeneric(cond, codegen);
2278}
2279
Donghui Bai426b49c2016-11-08 14:55:38 +08002280static bool CanEncodeConstantAs8BitImmediate(HConstant* constant) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002281 const DataType::Type type = constant->GetType();
Donghui Bai426b49c2016-11-08 14:55:38 +08002282 bool ret = false;
2283
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002284 DCHECK(DataType::IsIntegralType(type) || type == DataType::Type::kReference) << type;
Donghui Bai426b49c2016-11-08 14:55:38 +08002285
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002286 if (type == DataType::Type::kInt64) {
Donghui Bai426b49c2016-11-08 14:55:38 +08002287 const uint64_t value = Uint64ConstantFrom(constant);
2288
2289 ret = IsUint<8>(Low32Bits(value)) && IsUint<8>(High32Bits(value));
2290 } else {
2291 ret = IsUint<8>(Int32ConstantFrom(constant));
2292 }
2293
2294 return ret;
2295}
2296
2297static Location Arm8BitEncodableConstantOrRegister(HInstruction* constant) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002298 DCHECK(!DataType::IsFloatingPointType(constant->GetType()));
Donghui Bai426b49c2016-11-08 14:55:38 +08002299
2300 if (constant->IsConstant() && CanEncodeConstantAs8BitImmediate(constant->AsConstant())) {
2301 return Location::ConstantLocation(constant->AsConstant());
2302 }
2303
2304 return Location::RequiresRegister();
2305}
2306
2307static bool CanGenerateConditionalMove(const Location& out, const Location& src) {
2308 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
2309 // we check that we are not dealing with floating-point output (there is no
2310 // 16-bit VMOV encoding).
2311 if (!out.IsRegister() && !out.IsRegisterPair()) {
2312 return false;
2313 }
2314
2315 // For constants, we also check that the output is in one or two low registers,
2316 // and that the constants fit in an 8-bit unsigned integer, so that a 16-bit
2317 // MOV encoding can be used.
2318 if (src.IsConstant()) {
2319 if (!CanEncodeConstantAs8BitImmediate(src.GetConstant())) {
2320 return false;
2321 }
2322
2323 if (out.IsRegister()) {
2324 if (!RegisterFrom(out).IsLow()) {
2325 return false;
2326 }
2327 } else {
2328 DCHECK(out.IsRegisterPair());
2329
2330 if (!HighRegisterFrom(out).IsLow()) {
2331 return false;
2332 }
2333 }
2334 }
2335
2336 return true;
2337}
2338
Scott Wakelingfe885462016-09-22 10:24:38 +01002339#undef __
2340
Donghui Bai426b49c2016-11-08 14:55:38 +08002341vixl32::Label* CodeGeneratorARMVIXL::GetFinalLabel(HInstruction* instruction,
2342 vixl32::Label* final_label) {
2343 DCHECK(!instruction->IsControlFlow() && !instruction->IsSuspendCheck());
Anton Kirilov6f644202017-02-27 18:29:45 +00002344 DCHECK(!instruction->IsInvoke() || !instruction->GetLocations()->CanCall());
Donghui Bai426b49c2016-11-08 14:55:38 +08002345
2346 const HBasicBlock* const block = instruction->GetBlock();
2347 const HLoopInformation* const info = block->GetLoopInformation();
2348 HInstruction* const next = instruction->GetNext();
2349
2350 // Avoid a branch to a branch.
2351 if (next->IsGoto() && (info == nullptr ||
2352 !info->IsBackEdge(*block) ||
2353 !info->HasSuspendCheck())) {
2354 final_label = GetLabelOf(next->AsGoto()->GetSuccessor());
2355 }
2356
2357 return final_label;
2358}
2359
Scott Wakelingfe885462016-09-22 10:24:38 +01002360CodeGeneratorARMVIXL::CodeGeneratorARMVIXL(HGraph* graph,
2361 const ArmInstructionSetFeatures& isa_features,
2362 const CompilerOptions& compiler_options,
2363 OptimizingCompilerStats* stats)
2364 : CodeGenerator(graph,
2365 kNumberOfCoreRegisters,
2366 kNumberOfSRegisters,
2367 kNumberOfRegisterPairs,
2368 kCoreCalleeSaves.GetList(),
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002369 ComputeSRegisterListMask(kFpuCalleeSaves),
Scott Wakelingfe885462016-09-22 10:24:38 +01002370 compiler_options,
2371 stats),
2372 block_labels_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
Artem Serov551b28f2016-10-18 19:11:30 +01002373 jump_tables_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
Scott Wakelingfe885462016-09-22 10:24:38 +01002374 location_builder_(graph, this),
2375 instruction_visitor_(graph, this),
2376 move_resolver_(graph->GetArena(), this),
2377 assembler_(graph->GetArena()),
Artem Serovd4cc5b22016-11-04 11:19:09 +00002378 isa_features_(isa_features),
Artem Serovc5fcb442016-12-02 19:19:58 +00002379 uint32_literals_(std::less<uint32_t>(),
2380 graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Marko65979462017-05-19 17:25:12 +01002381 pc_relative_method_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Marko0eb882b2017-05-15 13:39:18 +01002382 method_bss_entry_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
Artem Serovc5fcb442016-12-02 19:19:58 +00002383 pc_relative_type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Marko1998cd02017-01-13 13:02:58 +00002384 type_bss_entry_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Marko65979462017-05-19 17:25:12 +01002385 pc_relative_string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01002386 string_bss_entry_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01002387 baker_read_barrier_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
Artem Serovc5fcb442016-12-02 19:19:58 +00002388 jit_string_patches_(StringReferenceValueComparator(),
2389 graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
2390 jit_class_patches_(TypeReferenceValueComparator(),
2391 graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
Scott Wakelingfe885462016-09-22 10:24:38 +01002392 // Always save the LR register to mimic Quick.
2393 AddAllocatedRegister(Location::RegisterLocation(LR));
Nicolas Geoffray13a797b2017-03-15 16:41:31 +00002394 // Give D30 and D31 as scratch register to VIXL. The register allocator only works on
2395 // S0-S31, which alias to D0-D15.
2396 GetVIXLAssembler()->GetScratchVRegisterList()->Combine(d31);
2397 GetVIXLAssembler()->GetScratchVRegisterList()->Combine(d30);
Scott Wakelingfe885462016-09-22 10:24:38 +01002398}
2399
Artem Serov551b28f2016-10-18 19:11:30 +01002400void JumpTableARMVIXL::EmitTable(CodeGeneratorARMVIXL* codegen) {
2401 uint32_t num_entries = switch_instr_->GetNumEntries();
2402 DCHECK_GE(num_entries, kPackedSwitchCompareJumpThreshold);
2403
2404 // We are about to use the assembler to place literals directly. Make sure we have enough
Scott Wakelingb77051e2016-11-21 19:46:00 +00002405 // underlying code buffer and we have generated a jump table of the right size, using
2406 // codegen->GetVIXLAssembler()->GetBuffer().Align();
Artem Serov0fb37192016-12-06 18:13:40 +00002407 ExactAssemblyScope aas(codegen->GetVIXLAssembler(),
2408 num_entries * sizeof(int32_t),
2409 CodeBufferCheckScope::kMaximumSize);
Artem Serov551b28f2016-10-18 19:11:30 +01002410 // TODO(VIXL): Check that using lower case bind is fine here.
2411 codegen->GetVIXLAssembler()->bind(&table_start_);
Artem Serov09a940d2016-11-11 16:15:11 +00002412 for (uint32_t i = 0; i < num_entries; i++) {
2413 codegen->GetVIXLAssembler()->place(bb_addresses_[i].get());
2414 }
2415}
2416
2417void JumpTableARMVIXL::FixTable(CodeGeneratorARMVIXL* codegen) {
2418 uint32_t num_entries = switch_instr_->GetNumEntries();
2419 DCHECK_GE(num_entries, kPackedSwitchCompareJumpThreshold);
2420
Artem Serov551b28f2016-10-18 19:11:30 +01002421 const ArenaVector<HBasicBlock*>& successors = switch_instr_->GetBlock()->GetSuccessors();
2422 for (uint32_t i = 0; i < num_entries; i++) {
2423 vixl32::Label* target_label = codegen->GetLabelOf(successors[i]);
2424 DCHECK(target_label->IsBound());
2425 int32_t jump_offset = target_label->GetLocation() - table_start_.GetLocation();
2426 // When doing BX to address we need to have lower bit set to 1 in T32.
2427 if (codegen->GetVIXLAssembler()->IsUsingT32()) {
2428 jump_offset++;
2429 }
2430 DCHECK_GT(jump_offset, std::numeric_limits<int32_t>::min());
2431 DCHECK_LE(jump_offset, std::numeric_limits<int32_t>::max());
Artem Serov09a940d2016-11-11 16:15:11 +00002432
Scott Wakelingb77051e2016-11-21 19:46:00 +00002433 bb_addresses_[i].get()->UpdateValue(jump_offset, codegen->GetVIXLAssembler()->GetBuffer());
Artem Serov551b28f2016-10-18 19:11:30 +01002434 }
2435}
2436
Artem Serov09a940d2016-11-11 16:15:11 +00002437void CodeGeneratorARMVIXL::FixJumpTables() {
Artem Serov551b28f2016-10-18 19:11:30 +01002438 for (auto&& jump_table : jump_tables_) {
Artem Serov09a940d2016-11-11 16:15:11 +00002439 jump_table->FixTable(this);
Artem Serov551b28f2016-10-18 19:11:30 +01002440 }
2441}
2442
Andreas Gampeca620d72016-11-08 08:09:33 -08002443#define __ reinterpret_cast<ArmVIXLAssembler*>(GetAssembler())->GetVIXLAssembler()-> // NOLINT
Scott Wakelingfe885462016-09-22 10:24:38 +01002444
2445void CodeGeneratorARMVIXL::Finalize(CodeAllocator* allocator) {
Artem Serov09a940d2016-11-11 16:15:11 +00002446 FixJumpTables();
Scott Wakelingfe885462016-09-22 10:24:38 +01002447 GetAssembler()->FinalizeCode();
2448 CodeGenerator::Finalize(allocator);
2449}
2450
2451void CodeGeneratorARMVIXL::SetupBlockedRegisters() const {
Scott Wakelingfe885462016-09-22 10:24:38 +01002452 // Stack register, LR and PC are always reserved.
2453 blocked_core_registers_[SP] = true;
2454 blocked_core_registers_[LR] = true;
2455 blocked_core_registers_[PC] = true;
2456
Roland Levillain6d729a72017-06-30 18:34:01 +01002457 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2458 // Reserve marking register.
2459 blocked_core_registers_[MR] = true;
2460 }
2461
Scott Wakelingfe885462016-09-22 10:24:38 +01002462 // Reserve thread register.
2463 blocked_core_registers_[TR] = true;
2464
2465 // Reserve temp register.
2466 blocked_core_registers_[IP] = true;
2467
2468 if (GetGraph()->IsDebuggable()) {
2469 // Stubs do not save callee-save floating point registers. If the graph
2470 // is debuggable, we need to deal with these registers differently. For
2471 // now, just block them.
2472 for (uint32_t i = kFpuCalleeSaves.GetFirstSRegister().GetCode();
2473 i <= kFpuCalleeSaves.GetLastSRegister().GetCode();
2474 ++i) {
2475 blocked_fpu_registers_[i] = true;
2476 }
2477 }
Scott Wakelingfe885462016-09-22 10:24:38 +01002478}
2479
Scott Wakelingfe885462016-09-22 10:24:38 +01002480InstructionCodeGeneratorARMVIXL::InstructionCodeGeneratorARMVIXL(HGraph* graph,
2481 CodeGeneratorARMVIXL* codegen)
2482 : InstructionCodeGenerator(graph, codegen),
2483 assembler_(codegen->GetAssembler()),
2484 codegen_(codegen) {}
2485
2486void CodeGeneratorARMVIXL::ComputeSpillMask() {
2487 core_spill_mask_ = allocated_registers_.GetCoreRegisters() & core_callee_save_mask_;
2488 DCHECK_NE(core_spill_mask_, 0u) << "At least the return address register must be saved";
2489 // There is no easy instruction to restore just the PC on thumb2. We spill and
2490 // restore another arbitrary register.
2491 core_spill_mask_ |= (1 << kCoreAlwaysSpillRegister.GetCode());
2492 fpu_spill_mask_ = allocated_registers_.GetFloatingPointRegisters() & fpu_callee_save_mask_;
2493 // We use vpush and vpop for saving and restoring floating point registers, which take
2494 // a SRegister and the number of registers to save/restore after that SRegister. We
2495 // therefore update the `fpu_spill_mask_` to also contain those registers not allocated,
2496 // but in the range.
2497 if (fpu_spill_mask_ != 0) {
2498 uint32_t least_significant_bit = LeastSignificantBit(fpu_spill_mask_);
2499 uint32_t most_significant_bit = MostSignificantBit(fpu_spill_mask_);
2500 for (uint32_t i = least_significant_bit + 1 ; i < most_significant_bit; ++i) {
2501 fpu_spill_mask_ |= (1 << i);
2502 }
2503 }
2504}
2505
2506void CodeGeneratorARMVIXL::GenerateFrameEntry() {
2507 bool skip_overflow_check =
2508 IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kArm);
2509 DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks());
2510 __ Bind(&frame_entry_label_);
2511
2512 if (HasEmptyFrame()) {
2513 return;
2514 }
2515
Scott Wakelingfe885462016-09-22 10:24:38 +01002516 if (!skip_overflow_check) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002517 UseScratchRegisterScope temps(GetVIXLAssembler());
2518 vixl32::Register temp = temps.Acquire();
Anton Kirilov644032c2016-12-06 17:51:43 +00002519 __ Sub(temp, sp, Operand::From(GetStackOverflowReservedBytes(kArm)));
Scott Wakelingfe885462016-09-22 10:24:38 +01002520 // The load must immediately precede RecordPcInfo.
Artem Serov0fb37192016-12-06 18:13:40 +00002521 ExactAssemblyScope aas(GetVIXLAssembler(),
2522 vixl32::kMaxInstructionSizeInBytes,
2523 CodeBufferCheckScope::kMaximumSize);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002524 __ ldr(temp, MemOperand(temp));
2525 RecordPcInfo(nullptr, 0);
Scott Wakelingfe885462016-09-22 10:24:38 +01002526 }
2527
2528 __ Push(RegisterList(core_spill_mask_));
2529 GetAssembler()->cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(core_spill_mask_));
2530 GetAssembler()->cfi().RelOffsetForMany(DWARFReg(kMethodRegister),
2531 0,
2532 core_spill_mask_,
2533 kArmWordSize);
2534 if (fpu_spill_mask_ != 0) {
2535 uint32_t first = LeastSignificantBit(fpu_spill_mask_);
2536
2537 // Check that list is contiguous.
2538 DCHECK_EQ(fpu_spill_mask_ >> CTZ(fpu_spill_mask_), ~0u >> (32 - POPCOUNT(fpu_spill_mask_)));
2539
2540 __ Vpush(SRegisterList(vixl32::SRegister(first), POPCOUNT(fpu_spill_mask_)));
2541 GetAssembler()->cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(fpu_spill_mask_));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002542 GetAssembler()->cfi().RelOffsetForMany(DWARFReg(s0), 0, fpu_spill_mask_, kArmWordSize);
Scott Wakelingfe885462016-09-22 10:24:38 +01002543 }
Scott Wakelingbffdc702016-12-07 17:46:03 +00002544
Scott Wakelingfe885462016-09-22 10:24:38 +01002545 int adjust = GetFrameSize() - FrameEntrySpillSize();
2546 __ Sub(sp, sp, adjust);
2547 GetAssembler()->cfi().AdjustCFAOffset(adjust);
Scott Wakelingbffdc702016-12-07 17:46:03 +00002548
2549 // Save the current method if we need it. Note that we do not
2550 // do this in HCurrentMethod, as the instruction might have been removed
2551 // in the SSA graph.
2552 if (RequiresCurrentMethod()) {
2553 GetAssembler()->StoreToOffset(kStoreWord, kMethodRegister, sp, 0);
2554 }
Nicolas Geoffrayf7893532017-06-15 12:34:36 +01002555
2556 if (GetGraph()->HasShouldDeoptimizeFlag()) {
2557 UseScratchRegisterScope temps(GetVIXLAssembler());
2558 vixl32::Register temp = temps.Acquire();
2559 // Initialize should_deoptimize flag to 0.
2560 __ Mov(temp, 0);
2561 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, GetStackOffsetOfShouldDeoptimizeFlag());
2562 }
Roland Levillain5daa4952017-07-03 17:23:56 +01002563
2564 MaybeGenerateMarkingRegisterCheck(/* code */ 1);
Scott Wakelingfe885462016-09-22 10:24:38 +01002565}
2566
2567void CodeGeneratorARMVIXL::GenerateFrameExit() {
2568 if (HasEmptyFrame()) {
2569 __ Bx(lr);
2570 return;
2571 }
2572 GetAssembler()->cfi().RememberState();
2573 int adjust = GetFrameSize() - FrameEntrySpillSize();
2574 __ Add(sp, sp, adjust);
2575 GetAssembler()->cfi().AdjustCFAOffset(-adjust);
2576 if (fpu_spill_mask_ != 0) {
2577 uint32_t first = LeastSignificantBit(fpu_spill_mask_);
2578
2579 // Check that list is contiguous.
2580 DCHECK_EQ(fpu_spill_mask_ >> CTZ(fpu_spill_mask_), ~0u >> (32 - POPCOUNT(fpu_spill_mask_)));
2581
2582 __ Vpop(SRegisterList(vixl32::SRegister(first), POPCOUNT(fpu_spill_mask_)));
2583 GetAssembler()->cfi().AdjustCFAOffset(
2584 -static_cast<int>(kArmWordSize) * POPCOUNT(fpu_spill_mask_));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002585 GetAssembler()->cfi().RestoreMany(DWARFReg(vixl32::SRegister(0)), fpu_spill_mask_);
Scott Wakelingfe885462016-09-22 10:24:38 +01002586 }
2587 // Pop LR into PC to return.
2588 DCHECK_NE(core_spill_mask_ & (1 << kLrCode), 0U);
2589 uint32_t pop_mask = (core_spill_mask_ & (~(1 << kLrCode))) | 1 << kPcCode;
2590 __ Pop(RegisterList(pop_mask));
2591 GetAssembler()->cfi().RestoreState();
2592 GetAssembler()->cfi().DefCFAOffset(GetFrameSize());
2593}
2594
2595void CodeGeneratorARMVIXL::Bind(HBasicBlock* block) {
2596 __ Bind(GetLabelOf(block));
2597}
2598
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002599Location InvokeDexCallingConventionVisitorARMVIXL::GetNextLocation(DataType::Type type) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002600 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002601 case DataType::Type::kBool:
2602 case DataType::Type::kInt8:
2603 case DataType::Type::kUint16:
2604 case DataType::Type::kInt16:
2605 case DataType::Type::kInt32:
2606 case DataType::Type::kReference: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002607 uint32_t index = gp_index_++;
2608 uint32_t stack_index = stack_index_++;
2609 if (index < calling_convention.GetNumberOfRegisters()) {
2610 return LocationFrom(calling_convention.GetRegisterAt(index));
2611 } else {
2612 return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index));
2613 }
2614 }
2615
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002616 case DataType::Type::kInt64: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002617 uint32_t index = gp_index_;
2618 uint32_t stack_index = stack_index_;
2619 gp_index_ += 2;
2620 stack_index_ += 2;
2621 if (index + 1 < calling_convention.GetNumberOfRegisters()) {
2622 if (calling_convention.GetRegisterAt(index).Is(r1)) {
2623 // Skip R1, and use R2_R3 instead.
2624 gp_index_++;
2625 index++;
2626 }
2627 }
2628 if (index + 1 < calling_convention.GetNumberOfRegisters()) {
2629 DCHECK_EQ(calling_convention.GetRegisterAt(index).GetCode() + 1,
2630 calling_convention.GetRegisterAt(index + 1).GetCode());
2631
2632 return LocationFrom(calling_convention.GetRegisterAt(index),
2633 calling_convention.GetRegisterAt(index + 1));
2634 } else {
2635 return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index));
2636 }
2637 }
2638
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002639 case DataType::Type::kFloat32: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002640 uint32_t stack_index = stack_index_++;
2641 if (float_index_ % 2 == 0) {
2642 float_index_ = std::max(double_index_, float_index_);
2643 }
2644 if (float_index_ < calling_convention.GetNumberOfFpuRegisters()) {
2645 return LocationFrom(calling_convention.GetFpuRegisterAt(float_index_++));
2646 } else {
2647 return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index));
2648 }
2649 }
2650
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002651 case DataType::Type::kFloat64: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002652 double_index_ = std::max(double_index_, RoundUp(float_index_, 2));
2653 uint32_t stack_index = stack_index_;
2654 stack_index_ += 2;
2655 if (double_index_ + 1 < calling_convention.GetNumberOfFpuRegisters()) {
2656 uint32_t index = double_index_;
2657 double_index_ += 2;
2658 Location result = LocationFrom(
2659 calling_convention.GetFpuRegisterAt(index),
2660 calling_convention.GetFpuRegisterAt(index + 1));
2661 DCHECK(ExpectedPairLayout(result));
2662 return result;
2663 } else {
2664 return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index));
2665 }
2666 }
2667
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002668 case DataType::Type::kVoid:
Artem Serovd4cc5b22016-11-04 11:19:09 +00002669 LOG(FATAL) << "Unexpected parameter type " << type;
2670 break;
2671 }
2672 return Location::NoLocation();
2673}
2674
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002675Location InvokeDexCallingConventionVisitorARMVIXL::GetReturnLocation(DataType::Type type) const {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002676 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002677 case DataType::Type::kBool:
2678 case DataType::Type::kInt8:
2679 case DataType::Type::kUint16:
2680 case DataType::Type::kInt16:
2681 case DataType::Type::kInt32:
2682 case DataType::Type::kReference: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002683 return LocationFrom(r0);
2684 }
2685
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002686 case DataType::Type::kFloat32: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002687 return LocationFrom(s0);
2688 }
2689
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002690 case DataType::Type::kInt64: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002691 return LocationFrom(r0, r1);
2692 }
2693
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002694 case DataType::Type::kFloat64: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002695 return LocationFrom(s0, s1);
2696 }
2697
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002698 case DataType::Type::kVoid:
Artem Serovd4cc5b22016-11-04 11:19:09 +00002699 return Location::NoLocation();
2700 }
2701
2702 UNREACHABLE();
2703}
2704
2705Location InvokeDexCallingConventionVisitorARMVIXL::GetMethodLocation() const {
2706 return LocationFrom(kMethodRegister);
2707}
2708
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002709void CodeGeneratorARMVIXL::Move32(Location destination, Location source) {
2710 if (source.Equals(destination)) {
2711 return;
2712 }
2713 if (destination.IsRegister()) {
2714 if (source.IsRegister()) {
2715 __ Mov(RegisterFrom(destination), RegisterFrom(source));
2716 } else if (source.IsFpuRegister()) {
2717 __ Vmov(RegisterFrom(destination), SRegisterFrom(source));
2718 } else {
2719 GetAssembler()->LoadFromOffset(kLoadWord,
2720 RegisterFrom(destination),
2721 sp,
2722 source.GetStackIndex());
2723 }
2724 } else if (destination.IsFpuRegister()) {
2725 if (source.IsRegister()) {
2726 __ Vmov(SRegisterFrom(destination), RegisterFrom(source));
2727 } else if (source.IsFpuRegister()) {
2728 __ Vmov(SRegisterFrom(destination), SRegisterFrom(source));
2729 } else {
2730 GetAssembler()->LoadSFromOffset(SRegisterFrom(destination), sp, source.GetStackIndex());
2731 }
2732 } else {
2733 DCHECK(destination.IsStackSlot()) << destination;
2734 if (source.IsRegister()) {
2735 GetAssembler()->StoreToOffset(kStoreWord,
2736 RegisterFrom(source),
2737 sp,
2738 destination.GetStackIndex());
2739 } else if (source.IsFpuRegister()) {
2740 GetAssembler()->StoreSToOffset(SRegisterFrom(source), sp, destination.GetStackIndex());
2741 } else {
2742 DCHECK(source.IsStackSlot()) << source;
2743 UseScratchRegisterScope temps(GetVIXLAssembler());
2744 vixl32::Register temp = temps.Acquire();
2745 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, source.GetStackIndex());
2746 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
2747 }
2748 }
2749}
2750
Artem Serovcfbe9132016-10-14 15:58:56 +01002751void CodeGeneratorARMVIXL::MoveConstant(Location location, int32_t value) {
2752 DCHECK(location.IsRegister());
2753 __ Mov(RegisterFrom(location), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01002754}
2755
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002756void CodeGeneratorARMVIXL::MoveLocation(Location dst, Location src, DataType::Type dst_type) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002757 // TODO(VIXL): Maybe refactor to have the 'move' implementation here and use it in
2758 // `ParallelMoveResolverARMVIXL::EmitMove`, as is done in the `arm64` backend.
2759 HParallelMove move(GetGraph()->GetArena());
2760 move.AddMove(src, dst, dst_type, nullptr);
2761 GetMoveResolver()->EmitNativeCode(&move);
Scott Wakelingfe885462016-09-22 10:24:38 +01002762}
2763
Artem Serovcfbe9132016-10-14 15:58:56 +01002764void CodeGeneratorARMVIXL::AddLocationAsTemp(Location location, LocationSummary* locations) {
2765 if (location.IsRegister()) {
2766 locations->AddTemp(location);
2767 } else if (location.IsRegisterPair()) {
2768 locations->AddTemp(LocationFrom(LowRegisterFrom(location)));
2769 locations->AddTemp(LocationFrom(HighRegisterFrom(location)));
2770 } else {
2771 UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location;
2772 }
Scott Wakelingfe885462016-09-22 10:24:38 +01002773}
2774
2775void CodeGeneratorARMVIXL::InvokeRuntime(QuickEntrypointEnum entrypoint,
2776 HInstruction* instruction,
2777 uint32_t dex_pc,
2778 SlowPathCode* slow_path) {
2779 ValidateInvokeRuntime(entrypoint, instruction, slow_path);
Alexandre Rames374ddf32016-11-04 10:40:49 +00002780 __ Ldr(lr, MemOperand(tr, GetThreadOffset<kArmPointerSize>(entrypoint).Int32Value()));
2781 // Ensure the pc position is recorded immediately after the `blx` instruction.
2782 // 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 +00002783 ExactAssemblyScope aas(GetVIXLAssembler(),
2784 vixl32::k16BitT32InstructionSizeInBytes,
2785 CodeBufferCheckScope::kExactSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00002786 __ blx(lr);
Scott Wakelingfe885462016-09-22 10:24:38 +01002787 if (EntrypointRequiresStackMap(entrypoint)) {
2788 RecordPcInfo(instruction, dex_pc, slow_path);
2789 }
2790}
2791
2792void CodeGeneratorARMVIXL::InvokeRuntimeWithoutRecordingPcInfo(int32_t entry_point_offset,
2793 HInstruction* instruction,
2794 SlowPathCode* slow_path) {
2795 ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction, slow_path);
Alexandre Rames374ddf32016-11-04 10:40:49 +00002796 __ Ldr(lr, MemOperand(tr, entry_point_offset));
Scott Wakelingfe885462016-09-22 10:24:38 +01002797 __ Blx(lr);
2798}
2799
Scott Wakelingfe885462016-09-22 10:24:38 +01002800void InstructionCodeGeneratorARMVIXL::HandleGoto(HInstruction* got, HBasicBlock* successor) {
2801 DCHECK(!successor->IsExitBlock());
2802 HBasicBlock* block = got->GetBlock();
2803 HInstruction* previous = got->GetPrevious();
2804 HLoopInformation* info = block->GetLoopInformation();
2805
2806 if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) {
2807 codegen_->ClearSpillSlotsFromLoopPhisInStackMap(info->GetSuspendCheck());
2808 GenerateSuspendCheck(info->GetSuspendCheck(), successor);
2809 return;
2810 }
2811 if (block->IsEntryBlock() && (previous != nullptr) && previous->IsSuspendCheck()) {
2812 GenerateSuspendCheck(previous->AsSuspendCheck(), nullptr);
Roland Levillain5daa4952017-07-03 17:23:56 +01002813 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 2);
Scott Wakelingfe885462016-09-22 10:24:38 +01002814 }
2815 if (!codegen_->GoesToNextBlock(block, successor)) {
2816 __ B(codegen_->GetLabelOf(successor));
2817 }
2818}
2819
2820void LocationsBuilderARMVIXL::VisitGoto(HGoto* got) {
2821 got->SetLocations(nullptr);
2822}
2823
2824void InstructionCodeGeneratorARMVIXL::VisitGoto(HGoto* got) {
2825 HandleGoto(got, got->GetSuccessor());
2826}
2827
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002828void LocationsBuilderARMVIXL::VisitTryBoundary(HTryBoundary* try_boundary) {
2829 try_boundary->SetLocations(nullptr);
2830}
2831
2832void InstructionCodeGeneratorARMVIXL::VisitTryBoundary(HTryBoundary* try_boundary) {
2833 HBasicBlock* successor = try_boundary->GetNormalFlowSuccessor();
2834 if (!successor->IsExitBlock()) {
2835 HandleGoto(try_boundary, successor);
2836 }
2837}
2838
Scott Wakelingfe885462016-09-22 10:24:38 +01002839void LocationsBuilderARMVIXL::VisitExit(HExit* exit) {
2840 exit->SetLocations(nullptr);
2841}
2842
2843void InstructionCodeGeneratorARMVIXL::VisitExit(HExit* exit ATTRIBUTE_UNUSED) {
2844}
2845
Scott Wakelingfe885462016-09-22 10:24:38 +01002846void InstructionCodeGeneratorARMVIXL::GenerateCompareTestAndBranch(HCondition* condition,
Anton Kirilov23b752b2017-07-20 14:40:44 +01002847 vixl32::Label* true_target,
2848 vixl32::Label* false_target,
Anton Kirilovfd522532017-05-10 12:46:57 +01002849 bool is_far_target) {
Anton Kirilov23b752b2017-07-20 14:40:44 +01002850 if (true_target == false_target) {
2851 DCHECK(true_target != nullptr);
2852 __ B(true_target);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00002853 return;
2854 }
2855
Anton Kirilov23b752b2017-07-20 14:40:44 +01002856 vixl32::Label* non_fallthrough_target;
2857 bool invert;
2858 bool emit_both_branches;
Scott Wakelingfe885462016-09-22 10:24:38 +01002859
Anton Kirilov23b752b2017-07-20 14:40:44 +01002860 if (true_target == nullptr) {
2861 // The true target is fallthrough.
2862 DCHECK(false_target != nullptr);
2863 non_fallthrough_target = false_target;
2864 invert = true;
2865 emit_both_branches = false;
2866 } else {
2867 non_fallthrough_target = true_target;
2868 invert = false;
2869 // Either the false target is fallthrough, or there is no fallthrough
2870 // and both branches must be emitted.
2871 emit_both_branches = (false_target != nullptr);
Scott Wakelingfe885462016-09-22 10:24:38 +01002872 }
2873
Anton Kirilov23b752b2017-07-20 14:40:44 +01002874 const auto cond = GenerateTest(condition, invert, codegen_);
2875
2876 __ B(cond.first, non_fallthrough_target, is_far_target);
2877
2878 if (emit_both_branches) {
2879 // No target falls through, we need to branch.
2880 __ B(false_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01002881 }
2882}
2883
2884void InstructionCodeGeneratorARMVIXL::GenerateTestAndBranch(HInstruction* instruction,
2885 size_t condition_input_index,
2886 vixl32::Label* true_target,
xueliang.zhongf51bc622016-11-04 09:23:32 +00002887 vixl32::Label* false_target,
2888 bool far_target) {
Scott Wakelingfe885462016-09-22 10:24:38 +01002889 HInstruction* cond = instruction->InputAt(condition_input_index);
2890
2891 if (true_target == nullptr && false_target == nullptr) {
2892 // Nothing to do. The code always falls through.
2893 return;
2894 } else if (cond->IsIntConstant()) {
2895 // Constant condition, statically compared against "true" (integer value 1).
2896 if (cond->AsIntConstant()->IsTrue()) {
2897 if (true_target != nullptr) {
2898 __ B(true_target);
2899 }
2900 } else {
Anton Kirilov644032c2016-12-06 17:51:43 +00002901 DCHECK(cond->AsIntConstant()->IsFalse()) << Int32ConstantFrom(cond);
Scott Wakelingfe885462016-09-22 10:24:38 +01002902 if (false_target != nullptr) {
2903 __ B(false_target);
2904 }
2905 }
2906 return;
2907 }
2908
2909 // The following code generates these patterns:
2910 // (1) true_target == nullptr && false_target != nullptr
2911 // - opposite condition true => branch to false_target
2912 // (2) true_target != nullptr && false_target == nullptr
2913 // - condition true => branch to true_target
2914 // (3) true_target != nullptr && false_target != nullptr
2915 // - condition true => branch to true_target
2916 // - branch to false_target
2917 if (IsBooleanValueOrMaterializedCondition(cond)) {
2918 // Condition has been materialized, compare the output to 0.
2919 if (kIsDebugBuild) {
2920 Location cond_val = instruction->GetLocations()->InAt(condition_input_index);
2921 DCHECK(cond_val.IsRegister());
2922 }
2923 if (true_target == nullptr) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00002924 __ CompareAndBranchIfZero(InputRegisterAt(instruction, condition_input_index),
2925 false_target,
2926 far_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01002927 } else {
xueliang.zhongf51bc622016-11-04 09:23:32 +00002928 __ CompareAndBranchIfNonZero(InputRegisterAt(instruction, condition_input_index),
2929 true_target,
2930 far_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01002931 }
2932 } else {
2933 // Condition has not been materialized. Use its inputs as the comparison and
2934 // its condition as the branch condition.
2935 HCondition* condition = cond->AsCondition();
2936
2937 // If this is a long or FP comparison that has been folded into
2938 // the HCondition, generate the comparison directly.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002939 DataType::Type type = condition->InputAt(0)->GetType();
2940 if (type == DataType::Type::kInt64 || DataType::IsFloatingPointType(type)) {
Anton Kirilovfd522532017-05-10 12:46:57 +01002941 GenerateCompareTestAndBranch(condition, true_target, false_target, far_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01002942 return;
2943 }
2944
Donghui Bai426b49c2016-11-08 14:55:38 +08002945 vixl32::Label* non_fallthrough_target;
2946 vixl32::Condition arm_cond = vixl32::Condition::None();
2947 const vixl32::Register left = InputRegisterAt(cond, 0);
2948 const Operand right = InputOperandAt(cond, 1);
2949
Scott Wakelingfe885462016-09-22 10:24:38 +01002950 if (true_target == nullptr) {
Donghui Bai426b49c2016-11-08 14:55:38 +08002951 arm_cond = ARMCondition(condition->GetOppositeCondition());
2952 non_fallthrough_target = false_target;
Scott Wakelingfe885462016-09-22 10:24:38 +01002953 } else {
Donghui Bai426b49c2016-11-08 14:55:38 +08002954 arm_cond = ARMCondition(condition->GetCondition());
2955 non_fallthrough_target = true_target;
2956 }
2957
2958 if (right.IsImmediate() && right.GetImmediate() == 0 && (arm_cond.Is(ne) || arm_cond.Is(eq))) {
2959 if (arm_cond.Is(eq)) {
Anton Kirilovfd522532017-05-10 12:46:57 +01002960 __ CompareAndBranchIfZero(left, non_fallthrough_target, far_target);
Donghui Bai426b49c2016-11-08 14:55:38 +08002961 } else {
2962 DCHECK(arm_cond.Is(ne));
Anton Kirilovfd522532017-05-10 12:46:57 +01002963 __ CompareAndBranchIfNonZero(left, non_fallthrough_target, far_target);
Donghui Bai426b49c2016-11-08 14:55:38 +08002964 }
2965 } else {
2966 __ Cmp(left, right);
Anton Kirilovfd522532017-05-10 12:46:57 +01002967 __ B(arm_cond, non_fallthrough_target, far_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01002968 }
2969 }
2970
2971 // If neither branch falls through (case 3), the conditional branch to `true_target`
2972 // was already emitted (case 2) and we need to emit a jump to `false_target`.
2973 if (true_target != nullptr && false_target != nullptr) {
2974 __ B(false_target);
2975 }
2976}
2977
2978void LocationsBuilderARMVIXL::VisitIf(HIf* if_instr) {
2979 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr);
2980 if (IsBooleanValueOrMaterializedCondition(if_instr->InputAt(0))) {
2981 locations->SetInAt(0, Location::RequiresRegister());
2982 }
2983}
2984
2985void InstructionCodeGeneratorARMVIXL::VisitIf(HIf* if_instr) {
2986 HBasicBlock* true_successor = if_instr->IfTrueSuccessor();
2987 HBasicBlock* false_successor = if_instr->IfFalseSuccessor();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002988 vixl32::Label* true_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), true_successor) ?
2989 nullptr : codegen_->GetLabelOf(true_successor);
2990 vixl32::Label* false_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), false_successor) ?
2991 nullptr : codegen_->GetLabelOf(false_successor);
Scott Wakelingfe885462016-09-22 10:24:38 +01002992 GenerateTestAndBranch(if_instr, /* condition_input_index */ 0, true_target, false_target);
2993}
2994
Scott Wakelingc34dba72016-10-03 10:14:44 +01002995void LocationsBuilderARMVIXL::VisitDeoptimize(HDeoptimize* deoptimize) {
2996 LocationSummary* locations = new (GetGraph()->GetArena())
2997 LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath);
Nicolas Geoffray4e92c3c2017-05-08 09:34:26 +01002998 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2999 RegisterSet caller_saves = RegisterSet::Empty();
3000 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0)));
3001 locations->SetCustomSlowPathCallerSaves(caller_saves);
Scott Wakelingc34dba72016-10-03 10:14:44 +01003002 if (IsBooleanValueOrMaterializedCondition(deoptimize->InputAt(0))) {
3003 locations->SetInAt(0, Location::RequiresRegister());
3004 }
3005}
3006
3007void InstructionCodeGeneratorARMVIXL::VisitDeoptimize(HDeoptimize* deoptimize) {
3008 SlowPathCodeARMVIXL* slow_path =
3009 deopt_slow_paths_.NewSlowPath<DeoptimizationSlowPathARMVIXL>(deoptimize);
3010 GenerateTestAndBranch(deoptimize,
3011 /* condition_input_index */ 0,
3012 slow_path->GetEntryLabel(),
3013 /* false_target */ nullptr);
3014}
3015
Artem Serovd4cc5b22016-11-04 11:19:09 +00003016void LocationsBuilderARMVIXL::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
3017 LocationSummary* locations = new (GetGraph()->GetArena())
3018 LocationSummary(flag, LocationSummary::kNoCall);
3019 locations->SetOut(Location::RequiresRegister());
3020}
3021
3022void InstructionCodeGeneratorARMVIXL::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
3023 GetAssembler()->LoadFromOffset(kLoadWord,
3024 OutputRegister(flag),
3025 sp,
3026 codegen_->GetStackOffsetOfShouldDeoptimizeFlag());
3027}
3028
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003029void LocationsBuilderARMVIXL::VisitSelect(HSelect* select) {
3030 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003031 const bool is_floating_point = DataType::IsFloatingPointType(select->GetType());
Donghui Bai426b49c2016-11-08 14:55:38 +08003032
3033 if (is_floating_point) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003034 locations->SetInAt(0, Location::RequiresFpuRegister());
Donghui Bai426b49c2016-11-08 14:55:38 +08003035 locations->SetInAt(1, Location::FpuRegisterOrConstant(select->GetTrueValue()));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003036 } else {
3037 locations->SetInAt(0, Location::RequiresRegister());
Donghui Bai426b49c2016-11-08 14:55:38 +08003038 locations->SetInAt(1, Arm8BitEncodableConstantOrRegister(select->GetTrueValue()));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003039 }
Donghui Bai426b49c2016-11-08 14:55:38 +08003040
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003041 if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) {
Donghui Bai426b49c2016-11-08 14:55:38 +08003042 locations->SetInAt(2, Location::RegisterOrConstant(select->GetCondition()));
3043 // The code generator handles overlap with the values, but not with the condition.
3044 locations->SetOut(Location::SameAsFirstInput());
3045 } else if (is_floating_point) {
3046 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3047 } else {
3048 if (!locations->InAt(1).IsConstant()) {
3049 locations->SetInAt(0, Arm8BitEncodableConstantOrRegister(select->GetFalseValue()));
3050 }
3051
3052 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003053 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003054}
3055
3056void InstructionCodeGeneratorARMVIXL::VisitSelect(HSelect* select) {
Donghui Bai426b49c2016-11-08 14:55:38 +08003057 HInstruction* const condition = select->GetCondition();
3058 const LocationSummary* const locations = select->GetLocations();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003059 const DataType::Type type = select->GetType();
Donghui Bai426b49c2016-11-08 14:55:38 +08003060 const Location first = locations->InAt(0);
3061 const Location out = locations->Out();
3062 const Location second = locations->InAt(1);
3063 Location src;
3064
3065 if (condition->IsIntConstant()) {
3066 if (condition->AsIntConstant()->IsFalse()) {
3067 src = first;
3068 } else {
3069 src = second;
3070 }
3071
3072 codegen_->MoveLocation(out, src, type);
3073 return;
3074 }
3075
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003076 if (!DataType::IsFloatingPointType(type)) {
Donghui Bai426b49c2016-11-08 14:55:38 +08003077 bool invert = false;
3078
3079 if (out.Equals(second)) {
3080 src = first;
3081 invert = true;
3082 } else if (out.Equals(first)) {
3083 src = second;
3084 } else if (second.IsConstant()) {
3085 DCHECK(CanEncodeConstantAs8BitImmediate(second.GetConstant()));
3086 src = second;
3087 } else if (first.IsConstant()) {
3088 DCHECK(CanEncodeConstantAs8BitImmediate(first.GetConstant()));
3089 src = first;
3090 invert = true;
3091 } else {
3092 src = second;
3093 }
3094
3095 if (CanGenerateConditionalMove(out, src)) {
3096 if (!out.Equals(first) && !out.Equals(second)) {
3097 codegen_->MoveLocation(out, src.Equals(first) ? second : first, type);
3098 }
3099
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003100 std::pair<vixl32::Condition, vixl32::Condition> cond(eq, ne);
3101
3102 if (IsBooleanValueOrMaterializedCondition(condition)) {
3103 __ Cmp(InputRegisterAt(select, 2), 0);
3104 cond = invert ? std::make_pair(eq, ne) : std::make_pair(ne, eq);
3105 } else {
3106 cond = GenerateTest(condition->AsCondition(), invert, codegen_);
3107 }
3108
Donghui Bai426b49c2016-11-08 14:55:38 +08003109 const size_t instr_count = out.IsRegisterPair() ? 4 : 2;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003110 // We use the scope because of the IT block that follows.
Donghui Bai426b49c2016-11-08 14:55:38 +08003111 ExactAssemblyScope guard(GetVIXLAssembler(),
3112 instr_count * vixl32::k16BitT32InstructionSizeInBytes,
3113 CodeBufferCheckScope::kExactSize);
3114
3115 if (out.IsRegister()) {
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003116 __ it(cond.first);
3117 __ mov(cond.first, RegisterFrom(out), OperandFrom(src, type));
Donghui Bai426b49c2016-11-08 14:55:38 +08003118 } else {
3119 DCHECK(out.IsRegisterPair());
3120
3121 Operand operand_high(0);
3122 Operand operand_low(0);
3123
3124 if (src.IsConstant()) {
3125 const int64_t value = Int64ConstantFrom(src);
3126
3127 operand_high = High32Bits(value);
3128 operand_low = Low32Bits(value);
3129 } else {
3130 DCHECK(src.IsRegisterPair());
3131 operand_high = HighRegisterFrom(src);
3132 operand_low = LowRegisterFrom(src);
3133 }
3134
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003135 __ it(cond.first);
3136 __ mov(cond.first, LowRegisterFrom(out), operand_low);
3137 __ it(cond.first);
3138 __ mov(cond.first, HighRegisterFrom(out), operand_high);
Donghui Bai426b49c2016-11-08 14:55:38 +08003139 }
3140
3141 return;
3142 }
3143 }
3144
3145 vixl32::Label* false_target = nullptr;
3146 vixl32::Label* true_target = nullptr;
3147 vixl32::Label select_end;
3148 vixl32::Label* const target = codegen_->GetFinalLabel(select, &select_end);
3149
3150 if (out.Equals(second)) {
3151 true_target = target;
3152 src = first;
3153 } else {
3154 false_target = target;
3155 src = second;
3156
3157 if (!out.Equals(first)) {
3158 codegen_->MoveLocation(out, first, type);
3159 }
3160 }
3161
3162 GenerateTestAndBranch(select, 2, true_target, false_target, /* far_target */ false);
3163 codegen_->MoveLocation(out, src, type);
3164
3165 if (select_end.IsReferenced()) {
3166 __ Bind(&select_end);
3167 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003168}
3169
Artem Serov551b28f2016-10-18 19:11:30 +01003170void LocationsBuilderARMVIXL::VisitNativeDebugInfo(HNativeDebugInfo* info) {
3171 new (GetGraph()->GetArena()) LocationSummary(info);
3172}
3173
3174void InstructionCodeGeneratorARMVIXL::VisitNativeDebugInfo(HNativeDebugInfo*) {
3175 // MaybeRecordNativeDebugInfo is already called implicitly in CodeGenerator::Compile.
3176}
3177
Scott Wakelingfe885462016-09-22 10:24:38 +01003178void CodeGeneratorARMVIXL::GenerateNop() {
3179 __ Nop();
3180}
3181
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003182// `temp` is an extra temporary register that is used for some conditions;
3183// callers may not specify it, in which case the method will use a scratch
3184// register instead.
3185void CodeGeneratorARMVIXL::GenerateConditionWithZero(IfCondition condition,
3186 vixl32::Register out,
3187 vixl32::Register in,
3188 vixl32::Register temp) {
3189 switch (condition) {
3190 case kCondEQ:
3191 // x <= 0 iff x == 0 when the comparison is unsigned.
3192 case kCondBE:
3193 if (!temp.IsValid() || (out.IsLow() && !out.Is(in))) {
3194 temp = out;
3195 }
3196
3197 // Avoid 32-bit instructions if possible; note that `in` and `temp` must be
3198 // different as well.
3199 if (in.IsLow() && temp.IsLow() && !in.Is(temp)) {
3200 // temp = - in; only 0 sets the carry flag.
3201 __ Rsbs(temp, in, 0);
3202
3203 if (out.Is(in)) {
3204 std::swap(in, temp);
3205 }
3206
3207 // out = - in + in + carry = carry
3208 __ Adc(out, temp, in);
3209 } else {
3210 // If `in` is 0, then it has 32 leading zeros, and less than that otherwise.
3211 __ Clz(out, in);
3212 // Any number less than 32 logically shifted right by 5 bits results in 0;
3213 // the same operation on 32 yields 1.
3214 __ Lsr(out, out, 5);
3215 }
3216
3217 break;
3218 case kCondNE:
3219 // x > 0 iff x != 0 when the comparison is unsigned.
3220 case kCondA: {
3221 UseScratchRegisterScope temps(GetVIXLAssembler());
3222
3223 if (out.Is(in)) {
3224 if (!temp.IsValid() || in.Is(temp)) {
3225 temp = temps.Acquire();
3226 }
3227 } else if (!temp.IsValid() || !temp.IsLow()) {
3228 temp = out;
3229 }
3230
3231 // temp = in - 1; only 0 does not set the carry flag.
3232 __ Subs(temp, in, 1);
3233 // out = in + ~temp + carry = in + (-(in - 1) - 1) + carry = in - in + 1 - 1 + carry = carry
3234 __ Sbc(out, in, temp);
3235 break;
3236 }
3237 case kCondGE:
3238 __ Mvn(out, in);
3239 in = out;
3240 FALLTHROUGH_INTENDED;
3241 case kCondLT:
3242 // We only care about the sign bit.
3243 __ Lsr(out, in, 31);
3244 break;
3245 case kCondAE:
3246 // Trivially true.
3247 __ Mov(out, 1);
3248 break;
3249 case kCondB:
3250 // Trivially false.
3251 __ Mov(out, 0);
3252 break;
3253 default:
3254 LOG(FATAL) << "Unexpected condition " << condition;
3255 UNREACHABLE();
3256 }
3257}
3258
Scott Wakelingfe885462016-09-22 10:24:38 +01003259void LocationsBuilderARMVIXL::HandleCondition(HCondition* cond) {
3260 LocationSummary* locations =
3261 new (GetGraph()->GetArena()) LocationSummary(cond, LocationSummary::kNoCall);
3262 // Handle the long/FP comparisons made in instruction simplification.
3263 switch (cond->InputAt(0)->GetType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003264 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003265 locations->SetInAt(0, Location::RequiresRegister());
3266 locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
3267 if (!cond->IsEmittedAtUseSite()) {
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003268 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Scott Wakelingfe885462016-09-22 10:24:38 +01003269 }
3270 break;
3271
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003272 case DataType::Type::kFloat32:
3273 case DataType::Type::kFloat64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003274 locations->SetInAt(0, Location::RequiresFpuRegister());
Artem Serov657022c2016-11-23 14:19:38 +00003275 locations->SetInAt(1, ArithmeticZeroOrFpuRegister(cond->InputAt(1)));
Scott Wakelingfe885462016-09-22 10:24:38 +01003276 if (!cond->IsEmittedAtUseSite()) {
3277 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3278 }
3279 break;
3280
3281 default:
3282 locations->SetInAt(0, Location::RequiresRegister());
3283 locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
3284 if (!cond->IsEmittedAtUseSite()) {
3285 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3286 }
3287 }
3288}
3289
3290void InstructionCodeGeneratorARMVIXL::HandleCondition(HCondition* cond) {
3291 if (cond->IsEmittedAtUseSite()) {
3292 return;
3293 }
3294
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003295 const DataType::Type type = cond->GetLeft()->GetType();
Scott Wakelingfe885462016-09-22 10:24:38 +01003296
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003297 if (DataType::IsFloatingPointType(type)) {
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003298 GenerateConditionGeneric(cond, codegen_);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003299 return;
Scott Wakelingfe885462016-09-22 10:24:38 +01003300 }
3301
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003302 DCHECK(DataType::IsIntegralType(type) || type == DataType::Type::kReference) << type;
Scott Wakelingfe885462016-09-22 10:24:38 +01003303
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003304 const IfCondition condition = cond->GetCondition();
Scott Wakelingfe885462016-09-22 10:24:38 +01003305
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003306 // A condition with only one boolean input, or two boolean inputs without being equality or
3307 // inequality results from transformations done by the instruction simplifier, and is handled
3308 // as a regular condition with integral inputs.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003309 if (type == DataType::Type::kBool &&
3310 cond->GetRight()->GetType() == DataType::Type::kBool &&
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003311 (condition == kCondEQ || condition == kCondNE)) {
3312 vixl32::Register left = InputRegisterAt(cond, 0);
3313 const vixl32::Register out = OutputRegister(cond);
3314 const Location right_loc = cond->GetLocations()->InAt(1);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003315
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003316 // The constant case is handled by the instruction simplifier.
3317 DCHECK(!right_loc.IsConstant());
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003318
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003319 vixl32::Register right = RegisterFrom(right_loc);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003320
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003321 // Avoid 32-bit instructions if possible.
3322 if (out.Is(right)) {
3323 std::swap(left, right);
3324 }
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003325
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003326 __ Eor(out, left, right);
3327
3328 if (condition == kCondEQ) {
3329 __ Eor(out, out, 1);
3330 }
3331
3332 return;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003333 }
Anton Kirilov6f644202017-02-27 18:29:45 +00003334
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003335 GenerateConditionIntegralOrNonPrimitive(cond, codegen_);
Scott Wakelingfe885462016-09-22 10:24:38 +01003336}
3337
3338void LocationsBuilderARMVIXL::VisitEqual(HEqual* comp) {
3339 HandleCondition(comp);
3340}
3341
3342void InstructionCodeGeneratorARMVIXL::VisitEqual(HEqual* comp) {
3343 HandleCondition(comp);
3344}
3345
3346void LocationsBuilderARMVIXL::VisitNotEqual(HNotEqual* comp) {
3347 HandleCondition(comp);
3348}
3349
3350void InstructionCodeGeneratorARMVIXL::VisitNotEqual(HNotEqual* comp) {
3351 HandleCondition(comp);
3352}
3353
3354void LocationsBuilderARMVIXL::VisitLessThan(HLessThan* comp) {
3355 HandleCondition(comp);
3356}
3357
3358void InstructionCodeGeneratorARMVIXL::VisitLessThan(HLessThan* comp) {
3359 HandleCondition(comp);
3360}
3361
3362void LocationsBuilderARMVIXL::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
3363 HandleCondition(comp);
3364}
3365
3366void InstructionCodeGeneratorARMVIXL::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
3367 HandleCondition(comp);
3368}
3369
3370void LocationsBuilderARMVIXL::VisitGreaterThan(HGreaterThan* comp) {
3371 HandleCondition(comp);
3372}
3373
3374void InstructionCodeGeneratorARMVIXL::VisitGreaterThan(HGreaterThan* comp) {
3375 HandleCondition(comp);
3376}
3377
3378void LocationsBuilderARMVIXL::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
3379 HandleCondition(comp);
3380}
3381
3382void InstructionCodeGeneratorARMVIXL::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
3383 HandleCondition(comp);
3384}
3385
3386void LocationsBuilderARMVIXL::VisitBelow(HBelow* comp) {
3387 HandleCondition(comp);
3388}
3389
3390void InstructionCodeGeneratorARMVIXL::VisitBelow(HBelow* comp) {
3391 HandleCondition(comp);
3392}
3393
3394void LocationsBuilderARMVIXL::VisitBelowOrEqual(HBelowOrEqual* comp) {
3395 HandleCondition(comp);
3396}
3397
3398void InstructionCodeGeneratorARMVIXL::VisitBelowOrEqual(HBelowOrEqual* comp) {
3399 HandleCondition(comp);
3400}
3401
3402void LocationsBuilderARMVIXL::VisitAbove(HAbove* comp) {
3403 HandleCondition(comp);
3404}
3405
3406void InstructionCodeGeneratorARMVIXL::VisitAbove(HAbove* comp) {
3407 HandleCondition(comp);
3408}
3409
3410void LocationsBuilderARMVIXL::VisitAboveOrEqual(HAboveOrEqual* comp) {
3411 HandleCondition(comp);
3412}
3413
3414void InstructionCodeGeneratorARMVIXL::VisitAboveOrEqual(HAboveOrEqual* comp) {
3415 HandleCondition(comp);
3416}
3417
3418void LocationsBuilderARMVIXL::VisitIntConstant(HIntConstant* constant) {
3419 LocationSummary* locations =
3420 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
3421 locations->SetOut(Location::ConstantLocation(constant));
3422}
3423
3424void InstructionCodeGeneratorARMVIXL::VisitIntConstant(HIntConstant* constant ATTRIBUTE_UNUSED) {
3425 // Will be generated at use site.
3426}
3427
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003428void LocationsBuilderARMVIXL::VisitNullConstant(HNullConstant* constant) {
3429 LocationSummary* locations =
3430 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
3431 locations->SetOut(Location::ConstantLocation(constant));
3432}
3433
3434void InstructionCodeGeneratorARMVIXL::VisitNullConstant(HNullConstant* constant ATTRIBUTE_UNUSED) {
3435 // Will be generated at use site.
3436}
3437
Scott Wakelingfe885462016-09-22 10:24:38 +01003438void LocationsBuilderARMVIXL::VisitLongConstant(HLongConstant* constant) {
3439 LocationSummary* locations =
3440 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
3441 locations->SetOut(Location::ConstantLocation(constant));
3442}
3443
3444void InstructionCodeGeneratorARMVIXL::VisitLongConstant(HLongConstant* constant ATTRIBUTE_UNUSED) {
3445 // Will be generated at use site.
3446}
3447
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01003448void LocationsBuilderARMVIXL::VisitFloatConstant(HFloatConstant* constant) {
3449 LocationSummary* locations =
3450 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
3451 locations->SetOut(Location::ConstantLocation(constant));
3452}
3453
Scott Wakelingc34dba72016-10-03 10:14:44 +01003454void InstructionCodeGeneratorARMVIXL::VisitFloatConstant(
3455 HFloatConstant* constant ATTRIBUTE_UNUSED) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01003456 // Will be generated at use site.
3457}
3458
3459void LocationsBuilderARMVIXL::VisitDoubleConstant(HDoubleConstant* constant) {
3460 LocationSummary* locations =
3461 new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
3462 locations->SetOut(Location::ConstantLocation(constant));
3463}
3464
Scott Wakelingc34dba72016-10-03 10:14:44 +01003465void InstructionCodeGeneratorARMVIXL::VisitDoubleConstant(
3466 HDoubleConstant* constant ATTRIBUTE_UNUSED) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01003467 // Will be generated at use site.
3468}
3469
Igor Murashkind01745e2017-04-05 16:40:31 -07003470void LocationsBuilderARMVIXL::VisitConstructorFence(HConstructorFence* constructor_fence) {
3471 constructor_fence->SetLocations(nullptr);
3472}
3473
3474void InstructionCodeGeneratorARMVIXL::VisitConstructorFence(
3475 HConstructorFence* constructor_fence ATTRIBUTE_UNUSED) {
3476 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
3477}
3478
Scott Wakelingfe885462016-09-22 10:24:38 +01003479void LocationsBuilderARMVIXL::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
3480 memory_barrier->SetLocations(nullptr);
3481}
3482
3483void InstructionCodeGeneratorARMVIXL::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
3484 codegen_->GenerateMemoryBarrier(memory_barrier->GetBarrierKind());
3485}
3486
3487void LocationsBuilderARMVIXL::VisitReturnVoid(HReturnVoid* ret) {
3488 ret->SetLocations(nullptr);
3489}
3490
3491void InstructionCodeGeneratorARMVIXL::VisitReturnVoid(HReturnVoid* ret ATTRIBUTE_UNUSED) {
3492 codegen_->GenerateFrameExit();
3493}
3494
3495void LocationsBuilderARMVIXL::VisitReturn(HReturn* ret) {
3496 LocationSummary* locations =
3497 new (GetGraph()->GetArena()) LocationSummary(ret, LocationSummary::kNoCall);
3498 locations->SetInAt(0, parameter_visitor_.GetReturnLocation(ret->InputAt(0)->GetType()));
3499}
3500
3501void InstructionCodeGeneratorARMVIXL::VisitReturn(HReturn* ret ATTRIBUTE_UNUSED) {
3502 codegen_->GenerateFrameExit();
3503}
3504
Artem Serovcfbe9132016-10-14 15:58:56 +01003505void LocationsBuilderARMVIXL::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
3506 // The trampoline uses the same calling convention as dex calling conventions,
3507 // except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain
3508 // the method_idx.
3509 HandleInvoke(invoke);
3510}
3511
3512void InstructionCodeGeneratorARMVIXL::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
3513 codegen_->GenerateInvokeUnresolvedRuntimeCall(invoke);
Roland Levillain5daa4952017-07-03 17:23:56 +01003514 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 3);
Artem Serovcfbe9132016-10-14 15:58:56 +01003515}
3516
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003517void LocationsBuilderARMVIXL::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
3518 // Explicit clinit checks triggered by static invokes must have been pruned by
3519 // art::PrepareForRegisterAllocation.
3520 DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
3521
Anton Kirilov5ec62182016-10-13 20:16:02 +01003522 IntrinsicLocationsBuilderARMVIXL intrinsic(codegen_);
3523 if (intrinsic.TryDispatch(invoke)) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01003524 return;
3525 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003526
3527 HandleInvoke(invoke);
3528}
3529
Anton Kirilov5ec62182016-10-13 20:16:02 +01003530static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorARMVIXL* codegen) {
3531 if (invoke->GetLocations()->Intrinsified()) {
3532 IntrinsicCodeGeneratorARMVIXL intrinsic(codegen);
3533 intrinsic.Dispatch(invoke);
3534 return true;
3535 }
3536 return false;
3537}
3538
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003539void InstructionCodeGeneratorARMVIXL::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
3540 // Explicit clinit checks triggered by static invokes must have been pruned by
3541 // art::PrepareForRegisterAllocation.
3542 DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
3543
Anton Kirilov5ec62182016-10-13 20:16:02 +01003544 if (TryGenerateIntrinsicCode(invoke, codegen_)) {
Roland Levillain5daa4952017-07-03 17:23:56 +01003545 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 4);
Anton Kirilov5ec62182016-10-13 20:16:02 +01003546 return;
3547 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003548
3549 LocationSummary* locations = invoke->GetLocations();
Artem Serovd4cc5b22016-11-04 11:19:09 +00003550 codegen_->GenerateStaticOrDirectCall(
3551 invoke, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation());
Roland Levillain5daa4952017-07-03 17:23:56 +01003552
3553 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 5);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003554}
3555
3556void LocationsBuilderARMVIXL::HandleInvoke(HInvoke* invoke) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00003557 InvokeDexCallingConventionVisitorARMVIXL calling_convention_visitor;
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003558 CodeGenerator::CreateCommonInvokeLocationSummary(invoke, &calling_convention_visitor);
3559}
3560
3561void LocationsBuilderARMVIXL::VisitInvokeVirtual(HInvokeVirtual* invoke) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01003562 IntrinsicLocationsBuilderARMVIXL intrinsic(codegen_);
3563 if (intrinsic.TryDispatch(invoke)) {
3564 return;
3565 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003566
3567 HandleInvoke(invoke);
3568}
3569
3570void InstructionCodeGeneratorARMVIXL::VisitInvokeVirtual(HInvokeVirtual* invoke) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01003571 if (TryGenerateIntrinsicCode(invoke, codegen_)) {
Roland Levillain5daa4952017-07-03 17:23:56 +01003572 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 6);
Anton Kirilov5ec62182016-10-13 20:16:02 +01003573 return;
3574 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003575
3576 codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0));
Alexandre Rames374ddf32016-11-04 10:40:49 +00003577 DCHECK(!codegen_->IsLeafMethod());
Roland Levillain5daa4952017-07-03 17:23:56 +01003578
3579 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 7);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003580}
3581
Artem Serovcfbe9132016-10-14 15:58:56 +01003582void LocationsBuilderARMVIXL::VisitInvokeInterface(HInvokeInterface* invoke) {
3583 HandleInvoke(invoke);
3584 // Add the hidden argument.
3585 invoke->GetLocations()->AddTemp(LocationFrom(r12));
3586}
3587
3588void InstructionCodeGeneratorARMVIXL::VisitInvokeInterface(HInvokeInterface* invoke) {
3589 // TODO: b/18116999, our IMTs can miss an IncompatibleClassChangeError.
3590 LocationSummary* locations = invoke->GetLocations();
3591 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
3592 vixl32::Register hidden_reg = RegisterFrom(locations->GetTemp(1));
3593 Location receiver = locations->InAt(0);
3594 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
3595
3596 DCHECK(!receiver.IsStackSlot());
3597
Alexandre Rames374ddf32016-11-04 10:40:49 +00003598 // Ensure the pc position is recorded immediately after the `ldr` instruction.
3599 {
Artem Serov0fb37192016-12-06 18:13:40 +00003600 ExactAssemblyScope aas(GetVIXLAssembler(),
3601 vixl32::kMaxInstructionSizeInBytes,
3602 CodeBufferCheckScope::kMaximumSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00003603 // /* HeapReference<Class> */ temp = receiver->klass_
3604 __ ldr(temp, MemOperand(RegisterFrom(receiver), class_offset));
3605 codegen_->MaybeRecordImplicitNullCheck(invoke);
3606 }
Artem Serovcfbe9132016-10-14 15:58:56 +01003607 // Instead of simply (possibly) unpoisoning `temp` here, we should
3608 // emit a read barrier for the previous class reference load.
3609 // However this is not required in practice, as this is an
3610 // intermediate/temporary reference and because the current
3611 // concurrent copying collector keeps the from-space memory
3612 // intact/accessible until the end of the marking phase (the
3613 // concurrent copying collector may not in the future).
3614 GetAssembler()->MaybeUnpoisonHeapReference(temp);
3615 GetAssembler()->LoadFromOffset(kLoadWord,
3616 temp,
3617 temp,
3618 mirror::Class::ImtPtrOffset(kArmPointerSize).Uint32Value());
3619 uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement(
3620 invoke->GetImtIndex(), kArmPointerSize));
3621 // temp = temp->GetImtEntryAt(method_offset);
3622 GetAssembler()->LoadFromOffset(kLoadWord, temp, temp, method_offset);
3623 uint32_t entry_point =
3624 ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value();
3625 // LR = temp->GetEntryPoint();
3626 GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, entry_point);
3627
3628 // Set the hidden (in r12) argument. It is done here, right before a BLX to prevent other
3629 // instruction from clobbering it as they might use r12 as a scratch register.
3630 DCHECK(hidden_reg.Is(r12));
Scott Wakelingb77051e2016-11-21 19:46:00 +00003631
3632 {
3633 // The VIXL macro assembler may clobber any of the scratch registers that are available to it,
3634 // so it checks if the application is using them (by passing them to the macro assembler
3635 // methods). The following application of UseScratchRegisterScope corrects VIXL's notion of
3636 // what is available, and is the opposite of the standard usage: Instead of requesting a
3637 // temporary location, it imposes an external constraint (i.e. a specific register is reserved
3638 // for the hidden argument). Note that this works even if VIXL needs a scratch register itself
3639 // (to materialize the constant), since the destination register becomes available for such use
3640 // internally for the duration of the macro instruction.
3641 UseScratchRegisterScope temps(GetVIXLAssembler());
3642 temps.Exclude(hidden_reg);
3643 __ Mov(hidden_reg, invoke->GetDexMethodIndex());
3644 }
Artem Serovcfbe9132016-10-14 15:58:56 +01003645 {
Alexandre Rames374ddf32016-11-04 10:40:49 +00003646 // Ensure the pc position is recorded immediately after the `blx` instruction.
3647 // 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 +00003648 ExactAssemblyScope aas(GetVIXLAssembler(),
Alexandre Rames374ddf32016-11-04 10:40:49 +00003649 vixl32::k16BitT32InstructionSizeInBytes,
3650 CodeBufferCheckScope::kExactSize);
Artem Serovcfbe9132016-10-14 15:58:56 +01003651 // LR();
3652 __ blx(lr);
Artem Serovcfbe9132016-10-14 15:58:56 +01003653 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
Alexandre Rames374ddf32016-11-04 10:40:49 +00003654 DCHECK(!codegen_->IsLeafMethod());
Artem Serovcfbe9132016-10-14 15:58:56 +01003655 }
Roland Levillain5daa4952017-07-03 17:23:56 +01003656
3657 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 8);
Artem Serovcfbe9132016-10-14 15:58:56 +01003658}
3659
Orion Hodsonac141392017-01-13 11:53:47 +00003660void LocationsBuilderARMVIXL::VisitInvokePolymorphic(HInvokePolymorphic* invoke) {
3661 HandleInvoke(invoke);
3662}
3663
3664void InstructionCodeGeneratorARMVIXL::VisitInvokePolymorphic(HInvokePolymorphic* invoke) {
3665 codegen_->GenerateInvokePolymorphicCall(invoke);
Roland Levillain5daa4952017-07-03 17:23:56 +01003666 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 9);
Orion Hodsonac141392017-01-13 11:53:47 +00003667}
3668
Artem Serov02109dd2016-09-23 17:17:54 +01003669void LocationsBuilderARMVIXL::VisitNeg(HNeg* neg) {
3670 LocationSummary* locations =
3671 new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall);
3672 switch (neg->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003673 case DataType::Type::kInt32: {
Artem Serov02109dd2016-09-23 17:17:54 +01003674 locations->SetInAt(0, Location::RequiresRegister());
3675 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3676 break;
3677 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003678 case DataType::Type::kInt64: {
Artem Serov02109dd2016-09-23 17:17:54 +01003679 locations->SetInAt(0, Location::RequiresRegister());
3680 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
3681 break;
3682 }
3683
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003684 case DataType::Type::kFloat32:
3685 case DataType::Type::kFloat64:
Artem Serov02109dd2016-09-23 17:17:54 +01003686 locations->SetInAt(0, Location::RequiresFpuRegister());
3687 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3688 break;
3689
3690 default:
3691 LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
3692 }
3693}
3694
3695void InstructionCodeGeneratorARMVIXL::VisitNeg(HNeg* neg) {
3696 LocationSummary* locations = neg->GetLocations();
3697 Location out = locations->Out();
3698 Location in = locations->InAt(0);
3699 switch (neg->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003700 case DataType::Type::kInt32:
Artem Serov02109dd2016-09-23 17:17:54 +01003701 __ Rsb(OutputRegister(neg), InputRegisterAt(neg, 0), 0);
3702 break;
3703
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003704 case DataType::Type::kInt64:
Artem Serov02109dd2016-09-23 17:17:54 +01003705 // out.lo = 0 - in.lo (and update the carry/borrow (C) flag)
3706 __ Rsbs(LowRegisterFrom(out), LowRegisterFrom(in), 0);
3707 // We cannot emit an RSC (Reverse Subtract with Carry)
3708 // instruction here, as it does not exist in the Thumb-2
3709 // instruction set. We use the following approach
3710 // using SBC and SUB instead.
3711 //
3712 // out.hi = -C
3713 __ Sbc(HighRegisterFrom(out), HighRegisterFrom(out), HighRegisterFrom(out));
3714 // out.hi = out.hi - in.hi
3715 __ Sub(HighRegisterFrom(out), HighRegisterFrom(out), HighRegisterFrom(in));
3716 break;
3717
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003718 case DataType::Type::kFloat32:
3719 case DataType::Type::kFloat64:
Anton Kirilov644032c2016-12-06 17:51:43 +00003720 __ Vneg(OutputVRegister(neg), InputVRegister(neg));
Artem Serov02109dd2016-09-23 17:17:54 +01003721 break;
3722
3723 default:
3724 LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
3725 }
3726}
3727
Scott Wakelingfe885462016-09-22 10:24:38 +01003728void LocationsBuilderARMVIXL::VisitTypeConversion(HTypeConversion* conversion) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003729 DataType::Type result_type = conversion->GetResultType();
3730 DataType::Type input_type = conversion->GetInputType();
Scott Wakelingfe885462016-09-22 10:24:38 +01003731 DCHECK_NE(result_type, input_type);
3732
3733 // The float-to-long, double-to-long and long-to-float type conversions
3734 // rely on a call to the runtime.
3735 LocationSummary::CallKind call_kind =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003736 (((input_type == DataType::Type::kFloat32 || input_type == DataType::Type::kFloat64)
3737 && result_type == DataType::Type::kInt64)
3738 || (input_type == DataType::Type::kInt64 && result_type == DataType::Type::kFloat32))
Scott Wakelingfe885462016-09-22 10:24:38 +01003739 ? LocationSummary::kCallOnMainOnly
3740 : LocationSummary::kNoCall;
3741 LocationSummary* locations =
3742 new (GetGraph()->GetArena()) LocationSummary(conversion, call_kind);
3743
3744 // The Java language does not allow treating boolean as an integral type but
3745 // our bit representation makes it safe.
3746
3747 switch (result_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003748 case DataType::Type::kInt8:
Scott Wakelingfe885462016-09-22 10:24:38 +01003749 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003750 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003751 // Type conversion from long to byte is a result of code transformations.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003752 case DataType::Type::kBool:
Scott Wakelingfe885462016-09-22 10:24:38 +01003753 // Boolean input is a result of code transformations.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003754 case DataType::Type::kInt16:
3755 case DataType::Type::kInt32:
3756 case DataType::Type::kUint16:
Scott Wakelingfe885462016-09-22 10:24:38 +01003757 // Processing a Dex `int-to-byte' instruction.
3758 locations->SetInAt(0, Location::RequiresRegister());
3759 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3760 break;
3761
3762 default:
3763 LOG(FATAL) << "Unexpected type conversion from " << input_type
3764 << " to " << result_type;
3765 }
3766 break;
3767
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003768 case DataType::Type::kInt16:
Scott Wakelingfe885462016-09-22 10:24:38 +01003769 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003770 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003771 // Type conversion from long to short is a result of code transformations.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003772 case DataType::Type::kBool:
Scott Wakelingfe885462016-09-22 10:24:38 +01003773 // Boolean input is a result of code transformations.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003774 case DataType::Type::kInt8:
3775 case DataType::Type::kInt32:
3776 case DataType::Type::kUint16:
Scott Wakelingfe885462016-09-22 10:24:38 +01003777 // Processing a Dex `int-to-short' instruction.
3778 locations->SetInAt(0, Location::RequiresRegister());
3779 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3780 break;
3781
3782 default:
3783 LOG(FATAL) << "Unexpected type conversion from " << input_type
3784 << " to " << result_type;
3785 }
3786 break;
3787
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003788 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003789 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003790 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003791 // Processing a Dex `long-to-int' instruction.
3792 locations->SetInAt(0, Location::Any());
3793 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3794 break;
3795
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003796 case DataType::Type::kFloat32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003797 // Processing a Dex `float-to-int' instruction.
3798 locations->SetInAt(0, Location::RequiresFpuRegister());
3799 locations->SetOut(Location::RequiresRegister());
3800 locations->AddTemp(Location::RequiresFpuRegister());
3801 break;
3802
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003803 case DataType::Type::kFloat64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003804 // Processing a Dex `double-to-int' instruction.
3805 locations->SetInAt(0, Location::RequiresFpuRegister());
3806 locations->SetOut(Location::RequiresRegister());
3807 locations->AddTemp(Location::RequiresFpuRegister());
3808 break;
3809
3810 default:
3811 LOG(FATAL) << "Unexpected type conversion from " << input_type
3812 << " to " << result_type;
3813 }
3814 break;
3815
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003816 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003817 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003818 case DataType::Type::kBool:
Scott Wakelingfe885462016-09-22 10:24:38 +01003819 // Boolean input is a result of code transformations.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003820 case DataType::Type::kInt8:
3821 case DataType::Type::kInt16:
3822 case DataType::Type::kInt32:
3823 case DataType::Type::kUint16:
Scott Wakelingfe885462016-09-22 10:24:38 +01003824 // Processing a Dex `int-to-long' instruction.
3825 locations->SetInAt(0, Location::RequiresRegister());
3826 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3827 break;
3828
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003829 case DataType::Type::kFloat32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01003830 // Processing a Dex `float-to-long' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003831 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3832 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
3833 locations->SetOut(LocationFrom(r0, r1));
Scott Wakelingfe885462016-09-22 10:24:38 +01003834 break;
3835 }
3836
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003837 case DataType::Type::kFloat64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01003838 // Processing a Dex `double-to-long' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003839 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3840 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0),
3841 calling_convention.GetFpuRegisterAt(1)));
3842 locations->SetOut(LocationFrom(r0, r1));
Scott Wakelingfe885462016-09-22 10:24:38 +01003843 break;
3844 }
3845
3846 default:
3847 LOG(FATAL) << "Unexpected type conversion from " << input_type
3848 << " to " << result_type;
3849 }
3850 break;
3851
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003852 case DataType::Type::kUint16:
Scott Wakelingfe885462016-09-22 10:24:38 +01003853 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003854 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003855 // Type conversion from long to char is a result of code transformations.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003856 case DataType::Type::kBool:
Scott Wakelingfe885462016-09-22 10:24:38 +01003857 // Boolean input is a result of code transformations.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003858 case DataType::Type::kInt8:
3859 case DataType::Type::kInt16:
3860 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003861 // Processing a Dex `int-to-char' instruction.
3862 locations->SetInAt(0, Location::RequiresRegister());
3863 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3864 break;
3865
3866 default:
3867 LOG(FATAL) << "Unexpected type conversion from " << input_type
3868 << " to " << result_type;
3869 }
3870 break;
3871
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003872 case DataType::Type::kFloat32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003873 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003874 case DataType::Type::kBool:
Scott Wakelingfe885462016-09-22 10:24:38 +01003875 // Boolean input is a result of code transformations.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003876 case DataType::Type::kInt8:
3877 case DataType::Type::kInt16:
3878 case DataType::Type::kInt32:
3879 case DataType::Type::kUint16:
Scott Wakelingfe885462016-09-22 10:24:38 +01003880 // Processing a Dex `int-to-float' instruction.
3881 locations->SetInAt(0, Location::RequiresRegister());
3882 locations->SetOut(Location::RequiresFpuRegister());
3883 break;
3884
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003885 case DataType::Type::kInt64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01003886 // Processing a Dex `long-to-float' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003887 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3888 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0),
3889 calling_convention.GetRegisterAt(1)));
3890 locations->SetOut(LocationFrom(calling_convention.GetFpuRegisterAt(0)));
Scott Wakelingfe885462016-09-22 10:24:38 +01003891 break;
3892 }
3893
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003894 case DataType::Type::kFloat64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003895 // Processing a Dex `double-to-float' instruction.
3896 locations->SetInAt(0, Location::RequiresFpuRegister());
3897 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3898 break;
3899
3900 default:
3901 LOG(FATAL) << "Unexpected type conversion from " << input_type
3902 << " to " << result_type;
3903 };
3904 break;
3905
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003906 case DataType::Type::kFloat64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003907 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003908 case DataType::Type::kBool:
Scott Wakelingfe885462016-09-22 10:24:38 +01003909 // Boolean input is a result of code transformations.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003910 case DataType::Type::kInt8:
3911 case DataType::Type::kInt16:
3912 case DataType::Type::kInt32:
3913 case DataType::Type::kUint16:
Scott Wakelingfe885462016-09-22 10:24:38 +01003914 // Processing a Dex `int-to-double' instruction.
3915 locations->SetInAt(0, Location::RequiresRegister());
3916 locations->SetOut(Location::RequiresFpuRegister());
3917 break;
3918
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003919 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003920 // Processing a Dex `long-to-double' instruction.
3921 locations->SetInAt(0, Location::RequiresRegister());
3922 locations->SetOut(Location::RequiresFpuRegister());
3923 locations->AddTemp(Location::RequiresFpuRegister());
3924 locations->AddTemp(Location::RequiresFpuRegister());
3925 break;
3926
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003927 case DataType::Type::kFloat32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003928 // Processing a Dex `float-to-double' instruction.
3929 locations->SetInAt(0, Location::RequiresFpuRegister());
3930 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3931 break;
3932
3933 default:
3934 LOG(FATAL) << "Unexpected type conversion from " << input_type
3935 << " to " << result_type;
3936 };
3937 break;
3938
3939 default:
3940 LOG(FATAL) << "Unexpected type conversion from " << input_type
3941 << " to " << result_type;
3942 }
3943}
3944
3945void InstructionCodeGeneratorARMVIXL::VisitTypeConversion(HTypeConversion* conversion) {
3946 LocationSummary* locations = conversion->GetLocations();
3947 Location out = locations->Out();
3948 Location in = locations->InAt(0);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003949 DataType::Type result_type = conversion->GetResultType();
3950 DataType::Type input_type = conversion->GetInputType();
Scott Wakelingfe885462016-09-22 10:24:38 +01003951 DCHECK_NE(result_type, input_type);
3952 switch (result_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003953 case DataType::Type::kInt8:
Scott Wakelingfe885462016-09-22 10:24:38 +01003954 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003955 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003956 // Type conversion from long to byte is a result of code transformations.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003957 __ Sbfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 8);
Scott Wakelingfe885462016-09-22 10:24:38 +01003958 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003959 case DataType::Type::kBool:
Scott Wakelingfe885462016-09-22 10:24:38 +01003960 // Boolean input is a result of code transformations.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003961 case DataType::Type::kInt16:
3962 case DataType::Type::kInt32:
3963 case DataType::Type::kUint16:
Scott Wakelingfe885462016-09-22 10:24:38 +01003964 // Processing a Dex `int-to-byte' instruction.
3965 __ Sbfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 8);
3966 break;
3967
3968 default:
3969 LOG(FATAL) << "Unexpected type conversion from " << input_type
3970 << " to " << result_type;
3971 }
3972 break;
3973
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003974 case DataType::Type::kInt16:
Scott Wakelingfe885462016-09-22 10:24:38 +01003975 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003976 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003977 // Type conversion from long to short is a result of code transformations.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003978 __ Sbfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 16);
Scott Wakelingfe885462016-09-22 10:24:38 +01003979 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003980 case DataType::Type::kBool:
Scott Wakelingfe885462016-09-22 10:24:38 +01003981 // Boolean input is a result of code transformations.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003982 case DataType::Type::kInt8:
3983 case DataType::Type::kInt32:
3984 case DataType::Type::kUint16:
Scott Wakelingfe885462016-09-22 10:24:38 +01003985 // Processing a Dex `int-to-short' instruction.
3986 __ Sbfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 16);
3987 break;
3988
3989 default:
3990 LOG(FATAL) << "Unexpected type conversion from " << input_type
3991 << " to " << result_type;
3992 }
3993 break;
3994
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003995 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003996 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003997 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003998 // Processing a Dex `long-to-int' instruction.
3999 DCHECK(out.IsRegister());
4000 if (in.IsRegisterPair()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004001 __ Mov(OutputRegister(conversion), LowRegisterFrom(in));
Scott Wakelingfe885462016-09-22 10:24:38 +01004002 } else if (in.IsDoubleStackSlot()) {
4003 GetAssembler()->LoadFromOffset(kLoadWord,
4004 OutputRegister(conversion),
4005 sp,
4006 in.GetStackIndex());
4007 } else {
4008 DCHECK(in.IsConstant());
4009 DCHECK(in.GetConstant()->IsLongConstant());
Vladimir Markoba1a48e2017-04-13 11:50:14 +01004010 int64_t value = in.GetConstant()->AsLongConstant()->GetValue();
4011 __ Mov(OutputRegister(conversion), static_cast<int32_t>(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01004012 }
4013 break;
4014
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004015 case DataType::Type::kFloat32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004016 // Processing a Dex `float-to-int' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004017 vixl32::SRegister temp = LowSRegisterFrom(locations->GetTemp(0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01004018 __ Vcvt(S32, F32, temp, InputSRegisterAt(conversion, 0));
Scott Wakelingfe885462016-09-22 10:24:38 +01004019 __ Vmov(OutputRegister(conversion), temp);
4020 break;
4021 }
4022
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004023 case DataType::Type::kFloat64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004024 // Processing a Dex `double-to-int' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004025 vixl32::SRegister temp_s = LowSRegisterFrom(locations->GetTemp(0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01004026 __ Vcvt(S32, F64, temp_s, DRegisterFrom(in));
Scott Wakelingfe885462016-09-22 10:24:38 +01004027 __ Vmov(OutputRegister(conversion), temp_s);
4028 break;
4029 }
4030
4031 default:
4032 LOG(FATAL) << "Unexpected type conversion from " << input_type
4033 << " to " << result_type;
4034 }
4035 break;
4036
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004037 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01004038 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004039 case DataType::Type::kBool:
Scott Wakelingfe885462016-09-22 10:24:38 +01004040 // Boolean input is a result of code transformations.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004041 case DataType::Type::kInt8:
4042 case DataType::Type::kInt16:
4043 case DataType::Type::kInt32:
4044 case DataType::Type::kUint16:
Scott Wakelingfe885462016-09-22 10:24:38 +01004045 // Processing a Dex `int-to-long' instruction.
4046 DCHECK(out.IsRegisterPair());
4047 DCHECK(in.IsRegister());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004048 __ Mov(LowRegisterFrom(out), InputRegisterAt(conversion, 0));
Scott Wakelingfe885462016-09-22 10:24:38 +01004049 // Sign extension.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004050 __ Asr(HighRegisterFrom(out), LowRegisterFrom(out), 31);
Scott Wakelingfe885462016-09-22 10:24:38 +01004051 break;
4052
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004053 case DataType::Type::kFloat32:
Scott Wakelingfe885462016-09-22 10:24:38 +01004054 // Processing a Dex `float-to-long' instruction.
4055 codegen_->InvokeRuntime(kQuickF2l, conversion, conversion->GetDexPc());
4056 CheckEntrypointTypes<kQuickF2l, int64_t, float>();
4057 break;
4058
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004059 case DataType::Type::kFloat64:
Scott Wakelingfe885462016-09-22 10:24:38 +01004060 // Processing a Dex `double-to-long' instruction.
4061 codegen_->InvokeRuntime(kQuickD2l, conversion, conversion->GetDexPc());
4062 CheckEntrypointTypes<kQuickD2l, int64_t, double>();
4063 break;
4064
4065 default:
4066 LOG(FATAL) << "Unexpected type conversion from " << input_type
4067 << " to " << result_type;
4068 }
4069 break;
4070
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004071 case DataType::Type::kUint16:
Scott Wakelingfe885462016-09-22 10:24:38 +01004072 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004073 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01004074 // Type conversion from long to char is a result of code transformations.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004075 __ Ubfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 16);
Scott Wakelingfe885462016-09-22 10:24:38 +01004076 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004077 case DataType::Type::kBool:
Scott Wakelingfe885462016-09-22 10:24:38 +01004078 // Boolean input is a result of code transformations.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004079 case DataType::Type::kInt8:
4080 case DataType::Type::kInt16:
4081 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01004082 // Processing a Dex `int-to-char' instruction.
4083 __ Ubfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 16);
4084 break;
4085
4086 default:
4087 LOG(FATAL) << "Unexpected type conversion from " << input_type
4088 << " to " << result_type;
4089 }
4090 break;
4091
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004092 case DataType::Type::kFloat32:
Scott Wakelingfe885462016-09-22 10:24:38 +01004093 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004094 case DataType::Type::kBool:
Scott Wakelingfe885462016-09-22 10:24:38 +01004095 // Boolean input is a result of code transformations.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004096 case DataType::Type::kInt8:
4097 case DataType::Type::kInt16:
4098 case DataType::Type::kInt32:
4099 case DataType::Type::kUint16: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004100 // Processing a Dex `int-to-float' instruction.
4101 __ Vmov(OutputSRegister(conversion), InputRegisterAt(conversion, 0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01004102 __ Vcvt(F32, S32, OutputSRegister(conversion), OutputSRegister(conversion));
Scott Wakelingfe885462016-09-22 10:24:38 +01004103 break;
4104 }
4105
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004106 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01004107 // Processing a Dex `long-to-float' instruction.
4108 codegen_->InvokeRuntime(kQuickL2f, conversion, conversion->GetDexPc());
4109 CheckEntrypointTypes<kQuickL2f, float, int64_t>();
4110 break;
4111
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004112 case DataType::Type::kFloat64:
Scott Wakelingfe885462016-09-22 10:24:38 +01004113 // Processing a Dex `double-to-float' instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01004114 __ Vcvt(F32, F64, OutputSRegister(conversion), DRegisterFrom(in));
Scott Wakelingfe885462016-09-22 10:24:38 +01004115 break;
4116
4117 default:
4118 LOG(FATAL) << "Unexpected type conversion from " << input_type
4119 << " to " << result_type;
4120 };
4121 break;
4122
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004123 case DataType::Type::kFloat64:
Scott Wakelingfe885462016-09-22 10:24:38 +01004124 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004125 case DataType::Type::kBool:
Scott Wakelingfe885462016-09-22 10:24:38 +01004126 // Boolean input is a result of code transformations.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004127 case DataType::Type::kInt8:
4128 case DataType::Type::kInt16:
4129 case DataType::Type::kInt32:
4130 case DataType::Type::kUint16: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004131 // Processing a Dex `int-to-double' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004132 __ Vmov(LowSRegisterFrom(out), InputRegisterAt(conversion, 0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01004133 __ Vcvt(F64, S32, DRegisterFrom(out), LowSRegisterFrom(out));
Scott Wakelingfe885462016-09-22 10:24:38 +01004134 break;
4135 }
4136
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004137 case DataType::Type::kInt64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004138 // Processing a Dex `long-to-double' instruction.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004139 vixl32::Register low = LowRegisterFrom(in);
4140 vixl32::Register high = HighRegisterFrom(in);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004141 vixl32::SRegister out_s = LowSRegisterFrom(out);
Scott Wakelingc34dba72016-10-03 10:14:44 +01004142 vixl32::DRegister out_d = DRegisterFrom(out);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004143 vixl32::SRegister temp_s = LowSRegisterFrom(locations->GetTemp(0));
Scott Wakelingc34dba72016-10-03 10:14:44 +01004144 vixl32::DRegister temp_d = DRegisterFrom(locations->GetTemp(0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01004145 vixl32::DRegister constant_d = DRegisterFrom(locations->GetTemp(1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004146
4147 // temp_d = int-to-double(high)
4148 __ Vmov(temp_s, high);
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01004149 __ Vcvt(F64, S32, temp_d, temp_s);
Scott Wakelingfe885462016-09-22 10:24:38 +01004150 // constant_d = k2Pow32EncodingForDouble
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004151 __ Vmov(constant_d, bit_cast<double, int64_t>(k2Pow32EncodingForDouble));
Scott Wakelingfe885462016-09-22 10:24:38 +01004152 // out_d = unsigned-to-double(low)
4153 __ Vmov(out_s, low);
4154 __ Vcvt(F64, U32, out_d, out_s);
4155 // out_d += temp_d * constant_d
4156 __ Vmla(F64, out_d, temp_d, constant_d);
4157 break;
4158 }
4159
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004160 case DataType::Type::kFloat32:
Scott Wakelingfe885462016-09-22 10:24:38 +01004161 // Processing a Dex `float-to-double' instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01004162 __ Vcvt(F64, F32, DRegisterFrom(out), InputSRegisterAt(conversion, 0));
Scott Wakelingfe885462016-09-22 10:24:38 +01004163 break;
4164
4165 default:
4166 LOG(FATAL) << "Unexpected type conversion from " << input_type
4167 << " to " << result_type;
4168 };
4169 break;
4170
4171 default:
4172 LOG(FATAL) << "Unexpected type conversion from " << input_type
4173 << " to " << result_type;
4174 }
4175}
4176
4177void LocationsBuilderARMVIXL::VisitAdd(HAdd* add) {
4178 LocationSummary* locations =
4179 new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall);
4180 switch (add->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004181 case DataType::Type::kInt32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004182 locations->SetInAt(0, Location::RequiresRegister());
4183 locations->SetInAt(1, Location::RegisterOrConstant(add->InputAt(1)));
4184 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4185 break;
4186 }
4187
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004188 case DataType::Type::kInt64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004189 locations->SetInAt(0, Location::RequiresRegister());
Anton Kirilovdda43962016-11-21 19:55:20 +00004190 locations->SetInAt(1, ArmEncodableConstantOrRegister(add->InputAt(1), ADD));
Scott Wakelingfe885462016-09-22 10:24:38 +01004191 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4192 break;
4193 }
4194
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004195 case DataType::Type::kFloat32:
4196 case DataType::Type::kFloat64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004197 locations->SetInAt(0, Location::RequiresFpuRegister());
4198 locations->SetInAt(1, Location::RequiresFpuRegister());
4199 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4200 break;
4201 }
4202
4203 default:
4204 LOG(FATAL) << "Unexpected add type " << add->GetResultType();
4205 }
4206}
4207
4208void InstructionCodeGeneratorARMVIXL::VisitAdd(HAdd* add) {
4209 LocationSummary* locations = add->GetLocations();
4210 Location out = locations->Out();
4211 Location first = locations->InAt(0);
4212 Location second = locations->InAt(1);
4213
4214 switch (add->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004215 case DataType::Type::kInt32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004216 __ Add(OutputRegister(add), InputRegisterAt(add, 0), InputOperandAt(add, 1));
4217 }
4218 break;
4219
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004220 case DataType::Type::kInt64: {
Anton Kirilovdda43962016-11-21 19:55:20 +00004221 if (second.IsConstant()) {
4222 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
4223 GenerateAddLongConst(out, first, value);
4224 } else {
4225 DCHECK(second.IsRegisterPair());
4226 __ Adds(LowRegisterFrom(out), LowRegisterFrom(first), LowRegisterFrom(second));
4227 __ Adc(HighRegisterFrom(out), HighRegisterFrom(first), HighRegisterFrom(second));
4228 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004229 break;
4230 }
4231
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004232 case DataType::Type::kFloat32:
4233 case DataType::Type::kFloat64:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004234 __ Vadd(OutputVRegister(add), InputVRegisterAt(add, 0), InputVRegisterAt(add, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004235 break;
4236
4237 default:
4238 LOG(FATAL) << "Unexpected add type " << add->GetResultType();
4239 }
4240}
4241
4242void LocationsBuilderARMVIXL::VisitSub(HSub* sub) {
4243 LocationSummary* locations =
4244 new (GetGraph()->GetArena()) LocationSummary(sub, LocationSummary::kNoCall);
4245 switch (sub->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004246 case DataType::Type::kInt32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004247 locations->SetInAt(0, Location::RequiresRegister());
4248 locations->SetInAt(1, Location::RegisterOrConstant(sub->InputAt(1)));
4249 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4250 break;
4251 }
4252
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004253 case DataType::Type::kInt64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004254 locations->SetInAt(0, Location::RequiresRegister());
Anton Kirilovdda43962016-11-21 19:55:20 +00004255 locations->SetInAt(1, ArmEncodableConstantOrRegister(sub->InputAt(1), SUB));
Scott Wakelingfe885462016-09-22 10:24:38 +01004256 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4257 break;
4258 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004259 case DataType::Type::kFloat32:
4260 case DataType::Type::kFloat64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004261 locations->SetInAt(0, Location::RequiresFpuRegister());
4262 locations->SetInAt(1, Location::RequiresFpuRegister());
4263 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4264 break;
4265 }
4266 default:
4267 LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
4268 }
4269}
4270
4271void InstructionCodeGeneratorARMVIXL::VisitSub(HSub* sub) {
4272 LocationSummary* locations = sub->GetLocations();
4273 Location out = locations->Out();
4274 Location first = locations->InAt(0);
4275 Location second = locations->InAt(1);
4276 switch (sub->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004277 case DataType::Type::kInt32: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004278 __ Sub(OutputRegister(sub), InputRegisterAt(sub, 0), InputOperandAt(sub, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004279 break;
4280 }
4281
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004282 case DataType::Type::kInt64: {
Anton Kirilovdda43962016-11-21 19:55:20 +00004283 if (second.IsConstant()) {
4284 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
4285 GenerateAddLongConst(out, first, -value);
4286 } else {
4287 DCHECK(second.IsRegisterPair());
4288 __ Subs(LowRegisterFrom(out), LowRegisterFrom(first), LowRegisterFrom(second));
4289 __ Sbc(HighRegisterFrom(out), HighRegisterFrom(first), HighRegisterFrom(second));
4290 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004291 break;
4292 }
4293
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004294 case DataType::Type::kFloat32:
4295 case DataType::Type::kFloat64:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004296 __ Vsub(OutputVRegister(sub), InputVRegisterAt(sub, 0), InputVRegisterAt(sub, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004297 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01004298
4299 default:
4300 LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
4301 }
4302}
4303
4304void LocationsBuilderARMVIXL::VisitMul(HMul* mul) {
4305 LocationSummary* locations =
4306 new (GetGraph()->GetArena()) LocationSummary(mul, LocationSummary::kNoCall);
4307 switch (mul->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004308 case DataType::Type::kInt32:
4309 case DataType::Type::kInt64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004310 locations->SetInAt(0, Location::RequiresRegister());
4311 locations->SetInAt(1, Location::RequiresRegister());
4312 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4313 break;
4314 }
4315
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004316 case DataType::Type::kFloat32:
4317 case DataType::Type::kFloat64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004318 locations->SetInAt(0, Location::RequiresFpuRegister());
4319 locations->SetInAt(1, Location::RequiresFpuRegister());
4320 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4321 break;
4322 }
4323
4324 default:
4325 LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
4326 }
4327}
4328
4329void InstructionCodeGeneratorARMVIXL::VisitMul(HMul* mul) {
4330 LocationSummary* locations = mul->GetLocations();
4331 Location out = locations->Out();
4332 Location first = locations->InAt(0);
4333 Location second = locations->InAt(1);
4334 switch (mul->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004335 case DataType::Type::kInt32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004336 __ Mul(OutputRegister(mul), InputRegisterAt(mul, 0), InputRegisterAt(mul, 1));
4337 break;
4338 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004339 case DataType::Type::kInt64: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004340 vixl32::Register out_hi = HighRegisterFrom(out);
4341 vixl32::Register out_lo = LowRegisterFrom(out);
4342 vixl32::Register in1_hi = HighRegisterFrom(first);
4343 vixl32::Register in1_lo = LowRegisterFrom(first);
4344 vixl32::Register in2_hi = HighRegisterFrom(second);
4345 vixl32::Register in2_lo = LowRegisterFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01004346
4347 // Extra checks to protect caused by the existence of R1_R2.
4348 // The algorithm is wrong if out.hi is either in1.lo or in2.lo:
4349 // (e.g. in1=r0_r1, in2=r2_r3 and out=r1_r2);
Anton Kirilov644032c2016-12-06 17:51:43 +00004350 DCHECK(!out_hi.Is(in1_lo));
4351 DCHECK(!out_hi.Is(in2_lo));
Scott Wakelingfe885462016-09-22 10:24:38 +01004352
4353 // input: in1 - 64 bits, in2 - 64 bits
4354 // output: out
4355 // formula: out.hi : out.lo = (in1.lo * in2.hi + in1.hi * in2.lo)* 2^32 + in1.lo * in2.lo
4356 // parts: out.hi = in1.lo * in2.hi + in1.hi * in2.lo + (in1.lo * in2.lo)[63:32]
4357 // parts: out.lo = (in1.lo * in2.lo)[31:0]
4358
4359 UseScratchRegisterScope temps(GetVIXLAssembler());
4360 vixl32::Register temp = temps.Acquire();
4361 // temp <- in1.lo * in2.hi
4362 __ Mul(temp, in1_lo, in2_hi);
4363 // out.hi <- in1.lo * in2.hi + in1.hi * in2.lo
4364 __ Mla(out_hi, in1_hi, in2_lo, temp);
4365 // out.lo <- (in1.lo * in2.lo)[31:0];
4366 __ Umull(out_lo, temp, in1_lo, in2_lo);
4367 // out.hi <- in2.hi * in1.lo + in2.lo * in1.hi + (in1.lo * in2.lo)[63:32]
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004368 __ Add(out_hi, out_hi, temp);
Scott Wakelingfe885462016-09-22 10:24:38 +01004369 break;
4370 }
4371
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004372 case DataType::Type::kFloat32:
4373 case DataType::Type::kFloat64:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004374 __ Vmul(OutputVRegister(mul), InputVRegisterAt(mul, 0), InputVRegisterAt(mul, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004375 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01004376
4377 default:
4378 LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
4379 }
4380}
4381
Scott Wakelingfe885462016-09-22 10:24:38 +01004382void InstructionCodeGeneratorARMVIXL::DivRemOneOrMinusOne(HBinaryOperation* instruction) {
4383 DCHECK(instruction->IsDiv() || instruction->IsRem());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004384 DCHECK(instruction->GetResultType() == DataType::Type::kInt32);
Scott Wakelingfe885462016-09-22 10:24:38 +01004385
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004386 Location second = instruction->GetLocations()->InAt(1);
Scott Wakelingfe885462016-09-22 10:24:38 +01004387 DCHECK(second.IsConstant());
4388
4389 vixl32::Register out = OutputRegister(instruction);
4390 vixl32::Register dividend = InputRegisterAt(instruction, 0);
Anton Kirilov644032c2016-12-06 17:51:43 +00004391 int32_t imm = Int32ConstantFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01004392 DCHECK(imm == 1 || imm == -1);
4393
4394 if (instruction->IsRem()) {
4395 __ Mov(out, 0);
4396 } else {
4397 if (imm == 1) {
4398 __ Mov(out, dividend);
4399 } else {
4400 __ Rsb(out, dividend, 0);
4401 }
4402 }
4403}
4404
4405void InstructionCodeGeneratorARMVIXL::DivRemByPowerOfTwo(HBinaryOperation* instruction) {
4406 DCHECK(instruction->IsDiv() || instruction->IsRem());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004407 DCHECK(instruction->GetResultType() == DataType::Type::kInt32);
Scott Wakelingfe885462016-09-22 10:24:38 +01004408
4409 LocationSummary* locations = instruction->GetLocations();
4410 Location second = locations->InAt(1);
4411 DCHECK(second.IsConstant());
4412
4413 vixl32::Register out = OutputRegister(instruction);
4414 vixl32::Register dividend = InputRegisterAt(instruction, 0);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004415 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
Anton Kirilov644032c2016-12-06 17:51:43 +00004416 int32_t imm = Int32ConstantFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01004417 uint32_t abs_imm = static_cast<uint32_t>(AbsOrMin(imm));
4418 int ctz_imm = CTZ(abs_imm);
4419
4420 if (ctz_imm == 1) {
4421 __ Lsr(temp, dividend, 32 - ctz_imm);
4422 } else {
4423 __ Asr(temp, dividend, 31);
4424 __ Lsr(temp, temp, 32 - ctz_imm);
4425 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004426 __ Add(out, temp, dividend);
Scott Wakelingfe885462016-09-22 10:24:38 +01004427
4428 if (instruction->IsDiv()) {
4429 __ Asr(out, out, ctz_imm);
4430 if (imm < 0) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004431 __ Rsb(out, out, 0);
Scott Wakelingfe885462016-09-22 10:24:38 +01004432 }
4433 } else {
4434 __ Ubfx(out, out, 0, ctz_imm);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004435 __ Sub(out, out, temp);
Scott Wakelingfe885462016-09-22 10:24:38 +01004436 }
4437}
4438
4439void InstructionCodeGeneratorARMVIXL::GenerateDivRemWithAnyConstant(HBinaryOperation* instruction) {
4440 DCHECK(instruction->IsDiv() || instruction->IsRem());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004441 DCHECK(instruction->GetResultType() == DataType::Type::kInt32);
Scott Wakelingfe885462016-09-22 10:24:38 +01004442
4443 LocationSummary* locations = instruction->GetLocations();
4444 Location second = locations->InAt(1);
4445 DCHECK(second.IsConstant());
4446
4447 vixl32::Register out = OutputRegister(instruction);
4448 vixl32::Register dividend = InputRegisterAt(instruction, 0);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004449 vixl32::Register temp1 = RegisterFrom(locations->GetTemp(0));
4450 vixl32::Register temp2 = RegisterFrom(locations->GetTemp(1));
Scott Wakelingb77051e2016-11-21 19:46:00 +00004451 int32_t imm = Int32ConstantFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01004452
4453 int64_t magic;
4454 int shift;
4455 CalculateMagicAndShiftForDivRem(imm, false /* is_long */, &magic, &shift);
4456
Anton Kirilovdda43962016-11-21 19:55:20 +00004457 // TODO(VIXL): Change the static cast to Operand::From() after VIXL is fixed.
4458 __ Mov(temp1, static_cast<int32_t>(magic));
Scott Wakelingfe885462016-09-22 10:24:38 +01004459 __ Smull(temp2, temp1, dividend, temp1);
4460
4461 if (imm > 0 && magic < 0) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004462 __ Add(temp1, temp1, dividend);
Scott Wakelingfe885462016-09-22 10:24:38 +01004463 } else if (imm < 0 && magic > 0) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004464 __ Sub(temp1, temp1, dividend);
Scott Wakelingfe885462016-09-22 10:24:38 +01004465 }
4466
4467 if (shift != 0) {
4468 __ Asr(temp1, temp1, shift);
4469 }
4470
4471 if (instruction->IsDiv()) {
4472 __ Sub(out, temp1, Operand(temp1, vixl32::Shift(ASR), 31));
4473 } else {
4474 __ Sub(temp1, temp1, Operand(temp1, vixl32::Shift(ASR), 31));
4475 // TODO: Strength reduction for mls.
4476 __ Mov(temp2, imm);
4477 __ Mls(out, temp1, temp2, dividend);
4478 }
4479}
4480
4481void InstructionCodeGeneratorARMVIXL::GenerateDivRemConstantIntegral(
4482 HBinaryOperation* instruction) {
4483 DCHECK(instruction->IsDiv() || instruction->IsRem());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004484 DCHECK(instruction->GetResultType() == DataType::Type::kInt32);
Scott Wakelingfe885462016-09-22 10:24:38 +01004485
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004486 Location second = instruction->GetLocations()->InAt(1);
Scott Wakelingfe885462016-09-22 10:24:38 +01004487 DCHECK(second.IsConstant());
4488
Anton Kirilov644032c2016-12-06 17:51:43 +00004489 int32_t imm = Int32ConstantFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01004490 if (imm == 0) {
4491 // Do not generate anything. DivZeroCheck would prevent any code to be executed.
4492 } else if (imm == 1 || imm == -1) {
4493 DivRemOneOrMinusOne(instruction);
4494 } else if (IsPowerOfTwo(AbsOrMin(imm))) {
4495 DivRemByPowerOfTwo(instruction);
4496 } else {
4497 DCHECK(imm <= -2 || imm >= 2);
4498 GenerateDivRemWithAnyConstant(instruction);
4499 }
4500}
4501
4502void LocationsBuilderARMVIXL::VisitDiv(HDiv* div) {
4503 LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004504 if (div->GetResultType() == DataType::Type::kInt64) {
Scott Wakelingfe885462016-09-22 10:24:38 +01004505 // pLdiv runtime call.
4506 call_kind = LocationSummary::kCallOnMainOnly;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004507 } else if (div->GetResultType() == DataType::Type::kInt32 && div->InputAt(1)->IsConstant()) {
Scott Wakelingfe885462016-09-22 10:24:38 +01004508 // sdiv will be replaced by other instruction sequence.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004509 } else if (div->GetResultType() == DataType::Type::kInt32 &&
Scott Wakelingfe885462016-09-22 10:24:38 +01004510 !codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4511 // pIdivmod runtime call.
4512 call_kind = LocationSummary::kCallOnMainOnly;
4513 }
4514
4515 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(div, call_kind);
4516
4517 switch (div->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004518 case DataType::Type::kInt32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004519 if (div->InputAt(1)->IsConstant()) {
4520 locations->SetInAt(0, Location::RequiresRegister());
4521 locations->SetInAt(1, Location::ConstantLocation(div->InputAt(1)->AsConstant()));
4522 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Anton Kirilov644032c2016-12-06 17:51:43 +00004523 int32_t value = Int32ConstantFrom(div->InputAt(1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004524 if (value == 1 || value == 0 || value == -1) {
4525 // No temp register required.
4526 } else {
4527 locations->AddTemp(Location::RequiresRegister());
4528 if (!IsPowerOfTwo(AbsOrMin(value))) {
4529 locations->AddTemp(Location::RequiresRegister());
4530 }
4531 }
4532 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4533 locations->SetInAt(0, Location::RequiresRegister());
4534 locations->SetInAt(1, Location::RequiresRegister());
4535 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4536 } else {
Artem Serov551b28f2016-10-18 19:11:30 +01004537 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4538 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
4539 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
Roland Levillain5e8d5f02016-10-18 18:03:43 +01004540 // Note: divmod will compute both the quotient and the remainder as the pair R0 and R1, but
Artem Serov551b28f2016-10-18 19:11:30 +01004541 // we only need the former.
4542 locations->SetOut(LocationFrom(r0));
Scott Wakelingfe885462016-09-22 10:24:38 +01004543 }
4544 break;
4545 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004546 case DataType::Type::kInt64: {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004547 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4548 locations->SetInAt(0, LocationFrom(
4549 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
4550 locations->SetInAt(1, LocationFrom(
4551 calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3)));
4552 locations->SetOut(LocationFrom(r0, r1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004553 break;
4554 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004555 case DataType::Type::kFloat32:
4556 case DataType::Type::kFloat64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004557 locations->SetInAt(0, Location::RequiresFpuRegister());
4558 locations->SetInAt(1, Location::RequiresFpuRegister());
4559 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4560 break;
4561 }
4562
4563 default:
4564 LOG(FATAL) << "Unexpected div type " << div->GetResultType();
4565 }
4566}
4567
4568void InstructionCodeGeneratorARMVIXL::VisitDiv(HDiv* div) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004569 Location lhs = div->GetLocations()->InAt(0);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004570 Location rhs = div->GetLocations()->InAt(1);
Scott Wakelingfe885462016-09-22 10:24:38 +01004571
4572 switch (div->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004573 case DataType::Type::kInt32: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004574 if (rhs.IsConstant()) {
Scott Wakelingfe885462016-09-22 10:24:38 +01004575 GenerateDivRemConstantIntegral(div);
4576 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4577 __ Sdiv(OutputRegister(div), InputRegisterAt(div, 0), InputRegisterAt(div, 1));
4578 } else {
Artem Serov551b28f2016-10-18 19:11:30 +01004579 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4580 DCHECK(calling_convention.GetRegisterAt(0).Is(RegisterFrom(lhs)));
4581 DCHECK(calling_convention.GetRegisterAt(1).Is(RegisterFrom(rhs)));
4582 DCHECK(r0.Is(OutputRegister(div)));
4583
4584 codegen_->InvokeRuntime(kQuickIdivmod, div, div->GetDexPc());
4585 CheckEntrypointTypes<kQuickIdivmod, int32_t, int32_t, int32_t>();
Scott Wakelingfe885462016-09-22 10:24:38 +01004586 }
4587 break;
4588 }
4589
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004590 case DataType::Type::kInt64: {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004591 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4592 DCHECK(calling_convention.GetRegisterAt(0).Is(LowRegisterFrom(lhs)));
4593 DCHECK(calling_convention.GetRegisterAt(1).Is(HighRegisterFrom(lhs)));
4594 DCHECK(calling_convention.GetRegisterAt(2).Is(LowRegisterFrom(rhs)));
4595 DCHECK(calling_convention.GetRegisterAt(3).Is(HighRegisterFrom(rhs)));
4596 DCHECK(LowRegisterFrom(div->GetLocations()->Out()).Is(r0));
4597 DCHECK(HighRegisterFrom(div->GetLocations()->Out()).Is(r1));
4598
4599 codegen_->InvokeRuntime(kQuickLdiv, div, div->GetDexPc());
4600 CheckEntrypointTypes<kQuickLdiv, int64_t, int64_t, int64_t>();
Scott Wakelingfe885462016-09-22 10:24:38 +01004601 break;
4602 }
4603
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004604 case DataType::Type::kFloat32:
4605 case DataType::Type::kFloat64:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004606 __ Vdiv(OutputVRegister(div), InputVRegisterAt(div, 0), InputVRegisterAt(div, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004607 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01004608
4609 default:
4610 LOG(FATAL) << "Unexpected div type " << div->GetResultType();
4611 }
4612}
4613
Artem Serov551b28f2016-10-18 19:11:30 +01004614void LocationsBuilderARMVIXL::VisitRem(HRem* rem) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004615 DataType::Type type = rem->GetResultType();
Artem Serov551b28f2016-10-18 19:11:30 +01004616
4617 // Most remainders are implemented in the runtime.
4618 LocationSummary::CallKind call_kind = LocationSummary::kCallOnMainOnly;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004619 if (rem->GetResultType() == DataType::Type::kInt32 && rem->InputAt(1)->IsConstant()) {
Artem Serov551b28f2016-10-18 19:11:30 +01004620 // sdiv will be replaced by other instruction sequence.
4621 call_kind = LocationSummary::kNoCall;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004622 } else if ((rem->GetResultType() == DataType::Type::kInt32)
Artem Serov551b28f2016-10-18 19:11:30 +01004623 && codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4624 // Have hardware divide instruction for int, do it with three instructions.
4625 call_kind = LocationSummary::kNoCall;
4626 }
4627
4628 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(rem, call_kind);
4629
4630 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004631 case DataType::Type::kInt32: {
Artem Serov551b28f2016-10-18 19:11:30 +01004632 if (rem->InputAt(1)->IsConstant()) {
4633 locations->SetInAt(0, Location::RequiresRegister());
4634 locations->SetInAt(1, Location::ConstantLocation(rem->InputAt(1)->AsConstant()));
4635 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Anton Kirilov644032c2016-12-06 17:51:43 +00004636 int32_t value = Int32ConstantFrom(rem->InputAt(1));
Artem Serov551b28f2016-10-18 19:11:30 +01004637 if (value == 1 || value == 0 || value == -1) {
4638 // No temp register required.
4639 } else {
4640 locations->AddTemp(Location::RequiresRegister());
4641 if (!IsPowerOfTwo(AbsOrMin(value))) {
4642 locations->AddTemp(Location::RequiresRegister());
4643 }
4644 }
4645 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4646 locations->SetInAt(0, Location::RequiresRegister());
4647 locations->SetInAt(1, Location::RequiresRegister());
4648 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4649 locations->AddTemp(Location::RequiresRegister());
4650 } else {
4651 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4652 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
4653 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
Roland Levillain5e8d5f02016-10-18 18:03:43 +01004654 // Note: divmod will compute both the quotient and the remainder as the pair R0 and R1, but
Artem Serov551b28f2016-10-18 19:11:30 +01004655 // we only need the latter.
4656 locations->SetOut(LocationFrom(r1));
4657 }
4658 break;
4659 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004660 case DataType::Type::kInt64: {
Artem Serov551b28f2016-10-18 19:11:30 +01004661 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4662 locations->SetInAt(0, LocationFrom(
4663 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
4664 locations->SetInAt(1, LocationFrom(
4665 calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3)));
4666 // The runtime helper puts the output in R2,R3.
4667 locations->SetOut(LocationFrom(r2, r3));
4668 break;
4669 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004670 case DataType::Type::kFloat32: {
Artem Serov551b28f2016-10-18 19:11:30 +01004671 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4672 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
4673 locations->SetInAt(1, LocationFrom(calling_convention.GetFpuRegisterAt(1)));
4674 locations->SetOut(LocationFrom(s0));
4675 break;
4676 }
4677
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004678 case DataType::Type::kFloat64: {
Artem Serov551b28f2016-10-18 19:11:30 +01004679 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4680 locations->SetInAt(0, LocationFrom(
4681 calling_convention.GetFpuRegisterAt(0), calling_convention.GetFpuRegisterAt(1)));
4682 locations->SetInAt(1, LocationFrom(
4683 calling_convention.GetFpuRegisterAt(2), calling_convention.GetFpuRegisterAt(3)));
4684 locations->SetOut(LocationFrom(s0, s1));
4685 break;
4686 }
4687
4688 default:
4689 LOG(FATAL) << "Unexpected rem type " << type;
4690 }
4691}
4692
4693void InstructionCodeGeneratorARMVIXL::VisitRem(HRem* rem) {
4694 LocationSummary* locations = rem->GetLocations();
4695 Location second = locations->InAt(1);
4696
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004697 DataType::Type type = rem->GetResultType();
Artem Serov551b28f2016-10-18 19:11:30 +01004698 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004699 case DataType::Type::kInt32: {
Artem Serov551b28f2016-10-18 19:11:30 +01004700 vixl32::Register reg1 = InputRegisterAt(rem, 0);
4701 vixl32::Register out_reg = OutputRegister(rem);
4702 if (second.IsConstant()) {
4703 GenerateDivRemConstantIntegral(rem);
4704 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4705 vixl32::Register reg2 = RegisterFrom(second);
4706 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
4707
4708 // temp = reg1 / reg2 (integer division)
4709 // dest = reg1 - temp * reg2
4710 __ Sdiv(temp, reg1, reg2);
4711 __ Mls(out_reg, temp, reg2, reg1);
4712 } else {
4713 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4714 DCHECK(reg1.Is(calling_convention.GetRegisterAt(0)));
4715 DCHECK(RegisterFrom(second).Is(calling_convention.GetRegisterAt(1)));
4716 DCHECK(out_reg.Is(r1));
4717
4718 codegen_->InvokeRuntime(kQuickIdivmod, rem, rem->GetDexPc());
4719 CheckEntrypointTypes<kQuickIdivmod, int32_t, int32_t, int32_t>();
4720 }
4721 break;
4722 }
4723
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004724 case DataType::Type::kInt64: {
Artem Serov551b28f2016-10-18 19:11:30 +01004725 codegen_->InvokeRuntime(kQuickLmod, rem, rem->GetDexPc());
4726 CheckEntrypointTypes<kQuickLmod, int64_t, int64_t, int64_t>();
4727 break;
4728 }
4729
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004730 case DataType::Type::kFloat32: {
Artem Serov551b28f2016-10-18 19:11:30 +01004731 codegen_->InvokeRuntime(kQuickFmodf, rem, rem->GetDexPc());
4732 CheckEntrypointTypes<kQuickFmodf, float, float, float>();
4733 break;
4734 }
4735
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004736 case DataType::Type::kFloat64: {
Artem Serov551b28f2016-10-18 19:11:30 +01004737 codegen_->InvokeRuntime(kQuickFmod, rem, rem->GetDexPc());
4738 CheckEntrypointTypes<kQuickFmod, double, double, double>();
4739 break;
4740 }
4741
4742 default:
4743 LOG(FATAL) << "Unexpected rem type " << type;
4744 }
4745}
4746
4747
Scott Wakelingfe885462016-09-22 10:24:38 +01004748void LocationsBuilderARMVIXL::VisitDivZeroCheck(HDivZeroCheck* instruction) {
Artem Serov657022c2016-11-23 14:19:38 +00004749 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
Scott Wakelingfe885462016-09-22 10:24:38 +01004750 locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0)));
Scott Wakelingfe885462016-09-22 10:24:38 +01004751}
4752
4753void InstructionCodeGeneratorARMVIXL::VisitDivZeroCheck(HDivZeroCheck* instruction) {
4754 DivZeroCheckSlowPathARMVIXL* slow_path =
4755 new (GetGraph()->GetArena()) DivZeroCheckSlowPathARMVIXL(instruction);
4756 codegen_->AddSlowPath(slow_path);
4757
4758 LocationSummary* locations = instruction->GetLocations();
4759 Location value = locations->InAt(0);
4760
4761 switch (instruction->GetType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004762 case DataType::Type::kBool:
4763 case DataType::Type::kInt8:
4764 case DataType::Type::kUint16:
4765 case DataType::Type::kInt16:
4766 case DataType::Type::kInt32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004767 if (value.IsRegister()) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00004768 __ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
Scott Wakelingfe885462016-09-22 10:24:38 +01004769 } else {
4770 DCHECK(value.IsConstant()) << value;
Anton Kirilov644032c2016-12-06 17:51:43 +00004771 if (Int32ConstantFrom(value) == 0) {
Scott Wakelingfe885462016-09-22 10:24:38 +01004772 __ B(slow_path->GetEntryLabel());
4773 }
4774 }
4775 break;
4776 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004777 case DataType::Type::kInt64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004778 if (value.IsRegisterPair()) {
4779 UseScratchRegisterScope temps(GetVIXLAssembler());
4780 vixl32::Register temp = temps.Acquire();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004781 __ Orrs(temp, LowRegisterFrom(value), HighRegisterFrom(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01004782 __ B(eq, slow_path->GetEntryLabel());
4783 } else {
4784 DCHECK(value.IsConstant()) << value;
Anton Kirilov644032c2016-12-06 17:51:43 +00004785 if (Int64ConstantFrom(value) == 0) {
Scott Wakelingfe885462016-09-22 10:24:38 +01004786 __ B(slow_path->GetEntryLabel());
4787 }
4788 }
4789 break;
4790 }
4791 default:
4792 LOG(FATAL) << "Unexpected type for HDivZeroCheck " << instruction->GetType();
4793 }
4794}
4795
Artem Serov02109dd2016-09-23 17:17:54 +01004796void InstructionCodeGeneratorARMVIXL::HandleIntegerRotate(HRor* ror) {
4797 LocationSummary* locations = ror->GetLocations();
4798 vixl32::Register in = InputRegisterAt(ror, 0);
4799 Location rhs = locations->InAt(1);
4800 vixl32::Register out = OutputRegister(ror);
4801
4802 if (rhs.IsConstant()) {
4803 // Arm32 and Thumb2 assemblers require a rotation on the interval [1,31],
4804 // so map all rotations to a +ve. equivalent in that range.
4805 // (e.g. left *or* right by -2 bits == 30 bits in the same direction.)
4806 uint32_t rot = CodeGenerator::GetInt32ValueOf(rhs.GetConstant()) & 0x1F;
4807 if (rot) {
4808 // Rotate, mapping left rotations to right equivalents if necessary.
4809 // (e.g. left by 2 bits == right by 30.)
4810 __ Ror(out, in, rot);
4811 } else if (!out.Is(in)) {
4812 __ Mov(out, in);
4813 }
4814 } else {
4815 __ Ror(out, in, RegisterFrom(rhs));
4816 }
4817}
4818
4819// Gain some speed by mapping all Long rotates onto equivalent pairs of Integer
4820// rotates by swapping input regs (effectively rotating by the first 32-bits of
4821// a larger rotation) or flipping direction (thus treating larger right/left
4822// rotations as sub-word sized rotations in the other direction) as appropriate.
4823void InstructionCodeGeneratorARMVIXL::HandleLongRotate(HRor* ror) {
4824 LocationSummary* locations = ror->GetLocations();
4825 vixl32::Register in_reg_lo = LowRegisterFrom(locations->InAt(0));
4826 vixl32::Register in_reg_hi = HighRegisterFrom(locations->InAt(0));
4827 Location rhs = locations->InAt(1);
4828 vixl32::Register out_reg_lo = LowRegisterFrom(locations->Out());
4829 vixl32::Register out_reg_hi = HighRegisterFrom(locations->Out());
4830
4831 if (rhs.IsConstant()) {
4832 uint64_t rot = CodeGenerator::GetInt64ValueOf(rhs.GetConstant());
4833 // Map all rotations to +ve. equivalents on the interval [0,63].
4834 rot &= kMaxLongShiftDistance;
4835 // For rotates over a word in size, 'pre-rotate' by 32-bits to keep rotate
4836 // logic below to a simple pair of binary orr.
4837 // (e.g. 34 bits == in_reg swap + 2 bits right.)
4838 if (rot >= kArmBitsPerWord) {
4839 rot -= kArmBitsPerWord;
4840 std::swap(in_reg_hi, in_reg_lo);
4841 }
4842 // Rotate, or mov to out for zero or word size rotations.
4843 if (rot != 0u) {
Scott Wakelingb77051e2016-11-21 19:46:00 +00004844 __ Lsr(out_reg_hi, in_reg_hi, Operand::From(rot));
Artem Serov02109dd2016-09-23 17:17:54 +01004845 __ Orr(out_reg_hi, out_reg_hi, Operand(in_reg_lo, ShiftType::LSL, kArmBitsPerWord - rot));
Scott Wakelingb77051e2016-11-21 19:46:00 +00004846 __ Lsr(out_reg_lo, in_reg_lo, Operand::From(rot));
Artem Serov02109dd2016-09-23 17:17:54 +01004847 __ Orr(out_reg_lo, out_reg_lo, Operand(in_reg_hi, ShiftType::LSL, kArmBitsPerWord - rot));
4848 } else {
4849 __ Mov(out_reg_lo, in_reg_lo);
4850 __ Mov(out_reg_hi, in_reg_hi);
4851 }
4852 } else {
4853 vixl32::Register shift_right = RegisterFrom(locations->GetTemp(0));
4854 vixl32::Register shift_left = RegisterFrom(locations->GetTemp(1));
4855 vixl32::Label end;
4856 vixl32::Label shift_by_32_plus_shift_right;
Anton Kirilov6f644202017-02-27 18:29:45 +00004857 vixl32::Label* final_label = codegen_->GetFinalLabel(ror, &end);
Artem Serov02109dd2016-09-23 17:17:54 +01004858
4859 __ And(shift_right, RegisterFrom(rhs), 0x1F);
4860 __ Lsrs(shift_left, RegisterFrom(rhs), 6);
Scott Wakelingbffdc702016-12-07 17:46:03 +00004861 __ Rsb(LeaveFlags, shift_left, shift_right, Operand::From(kArmBitsPerWord));
Artem Serov517d9f62016-12-12 15:51:15 +00004862 __ B(cc, &shift_by_32_plus_shift_right, /* far_target */ false);
Artem Serov02109dd2016-09-23 17:17:54 +01004863
4864 // out_reg_hi = (reg_hi << shift_left) | (reg_lo >> shift_right).
4865 // out_reg_lo = (reg_lo << shift_left) | (reg_hi >> shift_right).
4866 __ Lsl(out_reg_hi, in_reg_hi, shift_left);
4867 __ Lsr(out_reg_lo, in_reg_lo, shift_right);
4868 __ Add(out_reg_hi, out_reg_hi, out_reg_lo);
4869 __ Lsl(out_reg_lo, in_reg_lo, shift_left);
4870 __ Lsr(shift_left, in_reg_hi, shift_right);
4871 __ Add(out_reg_lo, out_reg_lo, shift_left);
Anton Kirilov6f644202017-02-27 18:29:45 +00004872 __ B(final_label);
Artem Serov02109dd2016-09-23 17:17:54 +01004873
4874 __ Bind(&shift_by_32_plus_shift_right); // Shift by 32+shift_right.
4875 // out_reg_hi = (reg_hi >> shift_right) | (reg_lo << shift_left).
4876 // out_reg_lo = (reg_lo >> shift_right) | (reg_hi << shift_left).
4877 __ Lsr(out_reg_hi, in_reg_hi, shift_right);
4878 __ Lsl(out_reg_lo, in_reg_lo, shift_left);
4879 __ Add(out_reg_hi, out_reg_hi, out_reg_lo);
4880 __ Lsr(out_reg_lo, in_reg_lo, shift_right);
4881 __ Lsl(shift_right, in_reg_hi, shift_left);
4882 __ Add(out_reg_lo, out_reg_lo, shift_right);
4883
Anton Kirilov6f644202017-02-27 18:29:45 +00004884 if (end.IsReferenced()) {
4885 __ Bind(&end);
4886 }
Artem Serov02109dd2016-09-23 17:17:54 +01004887 }
4888}
4889
4890void LocationsBuilderARMVIXL::VisitRor(HRor* ror) {
4891 LocationSummary* locations =
4892 new (GetGraph()->GetArena()) LocationSummary(ror, LocationSummary::kNoCall);
4893 switch (ror->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004894 case DataType::Type::kInt32: {
Artem Serov02109dd2016-09-23 17:17:54 +01004895 locations->SetInAt(0, Location::RequiresRegister());
4896 locations->SetInAt(1, Location::RegisterOrConstant(ror->InputAt(1)));
4897 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4898 break;
4899 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004900 case DataType::Type::kInt64: {
Artem Serov02109dd2016-09-23 17:17:54 +01004901 locations->SetInAt(0, Location::RequiresRegister());
4902 if (ror->InputAt(1)->IsConstant()) {
4903 locations->SetInAt(1, Location::ConstantLocation(ror->InputAt(1)->AsConstant()));
4904 } else {
4905 locations->SetInAt(1, Location::RequiresRegister());
4906 locations->AddTemp(Location::RequiresRegister());
4907 locations->AddTemp(Location::RequiresRegister());
4908 }
4909 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
4910 break;
4911 }
4912 default:
4913 LOG(FATAL) << "Unexpected operation type " << ror->GetResultType();
4914 }
4915}
4916
4917void InstructionCodeGeneratorARMVIXL::VisitRor(HRor* ror) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004918 DataType::Type type = ror->GetResultType();
Artem Serov02109dd2016-09-23 17:17:54 +01004919 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004920 case DataType::Type::kInt32: {
Artem Serov02109dd2016-09-23 17:17:54 +01004921 HandleIntegerRotate(ror);
4922 break;
4923 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004924 case DataType::Type::kInt64: {
Artem Serov02109dd2016-09-23 17:17:54 +01004925 HandleLongRotate(ror);
4926 break;
4927 }
4928 default:
4929 LOG(FATAL) << "Unexpected operation type " << type;
4930 UNREACHABLE();
4931 }
4932}
4933
Artem Serov02d37832016-10-25 15:25:33 +01004934void LocationsBuilderARMVIXL::HandleShift(HBinaryOperation* op) {
4935 DCHECK(op->IsShl() || op->IsShr() || op->IsUShr());
4936
4937 LocationSummary* locations =
4938 new (GetGraph()->GetArena()) LocationSummary(op, LocationSummary::kNoCall);
4939
4940 switch (op->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004941 case DataType::Type::kInt32: {
Artem Serov02d37832016-10-25 15:25:33 +01004942 locations->SetInAt(0, Location::RequiresRegister());
4943 if (op->InputAt(1)->IsConstant()) {
4944 locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant()));
4945 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4946 } else {
4947 locations->SetInAt(1, Location::RequiresRegister());
4948 // Make the output overlap, as it will be used to hold the masked
4949 // second input.
4950 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
4951 }
4952 break;
4953 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004954 case DataType::Type::kInt64: {
Artem Serov02d37832016-10-25 15:25:33 +01004955 locations->SetInAt(0, Location::RequiresRegister());
4956 if (op->InputAt(1)->IsConstant()) {
4957 locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant()));
4958 // For simplicity, use kOutputOverlap even though we only require that low registers
4959 // don't clash with high registers which the register allocator currently guarantees.
4960 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
4961 } else {
4962 locations->SetInAt(1, Location::RequiresRegister());
4963 locations->AddTemp(Location::RequiresRegister());
4964 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
4965 }
4966 break;
4967 }
4968 default:
4969 LOG(FATAL) << "Unexpected operation type " << op->GetResultType();
4970 }
4971}
4972
4973void InstructionCodeGeneratorARMVIXL::HandleShift(HBinaryOperation* op) {
4974 DCHECK(op->IsShl() || op->IsShr() || op->IsUShr());
4975
4976 LocationSummary* locations = op->GetLocations();
4977 Location out = locations->Out();
4978 Location first = locations->InAt(0);
4979 Location second = locations->InAt(1);
4980
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004981 DataType::Type type = op->GetResultType();
Artem Serov02d37832016-10-25 15:25:33 +01004982 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004983 case DataType::Type::kInt32: {
Artem Serov02d37832016-10-25 15:25:33 +01004984 vixl32::Register out_reg = OutputRegister(op);
4985 vixl32::Register first_reg = InputRegisterAt(op, 0);
4986 if (second.IsRegister()) {
4987 vixl32::Register second_reg = RegisterFrom(second);
4988 // ARM doesn't mask the shift count so we need to do it ourselves.
4989 __ And(out_reg, second_reg, kMaxIntShiftDistance);
4990 if (op->IsShl()) {
4991 __ Lsl(out_reg, first_reg, out_reg);
4992 } else if (op->IsShr()) {
4993 __ Asr(out_reg, first_reg, out_reg);
4994 } else {
4995 __ Lsr(out_reg, first_reg, out_reg);
4996 }
4997 } else {
Anton Kirilov644032c2016-12-06 17:51:43 +00004998 int32_t cst = Int32ConstantFrom(second);
Artem Serov02d37832016-10-25 15:25:33 +01004999 uint32_t shift_value = cst & kMaxIntShiftDistance;
5000 if (shift_value == 0) { // ARM does not support shifting with 0 immediate.
5001 __ Mov(out_reg, first_reg);
5002 } else if (op->IsShl()) {
5003 __ Lsl(out_reg, first_reg, shift_value);
5004 } else if (op->IsShr()) {
5005 __ Asr(out_reg, first_reg, shift_value);
5006 } else {
5007 __ Lsr(out_reg, first_reg, shift_value);
5008 }
5009 }
5010 break;
5011 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005012 case DataType::Type::kInt64: {
Artem Serov02d37832016-10-25 15:25:33 +01005013 vixl32::Register o_h = HighRegisterFrom(out);
5014 vixl32::Register o_l = LowRegisterFrom(out);
5015
5016 vixl32::Register high = HighRegisterFrom(first);
5017 vixl32::Register low = LowRegisterFrom(first);
5018
5019 if (second.IsRegister()) {
5020 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
5021
5022 vixl32::Register second_reg = RegisterFrom(second);
5023
5024 if (op->IsShl()) {
5025 __ And(o_l, second_reg, kMaxLongShiftDistance);
5026 // Shift the high part
5027 __ Lsl(o_h, high, o_l);
5028 // Shift the low part and `or` what overflew on the high part
Scott Wakelingb77051e2016-11-21 19:46:00 +00005029 __ Rsb(temp, o_l, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01005030 __ Lsr(temp, low, temp);
5031 __ Orr(o_h, o_h, temp);
5032 // If the shift is > 32 bits, override the high part
Scott Wakelingb77051e2016-11-21 19:46:00 +00005033 __ Subs(temp, o_l, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01005034 {
Artem Serov0fb37192016-12-06 18:13:40 +00005035 ExactAssemblyScope guard(GetVIXLAssembler(),
5036 2 * vixl32::kMaxInstructionSizeInBytes,
5037 CodeBufferCheckScope::kMaximumSize);
Artem Serov02d37832016-10-25 15:25:33 +01005038 __ it(pl);
5039 __ lsl(pl, o_h, low, temp);
5040 }
5041 // Shift the low part
5042 __ Lsl(o_l, low, o_l);
5043 } else if (op->IsShr()) {
5044 __ And(o_h, second_reg, kMaxLongShiftDistance);
5045 // Shift the low part
5046 __ Lsr(o_l, low, o_h);
5047 // Shift the high part and `or` what underflew on the low part
Scott Wakelingb77051e2016-11-21 19:46:00 +00005048 __ Rsb(temp, o_h, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01005049 __ Lsl(temp, high, temp);
5050 __ Orr(o_l, o_l, temp);
5051 // If the shift is > 32 bits, override the low part
Scott Wakelingb77051e2016-11-21 19:46:00 +00005052 __ Subs(temp, o_h, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01005053 {
Artem Serov0fb37192016-12-06 18:13:40 +00005054 ExactAssemblyScope guard(GetVIXLAssembler(),
5055 2 * vixl32::kMaxInstructionSizeInBytes,
5056 CodeBufferCheckScope::kMaximumSize);
Artem Serov02d37832016-10-25 15:25:33 +01005057 __ it(pl);
5058 __ asr(pl, o_l, high, temp);
5059 }
5060 // Shift the high part
5061 __ Asr(o_h, high, o_h);
5062 } else {
5063 __ And(o_h, second_reg, kMaxLongShiftDistance);
5064 // same as Shr except we use `Lsr`s and not `Asr`s
5065 __ Lsr(o_l, low, o_h);
Scott Wakelingb77051e2016-11-21 19:46:00 +00005066 __ Rsb(temp, o_h, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01005067 __ Lsl(temp, high, temp);
5068 __ Orr(o_l, o_l, temp);
Scott Wakelingb77051e2016-11-21 19:46:00 +00005069 __ Subs(temp, o_h, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01005070 {
Artem Serov0fb37192016-12-06 18:13:40 +00005071 ExactAssemblyScope guard(GetVIXLAssembler(),
5072 2 * vixl32::kMaxInstructionSizeInBytes,
5073 CodeBufferCheckScope::kMaximumSize);
Artem Serov02d37832016-10-25 15:25:33 +01005074 __ it(pl);
5075 __ lsr(pl, o_l, high, temp);
5076 }
5077 __ Lsr(o_h, high, o_h);
5078 }
5079 } else {
5080 // Register allocator doesn't create partial overlap.
5081 DCHECK(!o_l.Is(high));
5082 DCHECK(!o_h.Is(low));
Anton Kirilov644032c2016-12-06 17:51:43 +00005083 int32_t cst = Int32ConstantFrom(second);
Artem Serov02d37832016-10-25 15:25:33 +01005084 uint32_t shift_value = cst & kMaxLongShiftDistance;
5085 if (shift_value > 32) {
5086 if (op->IsShl()) {
5087 __ Lsl(o_h, low, shift_value - 32);
5088 __ Mov(o_l, 0);
5089 } else if (op->IsShr()) {
5090 __ Asr(o_l, high, shift_value - 32);
5091 __ Asr(o_h, high, 31);
5092 } else {
5093 __ Lsr(o_l, high, shift_value - 32);
5094 __ Mov(o_h, 0);
5095 }
5096 } else if (shift_value == 32) {
5097 if (op->IsShl()) {
5098 __ Mov(o_h, low);
5099 __ Mov(o_l, 0);
5100 } else if (op->IsShr()) {
5101 __ Mov(o_l, high);
5102 __ Asr(o_h, high, 31);
5103 } else {
5104 __ Mov(o_l, high);
5105 __ Mov(o_h, 0);
5106 }
5107 } else if (shift_value == 1) {
5108 if (op->IsShl()) {
5109 __ Lsls(o_l, low, 1);
5110 __ Adc(o_h, high, high);
5111 } else if (op->IsShr()) {
5112 __ Asrs(o_h, high, 1);
5113 __ Rrx(o_l, low);
5114 } else {
5115 __ Lsrs(o_h, high, 1);
5116 __ Rrx(o_l, low);
5117 }
5118 } else {
5119 DCHECK(2 <= shift_value && shift_value < 32) << shift_value;
5120 if (op->IsShl()) {
5121 __ Lsl(o_h, high, shift_value);
5122 __ Orr(o_h, o_h, Operand(low, ShiftType::LSR, 32 - shift_value));
5123 __ Lsl(o_l, low, shift_value);
5124 } else if (op->IsShr()) {
5125 __ Lsr(o_l, low, shift_value);
5126 __ Orr(o_l, o_l, Operand(high, ShiftType::LSL, 32 - shift_value));
5127 __ Asr(o_h, high, shift_value);
5128 } else {
5129 __ Lsr(o_l, low, shift_value);
5130 __ Orr(o_l, o_l, Operand(high, ShiftType::LSL, 32 - shift_value));
5131 __ Lsr(o_h, high, shift_value);
5132 }
5133 }
5134 }
5135 break;
5136 }
5137 default:
5138 LOG(FATAL) << "Unexpected operation type " << type;
5139 UNREACHABLE();
5140 }
5141}
5142
5143void LocationsBuilderARMVIXL::VisitShl(HShl* shl) {
5144 HandleShift(shl);
5145}
5146
5147void InstructionCodeGeneratorARMVIXL::VisitShl(HShl* shl) {
5148 HandleShift(shl);
5149}
5150
5151void LocationsBuilderARMVIXL::VisitShr(HShr* shr) {
5152 HandleShift(shr);
5153}
5154
5155void InstructionCodeGeneratorARMVIXL::VisitShr(HShr* shr) {
5156 HandleShift(shr);
5157}
5158
5159void LocationsBuilderARMVIXL::VisitUShr(HUShr* ushr) {
5160 HandleShift(ushr);
5161}
5162
5163void InstructionCodeGeneratorARMVIXL::VisitUShr(HUShr* ushr) {
5164 HandleShift(ushr);
5165}
5166
5167void LocationsBuilderARMVIXL::VisitNewInstance(HNewInstance* instruction) {
5168 LocationSummary* locations =
5169 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
5170 if (instruction->IsStringAlloc()) {
5171 locations->AddTemp(LocationFrom(kMethodRegister));
5172 } else {
5173 InvokeRuntimeCallingConventionARMVIXL calling_convention;
5174 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
Artem Serov02d37832016-10-25 15:25:33 +01005175 }
5176 locations->SetOut(LocationFrom(r0));
5177}
5178
5179void InstructionCodeGeneratorARMVIXL::VisitNewInstance(HNewInstance* instruction) {
5180 // Note: if heap poisoning is enabled, the entry point takes cares
5181 // of poisoning the reference.
5182 if (instruction->IsStringAlloc()) {
5183 // String is allocated through StringFactory. Call NewEmptyString entry point.
5184 vixl32::Register temp = RegisterFrom(instruction->GetLocations()->GetTemp(0));
5185 MemberOffset code_offset = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize);
5186 GetAssembler()->LoadFromOffset(kLoadWord, temp, tr, QUICK_ENTRY_POINT(pNewEmptyString));
5187 GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, code_offset.Int32Value());
Alexandre Rames374ddf32016-11-04 10:40:49 +00005188 // 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 +00005189 ExactAssemblyScope aas(GetVIXLAssembler(),
5190 vixl32::k16BitT32InstructionSizeInBytes,
5191 CodeBufferCheckScope::kExactSize);
Artem Serov02d37832016-10-25 15:25:33 +01005192 __ blx(lr);
5193 codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
5194 } else {
5195 codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
Nicolas Geoffray0d3998b2017-01-12 15:35:12 +00005196 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
Artem Serov02d37832016-10-25 15:25:33 +01005197 }
Roland Levillain5daa4952017-07-03 17:23:56 +01005198 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 10);
Artem Serov02d37832016-10-25 15:25:33 +01005199}
5200
5201void LocationsBuilderARMVIXL::VisitNewArray(HNewArray* instruction) {
5202 LocationSummary* locations =
5203 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
5204 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Artem Serov02d37832016-10-25 15:25:33 +01005205 locations->SetOut(LocationFrom(r0));
Nicolas Geoffray8c7c4f12017-01-26 10:13:11 +00005206 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
5207 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
Artem Serov02d37832016-10-25 15:25:33 +01005208}
5209
5210void InstructionCodeGeneratorARMVIXL::VisitNewArray(HNewArray* instruction) {
Artem Serov02d37832016-10-25 15:25:33 +01005211 // Note: if heap poisoning is enabled, the entry point takes cares
5212 // of poisoning the reference.
Artem Serov7b3672e2017-02-03 17:30:34 +00005213 QuickEntrypointEnum entrypoint =
5214 CodeGenerator::GetArrayAllocationEntrypoint(instruction->GetLoadClass()->GetClass());
5215 codegen_->InvokeRuntime(entrypoint, instruction, instruction->GetDexPc());
Nicolas Geoffraye761bcc2017-01-19 08:59:37 +00005216 CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>();
Artem Serov7b3672e2017-02-03 17:30:34 +00005217 DCHECK(!codegen_->IsLeafMethod());
Roland Levillain5daa4952017-07-03 17:23:56 +01005218 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 11);
Artem Serov02d37832016-10-25 15:25:33 +01005219}
5220
5221void LocationsBuilderARMVIXL::VisitParameterValue(HParameterValue* instruction) {
5222 LocationSummary* locations =
5223 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
5224 Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
5225 if (location.IsStackSlot()) {
5226 location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
5227 } else if (location.IsDoubleStackSlot()) {
5228 location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
5229 }
5230 locations->SetOut(location);
5231}
5232
5233void InstructionCodeGeneratorARMVIXL::VisitParameterValue(
5234 HParameterValue* instruction ATTRIBUTE_UNUSED) {
5235 // Nothing to do, the parameter is already at its location.
5236}
5237
5238void LocationsBuilderARMVIXL::VisitCurrentMethod(HCurrentMethod* instruction) {
5239 LocationSummary* locations =
5240 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
5241 locations->SetOut(LocationFrom(kMethodRegister));
5242}
5243
5244void InstructionCodeGeneratorARMVIXL::VisitCurrentMethod(
5245 HCurrentMethod* instruction ATTRIBUTE_UNUSED) {
5246 // Nothing to do, the method is already at its location.
5247}
5248
5249void LocationsBuilderARMVIXL::VisitNot(HNot* not_) {
5250 LocationSummary* locations =
5251 new (GetGraph()->GetArena()) LocationSummary(not_, LocationSummary::kNoCall);
5252 locations->SetInAt(0, Location::RequiresRegister());
5253 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5254}
5255
5256void InstructionCodeGeneratorARMVIXL::VisitNot(HNot* not_) {
5257 LocationSummary* locations = not_->GetLocations();
5258 Location out = locations->Out();
5259 Location in = locations->InAt(0);
5260 switch (not_->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005261 case DataType::Type::kInt32:
Artem Serov02d37832016-10-25 15:25:33 +01005262 __ Mvn(OutputRegister(not_), InputRegisterAt(not_, 0));
5263 break;
5264
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005265 case DataType::Type::kInt64:
Artem Serov02d37832016-10-25 15:25:33 +01005266 __ Mvn(LowRegisterFrom(out), LowRegisterFrom(in));
5267 __ Mvn(HighRegisterFrom(out), HighRegisterFrom(in));
5268 break;
5269
5270 default:
5271 LOG(FATAL) << "Unimplemented type for not operation " << not_->GetResultType();
5272 }
5273}
5274
Scott Wakelingc34dba72016-10-03 10:14:44 +01005275void LocationsBuilderARMVIXL::VisitBooleanNot(HBooleanNot* bool_not) {
5276 LocationSummary* locations =
5277 new (GetGraph()->GetArena()) LocationSummary(bool_not, LocationSummary::kNoCall);
5278 locations->SetInAt(0, Location::RequiresRegister());
5279 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5280}
5281
5282void InstructionCodeGeneratorARMVIXL::VisitBooleanNot(HBooleanNot* bool_not) {
5283 __ Eor(OutputRegister(bool_not), InputRegister(bool_not), 1);
5284}
5285
Artem Serov02d37832016-10-25 15:25:33 +01005286void LocationsBuilderARMVIXL::VisitCompare(HCompare* compare) {
5287 LocationSummary* locations =
5288 new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall);
5289 switch (compare->InputAt(0)->GetType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005290 case DataType::Type::kBool:
5291 case DataType::Type::kInt8:
5292 case DataType::Type::kInt16:
5293 case DataType::Type::kUint16:
5294 case DataType::Type::kInt32:
5295 case DataType::Type::kInt64: {
Artem Serov02d37832016-10-25 15:25:33 +01005296 locations->SetInAt(0, Location::RequiresRegister());
5297 locations->SetInAt(1, Location::RequiresRegister());
5298 // Output overlaps because it is written before doing the low comparison.
5299 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
5300 break;
5301 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005302 case DataType::Type::kFloat32:
5303 case DataType::Type::kFloat64: {
Artem Serov02d37832016-10-25 15:25:33 +01005304 locations->SetInAt(0, Location::RequiresFpuRegister());
5305 locations->SetInAt(1, ArithmeticZeroOrFpuRegister(compare->InputAt(1)));
5306 locations->SetOut(Location::RequiresRegister());
5307 break;
5308 }
5309 default:
5310 LOG(FATAL) << "Unexpected type for compare operation " << compare->InputAt(0)->GetType();
5311 }
5312}
5313
5314void InstructionCodeGeneratorARMVIXL::VisitCompare(HCompare* compare) {
5315 LocationSummary* locations = compare->GetLocations();
5316 vixl32::Register out = OutputRegister(compare);
5317 Location left = locations->InAt(0);
5318 Location right = locations->InAt(1);
5319
5320 vixl32::Label less, greater, done;
Anton Kirilov6f644202017-02-27 18:29:45 +00005321 vixl32::Label* final_label = codegen_->GetFinalLabel(compare, &done);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005322 DataType::Type type = compare->InputAt(0)->GetType();
Artem Serov02d37832016-10-25 15:25:33 +01005323 vixl32::Condition less_cond = vixl32::Condition(kNone);
5324 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005325 case DataType::Type::kBool:
5326 case DataType::Type::kInt8:
5327 case DataType::Type::kInt16:
5328 case DataType::Type::kUint16:
5329 case DataType::Type::kInt32: {
Artem Serov02d37832016-10-25 15:25:33 +01005330 // Emit move to `out` before the `Cmp`, as `Mov` might affect the status flags.
5331 __ Mov(out, 0);
5332 __ Cmp(RegisterFrom(left), RegisterFrom(right)); // Signed compare.
5333 less_cond = lt;
5334 break;
5335 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005336 case DataType::Type::kInt64: {
Artem Serov02d37832016-10-25 15:25:33 +01005337 __ Cmp(HighRegisterFrom(left), HighRegisterFrom(right)); // Signed compare.
Artem Serov517d9f62016-12-12 15:51:15 +00005338 __ B(lt, &less, /* far_target */ false);
5339 __ B(gt, &greater, /* far_target */ false);
Artem Serov02d37832016-10-25 15:25:33 +01005340 // Emit move to `out` before the last `Cmp`, as `Mov` might affect the status flags.
5341 __ Mov(out, 0);
5342 __ Cmp(LowRegisterFrom(left), LowRegisterFrom(right)); // Unsigned compare.
5343 less_cond = lo;
5344 break;
5345 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005346 case DataType::Type::kFloat32:
5347 case DataType::Type::kFloat64: {
Artem Serov02d37832016-10-25 15:25:33 +01005348 __ Mov(out, 0);
Donghui Bai426b49c2016-11-08 14:55:38 +08005349 GenerateVcmp(compare, codegen_);
Artem Serov02d37832016-10-25 15:25:33 +01005350 // To branch on the FP compare result we transfer FPSCR to APSR (encoded as PC in VMRS).
5351 __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
5352 less_cond = ARMFPCondition(kCondLT, compare->IsGtBias());
5353 break;
5354 }
5355 default:
5356 LOG(FATAL) << "Unexpected compare type " << type;
5357 UNREACHABLE();
5358 }
5359
Anton Kirilov6f644202017-02-27 18:29:45 +00005360 __ B(eq, final_label, /* far_target */ false);
Artem Serov517d9f62016-12-12 15:51:15 +00005361 __ B(less_cond, &less, /* far_target */ false);
Artem Serov02d37832016-10-25 15:25:33 +01005362
5363 __ Bind(&greater);
5364 __ Mov(out, 1);
Anton Kirilov6f644202017-02-27 18:29:45 +00005365 __ B(final_label);
Artem Serov02d37832016-10-25 15:25:33 +01005366
5367 __ Bind(&less);
5368 __ Mov(out, -1);
5369
Anton Kirilov6f644202017-02-27 18:29:45 +00005370 if (done.IsReferenced()) {
5371 __ Bind(&done);
5372 }
Artem Serov02d37832016-10-25 15:25:33 +01005373}
5374
5375void LocationsBuilderARMVIXL::VisitPhi(HPhi* instruction) {
5376 LocationSummary* locations =
5377 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
5378 for (size_t i = 0, e = locations->GetInputCount(); i < e; ++i) {
5379 locations->SetInAt(i, Location::Any());
5380 }
5381 locations->SetOut(Location::Any());
5382}
5383
5384void InstructionCodeGeneratorARMVIXL::VisitPhi(HPhi* instruction ATTRIBUTE_UNUSED) {
5385 LOG(FATAL) << "Unreachable";
5386}
5387
5388void CodeGeneratorARMVIXL::GenerateMemoryBarrier(MemBarrierKind kind) {
5389 // TODO (ported from quick): revisit ARM barrier kinds.
5390 DmbOptions flavor = DmbOptions::ISH; // Quiet C++ warnings.
5391 switch (kind) {
5392 case MemBarrierKind::kAnyStore:
5393 case MemBarrierKind::kLoadAny:
5394 case MemBarrierKind::kAnyAny: {
5395 flavor = DmbOptions::ISH;
5396 break;
5397 }
5398 case MemBarrierKind::kStoreStore: {
5399 flavor = DmbOptions::ISHST;
5400 break;
5401 }
5402 default:
5403 LOG(FATAL) << "Unexpected memory barrier " << kind;
5404 }
5405 __ Dmb(flavor);
5406}
5407
5408void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicLoad(vixl32::Register addr,
5409 uint32_t offset,
5410 vixl32::Register out_lo,
5411 vixl32::Register out_hi) {
5412 UseScratchRegisterScope temps(GetVIXLAssembler());
5413 if (offset != 0) {
5414 vixl32::Register temp = temps.Acquire();
5415 __ Add(temp, addr, offset);
5416 addr = temp;
5417 }
Scott Wakelingb77051e2016-11-21 19:46:00 +00005418 __ Ldrexd(out_lo, out_hi, MemOperand(addr));
Artem Serov02d37832016-10-25 15:25:33 +01005419}
5420
5421void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicStore(vixl32::Register addr,
5422 uint32_t offset,
5423 vixl32::Register value_lo,
5424 vixl32::Register value_hi,
5425 vixl32::Register temp1,
5426 vixl32::Register temp2,
5427 HInstruction* instruction) {
5428 UseScratchRegisterScope temps(GetVIXLAssembler());
5429 vixl32::Label fail;
5430 if (offset != 0) {
5431 vixl32::Register temp = temps.Acquire();
5432 __ Add(temp, addr, offset);
5433 addr = temp;
5434 }
5435 __ Bind(&fail);
Alexandre Rames374ddf32016-11-04 10:40:49 +00005436 {
5437 // Ensure the pc position is recorded immediately after the `ldrexd` instruction.
Artem Serov0fb37192016-12-06 18:13:40 +00005438 ExactAssemblyScope aas(GetVIXLAssembler(),
5439 vixl32::kMaxInstructionSizeInBytes,
5440 CodeBufferCheckScope::kMaximumSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00005441 // We need a load followed by store. (The address used in a STREX instruction must
5442 // be the same as the address in the most recently executed LDREX instruction.)
5443 __ ldrexd(temp1, temp2, MemOperand(addr));
5444 codegen_->MaybeRecordImplicitNullCheck(instruction);
5445 }
Scott Wakelingb77051e2016-11-21 19:46:00 +00005446 __ Strexd(temp1, value_lo, value_hi, MemOperand(addr));
xueliang.zhongf51bc622016-11-04 09:23:32 +00005447 __ CompareAndBranchIfNonZero(temp1, &fail);
Artem Serov02d37832016-10-25 15:25:33 +01005448}
Artem Serov02109dd2016-09-23 17:17:54 +01005449
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005450void LocationsBuilderARMVIXL::HandleFieldSet(
5451 HInstruction* instruction, const FieldInfo& field_info) {
5452 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
5453
5454 LocationSummary* locations =
5455 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
5456 locations->SetInAt(0, Location::RequiresRegister());
5457
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005458 DataType::Type field_type = field_info.GetFieldType();
5459 if (DataType::IsFloatingPointType(field_type)) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005460 locations->SetInAt(1, Location::RequiresFpuRegister());
5461 } else {
5462 locations->SetInAt(1, Location::RequiresRegister());
5463 }
5464
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005465 bool is_wide = field_type == DataType::Type::kInt64 || field_type == DataType::Type::kFloat64;
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005466 bool generate_volatile = field_info.IsVolatile()
5467 && is_wide
5468 && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
5469 bool needs_write_barrier =
5470 CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1));
5471 // Temporary registers for the write barrier.
5472 // TODO: consider renaming StoreNeedsWriteBarrier to StoreNeedsGCMark.
5473 if (needs_write_barrier) {
5474 locations->AddTemp(Location::RequiresRegister()); // Possibly used for reference poisoning too.
5475 locations->AddTemp(Location::RequiresRegister());
5476 } else if (generate_volatile) {
5477 // ARM encoding have some additional constraints for ldrexd/strexd:
5478 // - registers need to be consecutive
5479 // - the first register should be even but not R14.
5480 // We don't test for ARM yet, and the assertion makes sure that we
5481 // revisit this if we ever enable ARM encoding.
5482 DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet());
5483
5484 locations->AddTemp(Location::RequiresRegister());
5485 locations->AddTemp(Location::RequiresRegister());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005486 if (field_type == DataType::Type::kFloat64) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005487 // For doubles we need two more registers to copy the value.
5488 locations->AddTemp(LocationFrom(r2));
5489 locations->AddTemp(LocationFrom(r3));
5490 }
5491 }
5492}
5493
5494void InstructionCodeGeneratorARMVIXL::HandleFieldSet(HInstruction* instruction,
5495 const FieldInfo& field_info,
5496 bool value_can_be_null) {
5497 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
5498
5499 LocationSummary* locations = instruction->GetLocations();
5500 vixl32::Register base = InputRegisterAt(instruction, 0);
5501 Location value = locations->InAt(1);
5502
5503 bool is_volatile = field_info.IsVolatile();
5504 bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005505 DataType::Type field_type = field_info.GetFieldType();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005506 uint32_t offset = field_info.GetFieldOffset().Uint32Value();
5507 bool needs_write_barrier =
5508 CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1));
5509
5510 if (is_volatile) {
5511 codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
5512 }
5513
5514 switch (field_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005515 case DataType::Type::kBool:
5516 case DataType::Type::kInt8: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005517 GetAssembler()->StoreToOffset(kStoreByte, RegisterFrom(value), base, offset);
5518 break;
5519 }
5520
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005521 case DataType::Type::kInt16:
5522 case DataType::Type::kUint16: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005523 GetAssembler()->StoreToOffset(kStoreHalfword, RegisterFrom(value), base, offset);
5524 break;
5525 }
5526
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005527 case DataType::Type::kInt32:
5528 case DataType::Type::kReference: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005529 if (kPoisonHeapReferences && needs_write_barrier) {
5530 // Note that in the case where `value` is a null reference,
5531 // we do not enter this block, as a null reference does not
5532 // need poisoning.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005533 DCHECK_EQ(field_type, DataType::Type::kReference);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005534 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
5535 __ Mov(temp, RegisterFrom(value));
5536 GetAssembler()->PoisonHeapReference(temp);
5537 GetAssembler()->StoreToOffset(kStoreWord, temp, base, offset);
5538 } else {
5539 GetAssembler()->StoreToOffset(kStoreWord, RegisterFrom(value), base, offset);
5540 }
5541 break;
5542 }
5543
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005544 case DataType::Type::kInt64: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005545 if (is_volatile && !atomic_ldrd_strd) {
5546 GenerateWideAtomicStore(base,
5547 offset,
5548 LowRegisterFrom(value),
5549 HighRegisterFrom(value),
5550 RegisterFrom(locations->GetTemp(0)),
5551 RegisterFrom(locations->GetTemp(1)),
5552 instruction);
5553 } else {
5554 GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), base, offset);
5555 codegen_->MaybeRecordImplicitNullCheck(instruction);
5556 }
5557 break;
5558 }
5559
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005560 case DataType::Type::kFloat32: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005561 GetAssembler()->StoreSToOffset(SRegisterFrom(value), base, offset);
5562 break;
5563 }
5564
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005565 case DataType::Type::kFloat64: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01005566 vixl32::DRegister value_reg = DRegisterFrom(value);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005567 if (is_volatile && !atomic_ldrd_strd) {
5568 vixl32::Register value_reg_lo = RegisterFrom(locations->GetTemp(0));
5569 vixl32::Register value_reg_hi = RegisterFrom(locations->GetTemp(1));
5570
5571 __ Vmov(value_reg_lo, value_reg_hi, value_reg);
5572
5573 GenerateWideAtomicStore(base,
5574 offset,
5575 value_reg_lo,
5576 value_reg_hi,
5577 RegisterFrom(locations->GetTemp(2)),
5578 RegisterFrom(locations->GetTemp(3)),
5579 instruction);
5580 } else {
5581 GetAssembler()->StoreDToOffset(value_reg, base, offset);
5582 codegen_->MaybeRecordImplicitNullCheck(instruction);
5583 }
5584 break;
5585 }
5586
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005587 case DataType::Type::kVoid:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005588 LOG(FATAL) << "Unreachable type " << field_type;
5589 UNREACHABLE();
5590 }
5591
5592 // Longs and doubles are handled in the switch.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005593 if (field_type != DataType::Type::kInt64 && field_type != DataType::Type::kFloat64) {
Alexandre Rames374ddf32016-11-04 10:40:49 +00005594 // TODO(VIXL): Here and for other calls to `MaybeRecordImplicitNullCheck` in this method, we
5595 // should use a scope and the assembler to emit the store instruction to guarantee that we
5596 // record the pc at the correct position. But the `Assembler` does not automatically handle
5597 // unencodable offsets. Practically, everything is fine because the helper and VIXL, at the time
5598 // of writing, do generate the store instruction last.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005599 codegen_->MaybeRecordImplicitNullCheck(instruction);
5600 }
5601
5602 if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
5603 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
5604 vixl32::Register card = RegisterFrom(locations->GetTemp(1));
5605 codegen_->MarkGCCard(temp, card, base, RegisterFrom(value), value_can_be_null);
5606 }
5607
5608 if (is_volatile) {
5609 codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
5610 }
5611}
5612
Artem Serov02d37832016-10-25 15:25:33 +01005613void LocationsBuilderARMVIXL::HandleFieldGet(HInstruction* instruction,
5614 const FieldInfo& field_info) {
5615 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
5616
5617 bool object_field_get_with_read_barrier =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005618 kEmitCompilerReadBarrier && (field_info.GetFieldType() == DataType::Type::kReference);
Artem Serov02d37832016-10-25 15:25:33 +01005619 LocationSummary* locations =
5620 new (GetGraph()->GetArena()) LocationSummary(instruction,
5621 object_field_get_with_read_barrier ?
5622 LocationSummary::kCallOnSlowPath :
5623 LocationSummary::kNoCall);
5624 if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
5625 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
5626 }
5627 locations->SetInAt(0, Location::RequiresRegister());
5628
5629 bool volatile_for_double = field_info.IsVolatile()
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005630 && (field_info.GetFieldType() == DataType::Type::kFloat64)
Artem Serov02d37832016-10-25 15:25:33 +01005631 && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
5632 // The output overlaps in case of volatile long: we don't want the
5633 // code generated by GenerateWideAtomicLoad to overwrite the
5634 // object's location. Likewise, in the case of an object field get
5635 // with read barriers enabled, we do not want the load to overwrite
5636 // the object's location, as we need it to emit the read barrier.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005637 bool overlap =
5638 (field_info.IsVolatile() && (field_info.GetFieldType() == DataType::Type::kInt64)) ||
Artem Serov02d37832016-10-25 15:25:33 +01005639 object_field_get_with_read_barrier;
5640
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005641 if (DataType::IsFloatingPointType(instruction->GetType())) {
Artem Serov02d37832016-10-25 15:25:33 +01005642 locations->SetOut(Location::RequiresFpuRegister());
5643 } else {
5644 locations->SetOut(Location::RequiresRegister(),
5645 (overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap));
5646 }
5647 if (volatile_for_double) {
5648 // ARM encoding have some additional constraints for ldrexd/strexd:
5649 // - registers need to be consecutive
5650 // - the first register should be even but not R14.
5651 // We don't test for ARM yet, and the assertion makes sure that we
5652 // revisit this if we ever enable ARM encoding.
5653 DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet());
5654 locations->AddTemp(Location::RequiresRegister());
5655 locations->AddTemp(Location::RequiresRegister());
5656 } else if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
5657 // We need a temporary register for the read barrier marking slow
Artem Serovc5fcb442016-12-02 19:19:58 +00005658 // path in CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01005659 if (kBakerReadBarrierLinkTimeThunksEnableForFields &&
5660 !Runtime::Current()->UseJitCompilation()) {
5661 // If link-time thunks for the Baker read barrier are enabled, for AOT
5662 // loads we need a temporary only if the offset is too big.
5663 if (field_info.GetFieldOffset().Uint32Value() >= kReferenceLoadMinFarOffset) {
5664 locations->AddTemp(Location::RequiresRegister());
5665 }
5666 // And we always need the reserved entrypoint register.
5667 locations->AddTemp(Location::RegisterLocation(kBakerCcEntrypointRegister.GetCode()));
5668 } else {
5669 locations->AddTemp(Location::RequiresRegister());
5670 }
Artem Serov02d37832016-10-25 15:25:33 +01005671 }
5672}
5673
5674Location LocationsBuilderARMVIXL::ArithmeticZeroOrFpuRegister(HInstruction* input) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005675 DCHECK(DataType::IsFloatingPointType(input->GetType())) << input->GetType();
Artem Serov02d37832016-10-25 15:25:33 +01005676 if ((input->IsFloatConstant() && (input->AsFloatConstant()->IsArithmeticZero())) ||
5677 (input->IsDoubleConstant() && (input->AsDoubleConstant()->IsArithmeticZero()))) {
5678 return Location::ConstantLocation(input->AsConstant());
5679 } else {
5680 return Location::RequiresFpuRegister();
5681 }
5682}
5683
Artem Serov02109dd2016-09-23 17:17:54 +01005684Location LocationsBuilderARMVIXL::ArmEncodableConstantOrRegister(HInstruction* constant,
5685 Opcode opcode) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005686 DCHECK(!DataType::IsFloatingPointType(constant->GetType()));
Artem Serov02109dd2016-09-23 17:17:54 +01005687 if (constant->IsConstant() &&
5688 CanEncodeConstantAsImmediate(constant->AsConstant(), opcode)) {
5689 return Location::ConstantLocation(constant->AsConstant());
5690 }
5691 return Location::RequiresRegister();
5692}
5693
5694bool LocationsBuilderARMVIXL::CanEncodeConstantAsImmediate(HConstant* input_cst,
5695 Opcode opcode) {
5696 uint64_t value = static_cast<uint64_t>(Int64FromConstant(input_cst));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005697 if (DataType::Is64BitType(input_cst->GetType())) {
Artem Serov02109dd2016-09-23 17:17:54 +01005698 Opcode high_opcode = opcode;
5699 SetCc low_set_cc = kCcDontCare;
5700 switch (opcode) {
5701 case SUB:
5702 // Flip the operation to an ADD.
5703 value = -value;
5704 opcode = ADD;
5705 FALLTHROUGH_INTENDED;
5706 case ADD:
5707 if (Low32Bits(value) == 0u) {
5708 return CanEncodeConstantAsImmediate(High32Bits(value), opcode, kCcDontCare);
5709 }
5710 high_opcode = ADC;
5711 low_set_cc = kCcSet;
5712 break;
5713 default:
5714 break;
5715 }
5716 return CanEncodeConstantAsImmediate(Low32Bits(value), opcode, low_set_cc) &&
5717 CanEncodeConstantAsImmediate(High32Bits(value), high_opcode, kCcDontCare);
5718 } else {
5719 return CanEncodeConstantAsImmediate(Low32Bits(value), opcode);
5720 }
5721}
5722
5723// TODO(VIXL): Replace art::arm::SetCc` with `vixl32::FlagsUpdate after flags set optimization
5724// enabled.
5725bool LocationsBuilderARMVIXL::CanEncodeConstantAsImmediate(uint32_t value,
5726 Opcode opcode,
5727 SetCc set_cc) {
5728 ArmVIXLAssembler* assembler = codegen_->GetAssembler();
5729 if (assembler->ShifterOperandCanHold(opcode, value, set_cc)) {
5730 return true;
5731 }
5732 Opcode neg_opcode = kNoOperand;
Anton Kiriloveffd5bf2017-02-28 16:59:15 +00005733 uint32_t neg_value = 0;
Artem Serov02109dd2016-09-23 17:17:54 +01005734 switch (opcode) {
Anton Kiriloveffd5bf2017-02-28 16:59:15 +00005735 case AND: neg_opcode = BIC; neg_value = ~value; break;
5736 case ORR: neg_opcode = ORN; neg_value = ~value; break;
5737 case ADD: neg_opcode = SUB; neg_value = -value; break;
5738 case ADC: neg_opcode = SBC; neg_value = ~value; break;
5739 case SUB: neg_opcode = ADD; neg_value = -value; break;
5740 case SBC: neg_opcode = ADC; neg_value = ~value; break;
5741 case MOV: neg_opcode = MVN; neg_value = ~value; break;
Artem Serov02109dd2016-09-23 17:17:54 +01005742 default:
5743 return false;
5744 }
Anton Kiriloveffd5bf2017-02-28 16:59:15 +00005745
5746 if (assembler->ShifterOperandCanHold(neg_opcode, neg_value, set_cc)) {
5747 return true;
5748 }
5749
5750 return opcode == AND && IsPowerOfTwo(value + 1);
Artem Serov02109dd2016-09-23 17:17:54 +01005751}
5752
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005753void InstructionCodeGeneratorARMVIXL::HandleFieldGet(HInstruction* instruction,
5754 const FieldInfo& field_info) {
5755 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
5756
5757 LocationSummary* locations = instruction->GetLocations();
5758 vixl32::Register base = InputRegisterAt(instruction, 0);
5759 Location out = locations->Out();
5760 bool is_volatile = field_info.IsVolatile();
5761 bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005762 DataType::Type field_type = field_info.GetFieldType();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005763 uint32_t offset = field_info.GetFieldOffset().Uint32Value();
5764
5765 switch (field_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005766 case DataType::Type::kBool:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005767 GetAssembler()->LoadFromOffset(kLoadUnsignedByte, RegisterFrom(out), base, offset);
5768 break;
5769
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005770 case DataType::Type::kInt8:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005771 GetAssembler()->LoadFromOffset(kLoadSignedByte, RegisterFrom(out), base, offset);
5772 break;
5773
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005774 case DataType::Type::kInt16:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005775 GetAssembler()->LoadFromOffset(kLoadSignedHalfword, RegisterFrom(out), base, offset);
5776 break;
5777
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005778 case DataType::Type::kUint16:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005779 GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, RegisterFrom(out), base, offset);
5780 break;
5781
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005782 case DataType::Type::kInt32:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005783 GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(out), base, offset);
5784 break;
5785
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005786 case DataType::Type::kReference: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005787 // /* HeapReference<Object> */ out = *(base + offset)
5788 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00005789 Location temp_loc = locations->GetTemp(0);
5790 // Note that a potential implicit null check is handled in this
5791 // CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier call.
5792 codegen_->GenerateFieldLoadWithBakerReadBarrier(
5793 instruction, out, base, offset, temp_loc, /* needs_null_check */ true);
5794 if (is_volatile) {
5795 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
5796 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005797 } else {
5798 GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(out), base, offset);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005799 codegen_->MaybeRecordImplicitNullCheck(instruction);
5800 if (is_volatile) {
5801 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
5802 }
5803 // If read barriers are enabled, emit read barriers other than
5804 // Baker's using a slow path (and also unpoison the loaded
5805 // reference, if heap poisoning is enabled).
5806 codegen_->MaybeGenerateReadBarrierSlow(instruction, out, out, locations->InAt(0), offset);
5807 }
5808 break;
5809 }
5810
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005811 case DataType::Type::kInt64:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005812 if (is_volatile && !atomic_ldrd_strd) {
5813 GenerateWideAtomicLoad(base, offset, LowRegisterFrom(out), HighRegisterFrom(out));
5814 } else {
5815 GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out), base, offset);
5816 }
5817 break;
5818
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005819 case DataType::Type::kFloat32:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005820 GetAssembler()->LoadSFromOffset(SRegisterFrom(out), base, offset);
5821 break;
5822
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005823 case DataType::Type::kFloat64: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01005824 vixl32::DRegister out_dreg = DRegisterFrom(out);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005825 if (is_volatile && !atomic_ldrd_strd) {
5826 vixl32::Register lo = RegisterFrom(locations->GetTemp(0));
5827 vixl32::Register hi = RegisterFrom(locations->GetTemp(1));
5828 GenerateWideAtomicLoad(base, offset, lo, hi);
5829 // TODO(VIXL): Do we need to be immediately after the ldrexd instruction? If so we need a
5830 // scope.
5831 codegen_->MaybeRecordImplicitNullCheck(instruction);
5832 __ Vmov(out_dreg, lo, hi);
5833 } else {
5834 GetAssembler()->LoadDFromOffset(out_dreg, base, offset);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005835 codegen_->MaybeRecordImplicitNullCheck(instruction);
5836 }
5837 break;
5838 }
5839
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005840 case DataType::Type::kVoid:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005841 LOG(FATAL) << "Unreachable type " << field_type;
5842 UNREACHABLE();
5843 }
5844
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005845 if (field_type == DataType::Type::kReference || field_type == DataType::Type::kFloat64) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005846 // Potential implicit null checks, in the case of reference or
5847 // double fields, are handled in the previous switch statement.
5848 } else {
5849 // Address cases other than reference and double that may require an implicit null check.
Alexandre Rames374ddf32016-11-04 10:40:49 +00005850 // TODO(VIXL): Here and for other calls to `MaybeRecordImplicitNullCheck` in this method, we
5851 // should use a scope and the assembler to emit the load instruction to guarantee that we
5852 // record the pc at the correct position. But the `Assembler` does not automatically handle
5853 // unencodable offsets. Practically, everything is fine because the helper and VIXL, at the time
5854 // of writing, do generate the store instruction last.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005855 codegen_->MaybeRecordImplicitNullCheck(instruction);
5856 }
5857
5858 if (is_volatile) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005859 if (field_type == DataType::Type::kReference) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005860 // Memory barriers, in the case of references, are also handled
5861 // in the previous switch statement.
5862 } else {
5863 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
5864 }
5865 }
5866}
5867
5868void LocationsBuilderARMVIXL::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
5869 HandleFieldSet(instruction, instruction->GetFieldInfo());
5870}
5871
5872void InstructionCodeGeneratorARMVIXL::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
5873 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
5874}
5875
5876void LocationsBuilderARMVIXL::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
5877 HandleFieldGet(instruction, instruction->GetFieldInfo());
5878}
5879
5880void InstructionCodeGeneratorARMVIXL::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
5881 HandleFieldGet(instruction, instruction->GetFieldInfo());
5882}
5883
5884void LocationsBuilderARMVIXL::VisitStaticFieldGet(HStaticFieldGet* instruction) {
5885 HandleFieldGet(instruction, instruction->GetFieldInfo());
5886}
5887
5888void InstructionCodeGeneratorARMVIXL::VisitStaticFieldGet(HStaticFieldGet* instruction) {
5889 HandleFieldGet(instruction, instruction->GetFieldInfo());
5890}
5891
Scott Wakelingc34dba72016-10-03 10:14:44 +01005892void LocationsBuilderARMVIXL::VisitStaticFieldSet(HStaticFieldSet* instruction) {
5893 HandleFieldSet(instruction, instruction->GetFieldInfo());
5894}
5895
5896void InstructionCodeGeneratorARMVIXL::VisitStaticFieldSet(HStaticFieldSet* instruction) {
5897 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
5898}
5899
Artem Serovcfbe9132016-10-14 15:58:56 +01005900void LocationsBuilderARMVIXL::VisitUnresolvedInstanceFieldGet(
5901 HUnresolvedInstanceFieldGet* instruction) {
5902 FieldAccessCallingConventionARMVIXL calling_convention;
5903 codegen_->CreateUnresolvedFieldLocationSummary(
5904 instruction, instruction->GetFieldType(), calling_convention);
5905}
5906
5907void InstructionCodeGeneratorARMVIXL::VisitUnresolvedInstanceFieldGet(
5908 HUnresolvedInstanceFieldGet* instruction) {
5909 FieldAccessCallingConventionARMVIXL calling_convention;
5910 codegen_->GenerateUnresolvedFieldAccess(instruction,
5911 instruction->GetFieldType(),
5912 instruction->GetFieldIndex(),
5913 instruction->GetDexPc(),
5914 calling_convention);
5915}
5916
5917void LocationsBuilderARMVIXL::VisitUnresolvedInstanceFieldSet(
5918 HUnresolvedInstanceFieldSet* instruction) {
5919 FieldAccessCallingConventionARMVIXL calling_convention;
5920 codegen_->CreateUnresolvedFieldLocationSummary(
5921 instruction, instruction->GetFieldType(), calling_convention);
5922}
5923
5924void InstructionCodeGeneratorARMVIXL::VisitUnresolvedInstanceFieldSet(
5925 HUnresolvedInstanceFieldSet* instruction) {
5926 FieldAccessCallingConventionARMVIXL calling_convention;
5927 codegen_->GenerateUnresolvedFieldAccess(instruction,
5928 instruction->GetFieldType(),
5929 instruction->GetFieldIndex(),
5930 instruction->GetDexPc(),
5931 calling_convention);
5932}
5933
5934void LocationsBuilderARMVIXL::VisitUnresolvedStaticFieldGet(
5935 HUnresolvedStaticFieldGet* instruction) {
5936 FieldAccessCallingConventionARMVIXL calling_convention;
5937 codegen_->CreateUnresolvedFieldLocationSummary(
5938 instruction, instruction->GetFieldType(), calling_convention);
5939}
5940
5941void InstructionCodeGeneratorARMVIXL::VisitUnresolvedStaticFieldGet(
5942 HUnresolvedStaticFieldGet* instruction) {
5943 FieldAccessCallingConventionARMVIXL calling_convention;
5944 codegen_->GenerateUnresolvedFieldAccess(instruction,
5945 instruction->GetFieldType(),
5946 instruction->GetFieldIndex(),
5947 instruction->GetDexPc(),
5948 calling_convention);
5949}
5950
5951void LocationsBuilderARMVIXL::VisitUnresolvedStaticFieldSet(
5952 HUnresolvedStaticFieldSet* instruction) {
5953 FieldAccessCallingConventionARMVIXL calling_convention;
5954 codegen_->CreateUnresolvedFieldLocationSummary(
5955 instruction, instruction->GetFieldType(), calling_convention);
5956}
5957
5958void InstructionCodeGeneratorARMVIXL::VisitUnresolvedStaticFieldSet(
5959 HUnresolvedStaticFieldSet* instruction) {
5960 FieldAccessCallingConventionARMVIXL calling_convention;
5961 codegen_->GenerateUnresolvedFieldAccess(instruction,
5962 instruction->GetFieldType(),
5963 instruction->GetFieldIndex(),
5964 instruction->GetDexPc(),
5965 calling_convention);
5966}
5967
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005968void LocationsBuilderARMVIXL::VisitNullCheck(HNullCheck* instruction) {
Artem Serov657022c2016-11-23 14:19:38 +00005969 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005970 locations->SetInAt(0, Location::RequiresRegister());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005971}
5972
5973void CodeGeneratorARMVIXL::GenerateImplicitNullCheck(HNullCheck* instruction) {
5974 if (CanMoveNullCheckToUser(instruction)) {
5975 return;
5976 }
5977
5978 UseScratchRegisterScope temps(GetVIXLAssembler());
Alexandre Rames374ddf32016-11-04 10:40:49 +00005979 // Ensure the pc position is recorded immediately after the `ldr` instruction.
Artem Serov0fb37192016-12-06 18:13:40 +00005980 ExactAssemblyScope aas(GetVIXLAssembler(),
5981 vixl32::kMaxInstructionSizeInBytes,
5982 CodeBufferCheckScope::kMaximumSize);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005983 __ ldr(temps.Acquire(), MemOperand(InputRegisterAt(instruction, 0)));
5984 RecordPcInfo(instruction, instruction->GetDexPc());
5985}
5986
5987void CodeGeneratorARMVIXL::GenerateExplicitNullCheck(HNullCheck* instruction) {
5988 NullCheckSlowPathARMVIXL* slow_path =
5989 new (GetGraph()->GetArena()) NullCheckSlowPathARMVIXL(instruction);
5990 AddSlowPath(slow_path);
xueliang.zhongf51bc622016-11-04 09:23:32 +00005991 __ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005992}
5993
5994void InstructionCodeGeneratorARMVIXL::VisitNullCheck(HNullCheck* instruction) {
5995 codegen_->GenerateNullCheck(instruction);
5996}
5997
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005998static LoadOperandType GetLoadOperandType(DataType::Type type) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01005999 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006000 case DataType::Type::kReference:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006001 return kLoadWord;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006002 case DataType::Type::kBool:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006003 return kLoadUnsignedByte;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006004 case DataType::Type::kInt8:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006005 return kLoadSignedByte;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006006 case DataType::Type::kUint16:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006007 return kLoadUnsignedHalfword;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006008 case DataType::Type::kInt16:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006009 return kLoadSignedHalfword;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006010 case DataType::Type::kInt32:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006011 return kLoadWord;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006012 case DataType::Type::kInt64:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006013 return kLoadWordPair;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006014 case DataType::Type::kFloat32:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006015 return kLoadSWord;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006016 case DataType::Type::kFloat64:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006017 return kLoadDWord;
6018 default:
6019 LOG(FATAL) << "Unreachable type " << type;
6020 UNREACHABLE();
6021 }
6022}
6023
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006024static StoreOperandType GetStoreOperandType(DataType::Type type) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006025 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006026 case DataType::Type::kReference:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006027 return kStoreWord;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006028 case DataType::Type::kBool:
6029 case DataType::Type::kInt8:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006030 return kStoreByte;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006031 case DataType::Type::kUint16:
6032 case DataType::Type::kInt16:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006033 return kStoreHalfword;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006034 case DataType::Type::kInt32:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006035 return kStoreWord;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006036 case DataType::Type::kInt64:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006037 return kStoreWordPair;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006038 case DataType::Type::kFloat32:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006039 return kStoreSWord;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006040 case DataType::Type::kFloat64:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006041 return kStoreDWord;
6042 default:
6043 LOG(FATAL) << "Unreachable type " << type;
6044 UNREACHABLE();
6045 }
6046}
6047
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006048void CodeGeneratorARMVIXL::LoadFromShiftedRegOffset(DataType::Type type,
Scott Wakelingc34dba72016-10-03 10:14:44 +01006049 Location out_loc,
6050 vixl32::Register base,
6051 vixl32::Register reg_index,
6052 vixl32::Condition cond) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006053 uint32_t shift_count = DataType::SizeShift(type);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006054 MemOperand mem_address(base, reg_index, vixl32::LSL, shift_count);
6055
6056 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006057 case DataType::Type::kInt8:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006058 __ Ldrsb(cond, RegisterFrom(out_loc), mem_address);
6059 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006060 case DataType::Type::kBool:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006061 __ Ldrb(cond, RegisterFrom(out_loc), mem_address);
6062 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006063 case DataType::Type::kInt16:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006064 __ Ldrsh(cond, RegisterFrom(out_loc), mem_address);
6065 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006066 case DataType::Type::kUint16:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006067 __ Ldrh(cond, RegisterFrom(out_loc), mem_address);
6068 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006069 case DataType::Type::kReference:
6070 case DataType::Type::kInt32:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006071 __ Ldr(cond, RegisterFrom(out_loc), mem_address);
6072 break;
6073 // T32 doesn't support LoadFromShiftedRegOffset mem address mode for these types.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006074 case DataType::Type::kInt64:
6075 case DataType::Type::kFloat32:
6076 case DataType::Type::kFloat64:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006077 default:
6078 LOG(FATAL) << "Unreachable type " << type;
6079 UNREACHABLE();
6080 }
6081}
6082
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006083void CodeGeneratorARMVIXL::StoreToShiftedRegOffset(DataType::Type type,
Scott Wakelingc34dba72016-10-03 10:14:44 +01006084 Location loc,
6085 vixl32::Register base,
6086 vixl32::Register reg_index,
6087 vixl32::Condition cond) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006088 uint32_t shift_count = DataType::SizeShift(type);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006089 MemOperand mem_address(base, reg_index, vixl32::LSL, shift_count);
6090
6091 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006092 case DataType::Type::kInt8:
6093 case DataType::Type::kBool:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006094 __ Strb(cond, RegisterFrom(loc), mem_address);
6095 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006096 case DataType::Type::kInt16:
6097 case DataType::Type::kUint16:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006098 __ Strh(cond, RegisterFrom(loc), mem_address);
6099 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006100 case DataType::Type::kReference:
6101 case DataType::Type::kInt32:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006102 __ Str(cond, RegisterFrom(loc), mem_address);
6103 break;
6104 // T32 doesn't support StoreToShiftedRegOffset mem address mode for these types.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006105 case DataType::Type::kInt64:
6106 case DataType::Type::kFloat32:
6107 case DataType::Type::kFloat64:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006108 default:
6109 LOG(FATAL) << "Unreachable type " << type;
6110 UNREACHABLE();
6111 }
6112}
6113
6114void LocationsBuilderARMVIXL::VisitArrayGet(HArrayGet* instruction) {
6115 bool object_array_get_with_read_barrier =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006116 kEmitCompilerReadBarrier && (instruction->GetType() == DataType::Type::kReference);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006117 LocationSummary* locations =
6118 new (GetGraph()->GetArena()) LocationSummary(instruction,
6119 object_array_get_with_read_barrier ?
6120 LocationSummary::kCallOnSlowPath :
6121 LocationSummary::kNoCall);
6122 if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006123 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Scott Wakelingc34dba72016-10-03 10:14:44 +01006124 }
6125 locations->SetInAt(0, Location::RequiresRegister());
6126 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006127 if (DataType::IsFloatingPointType(instruction->GetType())) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006128 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
6129 } else {
6130 // The output overlaps in the case of an object array get with
6131 // read barriers enabled: we do not want the move to overwrite the
6132 // array's location, as we need it to emit the read barrier.
6133 locations->SetOut(
6134 Location::RequiresRegister(),
6135 object_array_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap);
6136 }
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006137 if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
6138 // We need a temporary register for the read barrier marking slow
6139 // path in CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier.
6140 if (kBakerReadBarrierLinkTimeThunksEnableForFields &&
6141 !Runtime::Current()->UseJitCompilation() &&
6142 instruction->GetIndex()->IsConstant()) {
6143 // Array loads with constant index are treated as field loads.
6144 // If link-time thunks for the Baker read barrier are enabled, for AOT
6145 // constant index loads we need a temporary only if the offset is too big.
6146 uint32_t offset = CodeGenerator::GetArrayDataOffset(instruction);
6147 uint32_t index = instruction->GetIndex()->AsIntConstant()->GetValue();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006148 offset += index << DataType::SizeShift(DataType::Type::kReference);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006149 if (offset >= kReferenceLoadMinFarOffset) {
6150 locations->AddTemp(Location::RequiresRegister());
6151 }
6152 // And we always need the reserved entrypoint register.
6153 locations->AddTemp(Location::RegisterLocation(kBakerCcEntrypointRegister.GetCode()));
6154 } else if (kBakerReadBarrierLinkTimeThunksEnableForArrays &&
6155 !Runtime::Current()->UseJitCompilation() &&
6156 !instruction->GetIndex()->IsConstant()) {
6157 // We need a non-scratch temporary for the array data pointer.
6158 locations->AddTemp(Location::RequiresRegister());
6159 // And we always need the reserved entrypoint register.
6160 locations->AddTemp(Location::RegisterLocation(kBakerCcEntrypointRegister.GetCode()));
6161 } else {
6162 locations->AddTemp(Location::RequiresRegister());
6163 }
6164 } else if (mirror::kUseStringCompression && instruction->IsStringCharAt()) {
6165 // Also need a temporary for String compression feature.
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006166 locations->AddTemp(Location::RequiresRegister());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006167 }
6168}
6169
6170void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006171 LocationSummary* locations = instruction->GetLocations();
6172 Location obj_loc = locations->InAt(0);
6173 vixl32::Register obj = InputRegisterAt(instruction, 0);
6174 Location index = locations->InAt(1);
6175 Location out_loc = locations->Out();
6176 uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006177 DataType::Type type = instruction->GetType();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006178 const bool maybe_compressed_char_at = mirror::kUseStringCompression &&
6179 instruction->IsStringCharAt();
6180 HInstruction* array_instr = instruction->GetArray();
6181 bool has_intermediate_address = array_instr->IsIntermediateAddress();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006182
6183 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006184 case DataType::Type::kBool:
6185 case DataType::Type::kInt8:
6186 case DataType::Type::kInt16:
6187 case DataType::Type::kUint16:
6188 case DataType::Type::kInt32: {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01006189 vixl32::Register length;
6190 if (maybe_compressed_char_at) {
6191 length = RegisterFrom(locations->GetTemp(0));
6192 uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
6193 GetAssembler()->LoadFromOffset(kLoadWord, length, obj, count_offset);
6194 codegen_->MaybeRecordImplicitNullCheck(instruction);
6195 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006196 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006197 int32_t const_index = Int32ConstantFrom(index);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006198 if (maybe_compressed_char_at) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006199 vixl32::Label uncompressed_load, done;
Anton Kirilov6f644202017-02-27 18:29:45 +00006200 vixl32::Label* final_label = codegen_->GetFinalLabel(instruction, &done);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01006201 __ Lsrs(length, length, 1u); // LSRS has a 16-bit encoding, TST (immediate) does not.
6202 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
6203 "Expecting 0=compressed, 1=uncompressed");
Artem Serov517d9f62016-12-12 15:51:15 +00006204 __ B(cs, &uncompressed_load, /* far_target */ false);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006205 GetAssembler()->LoadFromOffset(kLoadUnsignedByte,
6206 RegisterFrom(out_loc),
6207 obj,
6208 data_offset + const_index);
Anton Kirilov6f644202017-02-27 18:29:45 +00006209 __ B(final_label);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006210 __ Bind(&uncompressed_load);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006211 GetAssembler()->LoadFromOffset(GetLoadOperandType(DataType::Type::kUint16),
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006212 RegisterFrom(out_loc),
6213 obj,
6214 data_offset + (const_index << 1));
Anton Kirilov6f644202017-02-27 18:29:45 +00006215 if (done.IsReferenced()) {
6216 __ Bind(&done);
6217 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006218 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006219 uint32_t full_offset = data_offset + (const_index << DataType::SizeShift(type));
Scott Wakelingc34dba72016-10-03 10:14:44 +01006220
6221 LoadOperandType load_type = GetLoadOperandType(type);
6222 GetAssembler()->LoadFromOffset(load_type, RegisterFrom(out_loc), obj, full_offset);
6223 }
6224 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006225 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006226 vixl32::Register temp = temps.Acquire();
6227
6228 if (has_intermediate_address) {
Artem Serov2bbc9532016-10-21 11:51:50 +01006229 // We do not need to compute the intermediate address from the array: the
6230 // input instruction has done it already. See the comment in
6231 // `TryExtractArrayAccessAddress()`.
6232 if (kIsDebugBuild) {
6233 HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
Anton Kirilov644032c2016-12-06 17:51:43 +00006234 DCHECK_EQ(Uint64ConstantFrom(tmp->GetOffset()), data_offset);
Artem Serov2bbc9532016-10-21 11:51:50 +01006235 }
6236 temp = obj;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006237 } else {
6238 __ Add(temp, obj, data_offset);
6239 }
6240 if (maybe_compressed_char_at) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006241 vixl32::Label uncompressed_load, done;
Anton Kirilov6f644202017-02-27 18:29:45 +00006242 vixl32::Label* final_label = codegen_->GetFinalLabel(instruction, &done);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01006243 __ Lsrs(length, length, 1u); // LSRS has a 16-bit encoding, TST (immediate) does not.
6244 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
6245 "Expecting 0=compressed, 1=uncompressed");
Artem Serov517d9f62016-12-12 15:51:15 +00006246 __ B(cs, &uncompressed_load, /* far_target */ false);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006247 __ Ldrb(RegisterFrom(out_loc), MemOperand(temp, RegisterFrom(index), vixl32::LSL, 0));
Anton Kirilov6f644202017-02-27 18:29:45 +00006248 __ B(final_label);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006249 __ Bind(&uncompressed_load);
6250 __ Ldrh(RegisterFrom(out_loc), MemOperand(temp, RegisterFrom(index), vixl32::LSL, 1));
Anton Kirilov6f644202017-02-27 18:29:45 +00006251 if (done.IsReferenced()) {
6252 __ Bind(&done);
6253 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006254 } else {
6255 codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index));
6256 }
6257 }
6258 break;
6259 }
6260
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006261 case DataType::Type::kReference: {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006262 // The read barrier instrumentation of object ArrayGet
6263 // instructions does not support the HIntermediateAddress
6264 // instruction.
6265 DCHECK(!(has_intermediate_address && kEmitCompilerReadBarrier));
6266
Scott Wakelingc34dba72016-10-03 10:14:44 +01006267 static_assert(
6268 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
6269 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
6270 // /* HeapReference<Object> */ out =
6271 // *(obj + data_offset + index * sizeof(HeapReference<Object>))
6272 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006273 Location temp = locations->GetTemp(0);
6274 // Note that a potential implicit null check is handled in this
6275 // CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier call.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006276 DCHECK(!instruction->CanDoImplicitNullCheckOn(instruction->InputAt(0)));
6277 if (index.IsConstant()) {
6278 // Array load with a constant index can be treated as a field load.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006279 data_offset += Int32ConstantFrom(index) << DataType::SizeShift(type);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006280 codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction,
6281 out_loc,
6282 obj,
6283 data_offset,
6284 locations->GetTemp(0),
6285 /* needs_null_check */ false);
6286 } else {
6287 codegen_->GenerateArrayLoadWithBakerReadBarrier(
6288 instruction, out_loc, obj, data_offset, index, temp, /* needs_null_check */ false);
6289 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006290 } else {
6291 vixl32::Register out = OutputRegister(instruction);
6292 if (index.IsConstant()) {
6293 size_t offset =
Anton Kirilov644032c2016-12-06 17:51:43 +00006294 (Int32ConstantFrom(index) << TIMES_4) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006295 GetAssembler()->LoadFromOffset(kLoadWord, out, obj, offset);
Alexandre Rames374ddf32016-11-04 10:40:49 +00006296 // TODO(VIXL): Here and for other calls to `MaybeRecordImplicitNullCheck` in this method,
6297 // we should use a scope and the assembler to emit the load instruction to guarantee that
6298 // we record the pc at the correct position. But the `Assembler` does not automatically
6299 // handle unencodable offsets. Practically, everything is fine because the helper and
6300 // VIXL, at the time of writing, do generate the store instruction last.
Scott Wakelingc34dba72016-10-03 10:14:44 +01006301 codegen_->MaybeRecordImplicitNullCheck(instruction);
6302 // If read barriers are enabled, emit read barriers other than
6303 // Baker's using a slow path (and also unpoison the loaded
6304 // reference, if heap poisoning is enabled).
6305 codegen_->MaybeGenerateReadBarrierSlow(instruction, out_loc, out_loc, obj_loc, offset);
6306 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006307 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006308 vixl32::Register temp = temps.Acquire();
6309
6310 if (has_intermediate_address) {
Artem Serov2bbc9532016-10-21 11:51:50 +01006311 // We do not need to compute the intermediate address from the array: the
6312 // input instruction has done it already. See the comment in
6313 // `TryExtractArrayAccessAddress()`.
6314 if (kIsDebugBuild) {
6315 HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
Anton Kirilov644032c2016-12-06 17:51:43 +00006316 DCHECK_EQ(Uint64ConstantFrom(tmp->GetOffset()), data_offset);
Artem Serov2bbc9532016-10-21 11:51:50 +01006317 }
6318 temp = obj;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006319 } else {
6320 __ Add(temp, obj, data_offset);
6321 }
6322 codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index));
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006323 temps.Close();
Alexandre Rames374ddf32016-11-04 10:40:49 +00006324 // TODO(VIXL): Use a scope to ensure that we record the pc position immediately after the
6325 // load instruction. Practically, everything is fine because the helper and VIXL, at the
6326 // time of writing, do generate the store instruction last.
Scott Wakelingc34dba72016-10-03 10:14:44 +01006327 codegen_->MaybeRecordImplicitNullCheck(instruction);
6328 // If read barriers are enabled, emit read barriers other than
6329 // Baker's using a slow path (and also unpoison the loaded
6330 // reference, if heap poisoning is enabled).
6331 codegen_->MaybeGenerateReadBarrierSlow(
6332 instruction, out_loc, out_loc, obj_loc, data_offset, index);
6333 }
6334 }
6335 break;
6336 }
6337
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006338 case DataType::Type::kInt64: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006339 if (index.IsConstant()) {
6340 size_t offset =
Anton Kirilov644032c2016-12-06 17:51:43 +00006341 (Int32ConstantFrom(index) << TIMES_8) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006342 GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out_loc), obj, offset);
6343 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006344 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006345 vixl32::Register temp = temps.Acquire();
6346 __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
6347 GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out_loc), temp, data_offset);
6348 }
6349 break;
6350 }
6351
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006352 case DataType::Type::kFloat32: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006353 vixl32::SRegister out = SRegisterFrom(out_loc);
6354 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006355 size_t offset = (Int32ConstantFrom(index) << TIMES_4) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006356 GetAssembler()->LoadSFromOffset(out, obj, offset);
6357 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006358 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006359 vixl32::Register temp = temps.Acquire();
6360 __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_4));
6361 GetAssembler()->LoadSFromOffset(out, temp, data_offset);
6362 }
6363 break;
6364 }
6365
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006366 case DataType::Type::kFloat64: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006367 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006368 size_t offset = (Int32ConstantFrom(index) << TIMES_8) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006369 GetAssembler()->LoadDFromOffset(DRegisterFrom(out_loc), obj, offset);
6370 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006371 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006372 vixl32::Register temp = temps.Acquire();
6373 __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
6374 GetAssembler()->LoadDFromOffset(DRegisterFrom(out_loc), temp, data_offset);
6375 }
6376 break;
6377 }
6378
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006379 case DataType::Type::kVoid:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006380 LOG(FATAL) << "Unreachable type " << type;
6381 UNREACHABLE();
6382 }
6383
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006384 if (type == DataType::Type::kReference) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006385 // Potential implicit null checks, in the case of reference
6386 // arrays, are handled in the previous switch statement.
6387 } else if (!maybe_compressed_char_at) {
Alexandre Rames374ddf32016-11-04 10:40:49 +00006388 // TODO(VIXL): Use a scope to ensure we record the pc info immediately after
6389 // the preceding load instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01006390 codegen_->MaybeRecordImplicitNullCheck(instruction);
6391 }
6392}
6393
6394void LocationsBuilderARMVIXL::VisitArraySet(HArraySet* instruction) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006395 DataType::Type value_type = instruction->GetComponentType();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006396
6397 bool needs_write_barrier =
6398 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
6399 bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck();
6400
6401 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(
6402 instruction,
6403 may_need_runtime_call_for_type_check ?
6404 LocationSummary::kCallOnSlowPath :
6405 LocationSummary::kNoCall);
6406
6407 locations->SetInAt(0, Location::RequiresRegister());
6408 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006409 if (DataType::IsFloatingPointType(value_type)) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006410 locations->SetInAt(2, Location::RequiresFpuRegister());
6411 } else {
6412 locations->SetInAt(2, Location::RequiresRegister());
6413 }
6414 if (needs_write_barrier) {
6415 // Temporary registers for the write barrier.
6416 locations->AddTemp(Location::RequiresRegister()); // Possibly used for ref. poisoning too.
6417 locations->AddTemp(Location::RequiresRegister());
6418 }
6419}
6420
6421void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006422 LocationSummary* locations = instruction->GetLocations();
6423 vixl32::Register array = InputRegisterAt(instruction, 0);
6424 Location index = locations->InAt(1);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006425 DataType::Type value_type = instruction->GetComponentType();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006426 bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck();
6427 bool needs_write_barrier =
6428 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
6429 uint32_t data_offset =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006430 mirror::Array::DataOffset(DataType::Size(value_type)).Uint32Value();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006431 Location value_loc = locations->InAt(2);
6432 HInstruction* array_instr = instruction->GetArray();
6433 bool has_intermediate_address = array_instr->IsIntermediateAddress();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006434
6435 switch (value_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006436 case DataType::Type::kBool:
6437 case DataType::Type::kInt8:
6438 case DataType::Type::kInt16:
6439 case DataType::Type::kUint16:
6440 case DataType::Type::kInt32: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006441 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006442 int32_t const_index = Int32ConstantFrom(index);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006443 uint32_t full_offset =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006444 data_offset + (const_index << DataType::SizeShift(value_type));
Scott Wakelingc34dba72016-10-03 10:14:44 +01006445 StoreOperandType store_type = GetStoreOperandType(value_type);
6446 GetAssembler()->StoreToOffset(store_type, RegisterFrom(value_loc), array, full_offset);
6447 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006448 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006449 vixl32::Register temp = temps.Acquire();
6450
6451 if (has_intermediate_address) {
Artem Serov2bbc9532016-10-21 11:51:50 +01006452 // We do not need to compute the intermediate address from the array: the
6453 // input instruction has done it already. See the comment in
6454 // `TryExtractArrayAccessAddress()`.
6455 if (kIsDebugBuild) {
6456 HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
Anton Kirilov644032c2016-12-06 17:51:43 +00006457 DCHECK_EQ(Uint64ConstantFrom(tmp->GetOffset()), data_offset);
Artem Serov2bbc9532016-10-21 11:51:50 +01006458 }
6459 temp = array;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006460 } else {
6461 __ Add(temp, array, data_offset);
6462 }
6463 codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
6464 }
6465 break;
6466 }
6467
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006468 case DataType::Type::kReference: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006469 vixl32::Register value = RegisterFrom(value_loc);
6470 // TryExtractArrayAccessAddress optimization is never applied for non-primitive ArraySet.
6471 // See the comment in instruction_simplifier_shared.cc.
6472 DCHECK(!has_intermediate_address);
6473
6474 if (instruction->InputAt(2)->IsNullConstant()) {
6475 // Just setting null.
6476 if (index.IsConstant()) {
6477 size_t offset =
Anton Kirilov644032c2016-12-06 17:51:43 +00006478 (Int32ConstantFrom(index) << TIMES_4) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006479 GetAssembler()->StoreToOffset(kStoreWord, value, array, offset);
6480 } else {
6481 DCHECK(index.IsRegister()) << index;
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006482 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006483 vixl32::Register temp = temps.Acquire();
6484 __ Add(temp, array, data_offset);
6485 codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
6486 }
Alexandre Rames374ddf32016-11-04 10:40:49 +00006487 // TODO(VIXL): Use a scope to ensure we record the pc info immediately after the preceding
6488 // store instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01006489 codegen_->MaybeRecordImplicitNullCheck(instruction);
6490 DCHECK(!needs_write_barrier);
6491 DCHECK(!may_need_runtime_call_for_type_check);
6492 break;
6493 }
6494
6495 DCHECK(needs_write_barrier);
6496 Location temp1_loc = locations->GetTemp(0);
6497 vixl32::Register temp1 = RegisterFrom(temp1_loc);
6498 Location temp2_loc = locations->GetTemp(1);
6499 vixl32::Register temp2 = RegisterFrom(temp2_loc);
6500 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
6501 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
6502 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
6503 vixl32::Label done;
Anton Kirilov6f644202017-02-27 18:29:45 +00006504 vixl32::Label* final_label = codegen_->GetFinalLabel(instruction, &done);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006505 SlowPathCodeARMVIXL* slow_path = nullptr;
6506
6507 if (may_need_runtime_call_for_type_check) {
6508 slow_path = new (GetGraph()->GetArena()) ArraySetSlowPathARMVIXL(instruction);
6509 codegen_->AddSlowPath(slow_path);
6510 if (instruction->GetValueCanBeNull()) {
6511 vixl32::Label non_zero;
xueliang.zhongf51bc622016-11-04 09:23:32 +00006512 __ CompareAndBranchIfNonZero(value, &non_zero);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006513 if (index.IsConstant()) {
6514 size_t offset =
Anton Kirilov644032c2016-12-06 17:51:43 +00006515 (Int32ConstantFrom(index) << TIMES_4) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006516 GetAssembler()->StoreToOffset(kStoreWord, value, array, offset);
6517 } else {
6518 DCHECK(index.IsRegister()) << index;
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006519 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006520 vixl32::Register temp = temps.Acquire();
6521 __ Add(temp, array, data_offset);
6522 codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
6523 }
Alexandre Rames374ddf32016-11-04 10:40:49 +00006524 // TODO(VIXL): Use a scope to ensure we record the pc info immediately after the preceding
6525 // store instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01006526 codegen_->MaybeRecordImplicitNullCheck(instruction);
Anton Kirilov6f644202017-02-27 18:29:45 +00006527 __ B(final_label);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006528 __ Bind(&non_zero);
6529 }
6530
6531 // Note that when read barriers are enabled, the type checks
6532 // are performed without read barriers. This is fine, even in
6533 // the case where a class object is in the from-space after
6534 // the flip, as a comparison involving such a type would not
6535 // produce a false positive; it may of course produce a false
6536 // negative, in which case we would take the ArraySet slow
6537 // path.
6538
Alexandre Rames374ddf32016-11-04 10:40:49 +00006539 {
6540 // Ensure we record the pc position immediately after the `ldr` instruction.
Artem Serov0fb37192016-12-06 18:13:40 +00006541 ExactAssemblyScope aas(GetVIXLAssembler(),
6542 vixl32::kMaxInstructionSizeInBytes,
6543 CodeBufferCheckScope::kMaximumSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00006544 // /* HeapReference<Class> */ temp1 = array->klass_
6545 __ ldr(temp1, MemOperand(array, class_offset));
6546 codegen_->MaybeRecordImplicitNullCheck(instruction);
6547 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006548 GetAssembler()->MaybeUnpoisonHeapReference(temp1);
6549
6550 // /* HeapReference<Class> */ temp1 = temp1->component_type_
6551 GetAssembler()->LoadFromOffset(kLoadWord, temp1, temp1, component_offset);
6552 // /* HeapReference<Class> */ temp2 = value->klass_
6553 GetAssembler()->LoadFromOffset(kLoadWord, temp2, value, class_offset);
6554 // If heap poisoning is enabled, no need to unpoison `temp1`
6555 // nor `temp2`, as we are comparing two poisoned references.
6556 __ Cmp(temp1, temp2);
6557
6558 if (instruction->StaticTypeOfArrayIsObjectArray()) {
6559 vixl32::Label do_put;
Artem Serov517d9f62016-12-12 15:51:15 +00006560 __ B(eq, &do_put, /* far_target */ false);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006561 // If heap poisoning is enabled, the `temp1` reference has
6562 // not been unpoisoned yet; unpoison it now.
6563 GetAssembler()->MaybeUnpoisonHeapReference(temp1);
6564
6565 // /* HeapReference<Class> */ temp1 = temp1->super_class_
6566 GetAssembler()->LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
6567 // If heap poisoning is enabled, no need to unpoison
6568 // `temp1`, as we are comparing against null below.
xueliang.zhongf51bc622016-11-04 09:23:32 +00006569 __ CompareAndBranchIfNonZero(temp1, slow_path->GetEntryLabel());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006570 __ Bind(&do_put);
6571 } else {
6572 __ B(ne, slow_path->GetEntryLabel());
6573 }
6574 }
6575
6576 vixl32::Register source = value;
6577 if (kPoisonHeapReferences) {
6578 // Note that in the case where `value` is a null reference,
6579 // we do not enter this block, as a null reference does not
6580 // need poisoning.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006581 DCHECK_EQ(value_type, DataType::Type::kReference);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006582 __ Mov(temp1, value);
6583 GetAssembler()->PoisonHeapReference(temp1);
6584 source = temp1;
6585 }
6586
6587 if (index.IsConstant()) {
6588 size_t offset =
Anton Kirilov644032c2016-12-06 17:51:43 +00006589 (Int32ConstantFrom(index) << TIMES_4) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006590 GetAssembler()->StoreToOffset(kStoreWord, source, array, offset);
6591 } else {
6592 DCHECK(index.IsRegister()) << index;
6593
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006594 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006595 vixl32::Register temp = temps.Acquire();
6596 __ Add(temp, array, data_offset);
6597 codegen_->StoreToShiftedRegOffset(value_type,
6598 LocationFrom(source),
6599 temp,
6600 RegisterFrom(index));
6601 }
6602
6603 if (!may_need_runtime_call_for_type_check) {
Alexandre Rames374ddf32016-11-04 10:40:49 +00006604 // TODO(VIXL): Ensure we record the pc position immediately after the preceding store
6605 // instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01006606 codegen_->MaybeRecordImplicitNullCheck(instruction);
6607 }
6608
6609 codegen_->MarkGCCard(temp1, temp2, array, value, instruction->GetValueCanBeNull());
6610
6611 if (done.IsReferenced()) {
6612 __ Bind(&done);
6613 }
6614
6615 if (slow_path != nullptr) {
6616 __ Bind(slow_path->GetExitLabel());
6617 }
6618
6619 break;
6620 }
6621
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006622 case DataType::Type::kInt64: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006623 Location value = locations->InAt(2);
6624 if (index.IsConstant()) {
6625 size_t offset =
Anton Kirilov644032c2016-12-06 17:51:43 +00006626 (Int32ConstantFrom(index) << TIMES_8) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006627 GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), array, offset);
6628 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006629 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006630 vixl32::Register temp = temps.Acquire();
6631 __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
6632 GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), temp, data_offset);
6633 }
6634 break;
6635 }
6636
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006637 case DataType::Type::kFloat32: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006638 Location value = locations->InAt(2);
6639 DCHECK(value.IsFpuRegister());
6640 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006641 size_t offset = (Int32ConstantFrom(index) << TIMES_4) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006642 GetAssembler()->StoreSToOffset(SRegisterFrom(value), array, offset);
6643 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006644 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006645 vixl32::Register temp = temps.Acquire();
6646 __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_4));
6647 GetAssembler()->StoreSToOffset(SRegisterFrom(value), temp, data_offset);
6648 }
6649 break;
6650 }
6651
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006652 case DataType::Type::kFloat64: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006653 Location value = locations->InAt(2);
6654 DCHECK(value.IsFpuRegisterPair());
6655 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006656 size_t offset = (Int32ConstantFrom(index) << TIMES_8) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006657 GetAssembler()->StoreDToOffset(DRegisterFrom(value), array, offset);
6658 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006659 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006660 vixl32::Register temp = temps.Acquire();
6661 __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
6662 GetAssembler()->StoreDToOffset(DRegisterFrom(value), temp, data_offset);
6663 }
6664 break;
6665 }
6666
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006667 case DataType::Type::kVoid:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006668 LOG(FATAL) << "Unreachable type " << value_type;
6669 UNREACHABLE();
6670 }
6671
6672 // Objects are handled in the switch.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006673 if (value_type != DataType::Type::kReference) {
Alexandre Rames374ddf32016-11-04 10:40:49 +00006674 // TODO(VIXL): Ensure we record the pc position immediately after the preceding store
6675 // instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01006676 codegen_->MaybeRecordImplicitNullCheck(instruction);
6677 }
6678}
6679
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006680void LocationsBuilderARMVIXL::VisitArrayLength(HArrayLength* instruction) {
6681 LocationSummary* locations =
6682 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
6683 locations->SetInAt(0, Location::RequiresRegister());
6684 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
6685}
6686
6687void InstructionCodeGeneratorARMVIXL::VisitArrayLength(HArrayLength* instruction) {
6688 uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction);
6689 vixl32::Register obj = InputRegisterAt(instruction, 0);
6690 vixl32::Register out = OutputRegister(instruction);
Alexandre Rames374ddf32016-11-04 10:40:49 +00006691 {
Artem Serov0fb37192016-12-06 18:13:40 +00006692 ExactAssemblyScope aas(GetVIXLAssembler(),
6693 vixl32::kMaxInstructionSizeInBytes,
6694 CodeBufferCheckScope::kMaximumSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00006695 __ ldr(out, MemOperand(obj, offset));
6696 codegen_->MaybeRecordImplicitNullCheck(instruction);
6697 }
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006698 // Mask out compression flag from String's array length.
6699 if (mirror::kUseStringCompression && instruction->IsStringLength()) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01006700 __ Lsr(out, out, 1u);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006701 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006702}
6703
Artem Serov2bbc9532016-10-21 11:51:50 +01006704void LocationsBuilderARMVIXL::VisitIntermediateAddress(HIntermediateAddress* instruction) {
Artem Serov2bbc9532016-10-21 11:51:50 +01006705 LocationSummary* locations =
6706 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
6707
6708 locations->SetInAt(0, Location::RequiresRegister());
6709 locations->SetInAt(1, Location::RegisterOrConstant(instruction->GetOffset()));
6710 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
6711}
6712
6713void InstructionCodeGeneratorARMVIXL::VisitIntermediateAddress(HIntermediateAddress* instruction) {
6714 vixl32::Register out = OutputRegister(instruction);
6715 vixl32::Register first = InputRegisterAt(instruction, 0);
6716 Location second = instruction->GetLocations()->InAt(1);
6717
Artem Serov2bbc9532016-10-21 11:51:50 +01006718 if (second.IsRegister()) {
6719 __ Add(out, first, RegisterFrom(second));
6720 } else {
Anton Kirilov644032c2016-12-06 17:51:43 +00006721 __ Add(out, first, Int32ConstantFrom(second));
Artem Serov2bbc9532016-10-21 11:51:50 +01006722 }
6723}
6724
Artem Serove1811ed2017-04-27 16:50:47 +01006725void LocationsBuilderARMVIXL::VisitIntermediateAddressIndex(
6726 HIntermediateAddressIndex* instruction) {
6727 LOG(FATAL) << "Unreachable " << instruction->GetId();
6728}
6729
6730void InstructionCodeGeneratorARMVIXL::VisitIntermediateAddressIndex(
6731 HIntermediateAddressIndex* instruction) {
6732 LOG(FATAL) << "Unreachable " << instruction->GetId();
6733}
6734
Scott Wakelingc34dba72016-10-03 10:14:44 +01006735void LocationsBuilderARMVIXL::VisitBoundsCheck(HBoundsCheck* instruction) {
6736 RegisterSet caller_saves = RegisterSet::Empty();
6737 InvokeRuntimeCallingConventionARMVIXL calling_convention;
6738 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0)));
6739 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(1)));
6740 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction, caller_saves);
Artem Serov2dd053d2017-03-08 14:54:06 +00006741
6742 HInstruction* index = instruction->InputAt(0);
6743 HInstruction* length = instruction->InputAt(1);
6744 // If both index and length are constants we can statically check the bounds. But if at least one
6745 // of them is not encodable ArmEncodableConstantOrRegister will create
6746 // Location::RequiresRegister() which is not desired to happen. Instead we create constant
6747 // locations.
6748 bool both_const = index->IsConstant() && length->IsConstant();
6749 locations->SetInAt(0, both_const
6750 ? Location::ConstantLocation(index->AsConstant())
6751 : ArmEncodableConstantOrRegister(index, CMP));
6752 locations->SetInAt(1, both_const
6753 ? Location::ConstantLocation(length->AsConstant())
6754 : ArmEncodableConstantOrRegister(length, CMP));
Scott Wakelingc34dba72016-10-03 10:14:44 +01006755}
6756
6757void InstructionCodeGeneratorARMVIXL::VisitBoundsCheck(HBoundsCheck* instruction) {
Artem Serov2dd053d2017-03-08 14:54:06 +00006758 LocationSummary* locations = instruction->GetLocations();
6759 Location index_loc = locations->InAt(0);
6760 Location length_loc = locations->InAt(1);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006761
Artem Serov2dd053d2017-03-08 14:54:06 +00006762 if (length_loc.IsConstant()) {
6763 int32_t length = Int32ConstantFrom(length_loc);
6764 if (index_loc.IsConstant()) {
6765 // BCE will remove the bounds check if we are guaranteed to pass.
6766 int32_t index = Int32ConstantFrom(index_loc);
6767 if (index < 0 || index >= length) {
6768 SlowPathCodeARMVIXL* slow_path =
6769 new (GetGraph()->GetArena()) BoundsCheckSlowPathARMVIXL(instruction);
6770 codegen_->AddSlowPath(slow_path);
6771 __ B(slow_path->GetEntryLabel());
6772 } else {
6773 // Some optimization after BCE may have generated this, and we should not
6774 // generate a bounds check if it is a valid range.
6775 }
6776 return;
6777 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006778
Artem Serov2dd053d2017-03-08 14:54:06 +00006779 SlowPathCodeARMVIXL* slow_path =
6780 new (GetGraph()->GetArena()) BoundsCheckSlowPathARMVIXL(instruction);
6781 __ Cmp(RegisterFrom(index_loc), length);
6782 codegen_->AddSlowPath(slow_path);
6783 __ B(hs, slow_path->GetEntryLabel());
6784 } else {
6785 SlowPathCodeARMVIXL* slow_path =
6786 new (GetGraph()->GetArena()) BoundsCheckSlowPathARMVIXL(instruction);
6787 __ Cmp(RegisterFrom(length_loc), InputOperandAt(instruction, 0));
6788 codegen_->AddSlowPath(slow_path);
6789 __ B(ls, slow_path->GetEntryLabel());
6790 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006791}
6792
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006793void CodeGeneratorARMVIXL::MarkGCCard(vixl32::Register temp,
6794 vixl32::Register card,
6795 vixl32::Register object,
6796 vixl32::Register value,
6797 bool can_be_null) {
6798 vixl32::Label is_null;
6799 if (can_be_null) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00006800 __ CompareAndBranchIfZero(value, &is_null);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006801 }
6802 GetAssembler()->LoadFromOffset(
6803 kLoadWord, card, tr, Thread::CardTableOffset<kArmPointerSize>().Int32Value());
Scott Wakelingb77051e2016-11-21 19:46:00 +00006804 __ Lsr(temp, object, Operand::From(gc::accounting::CardTable::kCardShift));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006805 __ Strb(card, MemOperand(card, temp));
6806 if (can_be_null) {
6807 __ Bind(&is_null);
6808 }
6809}
6810
Scott Wakelingfe885462016-09-22 10:24:38 +01006811void LocationsBuilderARMVIXL::VisitParallelMove(HParallelMove* instruction ATTRIBUTE_UNUSED) {
6812 LOG(FATAL) << "Unreachable";
6813}
6814
6815void InstructionCodeGeneratorARMVIXL::VisitParallelMove(HParallelMove* instruction) {
6816 codegen_->GetMoveResolver()->EmitNativeCode(instruction);
6817}
6818
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006819void LocationsBuilderARMVIXL::VisitSuspendCheck(HSuspendCheck* instruction) {
Artem Serov657022c2016-11-23 14:19:38 +00006820 LocationSummary* locations =
6821 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
6822 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006823}
6824
6825void InstructionCodeGeneratorARMVIXL::VisitSuspendCheck(HSuspendCheck* instruction) {
6826 HBasicBlock* block = instruction->GetBlock();
6827 if (block->GetLoopInformation() != nullptr) {
6828 DCHECK(block->GetLoopInformation()->GetSuspendCheck() == instruction);
6829 // The back edge will generate the suspend check.
6830 return;
6831 }
6832 if (block->IsEntryBlock() && instruction->GetNext()->IsGoto()) {
6833 // The goto will generate the suspend check.
6834 return;
6835 }
6836 GenerateSuspendCheck(instruction, nullptr);
Roland Levillain5daa4952017-07-03 17:23:56 +01006837 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 12);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006838}
6839
6840void InstructionCodeGeneratorARMVIXL::GenerateSuspendCheck(HSuspendCheck* instruction,
6841 HBasicBlock* successor) {
6842 SuspendCheckSlowPathARMVIXL* slow_path =
6843 down_cast<SuspendCheckSlowPathARMVIXL*>(instruction->GetSlowPath());
6844 if (slow_path == nullptr) {
6845 slow_path = new (GetGraph()->GetArena()) SuspendCheckSlowPathARMVIXL(instruction, successor);
6846 instruction->SetSlowPath(slow_path);
6847 codegen_->AddSlowPath(slow_path);
6848 if (successor != nullptr) {
6849 DCHECK(successor->IsLoopHeader());
6850 codegen_->ClearSpillSlotsFromLoopPhisInStackMap(instruction);
6851 }
6852 } else {
6853 DCHECK_EQ(slow_path->GetSuccessor(), successor);
6854 }
6855
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006856 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006857 vixl32::Register temp = temps.Acquire();
6858 GetAssembler()->LoadFromOffset(
6859 kLoadUnsignedHalfword, temp, tr, Thread::ThreadFlagsOffset<kArmPointerSize>().Int32Value());
6860 if (successor == nullptr) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00006861 __ CompareAndBranchIfNonZero(temp, slow_path->GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006862 __ Bind(slow_path->GetReturnLabel());
6863 } else {
xueliang.zhongf51bc622016-11-04 09:23:32 +00006864 __ CompareAndBranchIfZero(temp, codegen_->GetLabelOf(successor));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006865 __ B(slow_path->GetEntryLabel());
6866 }
6867}
6868
Scott Wakelingfe885462016-09-22 10:24:38 +01006869ArmVIXLAssembler* ParallelMoveResolverARMVIXL::GetAssembler() const {
6870 return codegen_->GetAssembler();
6871}
6872
6873void ParallelMoveResolverARMVIXL::EmitMove(size_t index) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006874 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
Scott Wakelingfe885462016-09-22 10:24:38 +01006875 MoveOperands* move = moves_[index];
6876 Location source = move->GetSource();
6877 Location destination = move->GetDestination();
6878
6879 if (source.IsRegister()) {
6880 if (destination.IsRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006881 __ Mov(RegisterFrom(destination), RegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01006882 } else if (destination.IsFpuRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006883 __ Vmov(SRegisterFrom(destination), RegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01006884 } else {
6885 DCHECK(destination.IsStackSlot());
6886 GetAssembler()->StoreToOffset(kStoreWord,
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006887 RegisterFrom(source),
Scott Wakelingfe885462016-09-22 10:24:38 +01006888 sp,
6889 destination.GetStackIndex());
6890 }
6891 } else if (source.IsStackSlot()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006892 if (destination.IsRegister()) {
6893 GetAssembler()->LoadFromOffset(kLoadWord,
6894 RegisterFrom(destination),
6895 sp,
6896 source.GetStackIndex());
6897 } else if (destination.IsFpuRegister()) {
6898 GetAssembler()->LoadSFromOffset(SRegisterFrom(destination), sp, source.GetStackIndex());
6899 } else {
6900 DCHECK(destination.IsStackSlot());
6901 vixl32::Register temp = temps.Acquire();
6902 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, source.GetStackIndex());
6903 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
6904 }
Scott Wakelingfe885462016-09-22 10:24:38 +01006905 } else if (source.IsFpuRegister()) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01006906 if (destination.IsRegister()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006907 __ Vmov(RegisterFrom(destination), SRegisterFrom(source));
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01006908 } else if (destination.IsFpuRegister()) {
6909 __ Vmov(SRegisterFrom(destination), SRegisterFrom(source));
6910 } else {
6911 DCHECK(destination.IsStackSlot());
6912 GetAssembler()->StoreSToOffset(SRegisterFrom(source), sp, destination.GetStackIndex());
6913 }
Scott Wakelingfe885462016-09-22 10:24:38 +01006914 } else if (source.IsDoubleStackSlot()) {
Alexandre Rames9c19bd62016-10-24 11:50:32 +01006915 if (destination.IsDoubleStackSlot()) {
6916 vixl32::DRegister temp = temps.AcquireD();
6917 GetAssembler()->LoadDFromOffset(temp, sp, source.GetStackIndex());
6918 GetAssembler()->StoreDToOffset(temp, sp, destination.GetStackIndex());
6919 } else if (destination.IsRegisterPair()) {
6920 DCHECK(ExpectedPairLayout(destination));
6921 GetAssembler()->LoadFromOffset(
6922 kLoadWordPair, LowRegisterFrom(destination), sp, source.GetStackIndex());
6923 } else {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01006924 DCHECK(destination.IsFpuRegisterPair()) << destination;
6925 GetAssembler()->LoadDFromOffset(DRegisterFrom(destination), sp, source.GetStackIndex());
Alexandre Rames9c19bd62016-10-24 11:50:32 +01006926 }
Scott Wakelingfe885462016-09-22 10:24:38 +01006927 } else if (source.IsRegisterPair()) {
6928 if (destination.IsRegisterPair()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006929 __ Mov(LowRegisterFrom(destination), LowRegisterFrom(source));
6930 __ Mov(HighRegisterFrom(destination), HighRegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01006931 } else if (destination.IsFpuRegisterPair()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006932 __ Vmov(DRegisterFrom(destination), LowRegisterFrom(source), HighRegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01006933 } else {
6934 DCHECK(destination.IsDoubleStackSlot()) << destination;
6935 DCHECK(ExpectedPairLayout(source));
6936 GetAssembler()->StoreToOffset(kStoreWordPair,
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006937 LowRegisterFrom(source),
Scott Wakelingfe885462016-09-22 10:24:38 +01006938 sp,
6939 destination.GetStackIndex());
6940 }
6941 } else if (source.IsFpuRegisterPair()) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01006942 if (destination.IsRegisterPair()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006943 __ Vmov(LowRegisterFrom(destination), HighRegisterFrom(destination), DRegisterFrom(source));
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01006944 } else if (destination.IsFpuRegisterPair()) {
6945 __ Vmov(DRegisterFrom(destination), DRegisterFrom(source));
6946 } else {
6947 DCHECK(destination.IsDoubleStackSlot()) << destination;
6948 GetAssembler()->StoreDToOffset(DRegisterFrom(source), sp, destination.GetStackIndex());
6949 }
Scott Wakelingfe885462016-09-22 10:24:38 +01006950 } else {
6951 DCHECK(source.IsConstant()) << source;
6952 HConstant* constant = source.GetConstant();
6953 if (constant->IsIntConstant() || constant->IsNullConstant()) {
6954 int32_t value = CodeGenerator::GetInt32ValueOf(constant);
6955 if (destination.IsRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006956 __ Mov(RegisterFrom(destination), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01006957 } else {
6958 DCHECK(destination.IsStackSlot());
Scott Wakelingfe885462016-09-22 10:24:38 +01006959 vixl32::Register temp = temps.Acquire();
6960 __ Mov(temp, value);
6961 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
6962 }
6963 } else if (constant->IsLongConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006964 int64_t value = Int64ConstantFrom(source);
Scott Wakelingfe885462016-09-22 10:24:38 +01006965 if (destination.IsRegisterPair()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006966 __ Mov(LowRegisterFrom(destination), Low32Bits(value));
6967 __ Mov(HighRegisterFrom(destination), High32Bits(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01006968 } else {
6969 DCHECK(destination.IsDoubleStackSlot()) << destination;
Scott Wakelingfe885462016-09-22 10:24:38 +01006970 vixl32::Register temp = temps.Acquire();
6971 __ Mov(temp, Low32Bits(value));
6972 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
6973 __ Mov(temp, High32Bits(value));
6974 GetAssembler()->StoreToOffset(kStoreWord,
6975 temp,
6976 sp,
6977 destination.GetHighStackIndex(kArmWordSize));
6978 }
6979 } else if (constant->IsDoubleConstant()) {
6980 double value = constant->AsDoubleConstant()->GetValue();
6981 if (destination.IsFpuRegisterPair()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006982 __ Vmov(DRegisterFrom(destination), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01006983 } else {
6984 DCHECK(destination.IsDoubleStackSlot()) << destination;
6985 uint64_t int_value = bit_cast<uint64_t, double>(value);
Scott Wakelingfe885462016-09-22 10:24:38 +01006986 vixl32::Register temp = temps.Acquire();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006987 __ Mov(temp, Low32Bits(int_value));
Scott Wakelingfe885462016-09-22 10:24:38 +01006988 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006989 __ Mov(temp, High32Bits(int_value));
Scott Wakelingfe885462016-09-22 10:24:38 +01006990 GetAssembler()->StoreToOffset(kStoreWord,
6991 temp,
6992 sp,
6993 destination.GetHighStackIndex(kArmWordSize));
6994 }
6995 } else {
6996 DCHECK(constant->IsFloatConstant()) << constant->DebugName();
6997 float value = constant->AsFloatConstant()->GetValue();
6998 if (destination.IsFpuRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006999 __ Vmov(SRegisterFrom(destination), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01007000 } else {
7001 DCHECK(destination.IsStackSlot());
Scott Wakelingfe885462016-09-22 10:24:38 +01007002 vixl32::Register temp = temps.Acquire();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007003 __ Mov(temp, bit_cast<int32_t, float>(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01007004 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
7005 }
7006 }
7007 }
7008}
7009
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007010void ParallelMoveResolverARMVIXL::Exchange(vixl32::Register reg, int mem) {
7011 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
7012 vixl32::Register temp = temps.Acquire();
7013 __ Mov(temp, reg);
7014 GetAssembler()->LoadFromOffset(kLoadWord, reg, sp, mem);
7015 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, mem);
Scott Wakelingfe885462016-09-22 10:24:38 +01007016}
7017
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007018void ParallelMoveResolverARMVIXL::Exchange(int mem1, int mem2) {
7019 // TODO(VIXL32): Double check the performance of this implementation.
7020 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
Nicolas Geoffray13a797b2017-03-15 16:41:31 +00007021 vixl32::Register temp1 = temps.Acquire();
7022 ScratchRegisterScope ensure_scratch(
7023 this, temp1.GetCode(), r0.GetCode(), codegen_->GetNumberOfCoreRegisters());
7024 vixl32::Register temp2(ensure_scratch.GetRegister());
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007025
Nicolas Geoffray13a797b2017-03-15 16:41:31 +00007026 int stack_offset = ensure_scratch.IsSpilled() ? kArmWordSize : 0;
7027 GetAssembler()->LoadFromOffset(kLoadWord, temp1, sp, mem1 + stack_offset);
7028 GetAssembler()->LoadFromOffset(kLoadWord, temp2, sp, mem2 + stack_offset);
7029 GetAssembler()->StoreToOffset(kStoreWord, temp1, sp, mem2 + stack_offset);
7030 GetAssembler()->StoreToOffset(kStoreWord, temp2, sp, mem1 + stack_offset);
Scott Wakelingfe885462016-09-22 10:24:38 +01007031}
7032
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007033void ParallelMoveResolverARMVIXL::EmitSwap(size_t index) {
7034 MoveOperands* move = moves_[index];
7035 Location source = move->GetSource();
7036 Location destination = move->GetDestination();
7037 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
7038
7039 if (source.IsRegister() && destination.IsRegister()) {
7040 vixl32::Register temp = temps.Acquire();
7041 DCHECK(!RegisterFrom(source).Is(temp));
7042 DCHECK(!RegisterFrom(destination).Is(temp));
7043 __ Mov(temp, RegisterFrom(destination));
7044 __ Mov(RegisterFrom(destination), RegisterFrom(source));
7045 __ Mov(RegisterFrom(source), temp);
7046 } else if (source.IsRegister() && destination.IsStackSlot()) {
7047 Exchange(RegisterFrom(source), destination.GetStackIndex());
7048 } else if (source.IsStackSlot() && destination.IsRegister()) {
7049 Exchange(RegisterFrom(destination), source.GetStackIndex());
7050 } else if (source.IsStackSlot() && destination.IsStackSlot()) {
Anton Kirilovdda43962016-11-21 19:55:20 +00007051 Exchange(source.GetStackIndex(), destination.GetStackIndex());
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007052 } else if (source.IsFpuRegister() && destination.IsFpuRegister()) {
Nicolas Geoffray13a797b2017-03-15 16:41:31 +00007053 vixl32::Register temp = temps.Acquire();
Anton Kirilovdda43962016-11-21 19:55:20 +00007054 __ Vmov(temp, SRegisterFrom(source));
7055 __ Vmov(SRegisterFrom(source), SRegisterFrom(destination));
7056 __ Vmov(SRegisterFrom(destination), temp);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007057 } else if (source.IsRegisterPair() && destination.IsRegisterPair()) {
7058 vixl32::DRegister temp = temps.AcquireD();
7059 __ Vmov(temp, LowRegisterFrom(source), HighRegisterFrom(source));
7060 __ Mov(LowRegisterFrom(source), LowRegisterFrom(destination));
7061 __ Mov(HighRegisterFrom(source), HighRegisterFrom(destination));
7062 __ Vmov(LowRegisterFrom(destination), HighRegisterFrom(destination), temp);
7063 } else if (source.IsRegisterPair() || destination.IsRegisterPair()) {
7064 vixl32::Register low_reg = LowRegisterFrom(source.IsRegisterPair() ? source : destination);
7065 int mem = source.IsRegisterPair() ? destination.GetStackIndex() : source.GetStackIndex();
7066 DCHECK(ExpectedPairLayout(source.IsRegisterPair() ? source : destination));
7067 vixl32::DRegister temp = temps.AcquireD();
7068 __ Vmov(temp, low_reg, vixl32::Register(low_reg.GetCode() + 1));
7069 GetAssembler()->LoadFromOffset(kLoadWordPair, low_reg, sp, mem);
7070 GetAssembler()->StoreDToOffset(temp, sp, mem);
7071 } else if (source.IsFpuRegisterPair() && destination.IsFpuRegisterPair()) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007072 vixl32::DRegister first = DRegisterFrom(source);
7073 vixl32::DRegister second = DRegisterFrom(destination);
7074 vixl32::DRegister temp = temps.AcquireD();
7075 __ Vmov(temp, first);
7076 __ Vmov(first, second);
7077 __ Vmov(second, temp);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007078 } else if (source.IsFpuRegisterPair() || destination.IsFpuRegisterPair()) {
Anton Kirilovdda43962016-11-21 19:55:20 +00007079 vixl32::DRegister reg = source.IsFpuRegisterPair()
7080 ? DRegisterFrom(source)
7081 : DRegisterFrom(destination);
7082 int mem = source.IsFpuRegisterPair()
7083 ? destination.GetStackIndex()
7084 : source.GetStackIndex();
7085 vixl32::DRegister temp = temps.AcquireD();
7086 __ Vmov(temp, reg);
7087 GetAssembler()->LoadDFromOffset(reg, sp, mem);
7088 GetAssembler()->StoreDToOffset(temp, sp, mem);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007089 } else if (source.IsFpuRegister() || destination.IsFpuRegister()) {
Anton Kirilovdda43962016-11-21 19:55:20 +00007090 vixl32::SRegister reg = source.IsFpuRegister()
7091 ? SRegisterFrom(source)
7092 : SRegisterFrom(destination);
7093 int mem = source.IsFpuRegister()
7094 ? destination.GetStackIndex()
7095 : source.GetStackIndex();
7096 vixl32::Register temp = temps.Acquire();
7097 __ Vmov(temp, reg);
7098 GetAssembler()->LoadSFromOffset(reg, sp, mem);
7099 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, mem);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007100 } else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) {
7101 vixl32::DRegister temp1 = temps.AcquireD();
7102 vixl32::DRegister temp2 = temps.AcquireD();
7103 __ Vldr(temp1, MemOperand(sp, source.GetStackIndex()));
7104 __ Vldr(temp2, MemOperand(sp, destination.GetStackIndex()));
7105 __ Vstr(temp1, MemOperand(sp, destination.GetStackIndex()));
7106 __ Vstr(temp2, MemOperand(sp, source.GetStackIndex()));
7107 } else {
7108 LOG(FATAL) << "Unimplemented" << source << " <-> " << destination;
7109 }
Scott Wakelingfe885462016-09-22 10:24:38 +01007110}
7111
Nicolas Geoffray13a797b2017-03-15 16:41:31 +00007112void ParallelMoveResolverARMVIXL::SpillScratch(int reg) {
7113 __ Push(vixl32::Register(reg));
Scott Wakelingfe885462016-09-22 10:24:38 +01007114}
7115
Nicolas Geoffray13a797b2017-03-15 16:41:31 +00007116void ParallelMoveResolverARMVIXL::RestoreScratch(int reg) {
7117 __ Pop(vixl32::Register(reg));
Scott Wakelingfe885462016-09-22 10:24:38 +01007118}
7119
Artem Serov02d37832016-10-25 15:25:33 +01007120HLoadClass::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadClassKind(
Artem Serovd4cc5b22016-11-04 11:19:09 +00007121 HLoadClass::LoadKind desired_class_load_kind) {
7122 switch (desired_class_load_kind) {
Nicolas Geoffray83c8e272017-01-31 14:36:37 +00007123 case HLoadClass::LoadKind::kInvalid:
7124 LOG(FATAL) << "UNREACHABLE";
7125 UNREACHABLE();
Artem Serovd4cc5b22016-11-04 11:19:09 +00007126 case HLoadClass::LoadKind::kReferrersClass:
7127 break;
Artem Serovd4cc5b22016-11-04 11:19:09 +00007128 case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
Vladimir Marko94ec2db2017-09-06 17:21:03 +01007129 case HLoadClass::LoadKind::kBootImageClassTable:
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007130 case HLoadClass::LoadKind::kBssEntry:
7131 DCHECK(!Runtime::Current()->UseJitCompilation());
7132 break;
Nicolas Geoffray22384ae2016-12-12 22:33:36 +00007133 case HLoadClass::LoadKind::kJitTableAddress:
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007134 DCHECK(Runtime::Current()->UseJitCompilation());
Artem Serovc5fcb442016-12-02 19:19:58 +00007135 break;
Vladimir Marko764d4542017-05-16 10:31:41 +01007136 case HLoadClass::LoadKind::kBootImageAddress:
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007137 case HLoadClass::LoadKind::kRuntimeCall:
Artem Serovd4cc5b22016-11-04 11:19:09 +00007138 break;
7139 }
7140 return desired_class_load_kind;
Artem Serov02d37832016-10-25 15:25:33 +01007141}
7142
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007143void LocationsBuilderARMVIXL::VisitLoadClass(HLoadClass* cls) {
Vladimir Marko41559982017-01-06 14:04:23 +00007144 HLoadClass::LoadKind load_kind = cls->GetLoadKind();
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007145 if (load_kind == HLoadClass::LoadKind::kRuntimeCall) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007146 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Vladimir Marko41559982017-01-06 14:04:23 +00007147 CodeGenerator::CreateLoadClassRuntimeCallLocationSummary(
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007148 cls,
7149 LocationFrom(calling_convention.GetRegisterAt(0)),
Vladimir Marko41559982017-01-06 14:04:23 +00007150 LocationFrom(r0));
Vladimir Markoea4c1262017-02-06 19:59:33 +00007151 DCHECK(calling_convention.GetRegisterAt(0).Is(r0));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007152 return;
7153 }
Vladimir Marko41559982017-01-06 14:04:23 +00007154 DCHECK(!cls->NeedsAccessCheck());
Scott Wakelingfe885462016-09-22 10:24:38 +01007155
Artem Serovd4cc5b22016-11-04 11:19:09 +00007156 const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
7157 LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier)
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007158 ? LocationSummary::kCallOnSlowPath
7159 : LocationSummary::kNoCall;
7160 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
Artem Serovd4cc5b22016-11-04 11:19:09 +00007161 if (kUseBakerReadBarrier && requires_read_barrier && !cls->NeedsEnvironment()) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00007162 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Artem Serovd4cc5b22016-11-04 11:19:09 +00007163 }
7164
Vladimir Marko41559982017-01-06 14:04:23 +00007165 if (load_kind == HLoadClass::LoadKind::kReferrersClass) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007166 locations->SetInAt(0, Location::RequiresRegister());
7167 }
7168 locations->SetOut(Location::RequiresRegister());
Vladimir Markoea4c1262017-02-06 19:59:33 +00007169 if (load_kind == HLoadClass::LoadKind::kBssEntry) {
7170 if (!kUseReadBarrier || kUseBakerReadBarrier) {
7171 // Rely on the type resolution or initialization and marking to save everything we need.
7172 // Note that IP may be clobbered by saving/restoring the live register (only one thanks
7173 // to the custom calling convention) or by marking, so we request a different temp.
7174 locations->AddTemp(Location::RequiresRegister());
7175 RegisterSet caller_saves = RegisterSet::Empty();
7176 InvokeRuntimeCallingConventionARMVIXL calling_convention;
7177 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0)));
7178 // TODO: Add GetReturnLocation() to the calling convention so that we can DCHECK()
7179 // that the the kPrimNot result register is the same as the first argument register.
7180 locations->SetCustomSlowPathCallerSaves(caller_saves);
7181 } else {
7182 // For non-Baker read barrier we have a temp-clobbering call.
7183 }
7184 }
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01007185 if (kUseBakerReadBarrier && kBakerReadBarrierLinkTimeThunksEnableForGcRoots) {
7186 if (load_kind == HLoadClass::LoadKind::kBssEntry ||
7187 (load_kind == HLoadClass::LoadKind::kReferrersClass &&
7188 !Runtime::Current()->UseJitCompilation())) {
7189 locations->AddTemp(Location::RegisterLocation(kBakerCcEntrypointRegister.GetCode()));
7190 }
7191 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007192}
7193
Nicolas Geoffray5247c082017-01-13 14:17:29 +00007194// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
7195// move.
7196void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFETY_ANALYSIS {
Vladimir Marko41559982017-01-06 14:04:23 +00007197 HLoadClass::LoadKind load_kind = cls->GetLoadKind();
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007198 if (load_kind == HLoadClass::LoadKind::kRuntimeCall) {
Vladimir Marko41559982017-01-06 14:04:23 +00007199 codegen_->GenerateLoadClassRuntimeCall(cls);
Roland Levillain5daa4952017-07-03 17:23:56 +01007200 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 13);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007201 return;
7202 }
Vladimir Marko41559982017-01-06 14:04:23 +00007203 DCHECK(!cls->NeedsAccessCheck());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007204
Vladimir Marko41559982017-01-06 14:04:23 +00007205 LocationSummary* locations = cls->GetLocations();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007206 Location out_loc = locations->Out();
7207 vixl32::Register out = OutputRegister(cls);
7208
Artem Serovd4cc5b22016-11-04 11:19:09 +00007209 const ReadBarrierOption read_barrier_option = cls->IsInBootImage()
7210 ? kWithoutReadBarrier
7211 : kCompilerReadBarrierOption;
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007212 bool generate_null_check = false;
Vladimir Marko41559982017-01-06 14:04:23 +00007213 switch (load_kind) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007214 case HLoadClass::LoadKind::kReferrersClass: {
7215 DCHECK(!cls->CanCallRuntime());
7216 DCHECK(!cls->MustGenerateClinitCheck());
7217 // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
7218 vixl32::Register current_method = InputRegisterAt(cls, 0);
7219 GenerateGcRootFieldLoad(cls,
7220 out_loc,
7221 current_method,
Roland Levillain00468f32016-10-27 18:02:48 +01007222 ArtMethod::DeclaringClassOffset().Int32Value(),
Artem Serovd4cc5b22016-11-04 11:19:09 +00007223 read_barrier_option);
7224 break;
7225 }
Artem Serovd4cc5b22016-11-04 11:19:09 +00007226 case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: {
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007227 DCHECK(codegen_->GetCompilerOptions().IsBootImage());
Artem Serovd4cc5b22016-11-04 11:19:09 +00007228 DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
7229 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
7230 codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex());
7231 codegen_->EmitMovwMovtPlaceholder(labels, out);
7232 break;
7233 }
7234 case HLoadClass::LoadKind::kBootImageAddress: {
Artem Serovc5fcb442016-12-02 19:19:58 +00007235 DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
Nicolas Geoffray5247c082017-01-13 14:17:29 +00007236 uint32_t address = dchecked_integral_cast<uint32_t>(
7237 reinterpret_cast<uintptr_t>(cls->GetClass().Get()));
7238 DCHECK_NE(address, 0u);
Artem Serovc5fcb442016-12-02 19:19:58 +00007239 __ Ldr(out, codegen_->DeduplicateBootImageAddressLiteral(address));
Artem Serovd4cc5b22016-11-04 11:19:09 +00007240 break;
7241 }
Vladimir Marko94ec2db2017-09-06 17:21:03 +01007242 case HLoadClass::LoadKind::kBootImageClassTable: {
7243 DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
7244 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
7245 codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex());
7246 codegen_->EmitMovwMovtPlaceholder(labels, out);
7247 __ Ldr(out, MemOperand(out, /* offset */ 0));
7248 // Extract the reference from the slot data, i.e. clear the hash bits.
7249 int32_t masked_hash = ClassTable::TableSlot::MaskHash(
7250 ComputeModifiedUtf8Hash(cls->GetDexFile().StringByTypeIdx(cls->GetTypeIndex())));
7251 if (masked_hash != 0) {
7252 __ Sub(out, out, Operand(masked_hash));
7253 }
7254 break;
7255 }
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007256 case HLoadClass::LoadKind::kBssEntry: {
Vladimir Markoea4c1262017-02-06 19:59:33 +00007257 vixl32::Register temp = (!kUseReadBarrier || kUseBakerReadBarrier)
7258 ? RegisterFrom(locations->GetTemp(0))
7259 : out;
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007260 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
Vladimir Marko1998cd02017-01-13 13:02:58 +00007261 codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex());
Vladimir Markoea4c1262017-02-06 19:59:33 +00007262 codegen_->EmitMovwMovtPlaceholder(labels, temp);
7263 GenerateGcRootFieldLoad(cls, out_loc, temp, /* offset */ 0, read_barrier_option);
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007264 generate_null_check = true;
7265 break;
7266 }
Nicolas Geoffray22384ae2016-12-12 22:33:36 +00007267 case HLoadClass::LoadKind::kJitTableAddress: {
Artem Serovc5fcb442016-12-02 19:19:58 +00007268 __ Ldr(out, codegen_->DeduplicateJitClassLiteral(cls->GetDexFile(),
7269 cls->GetTypeIndex(),
Nicolas Geoffray5247c082017-01-13 14:17:29 +00007270 cls->GetClass()));
Artem Serovc5fcb442016-12-02 19:19:58 +00007271 // /* GcRoot<mirror::Class> */ out = *out
Vladimir Markoea4c1262017-02-06 19:59:33 +00007272 GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, read_barrier_option);
Artem Serovd4cc5b22016-11-04 11:19:09 +00007273 break;
7274 }
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007275 case HLoadClass::LoadKind::kRuntimeCall:
Nicolas Geoffray83c8e272017-01-31 14:36:37 +00007276 case HLoadClass::LoadKind::kInvalid:
Vladimir Marko41559982017-01-06 14:04:23 +00007277 LOG(FATAL) << "UNREACHABLE";
7278 UNREACHABLE();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007279 }
7280
7281 if (generate_null_check || cls->MustGenerateClinitCheck()) {
7282 DCHECK(cls->CanCallRuntime());
7283 LoadClassSlowPathARMVIXL* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARMVIXL(
7284 cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
7285 codegen_->AddSlowPath(slow_path);
7286 if (generate_null_check) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00007287 __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007288 }
7289 if (cls->MustGenerateClinitCheck()) {
7290 GenerateClassInitializationCheck(slow_path, out);
7291 } else {
7292 __ Bind(slow_path->GetExitLabel());
7293 }
Roland Levillain5daa4952017-07-03 17:23:56 +01007294 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 14);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007295 }
7296}
7297
Artem Serov02d37832016-10-25 15:25:33 +01007298void LocationsBuilderARMVIXL::VisitClinitCheck(HClinitCheck* check) {
7299 LocationSummary* locations =
7300 new (GetGraph()->GetArena()) LocationSummary(check, LocationSummary::kCallOnSlowPath);
7301 locations->SetInAt(0, Location::RequiresRegister());
7302 if (check->HasUses()) {
7303 locations->SetOut(Location::SameAsFirstInput());
7304 }
7305}
7306
7307void InstructionCodeGeneratorARMVIXL::VisitClinitCheck(HClinitCheck* check) {
7308 // We assume the class is not null.
7309 LoadClassSlowPathARMVIXL* slow_path =
7310 new (GetGraph()->GetArena()) LoadClassSlowPathARMVIXL(check->GetLoadClass(),
7311 check,
7312 check->GetDexPc(),
7313 /* do_clinit */ true);
7314 codegen_->AddSlowPath(slow_path);
7315 GenerateClassInitializationCheck(slow_path, InputRegisterAt(check, 0));
7316}
7317
7318void InstructionCodeGeneratorARMVIXL::GenerateClassInitializationCheck(
7319 LoadClassSlowPathARMVIXL* slow_path, vixl32::Register class_reg) {
7320 UseScratchRegisterScope temps(GetVIXLAssembler());
7321 vixl32::Register temp = temps.Acquire();
7322 GetAssembler()->LoadFromOffset(kLoadWord,
7323 temp,
7324 class_reg,
7325 mirror::Class::StatusOffset().Int32Value());
7326 __ Cmp(temp, mirror::Class::kStatusInitialized);
7327 __ B(lt, slow_path->GetEntryLabel());
7328 // Even if the initialized flag is set, we may be in a situation where caches are not synced
7329 // properly. Therefore, we do a memory fence.
7330 __ Dmb(ISH);
7331 __ Bind(slow_path->GetExitLabel());
7332}
7333
Artem Serov02d37832016-10-25 15:25:33 +01007334HLoadString::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadStringKind(
Artem Serovd4cc5b22016-11-04 11:19:09 +00007335 HLoadString::LoadKind desired_string_load_kind) {
7336 switch (desired_string_load_kind) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00007337 case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01007338 case HLoadString::LoadKind::kBootImageInternTable:
Artem Serovd4cc5b22016-11-04 11:19:09 +00007339 case HLoadString::LoadKind::kBssEntry:
7340 DCHECK(!Runtime::Current()->UseJitCompilation());
7341 break;
7342 case HLoadString::LoadKind::kJitTableAddress:
7343 DCHECK(Runtime::Current()->UseJitCompilation());
Artem Serovc5fcb442016-12-02 19:19:58 +00007344 break;
Vladimir Marko764d4542017-05-16 10:31:41 +01007345 case HLoadString::LoadKind::kBootImageAddress:
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007346 case HLoadString::LoadKind::kRuntimeCall:
Artem Serovd4cc5b22016-11-04 11:19:09 +00007347 break;
7348 }
7349 return desired_string_load_kind;
Artem Serov02d37832016-10-25 15:25:33 +01007350}
7351
7352void LocationsBuilderARMVIXL::VisitLoadString(HLoadString* load) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00007353 LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load);
Artem Serov02d37832016-10-25 15:25:33 +01007354 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
Artem Serov02d37832016-10-25 15:25:33 +01007355 HLoadString::LoadKind load_kind = load->GetLoadKind();
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007356 if (load_kind == HLoadString::LoadKind::kRuntimeCall) {
Artem Serov02d37832016-10-25 15:25:33 +01007357 locations->SetOut(LocationFrom(r0));
7358 } else {
7359 locations->SetOut(Location::RequiresRegister());
Artem Serovd4cc5b22016-11-04 11:19:09 +00007360 if (load_kind == HLoadString::LoadKind::kBssEntry) {
7361 if (!kUseReadBarrier || kUseBakerReadBarrier) {
Vladimir Markoea4c1262017-02-06 19:59:33 +00007362 // Rely on the pResolveString and marking to save everything we need, including temps.
7363 // Note that IP may be clobbered by saving/restoring the live register (only one thanks
7364 // to the custom calling convention) or by marking, so we request a different temp.
Artem Serovd4cc5b22016-11-04 11:19:09 +00007365 locations->AddTemp(Location::RequiresRegister());
7366 RegisterSet caller_saves = RegisterSet::Empty();
7367 InvokeRuntimeCallingConventionARMVIXL calling_convention;
7368 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0)));
7369 // TODO: Add GetReturnLocation() to the calling convention so that we can DCHECK()
7370 // that the the kPrimNot result register is the same as the first argument register.
7371 locations->SetCustomSlowPathCallerSaves(caller_saves);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01007372 if (kUseBakerReadBarrier && kBakerReadBarrierLinkTimeThunksEnableForGcRoots) {
7373 locations->AddTemp(Location::RegisterLocation(kBakerCcEntrypointRegister.GetCode()));
7374 }
Artem Serovd4cc5b22016-11-04 11:19:09 +00007375 } else {
7376 // For non-Baker read barrier we have a temp-clobbering call.
7377 }
7378 }
Artem Serov02d37832016-10-25 15:25:33 +01007379 }
7380}
7381
Nicolas Geoffrayf0acfe72017-01-09 20:54:52 +00007382// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
7383// move.
7384void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) NO_THREAD_SAFETY_ANALYSIS {
Artem Serovd4cc5b22016-11-04 11:19:09 +00007385 LocationSummary* locations = load->GetLocations();
7386 Location out_loc = locations->Out();
7387 vixl32::Register out = OutputRegister(load);
7388 HLoadString::LoadKind load_kind = load->GetLoadKind();
7389
7390 switch (load_kind) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00007391 case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
7392 DCHECK(codegen_->GetCompilerOptions().IsBootImage());
7393 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007394 codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex());
Artem Serovd4cc5b22016-11-04 11:19:09 +00007395 codegen_->EmitMovwMovtPlaceholder(labels, out);
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01007396 return;
Artem Serovd4cc5b22016-11-04 11:19:09 +00007397 }
7398 case HLoadString::LoadKind::kBootImageAddress: {
Nicolas Geoffrayf0acfe72017-01-09 20:54:52 +00007399 uint32_t address = dchecked_integral_cast<uint32_t>(
7400 reinterpret_cast<uintptr_t>(load->GetString().Get()));
7401 DCHECK_NE(address, 0u);
Artem Serovc5fcb442016-12-02 19:19:58 +00007402 __ Ldr(out, codegen_->DeduplicateBootImageAddressLiteral(address));
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01007403 return;
7404 }
7405 case HLoadString::LoadKind::kBootImageInternTable: {
7406 DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
7407 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
7408 codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex());
7409 codegen_->EmitMovwMovtPlaceholder(labels, out);
7410 __ Ldr(out, MemOperand(out, /* offset */ 0));
7411 return;
Artem Serovd4cc5b22016-11-04 11:19:09 +00007412 }
7413 case HLoadString::LoadKind::kBssEntry: {
7414 DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
Vladimir Markoea4c1262017-02-06 19:59:33 +00007415 vixl32::Register temp = (!kUseReadBarrier || kUseBakerReadBarrier)
7416 ? RegisterFrom(locations->GetTemp(0))
7417 : out;
Artem Serovd4cc5b22016-11-04 11:19:09 +00007418 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01007419 codegen_->NewStringBssEntryPatch(load->GetDexFile(), load->GetStringIndex());
Artem Serovd4cc5b22016-11-04 11:19:09 +00007420 codegen_->EmitMovwMovtPlaceholder(labels, temp);
7421 GenerateGcRootFieldLoad(load, out_loc, temp, /* offset */ 0, kCompilerReadBarrierOption);
7422 LoadStringSlowPathARMVIXL* slow_path =
7423 new (GetGraph()->GetArena()) LoadStringSlowPathARMVIXL(load);
7424 codegen_->AddSlowPath(slow_path);
7425 __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
7426 __ Bind(slow_path->GetExitLabel());
Roland Levillain5daa4952017-07-03 17:23:56 +01007427 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 15);
Artem Serovd4cc5b22016-11-04 11:19:09 +00007428 return;
7429 }
7430 case HLoadString::LoadKind::kJitTableAddress: {
Artem Serovc5fcb442016-12-02 19:19:58 +00007431 __ Ldr(out, codegen_->DeduplicateJitStringLiteral(load->GetDexFile(),
Nicolas Geoffrayf0acfe72017-01-09 20:54:52 +00007432 load->GetStringIndex(),
7433 load->GetString()));
Artem Serovc5fcb442016-12-02 19:19:58 +00007434 // /* GcRoot<mirror::String> */ out = *out
7435 GenerateGcRootFieldLoad(load, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption);
7436 return;
Artem Serovd4cc5b22016-11-04 11:19:09 +00007437 }
7438 default:
7439 break;
7440 }
Artem Serov02d37832016-10-25 15:25:33 +01007441
7442 // TODO: Re-add the compiler code to do string dex cache lookup again.
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007443 DCHECK_EQ(load->GetLoadKind(), HLoadString::LoadKind::kRuntimeCall);
Artem Serov02d37832016-10-25 15:25:33 +01007444 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Andreas Gampe8a0128a2016-11-28 07:38:35 -08007445 __ Mov(calling_convention.GetRegisterAt(0), load->GetStringIndex().index_);
Artem Serov02d37832016-10-25 15:25:33 +01007446 codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc());
7447 CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
Roland Levillain5daa4952017-07-03 17:23:56 +01007448 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 16);
Artem Serov02d37832016-10-25 15:25:33 +01007449}
7450
7451static int32_t GetExceptionTlsOffset() {
7452 return Thread::ExceptionOffset<kArmPointerSize>().Int32Value();
7453}
7454
7455void LocationsBuilderARMVIXL::VisitLoadException(HLoadException* load) {
7456 LocationSummary* locations =
7457 new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kNoCall);
7458 locations->SetOut(Location::RequiresRegister());
7459}
7460
7461void InstructionCodeGeneratorARMVIXL::VisitLoadException(HLoadException* load) {
7462 vixl32::Register out = OutputRegister(load);
7463 GetAssembler()->LoadFromOffset(kLoadWord, out, tr, GetExceptionTlsOffset());
7464}
7465
7466
7467void LocationsBuilderARMVIXL::VisitClearException(HClearException* clear) {
7468 new (GetGraph()->GetArena()) LocationSummary(clear, LocationSummary::kNoCall);
7469}
7470
7471void InstructionCodeGeneratorARMVIXL::VisitClearException(HClearException* clear ATTRIBUTE_UNUSED) {
7472 UseScratchRegisterScope temps(GetVIXLAssembler());
7473 vixl32::Register temp = temps.Acquire();
7474 __ Mov(temp, 0);
7475 GetAssembler()->StoreToOffset(kStoreWord, temp, tr, GetExceptionTlsOffset());
7476}
7477
7478void LocationsBuilderARMVIXL::VisitThrow(HThrow* instruction) {
7479 LocationSummary* locations =
7480 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
7481 InvokeRuntimeCallingConventionARMVIXL calling_convention;
7482 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
7483}
7484
7485void InstructionCodeGeneratorARMVIXL::VisitThrow(HThrow* instruction) {
7486 codegen_->InvokeRuntime(kQuickDeliverException, instruction, instruction->GetDexPc());
7487 CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
7488}
7489
Artem Serov657022c2016-11-23 14:19:38 +00007490// Temp is used for read barrier.
7491static size_t NumberOfInstanceOfTemps(TypeCheckKind type_check_kind) {
7492 if (kEmitCompilerReadBarrier &&
7493 (kUseBakerReadBarrier ||
7494 type_check_kind == TypeCheckKind::kAbstractClassCheck ||
7495 type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
7496 type_check_kind == TypeCheckKind::kArrayObjectCheck)) {
7497 return 1;
7498 }
7499 return 0;
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007500}
7501
Artem Serov657022c2016-11-23 14:19:38 +00007502// Interface case has 3 temps, one for holding the number of interfaces, one for the current
7503// interface pointer, one for loading the current interface.
7504// The other checks have one temp for loading the object's class.
7505static size_t NumberOfCheckCastTemps(TypeCheckKind type_check_kind) {
7506 if (type_check_kind == TypeCheckKind::kInterfaceCheck) {
7507 return 3;
7508 }
7509 return 1 + NumberOfInstanceOfTemps(type_check_kind);
7510}
Artem Serovcfbe9132016-10-14 15:58:56 +01007511
7512void LocationsBuilderARMVIXL::VisitInstanceOf(HInstanceOf* instruction) {
7513 LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
7514 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
7515 bool baker_read_barrier_slow_path = false;
7516 switch (type_check_kind) {
7517 case TypeCheckKind::kExactCheck:
7518 case TypeCheckKind::kAbstractClassCheck:
7519 case TypeCheckKind::kClassHierarchyCheck:
7520 case TypeCheckKind::kArrayObjectCheck:
7521 call_kind =
7522 kEmitCompilerReadBarrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall;
7523 baker_read_barrier_slow_path = kUseBakerReadBarrier;
7524 break;
7525 case TypeCheckKind::kArrayCheck:
7526 case TypeCheckKind::kUnresolvedCheck:
7527 case TypeCheckKind::kInterfaceCheck:
7528 call_kind = LocationSummary::kCallOnSlowPath;
7529 break;
7530 }
7531
7532 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
7533 if (baker_read_barrier_slow_path) {
7534 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
7535 }
7536 locations->SetInAt(0, Location::RequiresRegister());
7537 locations->SetInAt(1, Location::RequiresRegister());
7538 // The "out" register is used as a temporary, so it overlaps with the inputs.
7539 // Note that TypeCheckSlowPathARM uses this register too.
7540 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
Artem Serov657022c2016-11-23 14:19:38 +00007541 locations->AddRegisterTemps(NumberOfInstanceOfTemps(type_check_kind));
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01007542 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
7543 codegen_->MaybeAddBakerCcEntrypointTempForFields(locations);
7544 }
Artem Serovcfbe9132016-10-14 15:58:56 +01007545}
7546
7547void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) {
7548 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
7549 LocationSummary* locations = instruction->GetLocations();
7550 Location obj_loc = locations->InAt(0);
7551 vixl32::Register obj = InputRegisterAt(instruction, 0);
7552 vixl32::Register cls = InputRegisterAt(instruction, 1);
7553 Location out_loc = locations->Out();
7554 vixl32::Register out = OutputRegister(instruction);
Artem Serov657022c2016-11-23 14:19:38 +00007555 const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind);
7556 DCHECK_LE(num_temps, 1u);
7557 Location maybe_temp_loc = (num_temps >= 1) ? locations->GetTemp(0) : Location::NoLocation();
Artem Serovcfbe9132016-10-14 15:58:56 +01007558 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
7559 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
7560 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
7561 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007562 vixl32::Label done;
7563 vixl32::Label* const final_label = codegen_->GetFinalLabel(instruction, &done);
Artem Serovcfbe9132016-10-14 15:58:56 +01007564 SlowPathCodeARMVIXL* slow_path = nullptr;
7565
7566 // Return 0 if `obj` is null.
7567 // avoid null check if we know obj is not null.
7568 if (instruction->MustDoNullCheck()) {
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007569 DCHECK(!out.Is(obj));
7570 __ Mov(out, 0);
7571 __ CompareAndBranchIfZero(obj, final_label, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007572 }
7573
Artem Serovcfbe9132016-10-14 15:58:56 +01007574 switch (type_check_kind) {
7575 case TypeCheckKind::kExactCheck: {
Mathieu Chartier6beced42016-11-15 15:51:31 -08007576 // /* HeapReference<Class> */ out = obj->klass_
7577 GenerateReferenceLoadTwoRegisters(instruction,
7578 out_loc,
7579 obj_loc,
7580 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00007581 maybe_temp_loc,
7582 kCompilerReadBarrierOption);
Artem Serovcfbe9132016-10-14 15:58:56 +01007583 // Classes must be equal for the instanceof to succeed.
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007584 __ Cmp(out, cls);
7585 // We speculatively set the result to false without changing the condition
7586 // flags, which allows us to avoid some branching later.
7587 __ Mov(LeaveFlags, out, 0);
7588
7589 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
7590 // we check that the output is in a low register, so that a 16-bit MOV
7591 // encoding can be used.
7592 if (out.IsLow()) {
7593 // We use the scope because of the IT block that follows.
7594 ExactAssemblyScope guard(GetVIXLAssembler(),
7595 2 * vixl32::k16BitT32InstructionSizeInBytes,
7596 CodeBufferCheckScope::kExactSize);
7597
7598 __ it(eq);
7599 __ mov(eq, out, 1);
7600 } else {
7601 __ B(ne, final_label, /* far_target */ false);
7602 __ Mov(out, 1);
7603 }
7604
Artem Serovcfbe9132016-10-14 15:58:56 +01007605 break;
7606 }
7607
7608 case TypeCheckKind::kAbstractClassCheck: {
Mathieu Chartier6beced42016-11-15 15:51:31 -08007609 // /* HeapReference<Class> */ out = obj->klass_
7610 GenerateReferenceLoadTwoRegisters(instruction,
7611 out_loc,
7612 obj_loc,
7613 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00007614 maybe_temp_loc,
7615 kCompilerReadBarrierOption);
Artem Serovcfbe9132016-10-14 15:58:56 +01007616 // If the class is abstract, we eagerly fetch the super class of the
7617 // object to avoid doing a comparison we know will fail.
7618 vixl32::Label loop;
7619 __ Bind(&loop);
7620 // /* HeapReference<Class> */ out = out->super_class_
Artem Serov657022c2016-11-23 14:19:38 +00007621 GenerateReferenceLoadOneRegister(instruction,
7622 out_loc,
7623 super_offset,
7624 maybe_temp_loc,
7625 kCompilerReadBarrierOption);
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007626 // If `out` is null, we use it for the result, and jump to the final label.
Anton Kirilov6f644202017-02-27 18:29:45 +00007627 __ CompareAndBranchIfZero(out, final_label, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007628 __ Cmp(out, cls);
Artem Serov517d9f62016-12-12 15:51:15 +00007629 __ B(ne, &loop, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007630 __ Mov(out, 1);
Artem Serovcfbe9132016-10-14 15:58:56 +01007631 break;
7632 }
7633
7634 case TypeCheckKind::kClassHierarchyCheck: {
Mathieu Chartier6beced42016-11-15 15:51:31 -08007635 // /* HeapReference<Class> */ out = obj->klass_
7636 GenerateReferenceLoadTwoRegisters(instruction,
7637 out_loc,
7638 obj_loc,
7639 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00007640 maybe_temp_loc,
7641 kCompilerReadBarrierOption);
Artem Serovcfbe9132016-10-14 15:58:56 +01007642 // Walk over the class hierarchy to find a match.
7643 vixl32::Label loop, success;
7644 __ Bind(&loop);
7645 __ Cmp(out, cls);
Artem Serov517d9f62016-12-12 15:51:15 +00007646 __ B(eq, &success, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007647 // /* HeapReference<Class> */ out = out->super_class_
Artem Serov657022c2016-11-23 14:19:38 +00007648 GenerateReferenceLoadOneRegister(instruction,
7649 out_loc,
7650 super_offset,
7651 maybe_temp_loc,
7652 kCompilerReadBarrierOption);
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007653 // This is essentially a null check, but it sets the condition flags to the
7654 // proper value for the code that follows the loop, i.e. not `eq`.
7655 __ Cmp(out, 1);
7656 __ B(hs, &loop, /* far_target */ false);
7657
7658 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
7659 // we check that the output is in a low register, so that a 16-bit MOV
7660 // encoding can be used.
7661 if (out.IsLow()) {
7662 // If `out` is null, we use it for the result, and the condition flags
7663 // have already been set to `ne`, so the IT block that comes afterwards
7664 // (and which handles the successful case) turns into a NOP (instead of
7665 // overwriting `out`).
7666 __ Bind(&success);
7667
7668 // We use the scope because of the IT block that follows.
7669 ExactAssemblyScope guard(GetVIXLAssembler(),
7670 2 * vixl32::k16BitT32InstructionSizeInBytes,
7671 CodeBufferCheckScope::kExactSize);
7672
7673 // There is only one branch to the `success` label (which is bound to this
7674 // IT block), and it has the same condition, `eq`, so in that case the MOV
7675 // is executed.
7676 __ it(eq);
7677 __ mov(eq, out, 1);
7678 } else {
7679 // If `out` is null, we use it for the result, and jump to the final label.
Anton Kirilov6f644202017-02-27 18:29:45 +00007680 __ B(final_label);
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007681 __ Bind(&success);
7682 __ Mov(out, 1);
Artem Serovcfbe9132016-10-14 15:58:56 +01007683 }
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007684
Artem Serovcfbe9132016-10-14 15:58:56 +01007685 break;
7686 }
7687
7688 case TypeCheckKind::kArrayObjectCheck: {
Mathieu Chartier6beced42016-11-15 15:51:31 -08007689 // /* HeapReference<Class> */ out = obj->klass_
7690 GenerateReferenceLoadTwoRegisters(instruction,
7691 out_loc,
7692 obj_loc,
7693 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00007694 maybe_temp_loc,
7695 kCompilerReadBarrierOption);
Artem Serovcfbe9132016-10-14 15:58:56 +01007696 // Do an exact check.
7697 vixl32::Label exact_check;
7698 __ Cmp(out, cls);
Artem Serov517d9f62016-12-12 15:51:15 +00007699 __ B(eq, &exact_check, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007700 // Otherwise, we need to check that the object's class is a non-primitive array.
7701 // /* HeapReference<Class> */ out = out->component_type_
Artem Serov657022c2016-11-23 14:19:38 +00007702 GenerateReferenceLoadOneRegister(instruction,
7703 out_loc,
7704 component_offset,
7705 maybe_temp_loc,
7706 kCompilerReadBarrierOption);
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007707 // If `out` is null, we use it for the result, and jump to the final label.
Anton Kirilov6f644202017-02-27 18:29:45 +00007708 __ CompareAndBranchIfZero(out, final_label, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007709 GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset);
7710 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007711 __ Cmp(out, 0);
7712 // We speculatively set the result to false without changing the condition
7713 // flags, which allows us to avoid some branching later.
7714 __ Mov(LeaveFlags, out, 0);
7715
7716 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
7717 // we check that the output is in a low register, so that a 16-bit MOV
7718 // encoding can be used.
7719 if (out.IsLow()) {
7720 __ Bind(&exact_check);
7721
7722 // We use the scope because of the IT block that follows.
7723 ExactAssemblyScope guard(GetVIXLAssembler(),
7724 2 * vixl32::k16BitT32InstructionSizeInBytes,
7725 CodeBufferCheckScope::kExactSize);
7726
7727 __ it(eq);
7728 __ mov(eq, out, 1);
7729 } else {
7730 __ B(ne, final_label, /* far_target */ false);
7731 __ Bind(&exact_check);
7732 __ Mov(out, 1);
7733 }
7734
Artem Serovcfbe9132016-10-14 15:58:56 +01007735 break;
7736 }
7737
7738 case TypeCheckKind::kArrayCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00007739 // No read barrier since the slow path will retry upon failure.
Mathieu Chartier6beced42016-11-15 15:51:31 -08007740 // /* HeapReference<Class> */ out = obj->klass_
7741 GenerateReferenceLoadTwoRegisters(instruction,
7742 out_loc,
7743 obj_loc,
7744 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00007745 maybe_temp_loc,
7746 kWithoutReadBarrier);
Artem Serovcfbe9132016-10-14 15:58:56 +01007747 __ Cmp(out, cls);
7748 DCHECK(locations->OnlyCallsOnSlowPath());
7749 slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARMVIXL(instruction,
7750 /* is_fatal */ false);
7751 codegen_->AddSlowPath(slow_path);
7752 __ B(ne, slow_path->GetEntryLabel());
7753 __ Mov(out, 1);
Artem Serovcfbe9132016-10-14 15:58:56 +01007754 break;
7755 }
7756
7757 case TypeCheckKind::kUnresolvedCheck:
7758 case TypeCheckKind::kInterfaceCheck: {
7759 // Note that we indeed only call on slow path, but we always go
7760 // into the slow path for the unresolved and interface check
7761 // cases.
7762 //
7763 // We cannot directly call the InstanceofNonTrivial runtime
7764 // entry point without resorting to a type checking slow path
7765 // here (i.e. by calling InvokeRuntime directly), as it would
7766 // require to assign fixed registers for the inputs of this
7767 // HInstanceOf instruction (following the runtime calling
7768 // convention), which might be cluttered by the potential first
7769 // read barrier emission at the beginning of this method.
7770 //
7771 // TODO: Introduce a new runtime entry point taking the object
7772 // to test (instead of its class) as argument, and let it deal
7773 // with the read barrier issues. This will let us refactor this
7774 // case of the `switch` code as it was previously (with a direct
7775 // call to the runtime not using a type checking slow path).
7776 // This should also be beneficial for the other cases above.
7777 DCHECK(locations->OnlyCallsOnSlowPath());
7778 slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARMVIXL(instruction,
7779 /* is_fatal */ false);
7780 codegen_->AddSlowPath(slow_path);
7781 __ B(slow_path->GetEntryLabel());
Artem Serovcfbe9132016-10-14 15:58:56 +01007782 break;
7783 }
7784 }
7785
Artem Serovcfbe9132016-10-14 15:58:56 +01007786 if (done.IsReferenced()) {
7787 __ Bind(&done);
7788 }
7789
7790 if (slow_path != nullptr) {
7791 __ Bind(slow_path->GetExitLabel());
7792 }
7793}
7794
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007795void LocationsBuilderARMVIXL::VisitCheckCast(HCheckCast* instruction) {
7796 LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
7797 bool throws_into_catch = instruction->CanThrowIntoCatchBlock();
7798
7799 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
7800 switch (type_check_kind) {
7801 case TypeCheckKind::kExactCheck:
7802 case TypeCheckKind::kAbstractClassCheck:
7803 case TypeCheckKind::kClassHierarchyCheck:
7804 case TypeCheckKind::kArrayObjectCheck:
7805 call_kind = (throws_into_catch || kEmitCompilerReadBarrier) ?
7806 LocationSummary::kCallOnSlowPath :
7807 LocationSummary::kNoCall; // In fact, call on a fatal (non-returning) slow path.
7808 break;
7809 case TypeCheckKind::kArrayCheck:
7810 case TypeCheckKind::kUnresolvedCheck:
7811 case TypeCheckKind::kInterfaceCheck:
7812 call_kind = LocationSummary::kCallOnSlowPath;
7813 break;
7814 }
7815
7816 LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
7817 locations->SetInAt(0, Location::RequiresRegister());
7818 locations->SetInAt(1, Location::RequiresRegister());
Artem Serov657022c2016-11-23 14:19:38 +00007819 locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind));
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007820}
7821
7822void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) {
7823 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
7824 LocationSummary* locations = instruction->GetLocations();
7825 Location obj_loc = locations->InAt(0);
7826 vixl32::Register obj = InputRegisterAt(instruction, 0);
7827 vixl32::Register cls = InputRegisterAt(instruction, 1);
7828 Location temp_loc = locations->GetTemp(0);
7829 vixl32::Register temp = RegisterFrom(temp_loc);
Artem Serov657022c2016-11-23 14:19:38 +00007830 const size_t num_temps = NumberOfCheckCastTemps(type_check_kind);
7831 DCHECK_LE(num_temps, 3u);
7832 Location maybe_temp2_loc = (num_temps >= 2) ? locations->GetTemp(1) : Location::NoLocation();
7833 Location maybe_temp3_loc = (num_temps >= 3) ? locations->GetTemp(2) : Location::NoLocation();
7834 const uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
7835 const uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
7836 const uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
7837 const uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
7838 const uint32_t iftable_offset = mirror::Class::IfTableOffset().Uint32Value();
7839 const uint32_t array_length_offset = mirror::Array::LengthOffset().Uint32Value();
7840 const uint32_t object_array_data_offset =
7841 mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value();
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007842
Artem Serov657022c2016-11-23 14:19:38 +00007843 // Always false for read barriers since we may need to go to the entrypoint for non-fatal cases
7844 // from false negatives. The false negatives may come from avoiding read barriers below. Avoiding
7845 // read barriers is done for performance and code size reasons.
7846 bool is_type_check_slow_path_fatal = false;
7847 if (!kEmitCompilerReadBarrier) {
7848 is_type_check_slow_path_fatal =
7849 (type_check_kind == TypeCheckKind::kExactCheck ||
7850 type_check_kind == TypeCheckKind::kAbstractClassCheck ||
7851 type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
7852 type_check_kind == TypeCheckKind::kArrayObjectCheck) &&
7853 !instruction->CanThrowIntoCatchBlock();
7854 }
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007855 SlowPathCodeARMVIXL* type_check_slow_path =
7856 new (GetGraph()->GetArena()) TypeCheckSlowPathARMVIXL(instruction,
7857 is_type_check_slow_path_fatal);
7858 codegen_->AddSlowPath(type_check_slow_path);
7859
7860 vixl32::Label done;
Anton Kirilov6f644202017-02-27 18:29:45 +00007861 vixl32::Label* final_label = codegen_->GetFinalLabel(instruction, &done);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007862 // Avoid null check if we know obj is not null.
7863 if (instruction->MustDoNullCheck()) {
Anton Kirilov6f644202017-02-27 18:29:45 +00007864 __ CompareAndBranchIfZero(obj, final_label, /* far_target */ false);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007865 }
7866
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007867 switch (type_check_kind) {
7868 case TypeCheckKind::kExactCheck:
7869 case TypeCheckKind::kArrayCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00007870 // /* HeapReference<Class> */ temp = obj->klass_
7871 GenerateReferenceLoadTwoRegisters(instruction,
7872 temp_loc,
7873 obj_loc,
7874 class_offset,
7875 maybe_temp2_loc,
7876 kWithoutReadBarrier);
7877
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007878 __ Cmp(temp, cls);
7879 // Jump to slow path for throwing the exception or doing a
7880 // more involved array check.
7881 __ B(ne, type_check_slow_path->GetEntryLabel());
7882 break;
7883 }
7884
7885 case TypeCheckKind::kAbstractClassCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00007886 // /* HeapReference<Class> */ temp = obj->klass_
7887 GenerateReferenceLoadTwoRegisters(instruction,
7888 temp_loc,
7889 obj_loc,
7890 class_offset,
7891 maybe_temp2_loc,
7892 kWithoutReadBarrier);
7893
Artem Serovcfbe9132016-10-14 15:58:56 +01007894 // If the class is abstract, we eagerly fetch the super class of the
7895 // object to avoid doing a comparison we know will fail.
7896 vixl32::Label loop;
7897 __ Bind(&loop);
7898 // /* HeapReference<Class> */ temp = temp->super_class_
Artem Serov657022c2016-11-23 14:19:38 +00007899 GenerateReferenceLoadOneRegister(instruction,
7900 temp_loc,
7901 super_offset,
7902 maybe_temp2_loc,
7903 kWithoutReadBarrier);
Artem Serovcfbe9132016-10-14 15:58:56 +01007904
7905 // If the class reference currently in `temp` is null, jump to the slow path to throw the
7906 // exception.
xueliang.zhongf51bc622016-11-04 09:23:32 +00007907 __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
Artem Serovcfbe9132016-10-14 15:58:56 +01007908
7909 // Otherwise, compare the classes.
7910 __ Cmp(temp, cls);
Artem Serov517d9f62016-12-12 15:51:15 +00007911 __ B(ne, &loop, /* far_target */ false);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007912 break;
7913 }
7914
7915 case TypeCheckKind::kClassHierarchyCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00007916 // /* HeapReference<Class> */ temp = obj->klass_
7917 GenerateReferenceLoadTwoRegisters(instruction,
7918 temp_loc,
7919 obj_loc,
7920 class_offset,
7921 maybe_temp2_loc,
7922 kWithoutReadBarrier);
7923
Artem Serovcfbe9132016-10-14 15:58:56 +01007924 // Walk over the class hierarchy to find a match.
7925 vixl32::Label loop;
7926 __ Bind(&loop);
7927 __ Cmp(temp, cls);
Anton Kirilov6f644202017-02-27 18:29:45 +00007928 __ B(eq, final_label, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007929
7930 // /* HeapReference<Class> */ temp = temp->super_class_
Artem Serov657022c2016-11-23 14:19:38 +00007931 GenerateReferenceLoadOneRegister(instruction,
7932 temp_loc,
7933 super_offset,
7934 maybe_temp2_loc,
7935 kWithoutReadBarrier);
Artem Serovcfbe9132016-10-14 15:58:56 +01007936
7937 // If the class reference currently in `temp` is null, jump to the slow path to throw the
7938 // exception.
xueliang.zhongf51bc622016-11-04 09:23:32 +00007939 __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
Artem Serovcfbe9132016-10-14 15:58:56 +01007940 // Otherwise, jump to the beginning of the loop.
7941 __ B(&loop);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007942 break;
7943 }
7944
Artem Serovcfbe9132016-10-14 15:58:56 +01007945 case TypeCheckKind::kArrayObjectCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00007946 // /* HeapReference<Class> */ temp = obj->klass_
7947 GenerateReferenceLoadTwoRegisters(instruction,
7948 temp_loc,
7949 obj_loc,
7950 class_offset,
7951 maybe_temp2_loc,
7952 kWithoutReadBarrier);
7953
Artem Serovcfbe9132016-10-14 15:58:56 +01007954 // Do an exact check.
7955 __ Cmp(temp, cls);
Anton Kirilov6f644202017-02-27 18:29:45 +00007956 __ B(eq, final_label, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007957
7958 // Otherwise, we need to check that the object's class is a non-primitive array.
7959 // /* HeapReference<Class> */ temp = temp->component_type_
Artem Serov657022c2016-11-23 14:19:38 +00007960 GenerateReferenceLoadOneRegister(instruction,
7961 temp_loc,
7962 component_offset,
7963 maybe_temp2_loc,
7964 kWithoutReadBarrier);
Artem Serovcfbe9132016-10-14 15:58:56 +01007965 // If the component type is null, jump to the slow path to throw the exception.
xueliang.zhongf51bc622016-11-04 09:23:32 +00007966 __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
Artem Serovcfbe9132016-10-14 15:58:56 +01007967 // Otherwise,the object is indeed an array, jump to label `check_non_primitive_component_type`
7968 // to further check that this component type is not a primitive type.
7969 GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, temp, temp, primitive_offset);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01007970 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
xueliang.zhongf51bc622016-11-04 09:23:32 +00007971 __ CompareAndBranchIfNonZero(temp, type_check_slow_path->GetEntryLabel());
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007972 break;
7973 }
7974
7975 case TypeCheckKind::kUnresolvedCheck:
Artem Serov657022c2016-11-23 14:19:38 +00007976 // We always go into the type check slow path for the unresolved check case.
Artem Serovcfbe9132016-10-14 15:58:56 +01007977 // We cannot directly call the CheckCast runtime entry point
7978 // without resorting to a type checking slow path here (i.e. by
7979 // calling InvokeRuntime directly), as it would require to
7980 // assign fixed registers for the inputs of this HInstanceOf
7981 // instruction (following the runtime calling convention), which
7982 // might be cluttered by the potential first read barrier
7983 // emission at the beginning of this method.
Artem Serov657022c2016-11-23 14:19:38 +00007984
Artem Serovcfbe9132016-10-14 15:58:56 +01007985 __ B(type_check_slow_path->GetEntryLabel());
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007986 break;
Artem Serov657022c2016-11-23 14:19:38 +00007987
7988 case TypeCheckKind::kInterfaceCheck: {
7989 // Avoid read barriers to improve performance of the fast path. We can not get false
7990 // positives by doing this.
7991 // /* HeapReference<Class> */ temp = obj->klass_
7992 GenerateReferenceLoadTwoRegisters(instruction,
7993 temp_loc,
7994 obj_loc,
7995 class_offset,
7996 maybe_temp2_loc,
7997 kWithoutReadBarrier);
7998
7999 // /* HeapReference<Class> */ temp = temp->iftable_
8000 GenerateReferenceLoadTwoRegisters(instruction,
8001 temp_loc,
8002 temp_loc,
8003 iftable_offset,
8004 maybe_temp2_loc,
8005 kWithoutReadBarrier);
8006 // Iftable is never null.
8007 __ Ldr(RegisterFrom(maybe_temp2_loc), MemOperand(temp, array_length_offset));
8008 // Loop through the iftable and check if any class matches.
8009 vixl32::Label start_loop;
8010 __ Bind(&start_loop);
8011 __ CompareAndBranchIfZero(RegisterFrom(maybe_temp2_loc),
8012 type_check_slow_path->GetEntryLabel());
8013 __ Ldr(RegisterFrom(maybe_temp3_loc), MemOperand(temp, object_array_data_offset));
8014 GetAssembler()->MaybeUnpoisonHeapReference(RegisterFrom(maybe_temp3_loc));
8015 // Go to next interface.
8016 __ Add(temp, temp, Operand::From(2 * kHeapReferenceSize));
8017 __ Sub(RegisterFrom(maybe_temp2_loc), RegisterFrom(maybe_temp2_loc), 2);
8018 // Compare the classes and continue the loop if they do not match.
8019 __ Cmp(cls, RegisterFrom(maybe_temp3_loc));
Artem Serov517d9f62016-12-12 15:51:15 +00008020 __ B(ne, &start_loop, /* far_target */ false);
Artem Serov657022c2016-11-23 14:19:38 +00008021 break;
8022 }
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008023 }
Anton Kirilov6f644202017-02-27 18:29:45 +00008024 if (done.IsReferenced()) {
8025 __ Bind(&done);
8026 }
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008027
8028 __ Bind(type_check_slow_path->GetExitLabel());
8029}
8030
Artem Serov551b28f2016-10-18 19:11:30 +01008031void LocationsBuilderARMVIXL::VisitMonitorOperation(HMonitorOperation* instruction) {
8032 LocationSummary* locations =
8033 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
8034 InvokeRuntimeCallingConventionARMVIXL calling_convention;
8035 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
8036}
8037
8038void InstructionCodeGeneratorARMVIXL::VisitMonitorOperation(HMonitorOperation* instruction) {
8039 codegen_->InvokeRuntime(instruction->IsEnter() ? kQuickLockObject : kQuickUnlockObject,
8040 instruction,
8041 instruction->GetDexPc());
8042 if (instruction->IsEnter()) {
8043 CheckEntrypointTypes<kQuickLockObject, void, mirror::Object*>();
8044 } else {
8045 CheckEntrypointTypes<kQuickUnlockObject, void, mirror::Object*>();
8046 }
Roland Levillain5daa4952017-07-03 17:23:56 +01008047 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 17);
Artem Serov551b28f2016-10-18 19:11:30 +01008048}
8049
Artem Serov02109dd2016-09-23 17:17:54 +01008050void LocationsBuilderARMVIXL::VisitAnd(HAnd* instruction) {
8051 HandleBitwiseOperation(instruction, AND);
8052}
8053
8054void LocationsBuilderARMVIXL::VisitOr(HOr* instruction) {
8055 HandleBitwiseOperation(instruction, ORR);
8056}
8057
8058void LocationsBuilderARMVIXL::VisitXor(HXor* instruction) {
8059 HandleBitwiseOperation(instruction, EOR);
8060}
8061
8062void LocationsBuilderARMVIXL::HandleBitwiseOperation(HBinaryOperation* instruction, Opcode opcode) {
8063 LocationSummary* locations =
8064 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008065 DCHECK(instruction->GetResultType() == DataType::Type::kInt32
8066 || instruction->GetResultType() == DataType::Type::kInt64);
Artem Serov02109dd2016-09-23 17:17:54 +01008067 // Note: GVN reorders commutative operations to have the constant on the right hand side.
8068 locations->SetInAt(0, Location::RequiresRegister());
8069 locations->SetInAt(1, ArmEncodableConstantOrRegister(instruction->InputAt(1), opcode));
8070 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
8071}
8072
8073void InstructionCodeGeneratorARMVIXL::VisitAnd(HAnd* instruction) {
8074 HandleBitwiseOperation(instruction);
8075}
8076
8077void InstructionCodeGeneratorARMVIXL::VisitOr(HOr* instruction) {
8078 HandleBitwiseOperation(instruction);
8079}
8080
8081void InstructionCodeGeneratorARMVIXL::VisitXor(HXor* instruction) {
8082 HandleBitwiseOperation(instruction);
8083}
8084
Artem Serov2bbc9532016-10-21 11:51:50 +01008085void LocationsBuilderARMVIXL::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) {
8086 LocationSummary* locations =
8087 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008088 DCHECK(instruction->GetResultType() == DataType::Type::kInt32
8089 || instruction->GetResultType() == DataType::Type::kInt64);
Artem Serov2bbc9532016-10-21 11:51:50 +01008090
8091 locations->SetInAt(0, Location::RequiresRegister());
8092 locations->SetInAt(1, Location::RequiresRegister());
8093 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
8094}
8095
8096void InstructionCodeGeneratorARMVIXL::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) {
8097 LocationSummary* locations = instruction->GetLocations();
8098 Location first = locations->InAt(0);
8099 Location second = locations->InAt(1);
8100 Location out = locations->Out();
8101
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008102 if (instruction->GetResultType() == DataType::Type::kInt32) {
Artem Serov2bbc9532016-10-21 11:51:50 +01008103 vixl32::Register first_reg = RegisterFrom(first);
8104 vixl32::Register second_reg = RegisterFrom(second);
8105 vixl32::Register out_reg = RegisterFrom(out);
8106
8107 switch (instruction->GetOpKind()) {
8108 case HInstruction::kAnd:
8109 __ Bic(out_reg, first_reg, second_reg);
8110 break;
8111 case HInstruction::kOr:
8112 __ Orn(out_reg, first_reg, second_reg);
8113 break;
8114 // There is no EON on arm.
8115 case HInstruction::kXor:
8116 default:
8117 LOG(FATAL) << "Unexpected instruction " << instruction->DebugName();
8118 UNREACHABLE();
8119 }
8120 return;
8121
8122 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008123 DCHECK_EQ(instruction->GetResultType(), DataType::Type::kInt64);
Artem Serov2bbc9532016-10-21 11:51:50 +01008124 vixl32::Register first_low = LowRegisterFrom(first);
8125 vixl32::Register first_high = HighRegisterFrom(first);
8126 vixl32::Register second_low = LowRegisterFrom(second);
8127 vixl32::Register second_high = HighRegisterFrom(second);
8128 vixl32::Register out_low = LowRegisterFrom(out);
8129 vixl32::Register out_high = HighRegisterFrom(out);
8130
8131 switch (instruction->GetOpKind()) {
8132 case HInstruction::kAnd:
8133 __ Bic(out_low, first_low, second_low);
8134 __ Bic(out_high, first_high, second_high);
8135 break;
8136 case HInstruction::kOr:
8137 __ Orn(out_low, first_low, second_low);
8138 __ Orn(out_high, first_high, second_high);
8139 break;
8140 // There is no EON on arm.
8141 case HInstruction::kXor:
8142 default:
8143 LOG(FATAL) << "Unexpected instruction " << instruction->DebugName();
8144 UNREACHABLE();
8145 }
8146 }
8147}
8148
Anton Kirilov74234da2017-01-13 14:42:47 +00008149void LocationsBuilderARMVIXL::VisitDataProcWithShifterOp(
8150 HDataProcWithShifterOp* instruction) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008151 DCHECK(instruction->GetType() == DataType::Type::kInt32 ||
8152 instruction->GetType() == DataType::Type::kInt64);
Anton Kirilov74234da2017-01-13 14:42:47 +00008153 LocationSummary* locations =
8154 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008155 const bool overlap = instruction->GetType() == DataType::Type::kInt64 &&
Anton Kirilov74234da2017-01-13 14:42:47 +00008156 HDataProcWithShifterOp::IsExtensionOp(instruction->GetOpKind());
8157
8158 locations->SetInAt(0, Location::RequiresRegister());
8159 locations->SetInAt(1, Location::RequiresRegister());
8160 locations->SetOut(Location::RequiresRegister(),
8161 overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap);
8162}
8163
8164void InstructionCodeGeneratorARMVIXL::VisitDataProcWithShifterOp(
8165 HDataProcWithShifterOp* instruction) {
8166 const LocationSummary* const locations = instruction->GetLocations();
8167 const HInstruction::InstructionKind kind = instruction->GetInstrKind();
8168 const HDataProcWithShifterOp::OpKind op_kind = instruction->GetOpKind();
8169
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008170 if (instruction->GetType() == DataType::Type::kInt32) {
Anton Kirilov420ee302017-02-21 18:10:26 +00008171 const vixl32::Register first = InputRegisterAt(instruction, 0);
8172 const vixl32::Register output = OutputRegister(instruction);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008173 const vixl32::Register second = instruction->InputAt(1)->GetType() == DataType::Type::kInt64
Anton Kirilov74234da2017-01-13 14:42:47 +00008174 ? LowRegisterFrom(locations->InAt(1))
8175 : InputRegisterAt(instruction, 1);
8176
Anton Kirilov420ee302017-02-21 18:10:26 +00008177 if (HDataProcWithShifterOp::IsExtensionOp(op_kind)) {
8178 DCHECK_EQ(kind, HInstruction::kAdd);
8179
8180 switch (op_kind) {
8181 case HDataProcWithShifterOp::kUXTB:
8182 __ Uxtab(output, first, second);
8183 break;
8184 case HDataProcWithShifterOp::kUXTH:
8185 __ Uxtah(output, first, second);
8186 break;
8187 case HDataProcWithShifterOp::kSXTB:
8188 __ Sxtab(output, first, second);
8189 break;
8190 case HDataProcWithShifterOp::kSXTH:
8191 __ Sxtah(output, first, second);
8192 break;
8193 default:
8194 LOG(FATAL) << "Unexpected operation kind: " << op_kind;
8195 UNREACHABLE();
8196 }
8197 } else {
8198 GenerateDataProcInstruction(kind,
8199 output,
8200 first,
8201 Operand(second,
8202 ShiftFromOpKind(op_kind),
8203 instruction->GetShiftAmount()),
8204 codegen_);
8205 }
Anton Kirilov74234da2017-01-13 14:42:47 +00008206 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008207 DCHECK_EQ(instruction->GetType(), DataType::Type::kInt64);
Anton Kirilov74234da2017-01-13 14:42:47 +00008208
8209 if (HDataProcWithShifterOp::IsExtensionOp(op_kind)) {
8210 const vixl32::Register second = InputRegisterAt(instruction, 1);
8211
8212 DCHECK(!LowRegisterFrom(locations->Out()).Is(second));
8213 GenerateDataProc(kind,
8214 locations->Out(),
8215 locations->InAt(0),
8216 second,
8217 Operand(second, ShiftType::ASR, 31),
8218 codegen_);
8219 } else {
8220 GenerateLongDataProc(instruction, codegen_);
8221 }
8222 }
8223}
8224
Artem Serov02109dd2016-09-23 17:17:54 +01008225// TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
8226void InstructionCodeGeneratorARMVIXL::GenerateAndConst(vixl32::Register out,
8227 vixl32::Register first,
8228 uint32_t value) {
8229 // Optimize special cases for individual halfs of `and-long` (`and` is simplified earlier).
8230 if (value == 0xffffffffu) {
8231 if (!out.Is(first)) {
8232 __ Mov(out, first);
8233 }
8234 return;
8235 }
8236 if (value == 0u) {
8237 __ Mov(out, 0);
8238 return;
8239 }
8240 if (GetAssembler()->ShifterOperandCanHold(AND, value)) {
Anton Kiriloveffd5bf2017-02-28 16:59:15 +00008241 __ And(out, first, value);
8242 } else if (GetAssembler()->ShifterOperandCanHold(BIC, ~value)) {
8243 __ Bic(out, first, ~value);
Artem Serov02109dd2016-09-23 17:17:54 +01008244 } else {
Anton Kiriloveffd5bf2017-02-28 16:59:15 +00008245 DCHECK(IsPowerOfTwo(value + 1));
8246 __ Ubfx(out, first, 0, WhichPowerOf2(value + 1));
Artem Serov02109dd2016-09-23 17:17:54 +01008247 }
8248}
8249
8250// TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
8251void InstructionCodeGeneratorARMVIXL::GenerateOrrConst(vixl32::Register out,
8252 vixl32::Register first,
8253 uint32_t value) {
8254 // Optimize special cases for individual halfs of `or-long` (`or` is simplified earlier).
8255 if (value == 0u) {
8256 if (!out.Is(first)) {
8257 __ Mov(out, first);
8258 }
8259 return;
8260 }
8261 if (value == 0xffffffffu) {
8262 __ Mvn(out, 0);
8263 return;
8264 }
8265 if (GetAssembler()->ShifterOperandCanHold(ORR, value)) {
8266 __ Orr(out, first, value);
8267 } else {
8268 DCHECK(GetAssembler()->ShifterOperandCanHold(ORN, ~value));
8269 __ Orn(out, first, ~value);
8270 }
8271}
8272
8273// TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
8274void InstructionCodeGeneratorARMVIXL::GenerateEorConst(vixl32::Register out,
8275 vixl32::Register first,
8276 uint32_t value) {
8277 // Optimize special case for individual halfs of `xor-long` (`xor` is simplified earlier).
8278 if (value == 0u) {
8279 if (!out.Is(first)) {
8280 __ Mov(out, first);
8281 }
8282 return;
8283 }
8284 __ Eor(out, first, value);
8285}
8286
Anton Kirilovdda43962016-11-21 19:55:20 +00008287void InstructionCodeGeneratorARMVIXL::GenerateAddLongConst(Location out,
8288 Location first,
8289 uint64_t value) {
8290 vixl32::Register out_low = LowRegisterFrom(out);
8291 vixl32::Register out_high = HighRegisterFrom(out);
8292 vixl32::Register first_low = LowRegisterFrom(first);
8293 vixl32::Register first_high = HighRegisterFrom(first);
8294 uint32_t value_low = Low32Bits(value);
8295 uint32_t value_high = High32Bits(value);
8296 if (value_low == 0u) {
8297 if (!out_low.Is(first_low)) {
8298 __ Mov(out_low, first_low);
8299 }
8300 __ Add(out_high, first_high, value_high);
8301 return;
8302 }
8303 __ Adds(out_low, first_low, value_low);
Scott Wakelingbffdc702016-12-07 17:46:03 +00008304 if (GetAssembler()->ShifterOperandCanHold(ADC, value_high, kCcDontCare)) {
Anton Kirilovdda43962016-11-21 19:55:20 +00008305 __ Adc(out_high, first_high, value_high);
Scott Wakelingbffdc702016-12-07 17:46:03 +00008306 } else if (GetAssembler()->ShifterOperandCanHold(SBC, ~value_high, kCcDontCare)) {
Anton Kirilovdda43962016-11-21 19:55:20 +00008307 __ Sbc(out_high, first_high, ~value_high);
8308 } else {
8309 LOG(FATAL) << "Unexpected constant " << value_high;
8310 UNREACHABLE();
8311 }
8312}
8313
Artem Serov02109dd2016-09-23 17:17:54 +01008314void InstructionCodeGeneratorARMVIXL::HandleBitwiseOperation(HBinaryOperation* instruction) {
8315 LocationSummary* locations = instruction->GetLocations();
8316 Location first = locations->InAt(0);
8317 Location second = locations->InAt(1);
8318 Location out = locations->Out();
8319
8320 if (second.IsConstant()) {
8321 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
8322 uint32_t value_low = Low32Bits(value);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008323 if (instruction->GetResultType() == DataType::Type::kInt32) {
Artem Serov02109dd2016-09-23 17:17:54 +01008324 vixl32::Register first_reg = InputRegisterAt(instruction, 0);
8325 vixl32::Register out_reg = OutputRegister(instruction);
8326 if (instruction->IsAnd()) {
8327 GenerateAndConst(out_reg, first_reg, value_low);
8328 } else if (instruction->IsOr()) {
8329 GenerateOrrConst(out_reg, first_reg, value_low);
8330 } else {
8331 DCHECK(instruction->IsXor());
8332 GenerateEorConst(out_reg, first_reg, value_low);
8333 }
8334 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008335 DCHECK_EQ(instruction->GetResultType(), DataType::Type::kInt64);
Artem Serov02109dd2016-09-23 17:17:54 +01008336 uint32_t value_high = High32Bits(value);
8337 vixl32::Register first_low = LowRegisterFrom(first);
8338 vixl32::Register first_high = HighRegisterFrom(first);
8339 vixl32::Register out_low = LowRegisterFrom(out);
8340 vixl32::Register out_high = HighRegisterFrom(out);
8341 if (instruction->IsAnd()) {
8342 GenerateAndConst(out_low, first_low, value_low);
8343 GenerateAndConst(out_high, first_high, value_high);
8344 } else if (instruction->IsOr()) {
8345 GenerateOrrConst(out_low, first_low, value_low);
8346 GenerateOrrConst(out_high, first_high, value_high);
8347 } else {
8348 DCHECK(instruction->IsXor());
8349 GenerateEorConst(out_low, first_low, value_low);
8350 GenerateEorConst(out_high, first_high, value_high);
8351 }
8352 }
8353 return;
8354 }
8355
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008356 if (instruction->GetResultType() == DataType::Type::kInt32) {
Artem Serov02109dd2016-09-23 17:17:54 +01008357 vixl32::Register first_reg = InputRegisterAt(instruction, 0);
8358 vixl32::Register second_reg = InputRegisterAt(instruction, 1);
8359 vixl32::Register out_reg = OutputRegister(instruction);
8360 if (instruction->IsAnd()) {
8361 __ And(out_reg, first_reg, second_reg);
8362 } else if (instruction->IsOr()) {
8363 __ Orr(out_reg, first_reg, second_reg);
8364 } else {
8365 DCHECK(instruction->IsXor());
8366 __ Eor(out_reg, first_reg, second_reg);
8367 }
8368 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008369 DCHECK_EQ(instruction->GetResultType(), DataType::Type::kInt64);
Artem Serov02109dd2016-09-23 17:17:54 +01008370 vixl32::Register first_low = LowRegisterFrom(first);
8371 vixl32::Register first_high = HighRegisterFrom(first);
8372 vixl32::Register second_low = LowRegisterFrom(second);
8373 vixl32::Register second_high = HighRegisterFrom(second);
8374 vixl32::Register out_low = LowRegisterFrom(out);
8375 vixl32::Register out_high = HighRegisterFrom(out);
8376 if (instruction->IsAnd()) {
8377 __ And(out_low, first_low, second_low);
8378 __ And(out_high, first_high, second_high);
8379 } else if (instruction->IsOr()) {
8380 __ Orr(out_low, first_low, second_low);
8381 __ Orr(out_high, first_high, second_high);
8382 } else {
8383 DCHECK(instruction->IsXor());
8384 __ Eor(out_low, first_low, second_low);
8385 __ Eor(out_high, first_high, second_high);
8386 }
8387 }
8388}
8389
Artem Serovcfbe9132016-10-14 15:58:56 +01008390void InstructionCodeGeneratorARMVIXL::GenerateReferenceLoadOneRegister(
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008391 HInstruction* instruction,
Artem Serovcfbe9132016-10-14 15:58:56 +01008392 Location out,
8393 uint32_t offset,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008394 Location maybe_temp,
8395 ReadBarrierOption read_barrier_option) {
Artem Serovcfbe9132016-10-14 15:58:56 +01008396 vixl32::Register out_reg = RegisterFrom(out);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008397 if (read_barrier_option == kWithReadBarrier) {
8398 CHECK(kEmitCompilerReadBarrier);
8399 DCHECK(maybe_temp.IsRegister()) << maybe_temp;
8400 if (kUseBakerReadBarrier) {
8401 // Load with fast path based Baker's read barrier.
8402 // /* HeapReference<Object> */ out = *(out + offset)
8403 codegen_->GenerateFieldLoadWithBakerReadBarrier(
8404 instruction, out, out_reg, offset, maybe_temp, /* needs_null_check */ false);
8405 } else {
8406 // Load with slow path based read barrier.
8407 // Save the value of `out` into `maybe_temp` before overwriting it
8408 // in the following move operation, as we will need it for the
8409 // read barrier below.
8410 __ Mov(RegisterFrom(maybe_temp), out_reg);
8411 // /* HeapReference<Object> */ out = *(out + offset)
8412 GetAssembler()->LoadFromOffset(kLoadWord, out_reg, out_reg, offset);
8413 codegen_->GenerateReadBarrierSlow(instruction, out, out, maybe_temp, offset);
8414 }
Artem Serovcfbe9132016-10-14 15:58:56 +01008415 } else {
8416 // Plain load with no read barrier.
8417 // /* HeapReference<Object> */ out = *(out + offset)
8418 GetAssembler()->LoadFromOffset(kLoadWord, out_reg, out_reg, offset);
8419 GetAssembler()->MaybeUnpoisonHeapReference(out_reg);
8420 }
8421}
8422
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008423void InstructionCodeGeneratorARMVIXL::GenerateReferenceLoadTwoRegisters(
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008424 HInstruction* instruction,
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008425 Location out,
8426 Location obj,
8427 uint32_t offset,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008428 Location maybe_temp,
8429 ReadBarrierOption read_barrier_option) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008430 vixl32::Register out_reg = RegisterFrom(out);
8431 vixl32::Register obj_reg = RegisterFrom(obj);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008432 if (read_barrier_option == kWithReadBarrier) {
8433 CHECK(kEmitCompilerReadBarrier);
8434 if (kUseBakerReadBarrier) {
8435 DCHECK(maybe_temp.IsRegister()) << maybe_temp;
8436 // Load with fast path based Baker's read barrier.
8437 // /* HeapReference<Object> */ out = *(obj + offset)
8438 codegen_->GenerateFieldLoadWithBakerReadBarrier(
8439 instruction, out, obj_reg, offset, maybe_temp, /* needs_null_check */ false);
8440 } else {
8441 // Load with slow path based read barrier.
8442 // /* HeapReference<Object> */ out = *(obj + offset)
8443 GetAssembler()->LoadFromOffset(kLoadWord, out_reg, obj_reg, offset);
8444 codegen_->GenerateReadBarrierSlow(instruction, out, out, obj, offset);
8445 }
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008446 } else {
8447 // Plain load with no read barrier.
8448 // /* HeapReference<Object> */ out = *(obj + offset)
8449 GetAssembler()->LoadFromOffset(kLoadWord, out_reg, obj_reg, offset);
8450 GetAssembler()->MaybeUnpoisonHeapReference(out_reg);
8451 }
8452}
8453
Scott Wakelinga7812ae2016-10-17 10:03:36 +01008454void InstructionCodeGeneratorARMVIXL::GenerateGcRootFieldLoad(
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008455 HInstruction* instruction,
Scott Wakelinga7812ae2016-10-17 10:03:36 +01008456 Location root,
8457 vixl32::Register obj,
8458 uint32_t offset,
Artem Serovd4cc5b22016-11-04 11:19:09 +00008459 ReadBarrierOption read_barrier_option) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01008460 vixl32::Register root_reg = RegisterFrom(root);
Artem Serovd4cc5b22016-11-04 11:19:09 +00008461 if (read_barrier_option == kWithReadBarrier) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008462 DCHECK(kEmitCompilerReadBarrier);
8463 if (kUseBakerReadBarrier) {
8464 // Fast path implementation of art::ReadBarrier::BarrierForRoot when
Roland Levillainba650a42017-03-06 13:52:32 +00008465 // Baker's read barrier are used.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008466 if (kBakerReadBarrierLinkTimeThunksEnableForGcRoots &&
8467 !Runtime::Current()->UseJitCompilation()) {
Roland Levillain6d729a72017-06-30 18:34:01 +01008468 // Query `art::Thread::Current()->GetIsGcMarking()` (stored in
8469 // the Marking Register) to decide whether we need to enter
8470 // the slow path to mark the GC root.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008471 //
8472 // We use link-time generated thunks for the slow path. That thunk
8473 // checks the reference and jumps to the entrypoint if needed.
8474 //
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008475 // lr = &return_address;
8476 // GcRoot<mirror::Object> root = *(obj+offset); // Original reference load.
Roland Levillain6d729a72017-06-30 18:34:01 +01008477 // if (mr) { // Thread::Current()->GetIsGcMarking()
8478 // goto gc_root_thunk<root_reg>(lr)
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008479 // }
8480 // return_address:
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008481
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008482 UseScratchRegisterScope temps(GetVIXLAssembler());
8483 ExcludeIPAndBakerCcEntrypointRegister(&temps, instruction);
Vladimir Marko88abba22017-05-03 17:09:25 +01008484 bool narrow = CanEmitNarrowLdr(root_reg, obj, offset);
8485 uint32_t custom_data = linker::Thumb2RelativePatcher::EncodeBakerReadBarrierGcRootData(
8486 root_reg.GetCode(), narrow);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008487 vixl32::Label* bne_label = codegen_->NewBakerReadBarrierPatch(custom_data);
Roland Levillainba650a42017-03-06 13:52:32 +00008488
Roland Levillain6d729a72017-06-30 18:34:01 +01008489 vixl::EmissionCheckScope guard(GetVIXLAssembler(), 4 * vixl32::kMaxInstructionSizeInBytes);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008490 vixl32::Label return_address;
8491 EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
Roland Levillain6d729a72017-06-30 18:34:01 +01008492 __ cmp(mr, Operand(0));
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008493 // Currently the offset is always within range. If that changes,
8494 // we shall have to split the load the same way as for fields.
8495 DCHECK_LT(offset, kReferenceLoadMinFarOffset);
Vladimir Marko88abba22017-05-03 17:09:25 +01008496 ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
8497 __ ldr(EncodingSize(narrow ? Narrow : Wide), root_reg, MemOperand(obj, offset));
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008498 EmitPlaceholderBne(codegen_, bne_label);
8499 __ Bind(&return_address);
Vladimir Marko88abba22017-05-03 17:09:25 +01008500 DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
8501 narrow ? BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_OFFSET
8502 : BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_OFFSET);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008503 } else {
Roland Levillain6d729a72017-06-30 18:34:01 +01008504 // Query `art::Thread::Current()->GetIsGcMarking()` (stored in
8505 // the Marking Register) to decide whether we need to enter
8506 // the slow path to mark the GC root.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008507 //
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008508 // GcRoot<mirror::Object> root = *(obj+offset); // Original reference load.
Roland Levillain6d729a72017-06-30 18:34:01 +01008509 // if (mr) { // Thread::Current()->GetIsGcMarking()
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008510 // // Slow path.
Roland Levillain6d729a72017-06-30 18:34:01 +01008511 // entrypoint = Thread::Current()->pReadBarrierMarkReg ## root.reg()
8512 // root = entrypoint(root); // root = ReadBarrier::Mark(root); // Entry point call.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008513 // }
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008514
Roland Levillain6d729a72017-06-30 18:34:01 +01008515 // Slow path marking the GC root `root`. The entrypoint will
8516 // be loaded by the slow path code.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008517 SlowPathCodeARMVIXL* slow_path =
Roland Levillain6d729a72017-06-30 18:34:01 +01008518 new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathARMVIXL(instruction, root);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008519 codegen_->AddSlowPath(slow_path);
8520
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008521 // /* GcRoot<mirror::Object> */ root = *(obj + offset)
8522 GetAssembler()->LoadFromOffset(kLoadWord, root_reg, obj, offset);
8523 static_assert(
8524 sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(GcRoot<mirror::Object>),
8525 "art::mirror::CompressedReference<mirror::Object> and art::GcRoot<mirror::Object> "
8526 "have different sizes.");
8527 static_assert(sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(int32_t),
8528 "art::mirror::CompressedReference<mirror::Object> and int32_t "
8529 "have different sizes.");
8530
Roland Levillain6d729a72017-06-30 18:34:01 +01008531 __ CompareAndBranchIfNonZero(mr, slow_path->GetEntryLabel());
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008532 __ Bind(slow_path->GetExitLabel());
8533 }
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008534 } else {
8535 // GC root loaded through a slow path for read barriers other
8536 // than Baker's.
8537 // /* GcRoot<mirror::Object>* */ root = obj + offset
8538 __ Add(root_reg, obj, offset);
8539 // /* mirror::Object* */ root = root->Read()
8540 codegen_->GenerateReadBarrierForRootSlow(instruction, root, root);
8541 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01008542 } else {
8543 // Plain GC root load with no read barrier.
8544 // /* GcRoot<mirror::Object> */ root = *(obj + offset)
8545 GetAssembler()->LoadFromOffset(kLoadWord, root_reg, obj, offset);
8546 // Note that GC roots are not affected by heap poisoning, thus we
8547 // do not have to unpoison `root_reg` here.
8548 }
Roland Levillain5daa4952017-07-03 17:23:56 +01008549 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 18);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01008550}
8551
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008552void CodeGeneratorARMVIXL::MaybeAddBakerCcEntrypointTempForFields(LocationSummary* locations) {
8553 DCHECK(kEmitCompilerReadBarrier);
8554 DCHECK(kUseBakerReadBarrier);
8555 if (kBakerReadBarrierLinkTimeThunksEnableForFields) {
8556 if (!Runtime::Current()->UseJitCompilation()) {
8557 locations->AddTemp(Location::RegisterLocation(kBakerCcEntrypointRegister.GetCode()));
8558 }
8559 }
8560}
8561
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008562void CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
8563 Location ref,
8564 vixl32::Register obj,
8565 uint32_t offset,
8566 Location temp,
8567 bool needs_null_check) {
8568 DCHECK(kEmitCompilerReadBarrier);
8569 DCHECK(kUseBakerReadBarrier);
8570
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008571 if (kBakerReadBarrierLinkTimeThunksEnableForFields &&
8572 !Runtime::Current()->UseJitCompilation()) {
Roland Levillain6d729a72017-06-30 18:34:01 +01008573 // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
8574 // Marking Register) to decide whether we need to enter the slow
8575 // path to mark the reference. Then, in the slow path, check the
8576 // gray bit in the lock word of the reference's holder (`obj`) to
8577 // decide whether to mark `ref` or not.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008578 //
8579 // We use link-time generated thunks for the slow path. That thunk checks
8580 // the holder and jumps to the entrypoint if needed. If the holder is not
8581 // gray, it creates a fake dependency and returns to the LDR instruction.
8582 //
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008583 // lr = &gray_return_address;
Roland Levillain6d729a72017-06-30 18:34:01 +01008584 // if (mr) { // Thread::Current()->GetIsGcMarking()
8585 // goto field_thunk<holder_reg, base_reg>(lr)
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008586 // }
8587 // not_gray_return_address:
8588 // // Original reference load. If the offset is too large to fit
8589 // // into LDR, we use an adjusted base register here.
Vladimir Marko88abba22017-05-03 17:09:25 +01008590 // HeapReference<mirror::Object> reference = *(obj+offset);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008591 // gray_return_address:
8592
8593 DCHECK_ALIGNED(offset, sizeof(mirror::HeapReference<mirror::Object>));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008594 vixl32::Register ref_reg = RegisterFrom(ref, DataType::Type::kReference);
Vladimir Marko88abba22017-05-03 17:09:25 +01008595 bool narrow = CanEmitNarrowLdr(ref_reg, obj, offset);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008596 vixl32::Register base = obj;
8597 if (offset >= kReferenceLoadMinFarOffset) {
8598 base = RegisterFrom(temp);
8599 DCHECK(!base.Is(kBakerCcEntrypointRegister));
8600 static_assert(IsPowerOfTwo(kReferenceLoadMinFarOffset), "Expecting a power of 2.");
8601 __ Add(base, obj, Operand(offset & ~(kReferenceLoadMinFarOffset - 1u)));
8602 offset &= (kReferenceLoadMinFarOffset - 1u);
Vladimir Marko88abba22017-05-03 17:09:25 +01008603 // Use narrow LDR only for small offsets. Generating narrow encoding LDR for the large
8604 // offsets with `(offset & (kReferenceLoadMinFarOffset - 1u)) < 32u` would most likely
8605 // increase the overall code size when taking the generated thunks into account.
8606 DCHECK(!narrow);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008607 }
8608 UseScratchRegisterScope temps(GetVIXLAssembler());
8609 ExcludeIPAndBakerCcEntrypointRegister(&temps, instruction);
8610 uint32_t custom_data = linker::Thumb2RelativePatcher::EncodeBakerReadBarrierFieldData(
Vladimir Marko88abba22017-05-03 17:09:25 +01008611 base.GetCode(), obj.GetCode(), narrow);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008612 vixl32::Label* bne_label = NewBakerReadBarrierPatch(custom_data);
8613
Roland Levillain5daa4952017-07-03 17:23:56 +01008614 {
8615 vixl::EmissionCheckScope guard(
8616 GetVIXLAssembler(),
8617 (kPoisonHeapReferences ? 5u : 4u) * vixl32::kMaxInstructionSizeInBytes);
8618 vixl32::Label return_address;
8619 EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
8620 __ cmp(mr, Operand(0));
8621 EmitPlaceholderBne(this, bne_label);
8622 ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
8623 __ ldr(EncodingSize(narrow ? Narrow : Wide), ref_reg, MemOperand(base, offset));
8624 if (needs_null_check) {
8625 MaybeRecordImplicitNullCheck(instruction);
Vladimir Marko88abba22017-05-03 17:09:25 +01008626 }
Roland Levillain5daa4952017-07-03 17:23:56 +01008627 // Note: We need a specific width for the unpoisoning NEG.
8628 if (kPoisonHeapReferences) {
8629 if (narrow) {
8630 // The only 16-bit encoding is T1 which sets flags outside IT block (i.e. RSBS, not RSB).
8631 __ rsbs(EncodingSize(Narrow), ref_reg, ref_reg, Operand(0));
8632 } else {
8633 __ rsb(EncodingSize(Wide), ref_reg, ref_reg, Operand(0));
8634 }
8635 }
8636 __ Bind(&return_address);
8637 DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
8638 narrow ? BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET
8639 : BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008640 }
Roland Levillain5daa4952017-07-03 17:23:56 +01008641 MaybeGenerateMarkingRegisterCheck(/* code */ 19, /* temp_loc */ LocationFrom(ip));
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008642 return;
8643 }
8644
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008645 // /* HeapReference<Object> */ ref = *(obj + offset)
8646 Location no_index = Location::NoLocation();
8647 ScaleFactor no_scale_factor = TIMES_1;
8648 GenerateReferenceLoadWithBakerReadBarrier(
8649 instruction, ref, obj, offset, no_index, no_scale_factor, temp, needs_null_check);
Roland Levillain6070e882016-11-03 17:51:58 +00008650}
8651
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008652void CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction,
8653 Location ref,
8654 vixl32::Register obj,
8655 uint32_t data_offset,
8656 Location index,
8657 Location temp,
8658 bool needs_null_check) {
8659 DCHECK(kEmitCompilerReadBarrier);
8660 DCHECK(kUseBakerReadBarrier);
8661
8662 static_assert(
8663 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
8664 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008665 ScaleFactor scale_factor = TIMES_4;
8666
8667 if (kBakerReadBarrierLinkTimeThunksEnableForArrays &&
8668 !Runtime::Current()->UseJitCompilation()) {
Roland Levillain6d729a72017-06-30 18:34:01 +01008669 // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
8670 // Marking Register) to decide whether we need to enter the slow
8671 // path to mark the reference. Then, in the slow path, check the
8672 // gray bit in the lock word of the reference's holder (`obj`) to
8673 // decide whether to mark `ref` or not.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008674 //
8675 // We use link-time generated thunks for the slow path. That thunk checks
8676 // the holder and jumps to the entrypoint if needed. If the holder is not
8677 // gray, it creates a fake dependency and returns to the LDR instruction.
8678 //
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008679 // lr = &gray_return_address;
Roland Levillain6d729a72017-06-30 18:34:01 +01008680 // if (mr) { // Thread::Current()->GetIsGcMarking()
8681 // goto array_thunk<base_reg>(lr)
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008682 // }
8683 // not_gray_return_address:
8684 // // Original reference load. If the offset is too large to fit
8685 // // into LDR, we use an adjusted base register here.
Vladimir Marko88abba22017-05-03 17:09:25 +01008686 // HeapReference<mirror::Object> reference = data[index];
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008687 // gray_return_address:
8688
8689 DCHECK(index.IsValid());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008690 vixl32::Register index_reg = RegisterFrom(index, DataType::Type::kInt32);
8691 vixl32::Register ref_reg = RegisterFrom(ref, DataType::Type::kReference);
8692 vixl32::Register data_reg = RegisterFrom(temp, DataType::Type::kInt32); // Raw pointer.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008693 DCHECK(!data_reg.Is(kBakerCcEntrypointRegister));
8694
8695 UseScratchRegisterScope temps(GetVIXLAssembler());
8696 ExcludeIPAndBakerCcEntrypointRegister(&temps, instruction);
8697 uint32_t custom_data =
8698 linker::Thumb2RelativePatcher::EncodeBakerReadBarrierArrayData(data_reg.GetCode());
8699 vixl32::Label* bne_label = NewBakerReadBarrierPatch(custom_data);
8700
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008701 __ Add(data_reg, obj, Operand(data_offset));
Roland Levillain5daa4952017-07-03 17:23:56 +01008702 {
8703 vixl::EmissionCheckScope guard(
8704 GetVIXLAssembler(),
8705 (kPoisonHeapReferences ? 5u : 4u) * vixl32::kMaxInstructionSizeInBytes);
8706 vixl32::Label return_address;
8707 EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
8708 __ cmp(mr, Operand(0));
8709 EmitPlaceholderBne(this, bne_label);
8710 ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
8711 __ ldr(ref_reg, MemOperand(data_reg, index_reg, vixl32::LSL, scale_factor));
8712 DCHECK(!needs_null_check); // The thunk cannot handle the null check.
8713 // Note: We need a Wide NEG for the unpoisoning.
8714 if (kPoisonHeapReferences) {
8715 __ rsb(EncodingSize(Wide), ref_reg, ref_reg, Operand(0));
8716 }
8717 __ Bind(&return_address);
8718 DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
8719 BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008720 }
Roland Levillain5daa4952017-07-03 17:23:56 +01008721 MaybeGenerateMarkingRegisterCheck(/* code */ 20, /* temp_loc */ LocationFrom(ip));
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008722 return;
8723 }
8724
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008725 // /* HeapReference<Object> */ ref =
8726 // *(obj + data_offset + index * sizeof(HeapReference<Object>))
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008727 GenerateReferenceLoadWithBakerReadBarrier(
8728 instruction, ref, obj, data_offset, index, scale_factor, temp, needs_null_check);
Roland Levillain6070e882016-11-03 17:51:58 +00008729}
8730
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008731void CodeGeneratorARMVIXL::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
8732 Location ref,
8733 vixl32::Register obj,
8734 uint32_t offset,
8735 Location index,
8736 ScaleFactor scale_factor,
8737 Location temp,
Roland Levillainff487002017-03-07 16:50:01 +00008738 bool needs_null_check) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008739 DCHECK(kEmitCompilerReadBarrier);
8740 DCHECK(kUseBakerReadBarrier);
8741
Roland Levillain6d729a72017-06-30 18:34:01 +01008742 // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
8743 // Marking Register) to decide whether we need to enter the slow
8744 // path to mark the reference. Then, in the slow path, check the
8745 // gray bit in the lock word of the reference's holder (`obj`) to
8746 // decide whether to mark `ref` or not.
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008747 //
Roland Levillain6d729a72017-06-30 18:34:01 +01008748 // if (mr) { // Thread::Current()->GetIsGcMarking()
Roland Levillainff487002017-03-07 16:50:01 +00008749 // // Slow path.
8750 // uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
8751 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
8752 // HeapReference<mirror::Object> ref = *src; // Original reference load.
8753 // bool is_gray = (rb_state == ReadBarrier::GrayState());
8754 // if (is_gray) {
Roland Levillain6d729a72017-06-30 18:34:01 +01008755 // entrypoint = Thread::Current()->pReadBarrierMarkReg ## root.reg()
8756 // ref = entrypoint(ref); // ref = ReadBarrier::Mark(ref); // Runtime entry point call.
Roland Levillainff487002017-03-07 16:50:01 +00008757 // }
8758 // } else {
8759 // HeapReference<mirror::Object> ref = *src; // Original reference load.
8760 // }
8761
8762 vixl32::Register temp_reg = RegisterFrom(temp);
8763
8764 // Slow path marking the object `ref` when the GC is marking. The
Roland Levillain6d729a72017-06-30 18:34:01 +01008765 // entrypoint will be loaded by the slow path code.
Roland Levillainff487002017-03-07 16:50:01 +00008766 SlowPathCodeARMVIXL* slow_path =
8767 new (GetGraph()->GetArena()) LoadReferenceWithBakerReadBarrierSlowPathARMVIXL(
Roland Levillain6d729a72017-06-30 18:34:01 +01008768 instruction, ref, obj, offset, index, scale_factor, needs_null_check, temp_reg);
Roland Levillainff487002017-03-07 16:50:01 +00008769 AddSlowPath(slow_path);
8770
Roland Levillain6d729a72017-06-30 18:34:01 +01008771 __ CompareAndBranchIfNonZero(mr, slow_path->GetEntryLabel());
Roland Levillainff487002017-03-07 16:50:01 +00008772 // Fast path: the GC is not marking: just load the reference.
8773 GenerateRawReferenceLoad(instruction, ref, obj, offset, index, scale_factor, needs_null_check);
8774 __ Bind(slow_path->GetExitLabel());
Roland Levillain5daa4952017-07-03 17:23:56 +01008775 MaybeGenerateMarkingRegisterCheck(/* code */ 21);
Roland Levillainff487002017-03-07 16:50:01 +00008776}
8777
8778void CodeGeneratorARMVIXL::UpdateReferenceFieldWithBakerReadBarrier(HInstruction* instruction,
8779 Location ref,
8780 vixl32::Register obj,
8781 Location field_offset,
8782 Location temp,
8783 bool needs_null_check,
8784 vixl32::Register temp2) {
8785 DCHECK(kEmitCompilerReadBarrier);
8786 DCHECK(kUseBakerReadBarrier);
8787
Roland Levillain6d729a72017-06-30 18:34:01 +01008788 // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
8789 // Marking Register) to decide whether we need to enter the slow
8790 // path to update the reference field within `obj`. Then, in the
8791 // slow path, check the gray bit in the lock word of the reference's
8792 // holder (`obj`) to decide whether to mark `ref` and update the
8793 // field or not.
Roland Levillainff487002017-03-07 16:50:01 +00008794 //
Roland Levillain6d729a72017-06-30 18:34:01 +01008795 // if (mr) { // Thread::Current()->GetIsGcMarking()
Roland Levillainba650a42017-03-06 13:52:32 +00008796 // // Slow path.
Roland Levillain54f869e2017-03-06 13:54:11 +00008797 // uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
8798 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
Roland Levillainff487002017-03-07 16:50:01 +00008799 // HeapReference<mirror::Object> ref = *(obj + field_offset); // Reference load.
Roland Levillain54f869e2017-03-06 13:54:11 +00008800 // bool is_gray = (rb_state == ReadBarrier::GrayState());
8801 // if (is_gray) {
Roland Levillainff487002017-03-07 16:50:01 +00008802 // old_ref = ref;
Roland Levillain6d729a72017-06-30 18:34:01 +01008803 // entrypoint = Thread::Current()->pReadBarrierMarkReg ## root.reg()
8804 // ref = entrypoint(ref); // ref = ReadBarrier::Mark(ref); // Runtime entry point call.
Roland Levillainff487002017-03-07 16:50:01 +00008805 // compareAndSwapObject(obj, field_offset, old_ref, ref);
Roland Levillain54f869e2017-03-06 13:54:11 +00008806 // }
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008807 // }
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008808
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008809 vixl32::Register temp_reg = RegisterFrom(temp);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008810
Roland Levillainff487002017-03-07 16:50:01 +00008811 // Slow path updating the object reference at address `obj + field_offset`
Roland Levillain6d729a72017-06-30 18:34:01 +01008812 // when the GC is marking. The entrypoint will be loaded by the slow path code.
Roland Levillainff487002017-03-07 16:50:01 +00008813 SlowPathCodeARMVIXL* slow_path =
8814 new (GetGraph()->GetArena()) LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARMVIXL(
8815 instruction,
8816 ref,
8817 obj,
8818 /* offset */ 0u,
8819 /* index */ field_offset,
8820 /* scale_factor */ ScaleFactor::TIMES_1,
8821 needs_null_check,
8822 temp_reg,
Roland Levillain6d729a72017-06-30 18:34:01 +01008823 temp2);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008824 AddSlowPath(slow_path);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008825
Roland Levillain6d729a72017-06-30 18:34:01 +01008826 __ CompareAndBranchIfNonZero(mr, slow_path->GetEntryLabel());
Roland Levillainff487002017-03-07 16:50:01 +00008827 // Fast path: the GC is not marking: nothing to do (the field is
8828 // up-to-date, and we don't need to load the reference).
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008829 __ Bind(slow_path->GetExitLabel());
Roland Levillain5daa4952017-07-03 17:23:56 +01008830 MaybeGenerateMarkingRegisterCheck(/* code */ 22);
Roland Levillain844e6532016-11-03 16:09:47 +00008831}
Scott Wakelingfe885462016-09-22 10:24:38 +01008832
Roland Levillainba650a42017-03-06 13:52:32 +00008833void CodeGeneratorARMVIXL::GenerateRawReferenceLoad(HInstruction* instruction,
8834 Location ref,
8835 vixl::aarch32::Register obj,
8836 uint32_t offset,
8837 Location index,
8838 ScaleFactor scale_factor,
8839 bool needs_null_check) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008840 DataType::Type type = DataType::Type::kReference;
Roland Levillainba650a42017-03-06 13:52:32 +00008841 vixl32::Register ref_reg = RegisterFrom(ref, type);
8842
8843 // If needed, vixl::EmissionCheckScope guards are used to ensure
8844 // that no pools are emitted between the load (macro) instruction
8845 // and MaybeRecordImplicitNullCheck.
8846
Scott Wakelingfe885462016-09-22 10:24:38 +01008847 if (index.IsValid()) {
8848 // Load types involving an "index": ArrayGet,
8849 // UnsafeGetObject/UnsafeGetObjectVolatile and UnsafeCASObject
8850 // intrinsics.
Roland Levillainba650a42017-03-06 13:52:32 +00008851 // /* HeapReference<mirror::Object> */ ref = *(obj + offset + (index << scale_factor))
Scott Wakelingfe885462016-09-22 10:24:38 +01008852 if (index.IsConstant()) {
8853 size_t computed_offset =
8854 (Int32ConstantFrom(index) << scale_factor) + offset;
Roland Levillainba650a42017-03-06 13:52:32 +00008855 vixl::EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
Scott Wakelingfe885462016-09-22 10:24:38 +01008856 GetAssembler()->LoadFromOffset(kLoadWord, ref_reg, obj, computed_offset);
Roland Levillainba650a42017-03-06 13:52:32 +00008857 if (needs_null_check) {
8858 MaybeRecordImplicitNullCheck(instruction);
8859 }
Scott Wakelingfe885462016-09-22 10:24:38 +01008860 } else {
8861 // Handle the special case of the
8862 // UnsafeGetObject/UnsafeGetObjectVolatile and UnsafeCASObject
8863 // intrinsics, which use a register pair as index ("long
8864 // offset"), of which only the low part contains data.
8865 vixl32::Register index_reg = index.IsRegisterPair()
8866 ? LowRegisterFrom(index)
8867 : RegisterFrom(index);
8868 UseScratchRegisterScope temps(GetVIXLAssembler());
Roland Levillainba650a42017-03-06 13:52:32 +00008869 vixl32::Register temp = temps.Acquire();
8870 __ Add(temp, obj, Operand(index_reg, ShiftType::LSL, scale_factor));
8871 {
8872 vixl::EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
8873 GetAssembler()->LoadFromOffset(kLoadWord, ref_reg, temp, offset);
8874 if (needs_null_check) {
8875 MaybeRecordImplicitNullCheck(instruction);
8876 }
8877 }
Scott Wakelingfe885462016-09-22 10:24:38 +01008878 }
8879 } else {
Roland Levillainba650a42017-03-06 13:52:32 +00008880 // /* HeapReference<mirror::Object> */ ref = *(obj + offset)
8881 vixl::EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
Scott Wakelingfe885462016-09-22 10:24:38 +01008882 GetAssembler()->LoadFromOffset(kLoadWord, ref_reg, obj, offset);
Roland Levillainba650a42017-03-06 13:52:32 +00008883 if (needs_null_check) {
8884 MaybeRecordImplicitNullCheck(instruction);
8885 }
Scott Wakelingfe885462016-09-22 10:24:38 +01008886 }
8887
Roland Levillain844e6532016-11-03 16:09:47 +00008888 // Object* ref = ref_addr->AsMirrorPtr()
8889 GetAssembler()->MaybeUnpoisonHeapReference(ref_reg);
Roland Levillain844e6532016-11-03 16:09:47 +00008890}
8891
Roland Levillain5daa4952017-07-03 17:23:56 +01008892void CodeGeneratorARMVIXL::MaybeGenerateMarkingRegisterCheck(int code, Location temp_loc) {
8893 // The following condition is a compile-time one, so it does not have a run-time cost.
8894 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier && kIsDebugBuild) {
8895 // The following condition is a run-time one; it is executed after the
8896 // previous compile-time test, to avoid penalizing non-debug builds.
8897 if (GetCompilerOptions().EmitRunTimeChecksInDebugMode()) {
8898 UseScratchRegisterScope temps(GetVIXLAssembler());
8899 vixl32::Register temp = temp_loc.IsValid() ? RegisterFrom(temp_loc) : temps.Acquire();
8900 GetAssembler()->GenerateMarkingRegisterCheck(temp,
8901 kMarkingRegisterCheckBreakCodeBaseCode + code);
8902 }
8903 }
8904}
8905
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008906void CodeGeneratorARMVIXL::GenerateReadBarrierSlow(HInstruction* instruction,
8907 Location out,
8908 Location ref,
8909 Location obj,
8910 uint32_t offset,
8911 Location index) {
8912 DCHECK(kEmitCompilerReadBarrier);
8913
8914 // Insert a slow path based read barrier *after* the reference load.
8915 //
8916 // If heap poisoning is enabled, the unpoisoning of the loaded
8917 // reference will be carried out by the runtime within the slow
8918 // path.
8919 //
8920 // Note that `ref` currently does not get unpoisoned (when heap
8921 // poisoning is enabled), which is alright as the `ref` argument is
8922 // not used by the artReadBarrierSlow entry point.
8923 //
8924 // TODO: Unpoison `ref` when it is used by artReadBarrierSlow.
8925 SlowPathCodeARMVIXL* slow_path = new (GetGraph()->GetArena())
8926 ReadBarrierForHeapReferenceSlowPathARMVIXL(instruction, out, ref, obj, offset, index);
8927 AddSlowPath(slow_path);
8928
8929 __ B(slow_path->GetEntryLabel());
8930 __ Bind(slow_path->GetExitLabel());
8931}
8932
8933void CodeGeneratorARMVIXL::MaybeGenerateReadBarrierSlow(HInstruction* instruction,
Artem Serov02d37832016-10-25 15:25:33 +01008934 Location out,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008935 Location ref,
8936 Location obj,
8937 uint32_t offset,
8938 Location index) {
Artem Serov02d37832016-10-25 15:25:33 +01008939 if (kEmitCompilerReadBarrier) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008940 // Baker's read barriers shall be handled by the fast path
Roland Levillain9983e302017-07-14 14:34:22 +01008941 // (CodeGeneratorARMVIXL::GenerateReferenceLoadWithBakerReadBarrier).
Artem Serov02d37832016-10-25 15:25:33 +01008942 DCHECK(!kUseBakerReadBarrier);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008943 // If heap poisoning is enabled, unpoisoning will be taken care of
8944 // by the runtime within the slow path.
8945 GenerateReadBarrierSlow(instruction, out, ref, obj, offset, index);
Artem Serov02d37832016-10-25 15:25:33 +01008946 } else if (kPoisonHeapReferences) {
8947 GetAssembler()->UnpoisonHeapReference(RegisterFrom(out));
8948 }
8949}
8950
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008951void CodeGeneratorARMVIXL::GenerateReadBarrierForRootSlow(HInstruction* instruction,
8952 Location out,
8953 Location root) {
8954 DCHECK(kEmitCompilerReadBarrier);
8955
8956 // Insert a slow path based read barrier *after* the GC root load.
8957 //
8958 // Note that GC roots are not affected by heap poisoning, so we do
8959 // not need to do anything special for this here.
8960 SlowPathCodeARMVIXL* slow_path =
8961 new (GetGraph()->GetArena()) ReadBarrierForRootSlowPathARMVIXL(instruction, out, root);
8962 AddSlowPath(slow_path);
8963
8964 __ B(slow_path->GetEntryLabel());
8965 __ Bind(slow_path->GetExitLabel());
8966}
8967
Artem Serov02d37832016-10-25 15:25:33 +01008968// Check if the desired_dispatch_info is supported. If it is, return it,
8969// otherwise return a fall-back info that should be used instead.
8970HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARMVIXL::GetSupportedInvokeStaticOrDirectDispatch(
Artem Serovd4cc5b22016-11-04 11:19:09 +00008971 const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
Nicolas Geoffrayc1a42cf2016-12-18 15:52:36 +00008972 HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
Nicolas Geoffraye807ff72017-01-23 09:03:12 +00008973 return desired_dispatch_info;
Artem Serov02d37832016-10-25 15:25:33 +01008974}
8975
Scott Wakelingfe885462016-09-22 10:24:38 +01008976vixl32::Register CodeGeneratorARMVIXL::GetInvokeStaticOrDirectExtraParameter(
8977 HInvokeStaticOrDirect* invoke, vixl32::Register temp) {
8978 DCHECK_EQ(invoke->InputCount(), invoke->GetNumberOfArguments() + 1u);
8979 Location location = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
8980 if (!invoke->GetLocations()->Intrinsified()) {
8981 return RegisterFrom(location);
8982 }
8983 // For intrinsics we allow any location, so it may be on the stack.
8984 if (!location.IsRegister()) {
8985 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, location.GetStackIndex());
8986 return temp;
8987 }
8988 // For register locations, check if the register was saved. If so, get it from the stack.
8989 // Note: There is a chance that the register was saved but not overwritten, so we could
8990 // save one load. However, since this is just an intrinsic slow path we prefer this
8991 // simple and more robust approach rather that trying to determine if that's the case.
8992 SlowPathCode* slow_path = GetCurrentSlowPath();
Scott Wakelingd5cd4972017-02-03 11:38:35 +00008993 if (slow_path != nullptr && slow_path->IsCoreRegisterSaved(RegisterFrom(location).GetCode())) {
Scott Wakelingfe885462016-09-22 10:24:38 +01008994 int stack_offset = slow_path->GetStackOffsetOfCoreRegister(RegisterFrom(location).GetCode());
8995 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, stack_offset);
8996 return temp;
8997 }
8998 return RegisterFrom(location);
8999}
9000
Vladimir Markod254f5c2017-06-02 15:18:36 +00009001void CodeGeneratorARMVIXL::GenerateStaticOrDirectCall(
Vladimir Markoe7197bf2017-06-02 17:00:23 +01009002 HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00009003 Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp.
Scott Wakelingfe885462016-09-22 10:24:38 +01009004 switch (invoke->GetMethodLoadKind()) {
9005 case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: {
9006 uint32_t offset =
9007 GetThreadOffset<kArmPointerSize>(invoke->GetStringInitEntryPoint()).Int32Value();
9008 // temp = thread->string_init_entrypoint
Artem Serovd4cc5b22016-11-04 11:19:09 +00009009 GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(temp), tr, offset);
9010 break;
9011 }
9012 case HInvokeStaticOrDirect::MethodLoadKind::kRecursive:
9013 callee_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
9014 break;
Vladimir Marko65979462017-05-19 17:25:12 +01009015 case HInvokeStaticOrDirect::MethodLoadKind::kBootImageLinkTimePcRelative: {
9016 DCHECK(GetCompilerOptions().IsBootImage());
9017 PcRelativePatchInfo* labels = NewPcRelativeMethodPatch(invoke->GetTargetMethod());
9018 vixl32::Register temp_reg = RegisterFrom(temp);
9019 EmitMovwMovtPlaceholder(labels, temp_reg);
9020 break;
9021 }
Artem Serovd4cc5b22016-11-04 11:19:09 +00009022 case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress:
9023 __ Mov(RegisterFrom(temp), Operand::From(invoke->GetMethodAddress()));
9024 break;
Vladimir Marko0eb882b2017-05-15 13:39:18 +01009025 case HInvokeStaticOrDirect::MethodLoadKind::kBssEntry: {
9026 PcRelativePatchInfo* labels = NewMethodBssEntryPatch(
9027 MethodReference(&GetGraph()->GetDexFile(), invoke->GetDexMethodIndex()));
9028 vixl32::Register temp_reg = RegisterFrom(temp);
9029 EmitMovwMovtPlaceholder(labels, temp_reg);
9030 GetAssembler()->LoadFromOffset(kLoadWord, temp_reg, temp_reg, /* offset*/ 0);
Scott Wakelingfe885462016-09-22 10:24:38 +01009031 break;
9032 }
Vladimir Markoe7197bf2017-06-02 17:00:23 +01009033 case HInvokeStaticOrDirect::MethodLoadKind::kRuntimeCall: {
9034 GenerateInvokeStaticOrDirectRuntimeCall(invoke, temp, slow_path);
9035 return; // No code pointer retrieval; the runtime performs the call directly.
Scott Wakelingfe885462016-09-22 10:24:38 +01009036 }
Scott Wakelingfe885462016-09-22 10:24:38 +01009037 }
9038
Artem Serovd4cc5b22016-11-04 11:19:09 +00009039 switch (invoke->GetCodePtrLocation()) {
9040 case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf:
Vladimir Markoe7197bf2017-06-02 17:00:23 +01009041 {
9042 // Use a scope to help guarantee that `RecordPcInfo()` records the correct pc.
9043 ExactAssemblyScope aas(GetVIXLAssembler(),
9044 vixl32::k32BitT32InstructionSizeInBytes,
9045 CodeBufferCheckScope::kMaximumSize);
9046 __ bl(GetFrameEntryLabel());
9047 RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
9048 }
Artem Serovd4cc5b22016-11-04 11:19:09 +00009049 break;
Artem Serovd4cc5b22016-11-04 11:19:09 +00009050 case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod:
9051 // LR = callee_method->entry_point_from_quick_compiled_code_
9052 GetAssembler()->LoadFromOffset(
9053 kLoadWord,
9054 lr,
9055 RegisterFrom(callee_method),
9056 ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value());
Alexandre Rames374ddf32016-11-04 10:40:49 +00009057 {
Vladimir Markoe7197bf2017-06-02 17:00:23 +01009058 // Use a scope to help guarantee that `RecordPcInfo()` records the correct pc.
Alexandre Rames374ddf32016-11-04 10:40:49 +00009059 // 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 +00009060 ExactAssemblyScope aas(GetVIXLAssembler(),
9061 vixl32::k16BitT32InstructionSizeInBytes,
9062 CodeBufferCheckScope::kExactSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00009063 // LR()
9064 __ blx(lr);
Vladimir Markoe7197bf2017-06-02 17:00:23 +01009065 RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
Alexandre Rames374ddf32016-11-04 10:40:49 +00009066 }
Artem Serovd4cc5b22016-11-04 11:19:09 +00009067 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01009068 }
9069
Scott Wakelingfe885462016-09-22 10:24:38 +01009070 DCHECK(!IsLeafMethod());
9071}
9072
Vladimir Markoe7197bf2017-06-02 17:00:23 +01009073void CodeGeneratorARMVIXL::GenerateVirtualCall(
9074 HInvokeVirtual* invoke, Location temp_location, SlowPathCode* slow_path) {
Scott Wakelingfe885462016-09-22 10:24:38 +01009075 vixl32::Register temp = RegisterFrom(temp_location);
9076 uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
9077 invoke->GetVTableIndex(), kArmPointerSize).Uint32Value();
9078
9079 // Use the calling convention instead of the location of the receiver, as
9080 // intrinsics may have put the receiver in a different register. In the intrinsics
9081 // slow path, the arguments have been moved to the right place, so here we are
9082 // guaranteed that the receiver is the first register of the calling convention.
9083 InvokeDexCallingConventionARMVIXL calling_convention;
9084 vixl32::Register receiver = calling_convention.GetRegisterAt(0);
9085 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
Alexandre Rames374ddf32016-11-04 10:40:49 +00009086 {
9087 // Make sure the pc is recorded immediately after the `ldr` instruction.
Artem Serov0fb37192016-12-06 18:13:40 +00009088 ExactAssemblyScope aas(GetVIXLAssembler(),
9089 vixl32::kMaxInstructionSizeInBytes,
9090 CodeBufferCheckScope::kMaximumSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00009091 // /* HeapReference<Class> */ temp = receiver->klass_
9092 __ ldr(temp, MemOperand(receiver, class_offset));
9093 MaybeRecordImplicitNullCheck(invoke);
9094 }
Scott Wakelingfe885462016-09-22 10:24:38 +01009095 // Instead of simply (possibly) unpoisoning `temp` here, we should
9096 // emit a read barrier for the previous class reference load.
9097 // However this is not required in practice, as this is an
9098 // intermediate/temporary reference and because the current
9099 // concurrent copying collector keeps the from-space memory
9100 // intact/accessible until the end of the marking phase (the
9101 // concurrent copying collector may not in the future).
9102 GetAssembler()->MaybeUnpoisonHeapReference(temp);
9103
9104 // temp = temp->GetMethodAt(method_offset);
9105 uint32_t entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(
9106 kArmPointerSize).Int32Value();
9107 GetAssembler()->LoadFromOffset(kLoadWord, temp, temp, method_offset);
9108 // LR = temp->GetEntryPoint();
9109 GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, entry_point);
Vladimir Markoe7197bf2017-06-02 17:00:23 +01009110 {
9111 // Use a scope to help guarantee that `RecordPcInfo()` records the correct pc.
9112 // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
9113 ExactAssemblyScope aas(GetVIXLAssembler(),
9114 vixl32::k16BitT32InstructionSizeInBytes,
9115 CodeBufferCheckScope::kExactSize);
9116 // LR();
9117 __ blx(lr);
9118 RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
9119 }
Scott Wakelingfe885462016-09-22 10:24:38 +01009120}
9121
Vladimir Marko65979462017-05-19 17:25:12 +01009122CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewPcRelativeMethodPatch(
9123 MethodReference target_method) {
9124 return NewPcRelativePatch(*target_method.dex_file,
Mathieu Chartierfc8b4222017-09-17 13:44:24 -07009125 target_method.index,
Vladimir Marko65979462017-05-19 17:25:12 +01009126 &pc_relative_method_patches_);
Artem Serovd4cc5b22016-11-04 11:19:09 +00009127}
9128
Vladimir Marko0eb882b2017-05-15 13:39:18 +01009129CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewMethodBssEntryPatch(
9130 MethodReference target_method) {
9131 return NewPcRelativePatch(*target_method.dex_file,
Mathieu Chartierfc8b4222017-09-17 13:44:24 -07009132 target_method.index,
Vladimir Marko0eb882b2017-05-15 13:39:18 +01009133 &method_bss_entry_patches_);
9134}
9135
Artem Serovd4cc5b22016-11-04 11:19:09 +00009136CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewPcRelativeTypePatch(
9137 const DexFile& dex_file, dex::TypeIndex type_index) {
9138 return NewPcRelativePatch(dex_file, type_index.index_, &pc_relative_type_patches_);
9139}
9140
Vladimir Marko1998cd02017-01-13 13:02:58 +00009141CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewTypeBssEntryPatch(
9142 const DexFile& dex_file, dex::TypeIndex type_index) {
9143 return NewPcRelativePatch(dex_file, type_index.index_, &type_bss_entry_patches_);
9144}
9145
Vladimir Marko65979462017-05-19 17:25:12 +01009146CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewPcRelativeStringPatch(
9147 const DexFile& dex_file, dex::StringIndex string_index) {
9148 return NewPcRelativePatch(dex_file, string_index.index_, &pc_relative_string_patches_);
9149}
9150
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01009151CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewStringBssEntryPatch(
9152 const DexFile& dex_file, dex::StringIndex string_index) {
9153 return NewPcRelativePatch(dex_file, string_index.index_, &string_bss_entry_patches_);
9154}
9155
Artem Serovd4cc5b22016-11-04 11:19:09 +00009156CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewPcRelativePatch(
9157 const DexFile& dex_file, uint32_t offset_or_index, ArenaDeque<PcRelativePatchInfo>* patches) {
9158 patches->emplace_back(dex_file, offset_or_index);
9159 return &patches->back();
9160}
9161
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009162vixl::aarch32::Label* CodeGeneratorARMVIXL::NewBakerReadBarrierPatch(uint32_t custom_data) {
9163 baker_read_barrier_patches_.emplace_back(custom_data);
9164 return &baker_read_barrier_patches_.back().label;
9165}
9166
Artem Serovc5fcb442016-12-02 19:19:58 +00009167VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateBootImageAddressLiteral(uint32_t address) {
Richard Uhlerc52f3032017-03-02 13:45:45 +00009168 return DeduplicateUint32Literal(dchecked_integral_cast<uint32_t>(address), &uint32_literals_);
Artem Serovc5fcb442016-12-02 19:19:58 +00009169}
9170
Nicolas Geoffrayf0acfe72017-01-09 20:54:52 +00009171VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateJitStringLiteral(
9172 const DexFile& dex_file,
9173 dex::StringIndex string_index,
9174 Handle<mirror::String> handle) {
9175 jit_string_roots_.Overwrite(StringReference(&dex_file, string_index),
9176 reinterpret_cast64<uint64_t>(handle.GetReference()));
Artem Serovc5fcb442016-12-02 19:19:58 +00009177 return jit_string_patches_.GetOrCreate(
9178 StringReference(&dex_file, string_index),
9179 [this]() {
9180 return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u);
9181 });
9182}
9183
9184VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateJitClassLiteral(const DexFile& dex_file,
9185 dex::TypeIndex type_index,
Nicolas Geoffray5247c082017-01-13 14:17:29 +00009186 Handle<mirror::Class> handle) {
9187 jit_class_roots_.Overwrite(TypeReference(&dex_file, type_index),
9188 reinterpret_cast64<uint64_t>(handle.GetReference()));
Artem Serovc5fcb442016-12-02 19:19:58 +00009189 return jit_class_patches_.GetOrCreate(
9190 TypeReference(&dex_file, type_index),
9191 [this]() {
9192 return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u);
9193 });
9194}
9195
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009196template <linker::LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)>
Artem Serovd4cc5b22016-11-04 11:19:09 +00009197inline void CodeGeneratorARMVIXL::EmitPcRelativeLinkerPatches(
9198 const ArenaDeque<PcRelativePatchInfo>& infos,
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009199 ArenaVector<linker::LinkerPatch>* linker_patches) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00009200 for (const PcRelativePatchInfo& info : infos) {
9201 const DexFile& dex_file = info.target_dex_file;
9202 size_t offset_or_index = info.offset_or_index;
9203 DCHECK(info.add_pc_label.IsBound());
9204 uint32_t add_pc_offset = dchecked_integral_cast<uint32_t>(info.add_pc_label.GetLocation());
9205 // Add MOVW patch.
9206 DCHECK(info.movw_label.IsBound());
9207 uint32_t movw_offset = dchecked_integral_cast<uint32_t>(info.movw_label.GetLocation());
9208 linker_patches->push_back(Factory(movw_offset, &dex_file, add_pc_offset, offset_or_index));
9209 // Add MOVT patch.
9210 DCHECK(info.movt_label.IsBound());
9211 uint32_t movt_offset = dchecked_integral_cast<uint32_t>(info.movt_label.GetLocation());
9212 linker_patches->push_back(Factory(movt_offset, &dex_file, add_pc_offset, offset_or_index));
9213 }
9214}
9215
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009216void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* linker_patches) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00009217 DCHECK(linker_patches->empty());
9218 size_t size =
Vladimir Marko65979462017-05-19 17:25:12 +01009219 /* MOVW+MOVT for each entry */ 2u * pc_relative_method_patches_.size() +
Vladimir Marko0eb882b2017-05-15 13:39:18 +01009220 /* MOVW+MOVT for each entry */ 2u * method_bss_entry_patches_.size() +
Artem Serovc5fcb442016-12-02 19:19:58 +00009221 /* MOVW+MOVT for each entry */ 2u * pc_relative_type_patches_.size() +
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009222 /* MOVW+MOVT for each entry */ 2u * type_bss_entry_patches_.size() +
Vladimir Marko65979462017-05-19 17:25:12 +01009223 /* MOVW+MOVT for each entry */ 2u * pc_relative_string_patches_.size() +
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01009224 /* MOVW+MOVT for each entry */ 2u * string_bss_entry_patches_.size() +
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009225 baker_read_barrier_patches_.size();
Artem Serovd4cc5b22016-11-04 11:19:09 +00009226 linker_patches->reserve(size);
Vladimir Marko65979462017-05-19 17:25:12 +01009227 if (GetCompilerOptions().IsBootImage()) {
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009228 EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeMethodPatch>(
9229 pc_relative_method_patches_, linker_patches);
9230 EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeTypePatch>(
9231 pc_relative_type_patches_, linker_patches);
9232 EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeStringPatch>(
9233 pc_relative_string_patches_, linker_patches);
Vladimir Marko65979462017-05-19 17:25:12 +01009234 } else {
9235 DCHECK(pc_relative_method_patches_.empty());
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009236 EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeClassTablePatch>(
9237 pc_relative_type_patches_, linker_patches);
9238 EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringInternTablePatch>(
9239 pc_relative_string_patches_, linker_patches);
Artem Serovd4cc5b22016-11-04 11:19:09 +00009240 }
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009241 EmitPcRelativeLinkerPatches<linker::LinkerPatch::MethodBssEntryPatch>(
9242 method_bss_entry_patches_, linker_patches);
9243 EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeBssEntryPatch>(
9244 type_bss_entry_patches_, linker_patches);
9245 EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringBssEntryPatch>(
9246 string_bss_entry_patches_, linker_patches);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009247 for (const BakerReadBarrierPatchInfo& info : baker_read_barrier_patches_) {
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009248 linker_patches->push_back(linker::LinkerPatch::BakerReadBarrierBranchPatch(
9249 info.label.GetLocation(), info.custom_data));
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009250 }
Vladimir Marko1998cd02017-01-13 13:02:58 +00009251 DCHECK_EQ(size, linker_patches->size());
Artem Serovc5fcb442016-12-02 19:19:58 +00009252}
9253
9254VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateUint32Literal(
9255 uint32_t value,
9256 Uint32ToLiteralMap* map) {
9257 return map->GetOrCreate(
9258 value,
9259 [this, value]() {
9260 return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ value);
9261 });
9262}
9263
Artem Serov2bbc9532016-10-21 11:51:50 +01009264void LocationsBuilderARMVIXL::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
9265 LocationSummary* locations =
9266 new (GetGraph()->GetArena()) LocationSummary(instr, LocationSummary::kNoCall);
9267 locations->SetInAt(HMultiplyAccumulate::kInputAccumulatorIndex,
9268 Location::RequiresRegister());
9269 locations->SetInAt(HMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister());
9270 locations->SetInAt(HMultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister());
9271 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
9272}
9273
9274void InstructionCodeGeneratorARMVIXL::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
9275 vixl32::Register res = OutputRegister(instr);
9276 vixl32::Register accumulator =
9277 InputRegisterAt(instr, HMultiplyAccumulate::kInputAccumulatorIndex);
9278 vixl32::Register mul_left =
9279 InputRegisterAt(instr, HMultiplyAccumulate::kInputMulLeftIndex);
9280 vixl32::Register mul_right =
9281 InputRegisterAt(instr, HMultiplyAccumulate::kInputMulRightIndex);
9282
9283 if (instr->GetOpKind() == HInstruction::kAdd) {
9284 __ Mla(res, mul_left, mul_right, accumulator);
9285 } else {
9286 __ Mls(res, mul_left, mul_right, accumulator);
9287 }
9288}
9289
Artem Serov551b28f2016-10-18 19:11:30 +01009290void LocationsBuilderARMVIXL::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
9291 // Nothing to do, this should be removed during prepare for register allocator.
9292 LOG(FATAL) << "Unreachable";
9293}
9294
9295void InstructionCodeGeneratorARMVIXL::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
9296 // Nothing to do, this should be removed during prepare for register allocator.
9297 LOG(FATAL) << "Unreachable";
9298}
9299
9300// Simple implementation of packed switch - generate cascaded compare/jumps.
9301void LocationsBuilderARMVIXL::VisitPackedSwitch(HPackedSwitch* switch_instr) {
9302 LocationSummary* locations =
9303 new (GetGraph()->GetArena()) LocationSummary(switch_instr, LocationSummary::kNoCall);
9304 locations->SetInAt(0, Location::RequiresRegister());
9305 if (switch_instr->GetNumEntries() > kPackedSwitchCompareJumpThreshold &&
9306 codegen_->GetAssembler()->GetVIXLAssembler()->IsUsingT32()) {
9307 locations->AddTemp(Location::RequiresRegister()); // We need a temp for the table base.
9308 if (switch_instr->GetStartValue() != 0) {
9309 locations->AddTemp(Location::RequiresRegister()); // We need a temp for the bias.
9310 }
9311 }
9312}
9313
9314// TODO(VIXL): Investigate and reach the parity with old arm codegen.
9315void InstructionCodeGeneratorARMVIXL::VisitPackedSwitch(HPackedSwitch* switch_instr) {
9316 int32_t lower_bound = switch_instr->GetStartValue();
9317 uint32_t num_entries = switch_instr->GetNumEntries();
9318 LocationSummary* locations = switch_instr->GetLocations();
9319 vixl32::Register value_reg = InputRegisterAt(switch_instr, 0);
9320 HBasicBlock* default_block = switch_instr->GetDefaultBlock();
9321
9322 if (num_entries <= kPackedSwitchCompareJumpThreshold ||
9323 !codegen_->GetAssembler()->GetVIXLAssembler()->IsUsingT32()) {
9324 // Create a series of compare/jumps.
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009325 UseScratchRegisterScope temps(GetVIXLAssembler());
Artem Serov551b28f2016-10-18 19:11:30 +01009326 vixl32::Register temp_reg = temps.Acquire();
9327 // Note: It is fine for the below AddConstantSetFlags() using IP register to temporarily store
9328 // the immediate, because IP is used as the destination register. For the other
9329 // AddConstantSetFlags() and GenerateCompareWithImmediate(), the immediate values are constant,
9330 // and they can be encoded in the instruction without making use of IP register.
9331 __ Adds(temp_reg, value_reg, -lower_bound);
9332
9333 const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors();
9334 // Jump to successors[0] if value == lower_bound.
9335 __ B(eq, codegen_->GetLabelOf(successors[0]));
9336 int32_t last_index = 0;
9337 for (; num_entries - last_index > 2; last_index += 2) {
9338 __ Adds(temp_reg, temp_reg, -2);
9339 // Jump to successors[last_index + 1] if value < case_value[last_index + 2].
9340 __ B(lo, codegen_->GetLabelOf(successors[last_index + 1]));
9341 // Jump to successors[last_index + 2] if value == case_value[last_index + 2].
9342 __ B(eq, codegen_->GetLabelOf(successors[last_index + 2]));
9343 }
9344 if (num_entries - last_index == 2) {
9345 // The last missing case_value.
9346 __ Cmp(temp_reg, 1);
9347 __ B(eq, codegen_->GetLabelOf(successors[last_index + 1]));
9348 }
9349
9350 // And the default for any other value.
9351 if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) {
9352 __ B(codegen_->GetLabelOf(default_block));
9353 }
9354 } else {
9355 // Create a table lookup.
9356 vixl32::Register table_base = RegisterFrom(locations->GetTemp(0));
9357
9358 JumpTableARMVIXL* jump_table = codegen_->CreateJumpTable(switch_instr);
9359
9360 // Remove the bias.
9361 vixl32::Register key_reg;
9362 if (lower_bound != 0) {
9363 key_reg = RegisterFrom(locations->GetTemp(1));
9364 __ Sub(key_reg, value_reg, lower_bound);
9365 } else {
9366 key_reg = value_reg;
9367 }
9368
9369 // Check whether the value is in the table, jump to default block if not.
9370 __ Cmp(key_reg, num_entries - 1);
9371 __ B(hi, codegen_->GetLabelOf(default_block));
9372
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009373 UseScratchRegisterScope temps(GetVIXLAssembler());
Artem Serov551b28f2016-10-18 19:11:30 +01009374 vixl32::Register jump_offset = temps.Acquire();
9375
9376 // Load jump offset from the table.
Scott Wakeling86e9d262017-01-18 15:59:24 +00009377 {
9378 const size_t jump_size = switch_instr->GetNumEntries() * sizeof(int32_t);
9379 ExactAssemblyScope aas(GetVIXLAssembler(),
9380 (vixl32::kMaxInstructionSizeInBytes * 4) + jump_size,
9381 CodeBufferCheckScope::kMaximumSize);
9382 __ adr(table_base, jump_table->GetTableStartLabel());
9383 __ ldr(jump_offset, MemOperand(table_base, key_reg, vixl32::LSL, 2));
Artem Serov551b28f2016-10-18 19:11:30 +01009384
Scott Wakeling86e9d262017-01-18 15:59:24 +00009385 // Jump to target block by branching to table_base(pc related) + offset.
9386 vixl32::Register target_address = table_base;
9387 __ add(target_address, table_base, jump_offset);
9388 __ bx(target_address);
Artem Serov09a940d2016-11-11 16:15:11 +00009389
Scott Wakeling86e9d262017-01-18 15:59:24 +00009390 jump_table->EmitTable(codegen_);
9391 }
Artem Serov551b28f2016-10-18 19:11:30 +01009392 }
9393}
9394
Artem Serov02d37832016-10-25 15:25:33 +01009395// Copy the result of a call into the given target.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01009396void CodeGeneratorARMVIXL::MoveFromReturnRegister(Location trg, DataType::Type type) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009397 if (!trg.IsValid()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01009398 DCHECK_EQ(type, DataType::Type::kVoid);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009399 return;
9400 }
9401
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01009402 DCHECK_NE(type, DataType::Type::kVoid);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009403
Artem Serovd4cc5b22016-11-04 11:19:09 +00009404 Location return_loc = InvokeDexCallingConventionVisitorARMVIXL().GetReturnLocation(type);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009405 if (return_loc.Equals(trg)) {
9406 return;
9407 }
9408
9409 // TODO: Consider pairs in the parallel move resolver, then this could be nicely merged
9410 // with the last branch.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01009411 if (type == DataType::Type::kInt64) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009412 TODO_VIXL32(FATAL);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01009413 } else if (type == DataType::Type::kFloat64) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009414 TODO_VIXL32(FATAL);
9415 } else {
9416 // Let the parallel move resolver take care of all of this.
9417 HParallelMove parallel_move(GetGraph()->GetArena());
9418 parallel_move.AddMove(return_loc, trg, type, nullptr);
9419 GetMoveResolver()->EmitNativeCode(&parallel_move);
9420 }
Scott Wakelingfe885462016-09-22 10:24:38 +01009421}
9422
xueliang.zhong8d2c4592016-11-23 17:05:25 +00009423void LocationsBuilderARMVIXL::VisitClassTableGet(HClassTableGet* instruction) {
9424 LocationSummary* locations =
9425 new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
9426 locations->SetInAt(0, Location::RequiresRegister());
9427 locations->SetOut(Location::RequiresRegister());
Artem Serov551b28f2016-10-18 19:11:30 +01009428}
9429
xueliang.zhong8d2c4592016-11-23 17:05:25 +00009430void InstructionCodeGeneratorARMVIXL::VisitClassTableGet(HClassTableGet* instruction) {
9431 if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) {
9432 uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
9433 instruction->GetIndex(), kArmPointerSize).SizeValue();
9434 GetAssembler()->LoadFromOffset(kLoadWord,
9435 OutputRegister(instruction),
9436 InputRegisterAt(instruction, 0),
9437 method_offset);
9438 } else {
9439 uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement(
9440 instruction->GetIndex(), kArmPointerSize));
9441 GetAssembler()->LoadFromOffset(kLoadWord,
9442 OutputRegister(instruction),
9443 InputRegisterAt(instruction, 0),
9444 mirror::Class::ImtPtrOffset(kArmPointerSize).Uint32Value());
9445 GetAssembler()->LoadFromOffset(kLoadWord,
9446 OutputRegister(instruction),
9447 OutputRegister(instruction),
9448 method_offset);
9449 }
Artem Serov551b28f2016-10-18 19:11:30 +01009450}
9451
Artem Serovc5fcb442016-12-02 19:19:58 +00009452static void PatchJitRootUse(uint8_t* code,
9453 const uint8_t* roots_data,
9454 VIXLUInt32Literal* literal,
9455 uint64_t index_in_table) {
9456 DCHECK(literal->IsBound());
9457 uint32_t literal_offset = literal->GetLocation();
9458 uintptr_t address =
9459 reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>);
9460 uint8_t* data = code + literal_offset;
9461 reinterpret_cast<uint32_t*>(data)[0] = dchecked_integral_cast<uint32_t>(address);
9462}
9463
9464void CodeGeneratorARMVIXL::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) {
9465 for (const auto& entry : jit_string_patches_) {
Vladimir Marko7d157fc2017-05-10 16:29:23 +01009466 const StringReference& string_reference = entry.first;
9467 VIXLUInt32Literal* table_entry_literal = entry.second;
9468 const auto it = jit_string_roots_.find(string_reference);
Artem Serovc5fcb442016-12-02 19:19:58 +00009469 DCHECK(it != jit_string_roots_.end());
Vladimir Marko7d157fc2017-05-10 16:29:23 +01009470 uint64_t index_in_table = it->second;
9471 PatchJitRootUse(code, roots_data, table_entry_literal, index_in_table);
Artem Serovc5fcb442016-12-02 19:19:58 +00009472 }
9473 for (const auto& entry : jit_class_patches_) {
Vladimir Marko7d157fc2017-05-10 16:29:23 +01009474 const TypeReference& type_reference = entry.first;
9475 VIXLUInt32Literal* table_entry_literal = entry.second;
9476 const auto it = jit_class_roots_.find(type_reference);
Artem Serovc5fcb442016-12-02 19:19:58 +00009477 DCHECK(it != jit_class_roots_.end());
Vladimir Marko7d157fc2017-05-10 16:29:23 +01009478 uint64_t index_in_table = it->second;
9479 PatchJitRootUse(code, roots_data, table_entry_literal, index_in_table);
Artem Serovc5fcb442016-12-02 19:19:58 +00009480 }
9481}
9482
Artem Serovd4cc5b22016-11-04 11:19:09 +00009483void CodeGeneratorARMVIXL::EmitMovwMovtPlaceholder(
9484 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels,
9485 vixl32::Register out) {
Artem Serov0fb37192016-12-06 18:13:40 +00009486 ExactAssemblyScope aas(GetVIXLAssembler(),
9487 3 * vixl32::kMaxInstructionSizeInBytes,
9488 CodeBufferCheckScope::kMaximumSize);
Artem Serovd4cc5b22016-11-04 11:19:09 +00009489 // TODO(VIXL): Think about using mov instead of movw.
9490 __ bind(&labels->movw_label);
9491 __ movw(out, /* placeholder */ 0u);
9492 __ bind(&labels->movt_label);
9493 __ movt(out, /* placeholder */ 0u);
9494 __ bind(&labels->add_pc_label);
9495 __ add(out, out, pc);
9496}
9497
Scott Wakelingfe885462016-09-22 10:24:38 +01009498#undef __
9499#undef QUICK_ENTRY_POINT
9500#undef TODO_VIXL32
9501
9502} // namespace arm
9503} // namespace art