blob: 58ce9aa9f0c1d488485218885dcefa1c6a9fb38f [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 Markod8dbc8d2017-09-20 13:37:47 +010032#include "linker/linker_patch.h"
Scott Wakelingfe885462016-09-22 10:24:38 +010033#include "mirror/array-inl.h"
34#include "mirror/class-inl.h"
35#include "thread.h"
36#include "utils/arm/assembler_arm_vixl.h"
37#include "utils/arm/managed_register_arm.h"
38#include "utils/assembler.h"
39#include "utils/stack_checks.h"
40
41namespace art {
42namespace arm {
43
44namespace vixl32 = vixl::aarch32;
45using namespace vixl32; // NOLINT(build/namespaces)
46
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +010047using helpers::DRegisterFrom;
Scott Wakelingfe885462016-09-22 10:24:38 +010048using helpers::DWARFReg;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010049using helpers::HighDRegisterFrom;
50using helpers::HighRegisterFrom;
Donghui Bai426b49c2016-11-08 14:55:38 +080051using helpers::InputDRegisterAt;
Scott Wakelingfe885462016-09-22 10:24:38 +010052using helpers::InputOperandAt;
Scott Wakelingc34dba72016-10-03 10:14:44 +010053using helpers::InputRegister;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010054using helpers::InputRegisterAt;
Scott Wakelingfe885462016-09-22 10:24:38 +010055using helpers::InputSRegisterAt;
Anton Kirilov644032c2016-12-06 17:51:43 +000056using helpers::InputVRegister;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010057using helpers::InputVRegisterAt;
Scott Wakelingb77051e2016-11-21 19:46:00 +000058using helpers::Int32ConstantFrom;
Anton Kirilov644032c2016-12-06 17:51:43 +000059using helpers::Int64ConstantFrom;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010060using helpers::LocationFrom;
61using helpers::LowRegisterFrom;
62using helpers::LowSRegisterFrom;
Donghui Bai426b49c2016-11-08 14:55:38 +080063using helpers::OperandFrom;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010064using helpers::OutputRegister;
65using helpers::OutputSRegister;
66using helpers::OutputVRegister;
67using helpers::RegisterFrom;
68using helpers::SRegisterFrom;
Anton Kirilov644032c2016-12-06 17:51:43 +000069using helpers::Uint64ConstantFrom;
Scott Wakelingfe885462016-09-22 10:24:38 +010070
Artem Serov0fb37192016-12-06 18:13:40 +000071using vixl::ExactAssemblyScope;
72using vixl::CodeBufferCheckScope;
73
Scott Wakelingfe885462016-09-22 10:24:38 +010074using RegisterList = vixl32::RegisterList;
75
76static bool ExpectedPairLayout(Location location) {
77 // We expected this for both core and fpu register pairs.
78 return ((location.low() & 1) == 0) && (location.low() + 1 == location.high());
79}
Artem Serovd4cc5b22016-11-04 11:19:09 +000080// Use a local definition to prevent copying mistakes.
81static constexpr size_t kArmWordSize = static_cast<size_t>(kArmPointerSize);
82static constexpr size_t kArmBitsPerWord = kArmWordSize * kBitsPerByte;
Artem Serov551b28f2016-10-18 19:11:30 +010083static constexpr uint32_t kPackedSwitchCompareJumpThreshold = 7;
Scott Wakelingfe885462016-09-22 10:24:38 +010084
Vladimir Markoeee1c0e2017-04-21 17:58:41 +010085// Reference load (except object array loads) is using LDR Rt, [Rn, #offset] which can handle
86// offset < 4KiB. For offsets >= 4KiB, the load shall be emitted as two or more instructions.
87// For the Baker read barrier implementation using link-generated thunks we need to split
88// the offset explicitly.
89constexpr uint32_t kReferenceLoadMinFarOffset = 4 * KB;
90
91// Flags controlling the use of link-time generated thunks for Baker read barriers.
92constexpr bool kBakerReadBarrierLinkTimeThunksEnableForFields = true;
93constexpr bool kBakerReadBarrierLinkTimeThunksEnableForArrays = true;
94constexpr bool kBakerReadBarrierLinkTimeThunksEnableForGcRoots = true;
95
Roland Levillain5daa4952017-07-03 17:23:56 +010096// Using a base helps identify when we hit Marking Register check breakpoints.
97constexpr int kMarkingRegisterCheckBreakCodeBaseCode = 0x10;
98
Scott Wakelingfe885462016-09-22 10:24:38 +010099#ifdef __
100#error "ARM Codegen VIXL macro-assembler macro already defined."
101#endif
102
Scott Wakelingfe885462016-09-22 10:24:38 +0100103// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
104#define __ down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler()-> // NOLINT
105#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArmPointerSize, x).Int32Value()
106
107// Marker that code is yet to be, and must, be implemented.
108#define TODO_VIXL32(level) LOG(level) << __PRETTY_FUNCTION__ << " unimplemented "
109
Vladimir Markoeee1c0e2017-04-21 17:58:41 +0100110static inline void EmitPlaceholderBne(CodeGeneratorARMVIXL* codegen, vixl32::Label* patch_label) {
111 ExactAssemblyScope eas(codegen->GetVIXLAssembler(), kMaxInstructionSizeInBytes);
112 __ bind(patch_label);
113 vixl32::Label placeholder_label;
114 __ b(ne, EncodingSize(Wide), &placeholder_label); // Placeholder, patched at link-time.
115 __ bind(&placeholder_label);
116}
117
Vladimir Marko88abba22017-05-03 17:09:25 +0100118static inline bool CanEmitNarrowLdr(vixl32::Register rt, vixl32::Register rn, uint32_t offset) {
119 return rt.IsLow() && rn.IsLow() && offset < 32u;
120}
121
Vladimir Markoeee1c0e2017-04-21 17:58:41 +0100122class EmitAdrCode {
123 public:
124 EmitAdrCode(ArmVIXLMacroAssembler* assembler, vixl32::Register rd, vixl32::Label* label)
125 : assembler_(assembler), rd_(rd), label_(label) {
126 ExactAssemblyScope aas(assembler, kMaxInstructionSizeInBytes);
127 adr_location_ = assembler->GetCursorOffset();
128 assembler->adr(EncodingSize(Wide), rd, label);
129 }
130
131 ~EmitAdrCode() {
132 DCHECK(label_->IsBound());
133 // The ADR emitted by the assembler does not set the Thumb mode bit we need.
134 // TODO: Maybe extend VIXL to allow ADR for return address?
135 uint8_t* raw_adr = assembler_->GetBuffer()->GetOffsetAddress<uint8_t*>(adr_location_);
136 // Expecting ADR encoding T3 with `(offset & 1) == 0`.
137 DCHECK_EQ(raw_adr[1] & 0xfbu, 0xf2u); // Check bits 24-31, except 26.
138 DCHECK_EQ(raw_adr[0] & 0xffu, 0x0fu); // Check bits 16-23.
139 DCHECK_EQ(raw_adr[3] & 0x8fu, rd_.GetCode()); // Check bits 8-11 and 15.
140 DCHECK_EQ(raw_adr[2] & 0x01u, 0x00u); // Check bit 0, i.e. the `offset & 1`.
141 // Add the Thumb mode bit.
142 raw_adr[2] |= 0x01u;
143 }
144
145 private:
146 ArmVIXLMacroAssembler* const assembler_;
147 vixl32::Register rd_;
148 vixl32::Label* const label_;
149 int32_t adr_location_;
150};
151
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100152// SaveLiveRegisters and RestoreLiveRegisters from SlowPathCodeARM operate on sets of S registers,
153// for each live D registers they treat two corresponding S registers as live ones.
154//
155// Two following functions (SaveContiguousSRegisterList, RestoreContiguousSRegisterList) build
156// from a list of contiguous S registers a list of contiguous D registers (processing first/last
157// S registers corner cases) and save/restore this new list treating them as D registers.
158// - decreasing code size
159// - avoiding hazards on Cortex-A57, when a pair of S registers for an actual live D register is
160// restored and then used in regular non SlowPath code as D register.
161//
162// For the following example (v means the S register is live):
163// D names: | D0 | D1 | D2 | D4 | ...
164// S names: | S0 | S1 | S2 | S3 | S4 | S5 | S6 | S7 | ...
165// Live? | | v | v | v | v | v | v | | ...
166//
167// S1 and S6 will be saved/restored independently; D registers list (D1, D2) will be processed
168// as D registers.
169//
170// TODO(VIXL): All this code should be unnecessary once the VIXL AArch32 backend provides helpers
171// for lists of floating-point registers.
172static size_t SaveContiguousSRegisterList(size_t first,
173 size_t last,
174 CodeGenerator* codegen,
175 size_t stack_offset) {
176 static_assert(kSRegSizeInBytes == kArmWordSize, "Broken assumption on reg/word sizes.");
177 static_assert(kDRegSizeInBytes == 2 * kArmWordSize, "Broken assumption on reg/word sizes.");
178 DCHECK_LE(first, last);
179 if ((first == last) && (first == 0)) {
180 __ Vstr(vixl32::SRegister(first), MemOperand(sp, stack_offset));
181 return stack_offset + kSRegSizeInBytes;
182 }
183 if (first % 2 == 1) {
184 __ Vstr(vixl32::SRegister(first++), MemOperand(sp, stack_offset));
185 stack_offset += kSRegSizeInBytes;
186 }
187
188 bool save_last = false;
189 if (last % 2 == 0) {
190 save_last = true;
191 --last;
192 }
193
194 if (first < last) {
195 vixl32::DRegister d_reg = vixl32::DRegister(first / 2);
196 DCHECK_EQ((last - first + 1) % 2, 0u);
197 size_t number_of_d_regs = (last - first + 1) / 2;
198
199 if (number_of_d_regs == 1) {
200 __ Vstr(d_reg, MemOperand(sp, stack_offset));
201 } else if (number_of_d_regs > 1) {
202 UseScratchRegisterScope temps(down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler());
203 vixl32::Register base = sp;
204 if (stack_offset != 0) {
205 base = temps.Acquire();
Scott Wakelingb77051e2016-11-21 19:46:00 +0000206 __ Add(base, sp, Operand::From(stack_offset));
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100207 }
208 __ Vstm(F64, base, NO_WRITE_BACK, DRegisterList(d_reg, number_of_d_regs));
209 }
210 stack_offset += number_of_d_regs * kDRegSizeInBytes;
211 }
212
213 if (save_last) {
214 __ Vstr(vixl32::SRegister(last + 1), MemOperand(sp, stack_offset));
215 stack_offset += kSRegSizeInBytes;
216 }
217
218 return stack_offset;
219}
220
221static size_t RestoreContiguousSRegisterList(size_t first,
222 size_t last,
223 CodeGenerator* codegen,
224 size_t stack_offset) {
225 static_assert(kSRegSizeInBytes == kArmWordSize, "Broken assumption on reg/word sizes.");
226 static_assert(kDRegSizeInBytes == 2 * kArmWordSize, "Broken assumption on reg/word sizes.");
227 DCHECK_LE(first, last);
228 if ((first == last) && (first == 0)) {
229 __ Vldr(vixl32::SRegister(first), MemOperand(sp, stack_offset));
230 return stack_offset + kSRegSizeInBytes;
231 }
232 if (first % 2 == 1) {
233 __ Vldr(vixl32::SRegister(first++), MemOperand(sp, stack_offset));
234 stack_offset += kSRegSizeInBytes;
235 }
236
237 bool restore_last = false;
238 if (last % 2 == 0) {
239 restore_last = true;
240 --last;
241 }
242
243 if (first < last) {
244 vixl32::DRegister d_reg = vixl32::DRegister(first / 2);
245 DCHECK_EQ((last - first + 1) % 2, 0u);
246 size_t number_of_d_regs = (last - first + 1) / 2;
247 if (number_of_d_regs == 1) {
248 __ Vldr(d_reg, MemOperand(sp, stack_offset));
249 } else if (number_of_d_regs > 1) {
250 UseScratchRegisterScope temps(down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler());
251 vixl32::Register base = sp;
252 if (stack_offset != 0) {
253 base = temps.Acquire();
Scott Wakelingb77051e2016-11-21 19:46:00 +0000254 __ Add(base, sp, Operand::From(stack_offset));
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100255 }
256 __ Vldm(F64, base, NO_WRITE_BACK, DRegisterList(d_reg, number_of_d_regs));
257 }
258 stack_offset += number_of_d_regs * kDRegSizeInBytes;
259 }
260
261 if (restore_last) {
262 __ Vldr(vixl32::SRegister(last + 1), MemOperand(sp, stack_offset));
263 stack_offset += kSRegSizeInBytes;
264 }
265
266 return stack_offset;
267}
268
Vladimir Markod5d2f2c2017-09-26 12:37:26 +0100269static LoadOperandType GetLoadOperandType(DataType::Type type) {
270 switch (type) {
271 case DataType::Type::kReference:
272 return kLoadWord;
273 case DataType::Type::kBool:
274 case DataType::Type::kUint8:
275 return kLoadUnsignedByte;
276 case DataType::Type::kInt8:
277 return kLoadSignedByte;
278 case DataType::Type::kUint16:
279 return kLoadUnsignedHalfword;
280 case DataType::Type::kInt16:
281 return kLoadSignedHalfword;
282 case DataType::Type::kInt32:
283 return kLoadWord;
284 case DataType::Type::kInt64:
285 return kLoadWordPair;
286 case DataType::Type::kFloat32:
287 return kLoadSWord;
288 case DataType::Type::kFloat64:
289 return kLoadDWord;
290 default:
291 LOG(FATAL) << "Unreachable type " << type;
292 UNREACHABLE();
293 }
294}
295
296static StoreOperandType GetStoreOperandType(DataType::Type type) {
297 switch (type) {
298 case DataType::Type::kReference:
299 return kStoreWord;
300 case DataType::Type::kBool:
301 case DataType::Type::kUint8:
302 case DataType::Type::kInt8:
303 return kStoreByte;
304 case DataType::Type::kUint16:
305 case DataType::Type::kInt16:
306 return kStoreHalfword;
307 case DataType::Type::kInt32:
308 return kStoreWord;
309 case DataType::Type::kInt64:
310 return kStoreWordPair;
311 case DataType::Type::kFloat32:
312 return kStoreSWord;
313 case DataType::Type::kFloat64:
314 return kStoreDWord;
315 default:
316 LOG(FATAL) << "Unreachable type " << type;
317 UNREACHABLE();
318 }
319}
320
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100321void SlowPathCodeARMVIXL::SaveLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
322 size_t stack_offset = codegen->GetFirstRegisterSlotInSlowPath();
323 size_t orig_offset = stack_offset;
324
325 const uint32_t core_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ true);
326 for (uint32_t i : LowToHighBits(core_spills)) {
327 // If the register holds an object, update the stack mask.
328 if (locations->RegisterContainsObject(i)) {
329 locations->SetStackBit(stack_offset / kVRegSize);
330 }
331 DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
332 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
333 saved_core_stack_offsets_[i] = stack_offset;
334 stack_offset += kArmWordSize;
335 }
336
337 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
338 arm_codegen->GetAssembler()->StoreRegisterList(core_spills, orig_offset);
339
340 uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ false);
341 orig_offset = stack_offset;
342 for (uint32_t i : LowToHighBits(fp_spills)) {
343 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
344 saved_fpu_stack_offsets_[i] = stack_offset;
345 stack_offset += kArmWordSize;
346 }
347
348 stack_offset = orig_offset;
349 while (fp_spills != 0u) {
350 uint32_t begin = CTZ(fp_spills);
351 uint32_t tmp = fp_spills + (1u << begin);
352 fp_spills &= tmp; // Clear the contiguous range of 1s.
353 uint32_t end = (tmp == 0u) ? 32u : CTZ(tmp); // CTZ(0) is undefined.
354 stack_offset = SaveContiguousSRegisterList(begin, end - 1, codegen, stack_offset);
355 }
356 DCHECK_LE(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
357}
358
359void SlowPathCodeARMVIXL::RestoreLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
360 size_t stack_offset = codegen->GetFirstRegisterSlotInSlowPath();
361 size_t orig_offset = stack_offset;
362
363 const uint32_t core_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ true);
364 for (uint32_t i : LowToHighBits(core_spills)) {
365 DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
366 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
367 stack_offset += kArmWordSize;
368 }
369
370 // TODO(VIXL): Check the coherency of stack_offset after this with a test.
371 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
372 arm_codegen->GetAssembler()->LoadRegisterList(core_spills, orig_offset);
373
374 uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ false);
375 while (fp_spills != 0u) {
376 uint32_t begin = CTZ(fp_spills);
377 uint32_t tmp = fp_spills + (1u << begin);
378 fp_spills &= tmp; // Clear the contiguous range of 1s.
379 uint32_t end = (tmp == 0u) ? 32u : CTZ(tmp); // CTZ(0) is undefined.
380 stack_offset = RestoreContiguousSRegisterList(begin, end - 1, codegen, stack_offset);
381 }
382 DCHECK_LE(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
383}
384
385class NullCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
386 public:
387 explicit NullCheckSlowPathARMVIXL(HNullCheck* instruction) : SlowPathCodeARMVIXL(instruction) {}
388
389 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
390 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
391 __ Bind(GetEntryLabel());
392 if (instruction_->CanThrowIntoCatchBlock()) {
393 // Live registers will be restored in the catch block if caught.
394 SaveLiveRegisters(codegen, instruction_->GetLocations());
395 }
396 arm_codegen->InvokeRuntime(kQuickThrowNullPointer,
397 instruction_,
398 instruction_->GetDexPc(),
399 this);
400 CheckEntrypointTypes<kQuickThrowNullPointer, void, void>();
401 }
402
403 bool IsFatal() const OVERRIDE { return true; }
404
405 const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathARMVIXL"; }
406
407 private:
408 DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARMVIXL);
409};
410
Scott Wakelingfe885462016-09-22 10:24:38 +0100411class DivZeroCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
412 public:
413 explicit DivZeroCheckSlowPathARMVIXL(HDivZeroCheck* instruction)
414 : SlowPathCodeARMVIXL(instruction) {}
415
416 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100417 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
Scott Wakelingfe885462016-09-22 10:24:38 +0100418 __ Bind(GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100419 arm_codegen->InvokeRuntime(kQuickThrowDivZero, instruction_, instruction_->GetDexPc(), this);
Scott Wakelingfe885462016-09-22 10:24:38 +0100420 CheckEntrypointTypes<kQuickThrowDivZero, void, void>();
421 }
422
423 bool IsFatal() const OVERRIDE { return true; }
424
425 const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathARMVIXL"; }
426
427 private:
428 DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathARMVIXL);
429};
430
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100431class SuspendCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
432 public:
433 SuspendCheckSlowPathARMVIXL(HSuspendCheck* instruction, HBasicBlock* successor)
434 : SlowPathCodeARMVIXL(instruction), successor_(successor) {}
435
436 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
437 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
438 __ Bind(GetEntryLabel());
439 arm_codegen->InvokeRuntime(kQuickTestSuspend, instruction_, instruction_->GetDexPc(), this);
440 CheckEntrypointTypes<kQuickTestSuspend, void, void>();
441 if (successor_ == nullptr) {
442 __ B(GetReturnLabel());
443 } else {
444 __ B(arm_codegen->GetLabelOf(successor_));
445 }
446 }
447
448 vixl32::Label* GetReturnLabel() {
449 DCHECK(successor_ == nullptr);
450 return &return_label_;
451 }
452
453 HBasicBlock* GetSuccessor() const {
454 return successor_;
455 }
456
457 const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathARMVIXL"; }
458
459 private:
460 // If not null, the block to branch to after the suspend check.
461 HBasicBlock* const successor_;
462
463 // If `successor_` is null, the label to branch to after the suspend check.
464 vixl32::Label return_label_;
465
466 DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathARMVIXL);
467};
468
Scott Wakelingc34dba72016-10-03 10:14:44 +0100469class BoundsCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
470 public:
471 explicit BoundsCheckSlowPathARMVIXL(HBoundsCheck* instruction)
472 : SlowPathCodeARMVIXL(instruction) {}
473
474 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
475 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
476 LocationSummary* locations = instruction_->GetLocations();
477
478 __ Bind(GetEntryLabel());
479 if (instruction_->CanThrowIntoCatchBlock()) {
480 // Live registers will be restored in the catch block if caught.
481 SaveLiveRegisters(codegen, instruction_->GetLocations());
482 }
483 // We're moving two locations to locations that could overlap, so we need a parallel
484 // move resolver.
485 InvokeRuntimeCallingConventionARMVIXL calling_convention;
486 codegen->EmitParallelMoves(
487 locations->InAt(0),
488 LocationFrom(calling_convention.GetRegisterAt(0)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100489 DataType::Type::kInt32,
Scott Wakelingc34dba72016-10-03 10:14:44 +0100490 locations->InAt(1),
491 LocationFrom(calling_convention.GetRegisterAt(1)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100492 DataType::Type::kInt32);
Scott Wakelingc34dba72016-10-03 10:14:44 +0100493 QuickEntrypointEnum entrypoint = instruction_->AsBoundsCheck()->IsStringCharAt()
494 ? kQuickThrowStringBounds
495 : kQuickThrowArrayBounds;
496 arm_codegen->InvokeRuntime(entrypoint, instruction_, instruction_->GetDexPc(), this);
497 CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>();
498 CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>();
499 }
500
501 bool IsFatal() const OVERRIDE { return true; }
502
503 const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathARMVIXL"; }
504
505 private:
506 DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARMVIXL);
507};
508
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100509class LoadClassSlowPathARMVIXL : public SlowPathCodeARMVIXL {
510 public:
511 LoadClassSlowPathARMVIXL(HLoadClass* cls, HInstruction* at, uint32_t dex_pc, bool do_clinit)
Vladimir Marko6bec91c2017-01-09 15:03:12 +0000512 : SlowPathCodeARMVIXL(at), cls_(cls), dex_pc_(dex_pc), do_clinit_(do_clinit) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100513 DCHECK(at->IsLoadClass() || at->IsClinitCheck());
514 }
515
516 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
Vladimir Marko6bec91c2017-01-09 15:03:12 +0000517 LocationSummary* locations = instruction_->GetLocations();
Vladimir Markoea4c1262017-02-06 19:59:33 +0000518 Location out = locations->Out();
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100519
520 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
521 __ Bind(GetEntryLabel());
522 SaveLiveRegisters(codegen, locations);
523
524 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Vladimir Marko6bec91c2017-01-09 15:03:12 +0000525 dex::TypeIndex type_index = cls_->GetTypeIndex();
526 __ Mov(calling_convention.GetRegisterAt(0), type_index.index_);
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100527 QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage
528 : kQuickInitializeType;
Vladimir Marko6bec91c2017-01-09 15:03:12 +0000529 arm_codegen->InvokeRuntime(entrypoint, instruction_, dex_pc_, this);
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100530 if (do_clinit_) {
531 CheckEntrypointTypes<kQuickInitializeStaticStorage, void*, uint32_t>();
532 } else {
533 CheckEntrypointTypes<kQuickInitializeType, void*, uint32_t>();
534 }
535
536 // Move the class to the desired location.
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100537 if (out.IsValid()) {
538 DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
539 arm_codegen->Move32(locations->Out(), LocationFrom(r0));
540 }
541 RestoreLiveRegisters(codegen, locations);
542 __ B(GetExitLabel());
543 }
544
545 const char* GetDescription() const OVERRIDE { return "LoadClassSlowPathARMVIXL"; }
546
547 private:
548 // The class this slow path will load.
549 HLoadClass* const cls_;
550
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100551 // The dex PC of `at_`.
552 const uint32_t dex_pc_;
553
554 // Whether to initialize the class.
555 const bool do_clinit_;
556
557 DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathARMVIXL);
558};
559
Artem Serovd4cc5b22016-11-04 11:19:09 +0000560class LoadStringSlowPathARMVIXL : public SlowPathCodeARMVIXL {
561 public:
562 explicit LoadStringSlowPathARMVIXL(HLoadString* instruction)
563 : SlowPathCodeARMVIXL(instruction) {}
564
565 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
Vladimir Markoea4c1262017-02-06 19:59:33 +0000566 DCHECK(instruction_->IsLoadString());
567 DCHECK_EQ(instruction_->AsLoadString()->GetLoadKind(), HLoadString::LoadKind::kBssEntry);
Artem Serovd4cc5b22016-11-04 11:19:09 +0000568 LocationSummary* locations = instruction_->GetLocations();
569 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
Vladimir Markof3c52b42017-11-17 17:32:12 +0000570 const dex::StringIndex string_index = instruction_->AsLoadString()->GetStringIndex();
Artem Serovd4cc5b22016-11-04 11:19:09 +0000571
572 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
573 __ Bind(GetEntryLabel());
574 SaveLiveRegisters(codegen, locations);
575
576 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Vladimir Marko6bec91c2017-01-09 15:03:12 +0000577 __ Mov(calling_convention.GetRegisterAt(0), string_index.index_);
Artem Serovd4cc5b22016-11-04 11:19:09 +0000578 arm_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), this);
579 CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
580
Artem Serovd4cc5b22016-11-04 11:19:09 +0000581 arm_codegen->Move32(locations->Out(), LocationFrom(r0));
582 RestoreLiveRegisters(codegen, locations);
583
584 __ B(GetExitLabel());
585 }
586
587 const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathARMVIXL"; }
588
589 private:
590 DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathARMVIXL);
591};
592
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100593class TypeCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
594 public:
595 TypeCheckSlowPathARMVIXL(HInstruction* instruction, bool is_fatal)
596 : SlowPathCodeARMVIXL(instruction), is_fatal_(is_fatal) {}
597
598 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
599 LocationSummary* locations = instruction_->GetLocations();
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100600 DCHECK(instruction_->IsCheckCast()
601 || !locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
602
603 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
604 __ Bind(GetEntryLabel());
605
Vladimir Marko87584542017-12-12 17:47:52 +0000606 if (!is_fatal_ || instruction_->CanThrowIntoCatchBlock()) {
Artem Serovcfbe9132016-10-14 15:58:56 +0100607 SaveLiveRegisters(codegen, locations);
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100608 }
609
610 // We're moving two locations to locations that could overlap, so we need a parallel
611 // move resolver.
612 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100613
Mathieu Chartier9fd8c602016-11-14 14:38:53 -0800614 codegen->EmitParallelMoves(locations->InAt(0),
Mathieu Chartierb99f4d62016-11-07 16:17:26 -0800615 LocationFrom(calling_convention.GetRegisterAt(0)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100616 DataType::Type::kReference,
Mathieu Chartier9fd8c602016-11-14 14:38:53 -0800617 locations->InAt(1),
Mathieu Chartierb99f4d62016-11-07 16:17:26 -0800618 LocationFrom(calling_convention.GetRegisterAt(1)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100619 DataType::Type::kReference);
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100620 if (instruction_->IsInstanceOf()) {
Artem Serovcfbe9132016-10-14 15:58:56 +0100621 arm_codegen->InvokeRuntime(kQuickInstanceofNonTrivial,
622 instruction_,
623 instruction_->GetDexPc(),
624 this);
Mathieu Chartier9fd8c602016-11-14 14:38:53 -0800625 CheckEntrypointTypes<kQuickInstanceofNonTrivial, size_t, mirror::Object*, mirror::Class*>();
Artem Serovcfbe9132016-10-14 15:58:56 +0100626 arm_codegen->Move32(locations->Out(), LocationFrom(r0));
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100627 } else {
628 DCHECK(instruction_->IsCheckCast());
Mathieu Chartierb99f4d62016-11-07 16:17:26 -0800629 arm_codegen->InvokeRuntime(kQuickCheckInstanceOf,
630 instruction_,
631 instruction_->GetDexPc(),
632 this);
633 CheckEntrypointTypes<kQuickCheckInstanceOf, void, mirror::Object*, mirror::Class*>();
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100634 }
635
636 if (!is_fatal_) {
Artem Serovcfbe9132016-10-14 15:58:56 +0100637 RestoreLiveRegisters(codegen, locations);
638 __ B(GetExitLabel());
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100639 }
640 }
641
642 const char* GetDescription() const OVERRIDE { return "TypeCheckSlowPathARMVIXL"; }
643
644 bool IsFatal() const OVERRIDE { return is_fatal_; }
645
646 private:
647 const bool is_fatal_;
648
649 DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathARMVIXL);
650};
651
Scott Wakelingc34dba72016-10-03 10:14:44 +0100652class DeoptimizationSlowPathARMVIXL : public SlowPathCodeARMVIXL {
653 public:
654 explicit DeoptimizationSlowPathARMVIXL(HDeoptimize* instruction)
655 : SlowPathCodeARMVIXL(instruction) {}
656
657 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
658 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
659 __ Bind(GetEntryLabel());
Nicolas Geoffray4e92c3c2017-05-08 09:34:26 +0100660 LocationSummary* locations = instruction_->GetLocations();
661 SaveLiveRegisters(codegen, locations);
662 InvokeRuntimeCallingConventionARMVIXL calling_convention;
663 __ Mov(calling_convention.GetRegisterAt(0),
664 static_cast<uint32_t>(instruction_->AsDeoptimize()->GetDeoptimizationKind()));
665
Scott Wakelingc34dba72016-10-03 10:14:44 +0100666 arm_codegen->InvokeRuntime(kQuickDeoptimize, instruction_, instruction_->GetDexPc(), this);
Nicolas Geoffray4e92c3c2017-05-08 09:34:26 +0100667 CheckEntrypointTypes<kQuickDeoptimize, void, DeoptimizationKind>();
Scott Wakelingc34dba72016-10-03 10:14:44 +0100668 }
669
670 const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathARMVIXL"; }
671
672 private:
673 DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathARMVIXL);
674};
675
676class ArraySetSlowPathARMVIXL : public SlowPathCodeARMVIXL {
677 public:
678 explicit ArraySetSlowPathARMVIXL(HInstruction* instruction) : SlowPathCodeARMVIXL(instruction) {}
679
680 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
681 LocationSummary* locations = instruction_->GetLocations();
682 __ Bind(GetEntryLabel());
683 SaveLiveRegisters(codegen, locations);
684
685 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Vladimir Markoca6fff82017-10-03 14:49:14 +0100686 HParallelMove parallel_move(codegen->GetGraph()->GetAllocator());
Scott Wakelingc34dba72016-10-03 10:14:44 +0100687 parallel_move.AddMove(
688 locations->InAt(0),
689 LocationFrom(calling_convention.GetRegisterAt(0)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100690 DataType::Type::kReference,
Scott Wakelingc34dba72016-10-03 10:14:44 +0100691 nullptr);
692 parallel_move.AddMove(
693 locations->InAt(1),
694 LocationFrom(calling_convention.GetRegisterAt(1)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100695 DataType::Type::kInt32,
Scott Wakelingc34dba72016-10-03 10:14:44 +0100696 nullptr);
697 parallel_move.AddMove(
698 locations->InAt(2),
699 LocationFrom(calling_convention.GetRegisterAt(2)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100700 DataType::Type::kReference,
Scott Wakelingc34dba72016-10-03 10:14:44 +0100701 nullptr);
702 codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
703
704 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
705 arm_codegen->InvokeRuntime(kQuickAputObject, instruction_, instruction_->GetDexPc(), this);
706 CheckEntrypointTypes<kQuickAputObject, void, mirror::Array*, int32_t, mirror::Object*>();
707 RestoreLiveRegisters(codegen, locations);
708 __ B(GetExitLabel());
709 }
710
711 const char* GetDescription() const OVERRIDE { return "ArraySetSlowPathARMVIXL"; }
712
713 private:
714 DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathARMVIXL);
715};
716
Roland Levillain54f869e2017-03-06 13:54:11 +0000717// Abstract base class for read barrier slow paths marking a reference
718// `ref`.
Roland Levillain27b1f9c2017-01-17 16:56:34 +0000719//
Roland Levillain54f869e2017-03-06 13:54:11 +0000720// Argument `entrypoint` must be a register location holding the read
Roland Levillain6d729a72017-06-30 18:34:01 +0100721// barrier marking runtime entry point to be invoked or an empty
722// location; in the latter case, the read barrier marking runtime
723// entry point will be loaded by the slow path code itself.
Roland Levillain54f869e2017-03-06 13:54:11 +0000724class ReadBarrierMarkSlowPathBaseARMVIXL : public SlowPathCodeARMVIXL {
725 protected:
726 ReadBarrierMarkSlowPathBaseARMVIXL(HInstruction* instruction, Location ref, Location entrypoint)
Roland Levillain27b1f9c2017-01-17 16:56:34 +0000727 : SlowPathCodeARMVIXL(instruction), ref_(ref), entrypoint_(entrypoint) {
728 DCHECK(kEmitCompilerReadBarrier);
729 }
730
Roland Levillain54f869e2017-03-06 13:54:11 +0000731 const char* GetDescription() const OVERRIDE { return "ReadBarrierMarkSlowPathBaseARMVIXL"; }
Roland Levillain27b1f9c2017-01-17 16:56:34 +0000732
Roland Levillain54f869e2017-03-06 13:54:11 +0000733 // Generate assembly code calling the read barrier marking runtime
734 // entry point (ReadBarrierMarkRegX).
735 void GenerateReadBarrierMarkRuntimeCall(CodeGenerator* codegen) {
Roland Levillain27b1f9c2017-01-17 16:56:34 +0000736 vixl32::Register ref_reg = RegisterFrom(ref_);
Roland Levillain47b3ab22017-02-27 14:31:35 +0000737
Roland Levillain47b3ab22017-02-27 14:31:35 +0000738 // No need to save live registers; it's taken care of by the
739 // entrypoint. Also, there is no need to update the stack mask,
740 // as this runtime call will not trigger a garbage collection.
741 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
742 DCHECK(!ref_reg.Is(sp));
743 DCHECK(!ref_reg.Is(lr));
744 DCHECK(!ref_reg.Is(pc));
745 // IP is used internally by the ReadBarrierMarkRegX entry point
746 // as a temporary, it cannot be the entry point's input/output.
747 DCHECK(!ref_reg.Is(ip));
748 DCHECK(ref_reg.IsRegister()) << ref_reg;
749 // "Compact" slow path, saving two moves.
750 //
751 // Instead of using the standard runtime calling convention (input
752 // and output in R0):
753 //
754 // R0 <- ref
755 // R0 <- ReadBarrierMark(R0)
756 // ref <- R0
757 //
758 // we just use rX (the register containing `ref`) as input and output
759 // of a dedicated entrypoint:
760 //
761 // rX <- ReadBarrierMarkRegX(rX)
762 //
763 if (entrypoint_.IsValid()) {
764 arm_codegen->ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction_, this);
765 __ Blx(RegisterFrom(entrypoint_));
766 } else {
Roland Levillain54f869e2017-03-06 13:54:11 +0000767 // Entrypoint is not already loaded, load from the thread.
Roland Levillain47b3ab22017-02-27 14:31:35 +0000768 int32_t entry_point_offset =
Roland Levillain97c46462017-05-11 14:04:03 +0100769 Thread::ReadBarrierMarkEntryPointsOffset<kArmPointerSize>(ref_reg.GetCode());
Roland Levillain47b3ab22017-02-27 14:31:35 +0000770 // This runtime call does not require a stack map.
771 arm_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
772 }
Roland Levillain47b3ab22017-02-27 14:31:35 +0000773 }
774
Roland Levillain47b3ab22017-02-27 14:31:35 +0000775 // The location (register) of the marked object reference.
776 const Location ref_;
777
778 // The location of the entrypoint if already loaded.
779 const Location entrypoint_;
780
Roland Levillain54f869e2017-03-06 13:54:11 +0000781 private:
782 DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkSlowPathBaseARMVIXL);
Roland Levillain47b3ab22017-02-27 14:31:35 +0000783};
784
Scott Wakelingc34dba72016-10-03 10:14:44 +0100785// Slow path marking an object reference `ref` during a read
786// barrier. The field `obj.field` in the object `obj` holding this
Roland Levillain54f869e2017-03-06 13:54:11 +0000787// reference does not get updated by this slow path after marking.
Roland Levillain47b3ab22017-02-27 14:31:35 +0000788//
Scott Wakelingc34dba72016-10-03 10:14:44 +0100789// This means that after the execution of this slow path, `ref` will
Roland Levillain27b1f9c2017-01-17 16:56:34 +0000790// always be up-to-date, but `obj.field` may not; i.e., after the
791// flip, `ref` will be a to-space reference, but `obj.field` will
792// probably still be a from-space reference (unless it gets updated by
793// another thread, or if another thread installed another object
794// reference (different from `ref`) in `obj.field`).
Roland Levillainba650a42017-03-06 13:52:32 +0000795//
Roland Levillain6d729a72017-06-30 18:34:01 +0100796// Argument `entrypoint` must be a register location holding the read
797// barrier marking runtime entry point to be invoked or an empty
798// location; in the latter case, the read barrier marking runtime
799// entry point will be loaded by the slow path code itself.
Roland Levillain54f869e2017-03-06 13:54:11 +0000800class ReadBarrierMarkSlowPathARMVIXL : public ReadBarrierMarkSlowPathBaseARMVIXL {
Roland Levillain47b3ab22017-02-27 14:31:35 +0000801 public:
Roland Levillain27b1f9c2017-01-17 16:56:34 +0000802 ReadBarrierMarkSlowPathARMVIXL(HInstruction* instruction,
803 Location ref,
804 Location entrypoint = Location::NoLocation())
Roland Levillain54f869e2017-03-06 13:54:11 +0000805 : ReadBarrierMarkSlowPathBaseARMVIXL(instruction, ref, entrypoint) {
Roland Levillain47b3ab22017-02-27 14:31:35 +0000806 DCHECK(kEmitCompilerReadBarrier);
807 }
808
Roland Levillain47b3ab22017-02-27 14:31:35 +0000809 const char* GetDescription() const OVERRIDE { return "ReadBarrierMarkSlowPathARMVIXL"; }
810
811 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
812 LocationSummary* locations = instruction_->GetLocations();
Roland Levillain54f869e2017-03-06 13:54:11 +0000813 DCHECK(locations->CanCall());
814 DCHECK(ref_.IsRegister()) << ref_;
815 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_.reg())) << ref_.reg();
816 DCHECK(instruction_->IsLoadClass() || instruction_->IsLoadString())
817 << "Unexpected instruction in read barrier marking slow path: "
818 << instruction_->DebugName();
819
820 __ Bind(GetEntryLabel());
821 GenerateReadBarrierMarkRuntimeCall(codegen);
822 __ B(GetExitLabel());
823 }
824
825 private:
826 DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkSlowPathARMVIXL);
827};
828
829// Slow path loading `obj`'s lock word, loading a reference from
830// object `*(obj + offset + (index << scale_factor))` into `ref`, and
831// marking `ref` if `obj` is gray according to the lock word (Baker
832// read barrier). The field `obj.field` in the object `obj` holding
833// this reference does not get updated by this slow path after marking
834// (see LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARMVIXL
835// below for that).
836//
837// This means that after the execution of this slow path, `ref` will
838// always be up-to-date, but `obj.field` may not; i.e., after the
839// flip, `ref` will be a to-space reference, but `obj.field` will
840// probably still be a from-space reference (unless it gets updated by
841// another thread, or if another thread installed another object
842// reference (different from `ref`) in `obj.field`).
843//
844// Argument `entrypoint` must be a register location holding the read
Roland Levillain6d729a72017-06-30 18:34:01 +0100845// barrier marking runtime entry point to be invoked or an empty
846// location; in the latter case, the read barrier marking runtime
847// entry point will be loaded by the slow path code itself.
Roland Levillain54f869e2017-03-06 13:54:11 +0000848class LoadReferenceWithBakerReadBarrierSlowPathARMVIXL : public ReadBarrierMarkSlowPathBaseARMVIXL {
849 public:
850 LoadReferenceWithBakerReadBarrierSlowPathARMVIXL(HInstruction* instruction,
851 Location ref,
852 vixl32::Register obj,
853 uint32_t offset,
854 Location index,
855 ScaleFactor scale_factor,
856 bool needs_null_check,
857 vixl32::Register temp,
Roland Levillain6d729a72017-06-30 18:34:01 +0100858 Location entrypoint = Location::NoLocation())
Roland Levillain54f869e2017-03-06 13:54:11 +0000859 : ReadBarrierMarkSlowPathBaseARMVIXL(instruction, ref, entrypoint),
860 obj_(obj),
861 offset_(offset),
862 index_(index),
863 scale_factor_(scale_factor),
864 needs_null_check_(needs_null_check),
865 temp_(temp) {
866 DCHECK(kEmitCompilerReadBarrier);
867 DCHECK(kUseBakerReadBarrier);
868 }
869
Roland Levillain47b3ab22017-02-27 14:31:35 +0000870 const char* GetDescription() const OVERRIDE {
Roland Levillain54f869e2017-03-06 13:54:11 +0000871 return "LoadReferenceWithBakerReadBarrierSlowPathARMVIXL";
Roland Levillain47b3ab22017-02-27 14:31:35 +0000872 }
873
874 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
875 LocationSummary* locations = instruction_->GetLocations();
876 vixl32::Register ref_reg = RegisterFrom(ref_);
877 DCHECK(locations->CanCall());
878 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_reg.GetCode())) << ref_reg;
Roland Levillain47b3ab22017-02-27 14:31:35 +0000879 DCHECK(instruction_->IsInstanceFieldGet() ||
880 instruction_->IsStaticFieldGet() ||
881 instruction_->IsArrayGet() ||
882 instruction_->IsArraySet() ||
Roland Levillain47b3ab22017-02-27 14:31:35 +0000883 instruction_->IsInstanceOf() ||
884 instruction_->IsCheckCast() ||
885 (instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()) ||
886 (instruction_->IsInvokeStaticOrDirect() && instruction_->GetLocations()->Intrinsified()))
887 << "Unexpected instruction in read barrier marking slow path: "
888 << instruction_->DebugName();
889 // The read barrier instrumentation of object ArrayGet
890 // instructions does not support the HIntermediateAddress
891 // instruction.
892 DCHECK(!(instruction_->IsArrayGet() &&
893 instruction_->AsArrayGet()->GetArray()->IsIntermediateAddress()));
894
Roland Levillain54f869e2017-03-06 13:54:11 +0000895 // Temporary register `temp_`, used to store the lock word, must
896 // not be IP, as we may use it to emit the reference load (in the
897 // call to GenerateRawReferenceLoad below), and we need the lock
898 // word to still be in `temp_` after the reference load.
899 DCHECK(!temp_.Is(ip));
900
Roland Levillain47b3ab22017-02-27 14:31:35 +0000901 __ Bind(GetEntryLabel());
Roland Levillain54f869e2017-03-06 13:54:11 +0000902
903 // When using MaybeGenerateReadBarrierSlow, the read barrier call is
904 // inserted after the original load. However, in fast path based
905 // Baker's read barriers, we need to perform the load of
906 // mirror::Object::monitor_ *before* the original reference load.
907 // This load-load ordering is required by the read barrier.
Roland Levillainff487002017-03-07 16:50:01 +0000908 // The slow path (for Baker's algorithm) should look like:
Roland Levillain54f869e2017-03-06 13:54:11 +0000909 //
910 // uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
911 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
912 // HeapReference<mirror::Object> ref = *src; // Original reference load.
913 // bool is_gray = (rb_state == ReadBarrier::GrayState());
914 // if (is_gray) {
915 // ref = entrypoint(ref); // ref = ReadBarrier::Mark(ref); // Runtime entry point call.
916 // }
917 //
918 // Note: the original implementation in ReadBarrier::Barrier is
919 // slightly more complex as it performs additional checks that we do
920 // not do here for performance reasons.
921
Roland Levillain47b3ab22017-02-27 14:31:35 +0000922 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
Roland Levillain54f869e2017-03-06 13:54:11 +0000923
924 // /* int32_t */ monitor = obj->monitor_
925 uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
926 arm_codegen->GetAssembler()->LoadFromOffset(kLoadWord, temp_, obj_, monitor_offset);
927 if (needs_null_check_) {
928 codegen->MaybeRecordImplicitNullCheck(instruction_);
Roland Levillain47b3ab22017-02-27 14:31:35 +0000929 }
Roland Levillain54f869e2017-03-06 13:54:11 +0000930 // /* LockWord */ lock_word = LockWord(monitor)
931 static_assert(sizeof(LockWord) == sizeof(int32_t),
932 "art::LockWord and int32_t have different sizes.");
933
934 // Introduce a dependency on the lock_word including the rb_state,
935 // which shall prevent load-load reordering without using
936 // a memory barrier (which would be more expensive).
937 // `obj` is unchanged by this operation, but its value now depends
938 // on `temp`.
939 __ Add(obj_, obj_, Operand(temp_, ShiftType::LSR, 32));
940
941 // The actual reference load.
942 // A possible implicit null check has already been handled above.
943 arm_codegen->GenerateRawReferenceLoad(
944 instruction_, ref_, obj_, offset_, index_, scale_factor_, /* needs_null_check */ false);
945
946 // Mark the object `ref` when `obj` is gray.
947 //
948 // if (rb_state == ReadBarrier::GrayState())
949 // ref = ReadBarrier::Mark(ref);
950 //
951 // Given the numeric representation, it's enough to check the low bit of the
952 // rb_state. We do that by shifting the bit out of the lock word with LSRS
953 // which can be a 16-bit instruction unlike the TST immediate.
954 static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
955 static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
956 __ Lsrs(temp_, temp_, LockWord::kReadBarrierStateShift + 1);
957 __ B(cc, GetExitLabel()); // Carry flag is the last bit shifted out by LSRS.
958 GenerateReadBarrierMarkRuntimeCall(codegen);
959
Roland Levillain47b3ab22017-02-27 14:31:35 +0000960 __ B(GetExitLabel());
961 }
962
963 private:
Roland Levillain54f869e2017-03-06 13:54:11 +0000964 // The register containing the object holding the marked object reference field.
965 vixl32::Register obj_;
966 // The offset, index and scale factor to access the reference in `obj_`.
967 uint32_t offset_;
968 Location index_;
969 ScaleFactor scale_factor_;
970 // Is a null check required?
971 bool needs_null_check_;
972 // A temporary register used to hold the lock word of `obj_`.
973 vixl32::Register temp_;
Roland Levillain47b3ab22017-02-27 14:31:35 +0000974
Roland Levillain54f869e2017-03-06 13:54:11 +0000975 DISALLOW_COPY_AND_ASSIGN(LoadReferenceWithBakerReadBarrierSlowPathARMVIXL);
Roland Levillain47b3ab22017-02-27 14:31:35 +0000976};
977
Roland Levillain54f869e2017-03-06 13:54:11 +0000978// Slow path loading `obj`'s lock word, loading a reference from
979// object `*(obj + offset + (index << scale_factor))` into `ref`, and
980// marking `ref` if `obj` is gray according to the lock word (Baker
981// read barrier). If needed, this slow path also atomically updates
982// the field `obj.field` in the object `obj` holding this reference
983// after marking (contrary to
984// LoadReferenceWithBakerReadBarrierSlowPathARMVIXL above, which never
985// tries to update `obj.field`).
Roland Levillain47b3ab22017-02-27 14:31:35 +0000986//
987// This means that after the execution of this slow path, both `ref`
988// and `obj.field` will be up-to-date; i.e., after the flip, both will
989// hold the same to-space reference (unless another thread installed
990// another object reference (different from `ref`) in `obj.field`).
Roland Levillainba650a42017-03-06 13:52:32 +0000991//
Roland Levillain54f869e2017-03-06 13:54:11 +0000992// Argument `entrypoint` must be a register location holding the read
Roland Levillain6d729a72017-06-30 18:34:01 +0100993// barrier marking runtime entry point to be invoked or an empty
994// location; in the latter case, the read barrier marking runtime
995// entry point will be loaded by the slow path code itself.
Roland Levillain54f869e2017-03-06 13:54:11 +0000996class LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARMVIXL
997 : public ReadBarrierMarkSlowPathBaseARMVIXL {
Roland Levillain47b3ab22017-02-27 14:31:35 +0000998 public:
Roland Levillain6d729a72017-06-30 18:34:01 +0100999 LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARMVIXL(
1000 HInstruction* instruction,
1001 Location ref,
1002 vixl32::Register obj,
1003 uint32_t offset,
1004 Location index,
1005 ScaleFactor scale_factor,
1006 bool needs_null_check,
1007 vixl32::Register temp1,
1008 vixl32::Register temp2,
1009 Location entrypoint = Location::NoLocation())
Roland Levillain54f869e2017-03-06 13:54:11 +00001010 : ReadBarrierMarkSlowPathBaseARMVIXL(instruction, ref, entrypoint),
Roland Levillain47b3ab22017-02-27 14:31:35 +00001011 obj_(obj),
Roland Levillain54f869e2017-03-06 13:54:11 +00001012 offset_(offset),
1013 index_(index),
1014 scale_factor_(scale_factor),
1015 needs_null_check_(needs_null_check),
Roland Levillain47b3ab22017-02-27 14:31:35 +00001016 temp1_(temp1),
Roland Levillain54f869e2017-03-06 13:54:11 +00001017 temp2_(temp2) {
Roland Levillain47b3ab22017-02-27 14:31:35 +00001018 DCHECK(kEmitCompilerReadBarrier);
Roland Levillain54f869e2017-03-06 13:54:11 +00001019 DCHECK(kUseBakerReadBarrier);
Roland Levillain47b3ab22017-02-27 14:31:35 +00001020 }
1021
1022 const char* GetDescription() const OVERRIDE {
Roland Levillain54f869e2017-03-06 13:54:11 +00001023 return "LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARMVIXL";
Roland Levillain47b3ab22017-02-27 14:31:35 +00001024 }
1025
1026 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
1027 LocationSummary* locations = instruction_->GetLocations();
1028 vixl32::Register ref_reg = RegisterFrom(ref_);
1029 DCHECK(locations->CanCall());
1030 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_reg.GetCode())) << ref_reg;
Roland Levillain54f869e2017-03-06 13:54:11 +00001031 DCHECK_NE(ref_.reg(), LocationFrom(temp1_).reg());
1032
1033 // This slow path is only used by the UnsafeCASObject intrinsic at the moment.
Roland Levillain47b3ab22017-02-27 14:31:35 +00001034 DCHECK((instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()))
1035 << "Unexpected instruction in read barrier marking and field updating slow path: "
1036 << instruction_->DebugName();
1037 DCHECK(instruction_->GetLocations()->Intrinsified());
1038 DCHECK_EQ(instruction_->AsInvoke()->GetIntrinsic(), Intrinsics::kUnsafeCASObject);
Roland Levillain54f869e2017-03-06 13:54:11 +00001039 DCHECK_EQ(offset_, 0u);
1040 DCHECK_EQ(scale_factor_, ScaleFactor::TIMES_1);
1041 Location field_offset = index_;
1042 DCHECK(field_offset.IsRegisterPair()) << field_offset;
1043
1044 // Temporary register `temp1_`, used to store the lock word, must
1045 // not be IP, as we may use it to emit the reference load (in the
1046 // call to GenerateRawReferenceLoad below), and we need the lock
1047 // word to still be in `temp1_` after the reference load.
1048 DCHECK(!temp1_.Is(ip));
Roland Levillain47b3ab22017-02-27 14:31:35 +00001049
1050 __ Bind(GetEntryLabel());
1051
Roland Levillainff487002017-03-07 16:50:01 +00001052 // The implementation is similar to LoadReferenceWithBakerReadBarrierSlowPathARMVIXL's:
1053 //
1054 // uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
1055 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
1056 // HeapReference<mirror::Object> ref = *src; // Original reference load.
1057 // bool is_gray = (rb_state == ReadBarrier::GrayState());
1058 // if (is_gray) {
1059 // old_ref = ref;
1060 // ref = entrypoint(ref); // ref = ReadBarrier::Mark(ref); // Runtime entry point call.
1061 // compareAndSwapObject(obj, field_offset, old_ref, ref);
1062 // }
1063
Roland Levillain54f869e2017-03-06 13:54:11 +00001064 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
1065
1066 // /* int32_t */ monitor = obj->monitor_
1067 uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
1068 arm_codegen->GetAssembler()->LoadFromOffset(kLoadWord, temp1_, obj_, monitor_offset);
1069 if (needs_null_check_) {
1070 codegen->MaybeRecordImplicitNullCheck(instruction_);
1071 }
1072 // /* LockWord */ lock_word = LockWord(monitor)
1073 static_assert(sizeof(LockWord) == sizeof(int32_t),
1074 "art::LockWord and int32_t have different sizes.");
1075
1076 // Introduce a dependency on the lock_word including the rb_state,
1077 // which shall prevent load-load reordering without using
1078 // a memory barrier (which would be more expensive).
1079 // `obj` is unchanged by this operation, but its value now depends
1080 // on `temp`.
1081 __ Add(obj_, obj_, Operand(temp1_, ShiftType::LSR, 32));
1082
1083 // The actual reference load.
1084 // A possible implicit null check has already been handled above.
1085 arm_codegen->GenerateRawReferenceLoad(
1086 instruction_, ref_, obj_, offset_, index_, scale_factor_, /* needs_null_check */ false);
1087
1088 // Mark the object `ref` when `obj` is gray.
1089 //
1090 // if (rb_state == ReadBarrier::GrayState())
1091 // ref = ReadBarrier::Mark(ref);
1092 //
1093 // Given the numeric representation, it's enough to check the low bit of the
1094 // rb_state. We do that by shifting the bit out of the lock word with LSRS
1095 // which can be a 16-bit instruction unlike the TST immediate.
1096 static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
1097 static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
1098 __ Lsrs(temp1_, temp1_, LockWord::kReadBarrierStateShift + 1);
1099 __ B(cc, GetExitLabel()); // Carry flag is the last bit shifted out by LSRS.
1100
1101 // Save the old value of the reference before marking it.
Roland Levillain47b3ab22017-02-27 14:31:35 +00001102 // Note that we cannot use IP to save the old reference, as IP is
1103 // used internally by the ReadBarrierMarkRegX entry point, and we
1104 // need the old reference after the call to that entry point.
1105 DCHECK(!temp1_.Is(ip));
1106 __ Mov(temp1_, ref_reg);
Roland Levillain27b1f9c2017-01-17 16:56:34 +00001107
Roland Levillain54f869e2017-03-06 13:54:11 +00001108 GenerateReadBarrierMarkRuntimeCall(codegen);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001109
1110 // If the new reference is different from the old reference,
Roland Levillain54f869e2017-03-06 13:54:11 +00001111 // update the field in the holder (`*(obj_ + field_offset)`).
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001112 //
1113 // Note that this field could also hold a different object, if
1114 // another thread had concurrently changed it. In that case, the
Anton Kirilov349e61f2017-12-15 17:11:33 +00001115 // LDREX/CMP/BNE sequence of instructions in the compare-and-set
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001116 // (CAS) operation below would abort the CAS, leaving the field
1117 // as-is.
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001118 __ Cmp(temp1_, ref_reg);
Roland Levillain54f869e2017-03-06 13:54:11 +00001119 __ B(eq, GetExitLabel());
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001120
1121 // Update the the holder's field atomically. This may fail if
1122 // mutator updates before us, but it's OK. This is achieved
1123 // using a strong compare-and-set (CAS) operation with relaxed
1124 // memory synchronization ordering, where the expected value is
1125 // the old reference and the desired value is the new reference.
1126
1127 UseScratchRegisterScope temps(arm_codegen->GetVIXLAssembler());
1128 // Convenience aliases.
1129 vixl32::Register base = obj_;
1130 // The UnsafeCASObject intrinsic uses a register pair as field
1131 // offset ("long offset"), of which only the low part contains
1132 // data.
Roland Levillain54f869e2017-03-06 13:54:11 +00001133 vixl32::Register offset = LowRegisterFrom(field_offset);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001134 vixl32::Register expected = temp1_;
1135 vixl32::Register value = ref_reg;
1136 vixl32::Register tmp_ptr = temps.Acquire(); // Pointer to actual memory.
1137 vixl32::Register tmp = temp2_; // Value in memory.
1138
1139 __ Add(tmp_ptr, base, offset);
1140
1141 if (kPoisonHeapReferences) {
1142 arm_codegen->GetAssembler()->PoisonHeapReference(expected);
1143 if (value.Is(expected)) {
1144 // Do not poison `value`, as it is the same register as
1145 // `expected`, which has just been poisoned.
1146 } else {
1147 arm_codegen->GetAssembler()->PoisonHeapReference(value);
1148 }
1149 }
1150
1151 // do {
1152 // tmp = [r_ptr] - expected;
1153 // } while (tmp == 0 && failure([r_ptr] <- r_new_value));
1154
Anton Kirilov349e61f2017-12-15 17:11:33 +00001155 vixl32::Label loop_head, comparison_failed, exit_loop;
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001156 __ Bind(&loop_head);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001157 __ Ldrex(tmp, MemOperand(tmp_ptr));
Anton Kirilov349e61f2017-12-15 17:11:33 +00001158 __ Cmp(tmp, expected);
1159 __ B(ne, &comparison_failed, /* far_target */ false);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001160 __ Strex(tmp, value, MemOperand(tmp_ptr));
Anton Kirilov349e61f2017-12-15 17:11:33 +00001161 __ CompareAndBranchIfZero(tmp, &exit_loop, /* far_target */ false);
1162 __ B(&loop_head);
1163 __ Bind(&comparison_failed);
1164 __ Clrex();
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001165 __ Bind(&exit_loop);
1166
1167 if (kPoisonHeapReferences) {
1168 arm_codegen->GetAssembler()->UnpoisonHeapReference(expected);
1169 if (value.Is(expected)) {
1170 // Do not unpoison `value`, as it is the same register as
1171 // `expected`, which has just been unpoisoned.
1172 } else {
1173 arm_codegen->GetAssembler()->UnpoisonHeapReference(value);
1174 }
1175 }
1176
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001177 __ B(GetExitLabel());
1178 }
1179
1180 private:
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001181 // The register containing the object holding the marked object reference field.
1182 const vixl32::Register obj_;
Roland Levillain54f869e2017-03-06 13:54:11 +00001183 // The offset, index and scale factor to access the reference in `obj_`.
1184 uint32_t offset_;
1185 Location index_;
1186 ScaleFactor scale_factor_;
1187 // Is a null check required?
1188 bool needs_null_check_;
1189 // A temporary register used to hold the lock word of `obj_`; and
1190 // also to hold the original reference value, when the reference is
1191 // marked.
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001192 const vixl32::Register temp1_;
Roland Levillain54f869e2017-03-06 13:54:11 +00001193 // A temporary register used in the implementation of the CAS, to
1194 // update the object's reference field.
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001195 const vixl32::Register temp2_;
1196
Roland Levillain54f869e2017-03-06 13:54:11 +00001197 DISALLOW_COPY_AND_ASSIGN(LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARMVIXL);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001198};
1199
1200// Slow path generating a read barrier for a heap reference.
1201class ReadBarrierForHeapReferenceSlowPathARMVIXL : public SlowPathCodeARMVIXL {
1202 public:
1203 ReadBarrierForHeapReferenceSlowPathARMVIXL(HInstruction* instruction,
1204 Location out,
1205 Location ref,
1206 Location obj,
1207 uint32_t offset,
1208 Location index)
1209 : SlowPathCodeARMVIXL(instruction),
1210 out_(out),
1211 ref_(ref),
1212 obj_(obj),
1213 offset_(offset),
1214 index_(index) {
1215 DCHECK(kEmitCompilerReadBarrier);
1216 // If `obj` is equal to `out` or `ref`, it means the initial object
1217 // has been overwritten by (or after) the heap object reference load
1218 // to be instrumented, e.g.:
1219 //
1220 // __ LoadFromOffset(kLoadWord, out, out, offset);
1221 // codegen_->GenerateReadBarrierSlow(instruction, out_loc, out_loc, out_loc, offset);
1222 //
1223 // In that case, we have lost the information about the original
1224 // object, and the emitted read barrier cannot work properly.
1225 DCHECK(!obj.Equals(out)) << "obj=" << obj << " out=" << out;
1226 DCHECK(!obj.Equals(ref)) << "obj=" << obj << " ref=" << ref;
1227 }
1228
1229 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
1230 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
1231 LocationSummary* locations = instruction_->GetLocations();
1232 vixl32::Register reg_out = RegisterFrom(out_);
1233 DCHECK(locations->CanCall());
1234 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out.GetCode()));
1235 DCHECK(instruction_->IsInstanceFieldGet() ||
1236 instruction_->IsStaticFieldGet() ||
1237 instruction_->IsArrayGet() ||
1238 instruction_->IsInstanceOf() ||
1239 instruction_->IsCheckCast() ||
Andreas Gamped9911ee2017-03-27 13:27:24 -07001240 (instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()))
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001241 << "Unexpected instruction in read barrier for heap reference slow path: "
1242 << instruction_->DebugName();
1243 // The read barrier instrumentation of object ArrayGet
1244 // instructions does not support the HIntermediateAddress
1245 // instruction.
1246 DCHECK(!(instruction_->IsArrayGet() &&
1247 instruction_->AsArrayGet()->GetArray()->IsIntermediateAddress()));
1248
1249 __ Bind(GetEntryLabel());
1250 SaveLiveRegisters(codegen, locations);
1251
1252 // We may have to change the index's value, but as `index_` is a
1253 // constant member (like other "inputs" of this slow path),
1254 // introduce a copy of it, `index`.
1255 Location index = index_;
1256 if (index_.IsValid()) {
1257 // Handle `index_` for HArrayGet and UnsafeGetObject/UnsafeGetObjectVolatile intrinsics.
1258 if (instruction_->IsArrayGet()) {
1259 // Compute the actual memory offset and store it in `index`.
1260 vixl32::Register index_reg = RegisterFrom(index_);
1261 DCHECK(locations->GetLiveRegisters()->ContainsCoreRegister(index_reg.GetCode()));
1262 if (codegen->IsCoreCalleeSaveRegister(index_reg.GetCode())) {
1263 // We are about to change the value of `index_reg` (see the
Roland Levillain9983e302017-07-14 14:34:22 +01001264 // calls to art::arm::ArmVIXLMacroAssembler::Lsl and
1265 // art::arm::ArmVIXLMacroAssembler::Add below), but it has
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001266 // not been saved by the previous call to
1267 // art::SlowPathCode::SaveLiveRegisters, as it is a
1268 // callee-save register --
1269 // art::SlowPathCode::SaveLiveRegisters does not consider
1270 // callee-save registers, as it has been designed with the
1271 // assumption that callee-save registers are supposed to be
1272 // handled by the called function. So, as a callee-save
1273 // register, `index_reg` _would_ eventually be saved onto
1274 // the stack, but it would be too late: we would have
1275 // changed its value earlier. Therefore, we manually save
1276 // it here into another freely available register,
1277 // `free_reg`, chosen of course among the caller-save
1278 // registers (as a callee-save `free_reg` register would
1279 // exhibit the same problem).
1280 //
1281 // Note we could have requested a temporary register from
1282 // the register allocator instead; but we prefer not to, as
1283 // this is a slow path, and we know we can find a
1284 // caller-save register that is available.
1285 vixl32::Register free_reg = FindAvailableCallerSaveRegister(codegen);
1286 __ Mov(free_reg, index_reg);
1287 index_reg = free_reg;
1288 index = LocationFrom(index_reg);
1289 } else {
1290 // The initial register stored in `index_` has already been
1291 // saved in the call to art::SlowPathCode::SaveLiveRegisters
1292 // (as it is not a callee-save register), so we can freely
1293 // use it.
1294 }
1295 // Shifting the index value contained in `index_reg` by the scale
1296 // factor (2) cannot overflow in practice, as the runtime is
1297 // unable to allocate object arrays with a size larger than
1298 // 2^26 - 1 (that is, 2^28 - 4 bytes).
1299 __ Lsl(index_reg, index_reg, TIMES_4);
1300 static_assert(
1301 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
1302 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
1303 __ Add(index_reg, index_reg, offset_);
1304 } else {
1305 // In the case of the UnsafeGetObject/UnsafeGetObjectVolatile
1306 // intrinsics, `index_` is not shifted by a scale factor of 2
1307 // (as in the case of ArrayGet), as it is actually an offset
1308 // to an object field within an object.
1309 DCHECK(instruction_->IsInvoke()) << instruction_->DebugName();
1310 DCHECK(instruction_->GetLocations()->Intrinsified());
1311 DCHECK((instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObject) ||
1312 (instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile))
1313 << instruction_->AsInvoke()->GetIntrinsic();
1314 DCHECK_EQ(offset_, 0U);
1315 DCHECK(index_.IsRegisterPair());
1316 // UnsafeGet's offset location is a register pair, the low
1317 // part contains the correct offset.
1318 index = index_.ToLow();
1319 }
1320 }
1321
1322 // We're moving two or three locations to locations that could
1323 // overlap, so we need a parallel move resolver.
1324 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Vladimir Markoca6fff82017-10-03 14:49:14 +01001325 HParallelMove parallel_move(codegen->GetGraph()->GetAllocator());
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001326 parallel_move.AddMove(ref_,
1327 LocationFrom(calling_convention.GetRegisterAt(0)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001328 DataType::Type::kReference,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001329 nullptr);
1330 parallel_move.AddMove(obj_,
1331 LocationFrom(calling_convention.GetRegisterAt(1)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001332 DataType::Type::kReference,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001333 nullptr);
1334 if (index.IsValid()) {
1335 parallel_move.AddMove(index,
1336 LocationFrom(calling_convention.GetRegisterAt(2)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001337 DataType::Type::kInt32,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001338 nullptr);
1339 codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
1340 } else {
1341 codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
1342 __ Mov(calling_convention.GetRegisterAt(2), offset_);
1343 }
1344 arm_codegen->InvokeRuntime(kQuickReadBarrierSlow, instruction_, instruction_->GetDexPc(), this);
1345 CheckEntrypointTypes<
1346 kQuickReadBarrierSlow, mirror::Object*, mirror::Object*, mirror::Object*, uint32_t>();
1347 arm_codegen->Move32(out_, LocationFrom(r0));
1348
1349 RestoreLiveRegisters(codegen, locations);
1350 __ B(GetExitLabel());
1351 }
1352
1353 const char* GetDescription() const OVERRIDE {
1354 return "ReadBarrierForHeapReferenceSlowPathARMVIXL";
1355 }
1356
1357 private:
1358 vixl32::Register FindAvailableCallerSaveRegister(CodeGenerator* codegen) {
1359 uint32_t ref = RegisterFrom(ref_).GetCode();
1360 uint32_t obj = RegisterFrom(obj_).GetCode();
1361 for (uint32_t i = 0, e = codegen->GetNumberOfCoreRegisters(); i < e; ++i) {
1362 if (i != ref && i != obj && !codegen->IsCoreCalleeSaveRegister(i)) {
1363 return vixl32::Register(i);
1364 }
1365 }
1366 // We shall never fail to find a free caller-save register, as
1367 // there are more than two core caller-save registers on ARM
1368 // (meaning it is possible to find one which is different from
1369 // `ref` and `obj`).
1370 DCHECK_GT(codegen->GetNumberOfCoreCallerSaveRegisters(), 2u);
1371 LOG(FATAL) << "Could not find a free caller-save register";
1372 UNREACHABLE();
1373 }
1374
1375 const Location out_;
1376 const Location ref_;
1377 const Location obj_;
1378 const uint32_t offset_;
1379 // An additional location containing an index to an array.
1380 // Only used for HArrayGet and the UnsafeGetObject &
1381 // UnsafeGetObjectVolatile intrinsics.
1382 const Location index_;
1383
1384 DISALLOW_COPY_AND_ASSIGN(ReadBarrierForHeapReferenceSlowPathARMVIXL);
1385};
1386
1387// Slow path generating a read barrier for a GC root.
1388class ReadBarrierForRootSlowPathARMVIXL : public SlowPathCodeARMVIXL {
1389 public:
1390 ReadBarrierForRootSlowPathARMVIXL(HInstruction* instruction, Location out, Location root)
1391 : SlowPathCodeARMVIXL(instruction), out_(out), root_(root) {
1392 DCHECK(kEmitCompilerReadBarrier);
1393 }
1394
1395 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
1396 LocationSummary* locations = instruction_->GetLocations();
1397 vixl32::Register reg_out = RegisterFrom(out_);
1398 DCHECK(locations->CanCall());
1399 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out.GetCode()));
1400 DCHECK(instruction_->IsLoadClass() || instruction_->IsLoadString())
1401 << "Unexpected instruction in read barrier for GC root slow path: "
1402 << instruction_->DebugName();
1403
1404 __ Bind(GetEntryLabel());
1405 SaveLiveRegisters(codegen, locations);
1406
1407 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1408 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
1409 arm_codegen->Move32(LocationFrom(calling_convention.GetRegisterAt(0)), root_);
1410 arm_codegen->InvokeRuntime(kQuickReadBarrierForRootSlow,
1411 instruction_,
1412 instruction_->GetDexPc(),
1413 this);
1414 CheckEntrypointTypes<kQuickReadBarrierForRootSlow, mirror::Object*, GcRoot<mirror::Object>*>();
1415 arm_codegen->Move32(out_, LocationFrom(r0));
1416
1417 RestoreLiveRegisters(codegen, locations);
1418 __ B(GetExitLabel());
1419 }
1420
1421 const char* GetDescription() const OVERRIDE { return "ReadBarrierForRootSlowPathARMVIXL"; }
1422
1423 private:
1424 const Location out_;
1425 const Location root_;
1426
1427 DISALLOW_COPY_AND_ASSIGN(ReadBarrierForRootSlowPathARMVIXL);
1428};
Scott Wakelingc34dba72016-10-03 10:14:44 +01001429
Scott Wakelingfe885462016-09-22 10:24:38 +01001430inline vixl32::Condition ARMCondition(IfCondition cond) {
1431 switch (cond) {
1432 case kCondEQ: return eq;
1433 case kCondNE: return ne;
1434 case kCondLT: return lt;
1435 case kCondLE: return le;
1436 case kCondGT: return gt;
1437 case kCondGE: return ge;
1438 case kCondB: return lo;
1439 case kCondBE: return ls;
1440 case kCondA: return hi;
1441 case kCondAE: return hs;
1442 }
1443 LOG(FATAL) << "Unreachable";
1444 UNREACHABLE();
1445}
1446
1447// Maps signed condition to unsigned condition.
1448inline vixl32::Condition ARMUnsignedCondition(IfCondition cond) {
1449 switch (cond) {
1450 case kCondEQ: return eq;
1451 case kCondNE: return ne;
1452 // Signed to unsigned.
1453 case kCondLT: return lo;
1454 case kCondLE: return ls;
1455 case kCondGT: return hi;
1456 case kCondGE: return hs;
1457 // Unsigned remain unchanged.
1458 case kCondB: return lo;
1459 case kCondBE: return ls;
1460 case kCondA: return hi;
1461 case kCondAE: return hs;
1462 }
1463 LOG(FATAL) << "Unreachable";
1464 UNREACHABLE();
1465}
1466
1467inline vixl32::Condition ARMFPCondition(IfCondition cond, bool gt_bias) {
1468 // The ARM condition codes can express all the necessary branches, see the
1469 // "Meaning (floating-point)" column in the table A8-1 of the ARMv7 reference manual.
1470 // There is no dex instruction or HIR that would need the missing conditions
1471 // "equal or unordered" or "not equal".
1472 switch (cond) {
1473 case kCondEQ: return eq;
1474 case kCondNE: return ne /* unordered */;
1475 case kCondLT: return gt_bias ? cc : lt /* unordered */;
1476 case kCondLE: return gt_bias ? ls : le /* unordered */;
1477 case kCondGT: return gt_bias ? hi /* unordered */ : gt;
1478 case kCondGE: return gt_bias ? cs /* unordered */ : ge;
1479 default:
1480 LOG(FATAL) << "UNREACHABLE";
1481 UNREACHABLE();
1482 }
1483}
1484
Anton Kirilov74234da2017-01-13 14:42:47 +00001485inline ShiftType ShiftFromOpKind(HDataProcWithShifterOp::OpKind op_kind) {
1486 switch (op_kind) {
1487 case HDataProcWithShifterOp::kASR: return ShiftType::ASR;
1488 case HDataProcWithShifterOp::kLSL: return ShiftType::LSL;
1489 case HDataProcWithShifterOp::kLSR: return ShiftType::LSR;
1490 default:
1491 LOG(FATAL) << "Unexpected op kind " << op_kind;
1492 UNREACHABLE();
1493 }
1494}
1495
Scott Wakelingfe885462016-09-22 10:24:38 +01001496void CodeGeneratorARMVIXL::DumpCoreRegister(std::ostream& stream, int reg) const {
1497 stream << vixl32::Register(reg);
1498}
1499
1500void CodeGeneratorARMVIXL::DumpFloatingPointRegister(std::ostream& stream, int reg) const {
1501 stream << vixl32::SRegister(reg);
1502}
1503
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001504static uint32_t ComputeSRegisterListMask(const SRegisterList& regs) {
Scott Wakelingfe885462016-09-22 10:24:38 +01001505 uint32_t mask = 0;
1506 for (uint32_t i = regs.GetFirstSRegister().GetCode();
1507 i <= regs.GetLastSRegister().GetCode();
1508 ++i) {
1509 mask |= (1 << i);
1510 }
1511 return mask;
1512}
1513
Artem Serovd4cc5b22016-11-04 11:19:09 +00001514// Saves the register in the stack. Returns the size taken on stack.
1515size_t CodeGeneratorARMVIXL::SaveCoreRegister(size_t stack_index ATTRIBUTE_UNUSED,
1516 uint32_t reg_id ATTRIBUTE_UNUSED) {
1517 TODO_VIXL32(FATAL);
1518 return 0;
1519}
1520
1521// Restores the register from the stack. Returns the size taken on stack.
1522size_t CodeGeneratorARMVIXL::RestoreCoreRegister(size_t stack_index ATTRIBUTE_UNUSED,
1523 uint32_t reg_id ATTRIBUTE_UNUSED) {
1524 TODO_VIXL32(FATAL);
1525 return 0;
1526}
1527
1528size_t CodeGeneratorARMVIXL::SaveFloatingPointRegister(size_t stack_index ATTRIBUTE_UNUSED,
1529 uint32_t reg_id ATTRIBUTE_UNUSED) {
1530 TODO_VIXL32(FATAL);
1531 return 0;
1532}
1533
1534size_t CodeGeneratorARMVIXL::RestoreFloatingPointRegister(size_t stack_index ATTRIBUTE_UNUSED,
1535 uint32_t reg_id ATTRIBUTE_UNUSED) {
1536 TODO_VIXL32(FATAL);
1537 return 0;
Anton Kirilove28d9ae2016-10-25 18:17:23 +01001538}
1539
Anton Kirilov74234da2017-01-13 14:42:47 +00001540static void GenerateDataProcInstruction(HInstruction::InstructionKind kind,
1541 vixl32::Register out,
1542 vixl32::Register first,
1543 const Operand& second,
1544 CodeGeneratorARMVIXL* codegen) {
1545 if (second.IsImmediate() && second.GetImmediate() == 0) {
1546 const Operand in = kind == HInstruction::kAnd
1547 ? Operand(0)
1548 : Operand(first);
1549
1550 __ Mov(out, in);
1551 } else {
1552 switch (kind) {
1553 case HInstruction::kAdd:
1554 __ Add(out, first, second);
1555 break;
1556 case HInstruction::kAnd:
1557 __ And(out, first, second);
1558 break;
1559 case HInstruction::kOr:
1560 __ Orr(out, first, second);
1561 break;
1562 case HInstruction::kSub:
1563 __ Sub(out, first, second);
1564 break;
1565 case HInstruction::kXor:
1566 __ Eor(out, first, second);
1567 break;
1568 default:
1569 LOG(FATAL) << "Unexpected instruction kind: " << kind;
1570 UNREACHABLE();
1571 }
1572 }
1573}
1574
1575static void GenerateDataProc(HInstruction::InstructionKind kind,
1576 const Location& out,
1577 const Location& first,
1578 const Operand& second_lo,
1579 const Operand& second_hi,
1580 CodeGeneratorARMVIXL* codegen) {
1581 const vixl32::Register first_hi = HighRegisterFrom(first);
1582 const vixl32::Register first_lo = LowRegisterFrom(first);
1583 const vixl32::Register out_hi = HighRegisterFrom(out);
1584 const vixl32::Register out_lo = LowRegisterFrom(out);
1585
1586 if (kind == HInstruction::kAdd) {
1587 __ Adds(out_lo, first_lo, second_lo);
1588 __ Adc(out_hi, first_hi, second_hi);
1589 } else if (kind == HInstruction::kSub) {
1590 __ Subs(out_lo, first_lo, second_lo);
1591 __ Sbc(out_hi, first_hi, second_hi);
1592 } else {
1593 GenerateDataProcInstruction(kind, out_lo, first_lo, second_lo, codegen);
1594 GenerateDataProcInstruction(kind, out_hi, first_hi, second_hi, codegen);
1595 }
1596}
1597
1598static Operand GetShifterOperand(vixl32::Register rm, ShiftType shift, uint32_t shift_imm) {
1599 return shift_imm == 0 ? Operand(rm) : Operand(rm, shift, shift_imm);
1600}
1601
1602static void GenerateLongDataProc(HDataProcWithShifterOp* instruction,
1603 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001604 DCHECK_EQ(instruction->GetType(), DataType::Type::kInt64);
Anton Kirilov74234da2017-01-13 14:42:47 +00001605 DCHECK(HDataProcWithShifterOp::IsShiftOp(instruction->GetOpKind()));
1606
1607 const LocationSummary* const locations = instruction->GetLocations();
1608 const uint32_t shift_value = instruction->GetShiftAmount();
1609 const HInstruction::InstructionKind kind = instruction->GetInstrKind();
1610 const Location first = locations->InAt(0);
1611 const Location second = locations->InAt(1);
1612 const Location out = locations->Out();
1613 const vixl32::Register first_hi = HighRegisterFrom(first);
1614 const vixl32::Register first_lo = LowRegisterFrom(first);
1615 const vixl32::Register out_hi = HighRegisterFrom(out);
1616 const vixl32::Register out_lo = LowRegisterFrom(out);
1617 const vixl32::Register second_hi = HighRegisterFrom(second);
1618 const vixl32::Register second_lo = LowRegisterFrom(second);
1619 const ShiftType shift = ShiftFromOpKind(instruction->GetOpKind());
1620
1621 if (shift_value >= 32) {
1622 if (shift == ShiftType::LSL) {
1623 GenerateDataProcInstruction(kind,
1624 out_hi,
1625 first_hi,
1626 Operand(second_lo, ShiftType::LSL, shift_value - 32),
1627 codegen);
1628 GenerateDataProcInstruction(kind, out_lo, first_lo, 0, codegen);
1629 } else if (shift == ShiftType::ASR) {
1630 GenerateDataProc(kind,
1631 out,
1632 first,
1633 GetShifterOperand(second_hi, ShiftType::ASR, shift_value - 32),
1634 Operand(second_hi, ShiftType::ASR, 31),
1635 codegen);
1636 } else {
1637 DCHECK_EQ(shift, ShiftType::LSR);
1638 GenerateDataProc(kind,
1639 out,
1640 first,
1641 GetShifterOperand(second_hi, ShiftType::LSR, shift_value - 32),
1642 0,
1643 codegen);
1644 }
1645 } else {
1646 DCHECK_GT(shift_value, 1U);
1647 DCHECK_LT(shift_value, 32U);
1648
1649 UseScratchRegisterScope temps(codegen->GetVIXLAssembler());
1650
1651 if (shift == ShiftType::LSL) {
1652 // We are not doing this for HInstruction::kAdd because the output will require
1653 // Location::kOutputOverlap; not applicable to other cases.
1654 if (kind == HInstruction::kOr || kind == HInstruction::kXor) {
1655 GenerateDataProcInstruction(kind,
1656 out_hi,
1657 first_hi,
1658 Operand(second_hi, ShiftType::LSL, shift_value),
1659 codegen);
1660 GenerateDataProcInstruction(kind,
1661 out_hi,
1662 out_hi,
1663 Operand(second_lo, ShiftType::LSR, 32 - shift_value),
1664 codegen);
1665 GenerateDataProcInstruction(kind,
1666 out_lo,
1667 first_lo,
1668 Operand(second_lo, ShiftType::LSL, shift_value),
1669 codegen);
1670 } else {
1671 const vixl32::Register temp = temps.Acquire();
1672
1673 __ Lsl(temp, second_hi, shift_value);
1674 __ Orr(temp, temp, Operand(second_lo, ShiftType::LSR, 32 - shift_value));
1675 GenerateDataProc(kind,
1676 out,
1677 first,
1678 Operand(second_lo, ShiftType::LSL, shift_value),
1679 temp,
1680 codegen);
1681 }
1682 } else {
1683 DCHECK(shift == ShiftType::ASR || shift == ShiftType::LSR);
1684
1685 // We are not doing this for HInstruction::kAdd because the output will require
1686 // Location::kOutputOverlap; not applicable to other cases.
1687 if (kind == HInstruction::kOr || kind == HInstruction::kXor) {
1688 GenerateDataProcInstruction(kind,
1689 out_lo,
1690 first_lo,
1691 Operand(second_lo, ShiftType::LSR, shift_value),
1692 codegen);
1693 GenerateDataProcInstruction(kind,
1694 out_lo,
1695 out_lo,
1696 Operand(second_hi, ShiftType::LSL, 32 - shift_value),
1697 codegen);
1698 GenerateDataProcInstruction(kind,
1699 out_hi,
1700 first_hi,
1701 Operand(second_hi, shift, shift_value),
1702 codegen);
1703 } else {
1704 const vixl32::Register temp = temps.Acquire();
1705
1706 __ Lsr(temp, second_lo, shift_value);
1707 __ Orr(temp, temp, Operand(second_hi, ShiftType::LSL, 32 - shift_value));
1708 GenerateDataProc(kind,
1709 out,
1710 first,
1711 temp,
1712 Operand(second_hi, shift, shift_value),
1713 codegen);
1714 }
1715 }
1716 }
1717}
1718
Donghui Bai426b49c2016-11-08 14:55:38 +08001719static void GenerateVcmp(HInstruction* instruction, CodeGeneratorARMVIXL* codegen) {
1720 const Location rhs_loc = instruction->GetLocations()->InAt(1);
1721 if (rhs_loc.IsConstant()) {
1722 // 0.0 is the only immediate that can be encoded directly in
1723 // a VCMP instruction.
1724 //
1725 // Both the JLS (section 15.20.1) and the JVMS (section 6.5)
1726 // specify that in a floating-point comparison, positive zero
1727 // and negative zero are considered equal, so we can use the
1728 // literal 0.0 for both cases here.
1729 //
1730 // Note however that some methods (Float.equal, Float.compare,
1731 // Float.compareTo, Double.equal, Double.compare,
1732 // Double.compareTo, Math.max, Math.min, StrictMath.max,
1733 // StrictMath.min) consider 0.0 to be (strictly) greater than
1734 // -0.0. So if we ever translate calls to these methods into a
1735 // HCompare instruction, we must handle the -0.0 case with
1736 // care here.
1737 DCHECK(rhs_loc.GetConstant()->IsArithmeticZero());
1738
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001739 const DataType::Type type = instruction->InputAt(0)->GetType();
Donghui Bai426b49c2016-11-08 14:55:38 +08001740
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001741 if (type == DataType::Type::kFloat32) {
Donghui Bai426b49c2016-11-08 14:55:38 +08001742 __ Vcmp(F32, InputSRegisterAt(instruction, 0), 0.0);
1743 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001744 DCHECK_EQ(type, DataType::Type::kFloat64);
Donghui Bai426b49c2016-11-08 14:55:38 +08001745 __ Vcmp(F64, InputDRegisterAt(instruction, 0), 0.0);
1746 }
1747 } else {
1748 __ Vcmp(InputVRegisterAt(instruction, 0), InputVRegisterAt(instruction, 1));
1749 }
1750}
1751
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001752static int64_t AdjustConstantForCondition(int64_t value,
1753 IfCondition* condition,
1754 IfCondition* opposite) {
1755 if (value == 1) {
1756 if (*condition == kCondB) {
1757 value = 0;
1758 *condition = kCondEQ;
1759 *opposite = kCondNE;
1760 } else if (*condition == kCondAE) {
1761 value = 0;
1762 *condition = kCondNE;
1763 *opposite = kCondEQ;
1764 }
1765 } else if (value == -1) {
1766 if (*condition == kCondGT) {
1767 value = 0;
1768 *condition = kCondGE;
1769 *opposite = kCondLT;
1770 } else if (*condition == kCondLE) {
1771 value = 0;
1772 *condition = kCondLT;
1773 *opposite = kCondGE;
1774 }
1775 }
1776
1777 return value;
1778}
1779
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001780static std::pair<vixl32::Condition, vixl32::Condition> GenerateLongTestConstant(
1781 HCondition* condition,
1782 bool invert,
1783 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001784 DCHECK_EQ(condition->GetLeft()->GetType(), DataType::Type::kInt64);
Donghui Bai426b49c2016-11-08 14:55:38 +08001785
1786 const LocationSummary* const locations = condition->GetLocations();
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001787 IfCondition cond = condition->GetCondition();
1788 IfCondition opposite = condition->GetOppositeCondition();
1789
1790 if (invert) {
1791 std::swap(cond, opposite);
1792 }
1793
1794 std::pair<vixl32::Condition, vixl32::Condition> ret(eq, ne);
Donghui Bai426b49c2016-11-08 14:55:38 +08001795 const Location left = locations->InAt(0);
1796 const Location right = locations->InAt(1);
1797
1798 DCHECK(right.IsConstant());
1799
1800 const vixl32::Register left_high = HighRegisterFrom(left);
1801 const vixl32::Register left_low = LowRegisterFrom(left);
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001802 int64_t value = AdjustConstantForCondition(Int64ConstantFrom(right), &cond, &opposite);
1803 UseScratchRegisterScope temps(codegen->GetVIXLAssembler());
1804
1805 // Comparisons against 0 are common enough to deserve special attention.
1806 if (value == 0) {
1807 switch (cond) {
1808 case kCondNE:
1809 // x > 0 iff x != 0 when the comparison is unsigned.
1810 case kCondA:
1811 ret = std::make_pair(ne, eq);
1812 FALLTHROUGH_INTENDED;
1813 case kCondEQ:
1814 // x <= 0 iff x == 0 when the comparison is unsigned.
1815 case kCondBE:
1816 __ Orrs(temps.Acquire(), left_low, left_high);
1817 return ret;
1818 case kCondLT:
1819 case kCondGE:
1820 __ Cmp(left_high, 0);
1821 return std::make_pair(ARMCondition(cond), ARMCondition(opposite));
1822 // Trivially true or false.
1823 case kCondB:
1824 ret = std::make_pair(ne, eq);
1825 FALLTHROUGH_INTENDED;
1826 case kCondAE:
1827 __ Cmp(left_low, left_low);
1828 return ret;
1829 default:
1830 break;
1831 }
1832 }
Donghui Bai426b49c2016-11-08 14:55:38 +08001833
1834 switch (cond) {
1835 case kCondEQ:
1836 case kCondNE:
1837 case kCondB:
1838 case kCondBE:
1839 case kCondA:
1840 case kCondAE: {
Anton Kirilov23b752b2017-07-20 14:40:44 +01001841 const uint32_t value_low = Low32Bits(value);
1842 Operand operand_low(value_low);
1843
Donghui Bai426b49c2016-11-08 14:55:38 +08001844 __ Cmp(left_high, High32Bits(value));
1845
Anton Kirilov23b752b2017-07-20 14:40:44 +01001846 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
1847 // we must ensure that the operands corresponding to the least significant
1848 // halves of the inputs fit into a 16-bit CMP encoding.
1849 if (!left_low.IsLow() || !IsUint<8>(value_low)) {
1850 operand_low = Operand(temps.Acquire());
1851 __ Mov(LeaveFlags, operand_low.GetBaseRegister(), value_low);
1852 }
1853
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001854 // We use the scope because of the IT block that follows.
Donghui Bai426b49c2016-11-08 14:55:38 +08001855 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
1856 2 * vixl32::k16BitT32InstructionSizeInBytes,
1857 CodeBufferCheckScope::kExactSize);
1858
1859 __ it(eq);
Anton Kirilov23b752b2017-07-20 14:40:44 +01001860 __ cmp(eq, left_low, operand_low);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001861 ret = std::make_pair(ARMUnsignedCondition(cond), ARMUnsignedCondition(opposite));
Donghui Bai426b49c2016-11-08 14:55:38 +08001862 break;
1863 }
1864 case kCondLE:
1865 case kCondGT:
1866 // Trivially true or false.
1867 if (value == std::numeric_limits<int64_t>::max()) {
1868 __ Cmp(left_low, left_low);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001869 ret = cond == kCondLE ? std::make_pair(eq, ne) : std::make_pair(ne, eq);
Donghui Bai426b49c2016-11-08 14:55:38 +08001870 break;
1871 }
1872
1873 if (cond == kCondLE) {
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001874 DCHECK_EQ(opposite, kCondGT);
Donghui Bai426b49c2016-11-08 14:55:38 +08001875 cond = kCondLT;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001876 opposite = kCondGE;
Donghui Bai426b49c2016-11-08 14:55:38 +08001877 } else {
1878 DCHECK_EQ(cond, kCondGT);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001879 DCHECK_EQ(opposite, kCondLE);
Donghui Bai426b49c2016-11-08 14:55:38 +08001880 cond = kCondGE;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001881 opposite = kCondLT;
Donghui Bai426b49c2016-11-08 14:55:38 +08001882 }
1883
1884 value++;
1885 FALLTHROUGH_INTENDED;
1886 case kCondGE:
1887 case kCondLT: {
Donghui Bai426b49c2016-11-08 14:55:38 +08001888 __ Cmp(left_low, Low32Bits(value));
1889 __ Sbcs(temps.Acquire(), left_high, High32Bits(value));
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001890 ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite));
Donghui Bai426b49c2016-11-08 14:55:38 +08001891 break;
1892 }
1893 default:
1894 LOG(FATAL) << "Unreachable";
1895 UNREACHABLE();
1896 }
1897
1898 return ret;
1899}
1900
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001901static std::pair<vixl32::Condition, vixl32::Condition> GenerateLongTest(
1902 HCondition* condition,
1903 bool invert,
1904 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001905 DCHECK_EQ(condition->GetLeft()->GetType(), DataType::Type::kInt64);
Donghui Bai426b49c2016-11-08 14:55:38 +08001906
1907 const LocationSummary* const locations = condition->GetLocations();
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001908 IfCondition cond = condition->GetCondition();
1909 IfCondition opposite = condition->GetOppositeCondition();
1910
1911 if (invert) {
1912 std::swap(cond, opposite);
1913 }
1914
1915 std::pair<vixl32::Condition, vixl32::Condition> ret(eq, ne);
Donghui Bai426b49c2016-11-08 14:55:38 +08001916 Location left = locations->InAt(0);
1917 Location right = locations->InAt(1);
1918
1919 DCHECK(right.IsRegisterPair());
1920
1921 switch (cond) {
1922 case kCondEQ:
1923 case kCondNE:
1924 case kCondB:
1925 case kCondBE:
1926 case kCondA:
1927 case kCondAE: {
1928 __ Cmp(HighRegisterFrom(left), HighRegisterFrom(right));
1929
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001930 // We use the scope because of the IT block that follows.
Donghui Bai426b49c2016-11-08 14:55:38 +08001931 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
1932 2 * vixl32::k16BitT32InstructionSizeInBytes,
1933 CodeBufferCheckScope::kExactSize);
1934
1935 __ it(eq);
1936 __ cmp(eq, LowRegisterFrom(left), LowRegisterFrom(right));
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001937 ret = std::make_pair(ARMUnsignedCondition(cond), ARMUnsignedCondition(opposite));
Donghui Bai426b49c2016-11-08 14:55:38 +08001938 break;
1939 }
1940 case kCondLE:
1941 case kCondGT:
1942 if (cond == kCondLE) {
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001943 DCHECK_EQ(opposite, kCondGT);
Donghui Bai426b49c2016-11-08 14:55:38 +08001944 cond = kCondGE;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001945 opposite = kCondLT;
Donghui Bai426b49c2016-11-08 14:55:38 +08001946 } else {
1947 DCHECK_EQ(cond, kCondGT);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001948 DCHECK_EQ(opposite, kCondLE);
Donghui Bai426b49c2016-11-08 14:55:38 +08001949 cond = kCondLT;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001950 opposite = kCondGE;
Donghui Bai426b49c2016-11-08 14:55:38 +08001951 }
1952
1953 std::swap(left, right);
1954 FALLTHROUGH_INTENDED;
1955 case kCondGE:
1956 case kCondLT: {
1957 UseScratchRegisterScope temps(codegen->GetVIXLAssembler());
1958
1959 __ Cmp(LowRegisterFrom(left), LowRegisterFrom(right));
1960 __ Sbcs(temps.Acquire(), HighRegisterFrom(left), HighRegisterFrom(right));
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001961 ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite));
Donghui Bai426b49c2016-11-08 14:55:38 +08001962 break;
1963 }
1964 default:
1965 LOG(FATAL) << "Unreachable";
1966 UNREACHABLE();
1967 }
1968
1969 return ret;
1970}
1971
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001972static std::pair<vixl32::Condition, vixl32::Condition> GenerateTest(HCondition* condition,
1973 bool invert,
1974 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001975 const DataType::Type type = condition->GetLeft()->GetType();
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001976 IfCondition cond = condition->GetCondition();
1977 IfCondition opposite = condition->GetOppositeCondition();
1978 std::pair<vixl32::Condition, vixl32::Condition> ret(eq, ne);
Donghui Bai426b49c2016-11-08 14:55:38 +08001979
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001980 if (invert) {
1981 std::swap(cond, opposite);
1982 }
Donghui Bai426b49c2016-11-08 14:55:38 +08001983
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001984 if (type == DataType::Type::kInt64) {
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001985 ret = condition->GetLocations()->InAt(1).IsConstant()
1986 ? GenerateLongTestConstant(condition, invert, codegen)
1987 : GenerateLongTest(condition, invert, codegen);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001988 } else if (DataType::IsFloatingPointType(type)) {
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001989 GenerateVcmp(condition, codegen);
1990 __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
1991 ret = std::make_pair(ARMFPCondition(cond, condition->IsGtBias()),
1992 ARMFPCondition(opposite, condition->IsGtBias()));
Donghui Bai426b49c2016-11-08 14:55:38 +08001993 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001994 DCHECK(DataType::IsIntegralType(type) || type == DataType::Type::kReference) << type;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001995 __ Cmp(InputRegisterAt(condition, 0), InputOperandAt(condition, 1));
1996 ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite));
Donghui Bai426b49c2016-11-08 14:55:38 +08001997 }
1998
1999 return ret;
2000}
2001
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002002static void GenerateConditionGeneric(HCondition* cond, CodeGeneratorARMVIXL* codegen) {
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002003 const vixl32::Register out = OutputRegister(cond);
2004 const auto condition = GenerateTest(cond, false, codegen);
2005
2006 __ Mov(LeaveFlags, out, 0);
2007
2008 if (out.IsLow()) {
2009 // We use the scope because of the IT block that follows.
2010 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
2011 2 * vixl32::k16BitT32InstructionSizeInBytes,
2012 CodeBufferCheckScope::kExactSize);
2013
2014 __ it(condition.first);
2015 __ mov(condition.first, out, 1);
2016 } else {
2017 vixl32::Label done_label;
2018 vixl32::Label* const final_label = codegen->GetFinalLabel(cond, &done_label);
2019
2020 __ B(condition.second, final_label, /* far_target */ false);
2021 __ Mov(out, 1);
2022
2023 if (done_label.IsReferenced()) {
2024 __ Bind(&done_label);
2025 }
2026 }
2027}
2028
2029static void GenerateEqualLong(HCondition* cond, CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002030 DCHECK_EQ(cond->GetLeft()->GetType(), DataType::Type::kInt64);
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002031
2032 const LocationSummary* const locations = cond->GetLocations();
2033 IfCondition condition = cond->GetCondition();
2034 const vixl32::Register out = OutputRegister(cond);
2035 const Location left = locations->InAt(0);
2036 const Location right = locations->InAt(1);
2037 vixl32::Register left_high = HighRegisterFrom(left);
2038 vixl32::Register left_low = LowRegisterFrom(left);
2039 vixl32::Register temp;
2040 UseScratchRegisterScope temps(codegen->GetVIXLAssembler());
2041
2042 if (right.IsConstant()) {
2043 IfCondition opposite = cond->GetOppositeCondition();
2044 const int64_t value = AdjustConstantForCondition(Int64ConstantFrom(right),
2045 &condition,
2046 &opposite);
2047 Operand right_high = High32Bits(value);
2048 Operand right_low = Low32Bits(value);
2049
2050 // The output uses Location::kNoOutputOverlap.
2051 if (out.Is(left_high)) {
2052 std::swap(left_low, left_high);
2053 std::swap(right_low, right_high);
2054 }
2055
2056 __ Sub(out, left_low, right_low);
2057 temp = temps.Acquire();
2058 __ Sub(temp, left_high, right_high);
2059 } else {
2060 DCHECK(right.IsRegisterPair());
2061 temp = temps.Acquire();
2062 __ Sub(temp, left_high, HighRegisterFrom(right));
2063 __ Sub(out, left_low, LowRegisterFrom(right));
2064 }
2065
2066 // Need to check after calling AdjustConstantForCondition().
2067 DCHECK(condition == kCondEQ || condition == kCondNE) << condition;
2068
2069 if (condition == kCondNE && out.IsLow()) {
2070 __ Orrs(out, out, temp);
2071
2072 // We use the scope because of the IT block that follows.
2073 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
2074 2 * vixl32::k16BitT32InstructionSizeInBytes,
2075 CodeBufferCheckScope::kExactSize);
2076
2077 __ it(ne);
2078 __ mov(ne, out, 1);
2079 } else {
2080 __ Orr(out, out, temp);
2081 codegen->GenerateConditionWithZero(condition, out, out, temp);
2082 }
2083}
2084
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002085static void GenerateConditionLong(HCondition* cond, CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002086 DCHECK_EQ(cond->GetLeft()->GetType(), DataType::Type::kInt64);
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002087
2088 const LocationSummary* const locations = cond->GetLocations();
2089 IfCondition condition = cond->GetCondition();
2090 const vixl32::Register out = OutputRegister(cond);
2091 const Location left = locations->InAt(0);
2092 const Location right = locations->InAt(1);
2093
2094 if (right.IsConstant()) {
2095 IfCondition opposite = cond->GetOppositeCondition();
2096
2097 // Comparisons against 0 are common enough to deserve special attention.
2098 if (AdjustConstantForCondition(Int64ConstantFrom(right), &condition, &opposite) == 0) {
2099 switch (condition) {
2100 case kCondNE:
2101 case kCondA:
2102 if (out.IsLow()) {
2103 // We only care if both input registers are 0 or not.
2104 __ Orrs(out, LowRegisterFrom(left), HighRegisterFrom(left));
2105
2106 // We use the scope because of the IT block that follows.
2107 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
2108 2 * vixl32::k16BitT32InstructionSizeInBytes,
2109 CodeBufferCheckScope::kExactSize);
2110
2111 __ it(ne);
2112 __ mov(ne, out, 1);
2113 return;
2114 }
2115
2116 FALLTHROUGH_INTENDED;
2117 case kCondEQ:
2118 case kCondBE:
2119 // We only care if both input registers are 0 or not.
2120 __ Orr(out, LowRegisterFrom(left), HighRegisterFrom(left));
2121 codegen->GenerateConditionWithZero(condition, out, out);
2122 return;
2123 case kCondLT:
2124 case kCondGE:
2125 // We only care about the sign bit.
2126 FALLTHROUGH_INTENDED;
2127 case kCondAE:
2128 case kCondB:
2129 codegen->GenerateConditionWithZero(condition, out, HighRegisterFrom(left));
2130 return;
2131 case kCondLE:
2132 case kCondGT:
2133 default:
2134 break;
2135 }
2136 }
2137 }
2138
Anton Kirilov23b752b2017-07-20 14:40:44 +01002139 // If `out` is a low register, then the GenerateConditionGeneric()
2140 // function generates a shorter code sequence that is still branchless.
2141 if ((condition == kCondEQ || condition == kCondNE) && !out.IsLow()) {
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002142 GenerateEqualLong(cond, codegen);
2143 return;
2144 }
2145
Anton Kirilov23b752b2017-07-20 14:40:44 +01002146 GenerateConditionGeneric(cond, codegen);
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002147}
2148
Roland Levillain6d729a72017-06-30 18:34:01 +01002149static void GenerateConditionIntegralOrNonPrimitive(HCondition* cond,
2150 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002151 const DataType::Type type = cond->GetLeft()->GetType();
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002152
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002153 DCHECK(DataType::IsIntegralType(type) || type == DataType::Type::kReference) << type;
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002154
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002155 if (type == DataType::Type::kInt64) {
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002156 GenerateConditionLong(cond, codegen);
2157 return;
2158 }
2159
2160 IfCondition condition = cond->GetCondition();
2161 vixl32::Register in = InputRegisterAt(cond, 0);
2162 const vixl32::Register out = OutputRegister(cond);
2163 const Location right = cond->GetLocations()->InAt(1);
2164 int64_t value;
2165
2166 if (right.IsConstant()) {
2167 IfCondition opposite = cond->GetOppositeCondition();
2168
2169 value = AdjustConstantForCondition(Int64ConstantFrom(right), &condition, &opposite);
2170
2171 // Comparisons against 0 are common enough to deserve special attention.
2172 if (value == 0) {
2173 switch (condition) {
2174 case kCondNE:
2175 case kCondA:
2176 if (out.IsLow() && out.Is(in)) {
2177 __ Cmp(out, 0);
2178
2179 // We use the scope because of the IT block that follows.
2180 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
2181 2 * vixl32::k16BitT32InstructionSizeInBytes,
2182 CodeBufferCheckScope::kExactSize);
2183
2184 __ it(ne);
2185 __ mov(ne, out, 1);
2186 return;
2187 }
2188
2189 FALLTHROUGH_INTENDED;
2190 case kCondEQ:
2191 case kCondBE:
2192 case kCondLT:
2193 case kCondGE:
2194 case kCondAE:
2195 case kCondB:
2196 codegen->GenerateConditionWithZero(condition, out, in);
2197 return;
2198 case kCondLE:
2199 case kCondGT:
2200 default:
2201 break;
2202 }
2203 }
2204 }
2205
2206 if (condition == kCondEQ || condition == kCondNE) {
2207 Operand operand(0);
2208
2209 if (right.IsConstant()) {
2210 operand = Operand::From(value);
2211 } else if (out.Is(RegisterFrom(right))) {
2212 // Avoid 32-bit instructions if possible.
2213 operand = InputOperandAt(cond, 0);
2214 in = RegisterFrom(right);
2215 } else {
2216 operand = InputOperandAt(cond, 1);
2217 }
2218
2219 if (condition == kCondNE && out.IsLow()) {
2220 __ Subs(out, in, operand);
2221
2222 // We use the scope because of the IT block that follows.
2223 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
2224 2 * vixl32::k16BitT32InstructionSizeInBytes,
2225 CodeBufferCheckScope::kExactSize);
2226
2227 __ it(ne);
2228 __ mov(ne, out, 1);
2229 } else {
2230 __ Sub(out, in, operand);
2231 codegen->GenerateConditionWithZero(condition, out, out);
2232 }
2233
2234 return;
2235 }
2236
2237 GenerateConditionGeneric(cond, codegen);
2238}
2239
Donghui Bai426b49c2016-11-08 14:55:38 +08002240static bool CanEncodeConstantAs8BitImmediate(HConstant* constant) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002241 const DataType::Type type = constant->GetType();
Donghui Bai426b49c2016-11-08 14:55:38 +08002242 bool ret = false;
2243
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002244 DCHECK(DataType::IsIntegralType(type) || type == DataType::Type::kReference) << type;
Donghui Bai426b49c2016-11-08 14:55:38 +08002245
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002246 if (type == DataType::Type::kInt64) {
Donghui Bai426b49c2016-11-08 14:55:38 +08002247 const uint64_t value = Uint64ConstantFrom(constant);
2248
2249 ret = IsUint<8>(Low32Bits(value)) && IsUint<8>(High32Bits(value));
2250 } else {
2251 ret = IsUint<8>(Int32ConstantFrom(constant));
2252 }
2253
2254 return ret;
2255}
2256
2257static Location Arm8BitEncodableConstantOrRegister(HInstruction* constant) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002258 DCHECK(!DataType::IsFloatingPointType(constant->GetType()));
Donghui Bai426b49c2016-11-08 14:55:38 +08002259
2260 if (constant->IsConstant() && CanEncodeConstantAs8BitImmediate(constant->AsConstant())) {
2261 return Location::ConstantLocation(constant->AsConstant());
2262 }
2263
2264 return Location::RequiresRegister();
2265}
2266
2267static bool CanGenerateConditionalMove(const Location& out, const Location& src) {
2268 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
2269 // we check that we are not dealing with floating-point output (there is no
2270 // 16-bit VMOV encoding).
2271 if (!out.IsRegister() && !out.IsRegisterPair()) {
2272 return false;
2273 }
2274
2275 // For constants, we also check that the output is in one or two low registers,
2276 // and that the constants fit in an 8-bit unsigned integer, so that a 16-bit
2277 // MOV encoding can be used.
2278 if (src.IsConstant()) {
2279 if (!CanEncodeConstantAs8BitImmediate(src.GetConstant())) {
2280 return false;
2281 }
2282
2283 if (out.IsRegister()) {
2284 if (!RegisterFrom(out).IsLow()) {
2285 return false;
2286 }
2287 } else {
2288 DCHECK(out.IsRegisterPair());
2289
2290 if (!HighRegisterFrom(out).IsLow()) {
2291 return false;
2292 }
2293 }
2294 }
2295
2296 return true;
2297}
2298
Scott Wakelingfe885462016-09-22 10:24:38 +01002299#undef __
2300
Donghui Bai426b49c2016-11-08 14:55:38 +08002301vixl32::Label* CodeGeneratorARMVIXL::GetFinalLabel(HInstruction* instruction,
2302 vixl32::Label* final_label) {
2303 DCHECK(!instruction->IsControlFlow() && !instruction->IsSuspendCheck());
Anton Kirilov6f644202017-02-27 18:29:45 +00002304 DCHECK(!instruction->IsInvoke() || !instruction->GetLocations()->CanCall());
Donghui Bai426b49c2016-11-08 14:55:38 +08002305
2306 const HBasicBlock* const block = instruction->GetBlock();
2307 const HLoopInformation* const info = block->GetLoopInformation();
2308 HInstruction* const next = instruction->GetNext();
2309
2310 // Avoid a branch to a branch.
2311 if (next->IsGoto() && (info == nullptr ||
2312 !info->IsBackEdge(*block) ||
2313 !info->HasSuspendCheck())) {
2314 final_label = GetLabelOf(next->AsGoto()->GetSuccessor());
2315 }
2316
2317 return final_label;
2318}
2319
Scott Wakelingfe885462016-09-22 10:24:38 +01002320CodeGeneratorARMVIXL::CodeGeneratorARMVIXL(HGraph* graph,
2321 const ArmInstructionSetFeatures& isa_features,
2322 const CompilerOptions& compiler_options,
2323 OptimizingCompilerStats* stats)
2324 : CodeGenerator(graph,
2325 kNumberOfCoreRegisters,
2326 kNumberOfSRegisters,
2327 kNumberOfRegisterPairs,
2328 kCoreCalleeSaves.GetList(),
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002329 ComputeSRegisterListMask(kFpuCalleeSaves),
Scott Wakelingfe885462016-09-22 10:24:38 +01002330 compiler_options,
2331 stats),
Vladimir Markoca6fff82017-10-03 14:49:14 +01002332 block_labels_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
2333 jump_tables_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Scott Wakelingfe885462016-09-22 10:24:38 +01002334 location_builder_(graph, this),
2335 instruction_visitor_(graph, this),
Vladimir Markoca6fff82017-10-03 14:49:14 +01002336 move_resolver_(graph->GetAllocator(), this),
2337 assembler_(graph->GetAllocator()),
Artem Serovd4cc5b22016-11-04 11:19:09 +00002338 isa_features_(isa_features),
Artem Serovc5fcb442016-12-02 19:19:58 +00002339 uint32_literals_(std::less<uint32_t>(),
Vladimir Markoca6fff82017-10-03 14:49:14 +01002340 graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Marko59eb30f2018-02-20 11:52:34 +00002341 boot_image_method_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Markoca6fff82017-10-03 14:49:14 +01002342 method_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Marko59eb30f2018-02-20 11:52:34 +00002343 boot_image_type_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Markoca6fff82017-10-03 14:49:14 +01002344 type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Marko59eb30f2018-02-20 11:52:34 +00002345 boot_image_string_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Markoca6fff82017-10-03 14:49:14 +01002346 string_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
2347 baker_read_barrier_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Artem Serovc5fcb442016-12-02 19:19:58 +00002348 jit_string_patches_(StringReferenceValueComparator(),
Vladimir Markoca6fff82017-10-03 14:49:14 +01002349 graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Artem Serovc5fcb442016-12-02 19:19:58 +00002350 jit_class_patches_(TypeReferenceValueComparator(),
Vladimir Markoca6fff82017-10-03 14:49:14 +01002351 graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)) {
Scott Wakelingfe885462016-09-22 10:24:38 +01002352 // Always save the LR register to mimic Quick.
2353 AddAllocatedRegister(Location::RegisterLocation(LR));
Nicolas Geoffray13a797b2017-03-15 16:41:31 +00002354 // Give D30 and D31 as scratch register to VIXL. The register allocator only works on
2355 // S0-S31, which alias to D0-D15.
2356 GetVIXLAssembler()->GetScratchVRegisterList()->Combine(d31);
2357 GetVIXLAssembler()->GetScratchVRegisterList()->Combine(d30);
Scott Wakelingfe885462016-09-22 10:24:38 +01002358}
2359
Artem Serov551b28f2016-10-18 19:11:30 +01002360void JumpTableARMVIXL::EmitTable(CodeGeneratorARMVIXL* codegen) {
2361 uint32_t num_entries = switch_instr_->GetNumEntries();
2362 DCHECK_GE(num_entries, kPackedSwitchCompareJumpThreshold);
2363
2364 // We are about to use the assembler to place literals directly. Make sure we have enough
Scott Wakelingb77051e2016-11-21 19:46:00 +00002365 // underlying code buffer and we have generated a jump table of the right size, using
2366 // codegen->GetVIXLAssembler()->GetBuffer().Align();
Artem Serov0fb37192016-12-06 18:13:40 +00002367 ExactAssemblyScope aas(codegen->GetVIXLAssembler(),
2368 num_entries * sizeof(int32_t),
2369 CodeBufferCheckScope::kMaximumSize);
Artem Serov551b28f2016-10-18 19:11:30 +01002370 // TODO(VIXL): Check that using lower case bind is fine here.
2371 codegen->GetVIXLAssembler()->bind(&table_start_);
Artem Serov09a940d2016-11-11 16:15:11 +00002372 for (uint32_t i = 0; i < num_entries; i++) {
2373 codegen->GetVIXLAssembler()->place(bb_addresses_[i].get());
2374 }
2375}
2376
2377void JumpTableARMVIXL::FixTable(CodeGeneratorARMVIXL* codegen) {
2378 uint32_t num_entries = switch_instr_->GetNumEntries();
2379 DCHECK_GE(num_entries, kPackedSwitchCompareJumpThreshold);
2380
Artem Serov551b28f2016-10-18 19:11:30 +01002381 const ArenaVector<HBasicBlock*>& successors = switch_instr_->GetBlock()->GetSuccessors();
2382 for (uint32_t i = 0; i < num_entries; i++) {
2383 vixl32::Label* target_label = codegen->GetLabelOf(successors[i]);
2384 DCHECK(target_label->IsBound());
2385 int32_t jump_offset = target_label->GetLocation() - table_start_.GetLocation();
2386 // When doing BX to address we need to have lower bit set to 1 in T32.
2387 if (codegen->GetVIXLAssembler()->IsUsingT32()) {
2388 jump_offset++;
2389 }
2390 DCHECK_GT(jump_offset, std::numeric_limits<int32_t>::min());
2391 DCHECK_LE(jump_offset, std::numeric_limits<int32_t>::max());
Artem Serov09a940d2016-11-11 16:15:11 +00002392
Scott Wakelingb77051e2016-11-21 19:46:00 +00002393 bb_addresses_[i].get()->UpdateValue(jump_offset, codegen->GetVIXLAssembler()->GetBuffer());
Artem Serov551b28f2016-10-18 19:11:30 +01002394 }
2395}
2396
Artem Serov09a940d2016-11-11 16:15:11 +00002397void CodeGeneratorARMVIXL::FixJumpTables() {
Artem Serov551b28f2016-10-18 19:11:30 +01002398 for (auto&& jump_table : jump_tables_) {
Artem Serov09a940d2016-11-11 16:15:11 +00002399 jump_table->FixTable(this);
Artem Serov551b28f2016-10-18 19:11:30 +01002400 }
2401}
2402
Andreas Gampeca620d72016-11-08 08:09:33 -08002403#define __ reinterpret_cast<ArmVIXLAssembler*>(GetAssembler())->GetVIXLAssembler()-> // NOLINT
Scott Wakelingfe885462016-09-22 10:24:38 +01002404
2405void CodeGeneratorARMVIXL::Finalize(CodeAllocator* allocator) {
Artem Serov09a940d2016-11-11 16:15:11 +00002406 FixJumpTables();
Scott Wakelingfe885462016-09-22 10:24:38 +01002407 GetAssembler()->FinalizeCode();
2408 CodeGenerator::Finalize(allocator);
Vladimir Markoca1e0382018-04-11 09:58:41 +00002409
2410 // Verify Baker read barrier linker patches.
2411 if (kIsDebugBuild) {
2412 ArrayRef<const uint8_t> code = allocator->GetMemory();
2413 for (const BakerReadBarrierPatchInfo& info : baker_read_barrier_patches_) {
2414 DCHECK(info.label.IsBound());
2415 uint32_t literal_offset = info.label.GetLocation();
2416 DCHECK_ALIGNED(literal_offset, 2u);
2417
2418 auto GetInsn16 = [&code](uint32_t offset) {
2419 DCHECK_ALIGNED(offset, 2u);
2420 return (static_cast<uint32_t>(code[offset + 0]) << 0) +
2421 (static_cast<uint32_t>(code[offset + 1]) << 8);
2422 };
2423 auto GetInsn32 = [=](uint32_t offset) {
2424 return (GetInsn16(offset) << 16) + (GetInsn16(offset + 2u) << 0);
2425 };
2426
2427 uint32_t encoded_data = info.custom_data;
2428 BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data);
2429 // Check that the next instruction matches the expected LDR.
2430 switch (kind) {
2431 case BakerReadBarrierKind::kField: {
2432 BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data);
2433 if (width == BakerReadBarrierWidth::kWide) {
2434 DCHECK_GE(code.size() - literal_offset, 8u);
2435 uint32_t next_insn = GetInsn32(literal_offset + 4u);
2436 // LDR (immediate), encoding T3, with correct base_reg.
2437 CheckValidReg((next_insn >> 12) & 0xfu); // Check destination register.
2438 const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
2439 CHECK_EQ(next_insn & 0xffff0000u, 0xf8d00000u | (base_reg << 16));
2440 } else {
2441 DCHECK_GE(code.size() - literal_offset, 6u);
2442 uint32_t next_insn = GetInsn16(literal_offset + 4u);
2443 // LDR (immediate), encoding T1, with correct base_reg.
2444 CheckValidReg(next_insn & 0x7u); // Check destination register.
2445 const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
2446 CHECK_EQ(next_insn & 0xf838u, 0x6800u | (base_reg << 3));
2447 }
2448 break;
2449 }
2450 case BakerReadBarrierKind::kArray: {
2451 DCHECK_GE(code.size() - literal_offset, 8u);
2452 uint32_t next_insn = GetInsn32(literal_offset + 4u);
2453 // LDR (register) with correct base_reg, S=1 and option=011 (LDR Wt, [Xn, Xm, LSL #2]).
2454 CheckValidReg((next_insn >> 12) & 0xfu); // Check destination register.
2455 const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
2456 CHECK_EQ(next_insn & 0xffff0ff0u, 0xf8500020u | (base_reg << 16));
2457 CheckValidReg(next_insn & 0xf); // Check index register
2458 break;
2459 }
2460 case BakerReadBarrierKind::kGcRoot: {
2461 BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data);
2462 if (width == BakerReadBarrierWidth::kWide) {
2463 DCHECK_GE(literal_offset, 4u);
2464 uint32_t prev_insn = GetInsn32(literal_offset - 4u);
2465 // LDR (immediate), encoding T3, with correct root_reg.
2466 const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
2467 CHECK_EQ(prev_insn & 0xfff0f000u, 0xf8d00000u | (root_reg << 12));
2468 } else {
2469 DCHECK_GE(literal_offset, 2u);
2470 uint32_t prev_insn = GetInsn16(literal_offset - 2u);
2471 // LDR (immediate), encoding T1, with correct root_reg.
2472 const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
2473 CHECK_EQ(prev_insn & 0xf807u, 0x6800u | root_reg);
2474 }
2475 break;
2476 }
2477 default:
2478 LOG(FATAL) << "Unexpected kind: " << static_cast<uint32_t>(kind);
2479 UNREACHABLE();
2480 }
2481 }
2482 }
Scott Wakelingfe885462016-09-22 10:24:38 +01002483}
2484
2485void CodeGeneratorARMVIXL::SetupBlockedRegisters() const {
Scott Wakelingfe885462016-09-22 10:24:38 +01002486 // Stack register, LR and PC are always reserved.
2487 blocked_core_registers_[SP] = true;
2488 blocked_core_registers_[LR] = true;
2489 blocked_core_registers_[PC] = true;
2490
Roland Levillain6d729a72017-06-30 18:34:01 +01002491 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2492 // Reserve marking register.
2493 blocked_core_registers_[MR] = true;
2494 }
2495
Scott Wakelingfe885462016-09-22 10:24:38 +01002496 // Reserve thread register.
2497 blocked_core_registers_[TR] = true;
2498
2499 // Reserve temp register.
2500 blocked_core_registers_[IP] = true;
2501
2502 if (GetGraph()->IsDebuggable()) {
2503 // Stubs do not save callee-save floating point registers. If the graph
2504 // is debuggable, we need to deal with these registers differently. For
2505 // now, just block them.
2506 for (uint32_t i = kFpuCalleeSaves.GetFirstSRegister().GetCode();
2507 i <= kFpuCalleeSaves.GetLastSRegister().GetCode();
2508 ++i) {
2509 blocked_fpu_registers_[i] = true;
2510 }
2511 }
Scott Wakelingfe885462016-09-22 10:24:38 +01002512}
2513
Scott Wakelingfe885462016-09-22 10:24:38 +01002514InstructionCodeGeneratorARMVIXL::InstructionCodeGeneratorARMVIXL(HGraph* graph,
2515 CodeGeneratorARMVIXL* codegen)
2516 : InstructionCodeGenerator(graph, codegen),
2517 assembler_(codegen->GetAssembler()),
2518 codegen_(codegen) {}
2519
2520void CodeGeneratorARMVIXL::ComputeSpillMask() {
2521 core_spill_mask_ = allocated_registers_.GetCoreRegisters() & core_callee_save_mask_;
2522 DCHECK_NE(core_spill_mask_, 0u) << "At least the return address register must be saved";
2523 // There is no easy instruction to restore just the PC on thumb2. We spill and
2524 // restore another arbitrary register.
2525 core_spill_mask_ |= (1 << kCoreAlwaysSpillRegister.GetCode());
2526 fpu_spill_mask_ = allocated_registers_.GetFloatingPointRegisters() & fpu_callee_save_mask_;
2527 // We use vpush and vpop for saving and restoring floating point registers, which take
2528 // a SRegister and the number of registers to save/restore after that SRegister. We
2529 // therefore update the `fpu_spill_mask_` to also contain those registers not allocated,
2530 // but in the range.
2531 if (fpu_spill_mask_ != 0) {
2532 uint32_t least_significant_bit = LeastSignificantBit(fpu_spill_mask_);
2533 uint32_t most_significant_bit = MostSignificantBit(fpu_spill_mask_);
2534 for (uint32_t i = least_significant_bit + 1 ; i < most_significant_bit; ++i) {
2535 fpu_spill_mask_ |= (1 << i);
2536 }
2537 }
2538}
2539
2540void CodeGeneratorARMVIXL::GenerateFrameEntry() {
2541 bool skip_overflow_check =
2542 IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kArm);
2543 DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks());
2544 __ Bind(&frame_entry_label_);
2545
Nicolas Geoffray8d728322018-01-18 22:44:32 +00002546 if (GetCompilerOptions().CountHotnessInCompiledCode()) {
2547 UseScratchRegisterScope temps(GetVIXLAssembler());
2548 vixl32::Register temp = temps.Acquire();
2549 __ Ldrh(temp, MemOperand(kMethodRegister, ArtMethod::HotnessCountOffset().Int32Value()));
2550 __ Add(temp, temp, 1);
2551 __ Strh(temp, MemOperand(kMethodRegister, ArtMethod::HotnessCountOffset().Int32Value()));
2552 }
2553
Scott Wakelingfe885462016-09-22 10:24:38 +01002554 if (HasEmptyFrame()) {
2555 return;
2556 }
2557
Scott Wakelingfe885462016-09-22 10:24:38 +01002558 if (!skip_overflow_check) {
xueliang.zhong10049552018-01-31 17:10:36 +00002559 // Using r4 instead of IP saves 2 bytes.
Nicolas Geoffray1a4f3ca2018-01-25 14:07:15 +00002560 UseScratchRegisterScope temps(GetVIXLAssembler());
xueliang.zhong10049552018-01-31 17:10:36 +00002561 vixl32::Register temp;
2562 // TODO: Remove this check when R4 is made a callee-save register
2563 // in ART compiled code (b/72801708). Currently we need to make
2564 // sure r4 is not blocked, e.g. in special purpose
2565 // TestCodeGeneratorARMVIXL; also asserting that r4 is available
2566 // here.
2567 if (!blocked_core_registers_[R4]) {
2568 for (vixl32::Register reg : kParameterCoreRegistersVIXL) {
2569 DCHECK(!reg.Is(r4));
2570 }
2571 DCHECK(!kCoreCalleeSaves.Includes(r4));
2572 temp = r4;
2573 } else {
2574 temp = temps.Acquire();
2575 }
Vladimir Marko33bff252017-11-01 14:35:42 +00002576 __ Sub(temp, sp, Operand::From(GetStackOverflowReservedBytes(InstructionSet::kArm)));
Scott Wakelingfe885462016-09-22 10:24:38 +01002577 // The load must immediately precede RecordPcInfo.
Artem Serov0fb37192016-12-06 18:13:40 +00002578 ExactAssemblyScope aas(GetVIXLAssembler(),
2579 vixl32::kMaxInstructionSizeInBytes,
2580 CodeBufferCheckScope::kMaximumSize);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002581 __ ldr(temp, MemOperand(temp));
2582 RecordPcInfo(nullptr, 0);
Scott Wakelingfe885462016-09-22 10:24:38 +01002583 }
2584
2585 __ Push(RegisterList(core_spill_mask_));
2586 GetAssembler()->cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(core_spill_mask_));
2587 GetAssembler()->cfi().RelOffsetForMany(DWARFReg(kMethodRegister),
2588 0,
2589 core_spill_mask_,
2590 kArmWordSize);
2591 if (fpu_spill_mask_ != 0) {
2592 uint32_t first = LeastSignificantBit(fpu_spill_mask_);
2593
2594 // Check that list is contiguous.
2595 DCHECK_EQ(fpu_spill_mask_ >> CTZ(fpu_spill_mask_), ~0u >> (32 - POPCOUNT(fpu_spill_mask_)));
2596
2597 __ Vpush(SRegisterList(vixl32::SRegister(first), POPCOUNT(fpu_spill_mask_)));
2598 GetAssembler()->cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(fpu_spill_mask_));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002599 GetAssembler()->cfi().RelOffsetForMany(DWARFReg(s0), 0, fpu_spill_mask_, kArmWordSize);
Scott Wakelingfe885462016-09-22 10:24:38 +01002600 }
Scott Wakelingbffdc702016-12-07 17:46:03 +00002601
Scott Wakelingfe885462016-09-22 10:24:38 +01002602 int adjust = GetFrameSize() - FrameEntrySpillSize();
2603 __ Sub(sp, sp, adjust);
2604 GetAssembler()->cfi().AdjustCFAOffset(adjust);
Scott Wakelingbffdc702016-12-07 17:46:03 +00002605
2606 // Save the current method if we need it. Note that we do not
2607 // do this in HCurrentMethod, as the instruction might have been removed
2608 // in the SSA graph.
2609 if (RequiresCurrentMethod()) {
2610 GetAssembler()->StoreToOffset(kStoreWord, kMethodRegister, sp, 0);
2611 }
Nicolas Geoffrayf7893532017-06-15 12:34:36 +01002612
2613 if (GetGraph()->HasShouldDeoptimizeFlag()) {
2614 UseScratchRegisterScope temps(GetVIXLAssembler());
2615 vixl32::Register temp = temps.Acquire();
2616 // Initialize should_deoptimize flag to 0.
2617 __ Mov(temp, 0);
2618 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, GetStackOffsetOfShouldDeoptimizeFlag());
2619 }
Roland Levillain5daa4952017-07-03 17:23:56 +01002620
2621 MaybeGenerateMarkingRegisterCheck(/* code */ 1);
Scott Wakelingfe885462016-09-22 10:24:38 +01002622}
2623
2624void CodeGeneratorARMVIXL::GenerateFrameExit() {
2625 if (HasEmptyFrame()) {
2626 __ Bx(lr);
2627 return;
2628 }
2629 GetAssembler()->cfi().RememberState();
2630 int adjust = GetFrameSize() - FrameEntrySpillSize();
2631 __ Add(sp, sp, adjust);
2632 GetAssembler()->cfi().AdjustCFAOffset(-adjust);
2633 if (fpu_spill_mask_ != 0) {
2634 uint32_t first = LeastSignificantBit(fpu_spill_mask_);
2635
2636 // Check that list is contiguous.
2637 DCHECK_EQ(fpu_spill_mask_ >> CTZ(fpu_spill_mask_), ~0u >> (32 - POPCOUNT(fpu_spill_mask_)));
2638
2639 __ Vpop(SRegisterList(vixl32::SRegister(first), POPCOUNT(fpu_spill_mask_)));
2640 GetAssembler()->cfi().AdjustCFAOffset(
2641 -static_cast<int>(kArmWordSize) * POPCOUNT(fpu_spill_mask_));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002642 GetAssembler()->cfi().RestoreMany(DWARFReg(vixl32::SRegister(0)), fpu_spill_mask_);
Scott Wakelingfe885462016-09-22 10:24:38 +01002643 }
2644 // Pop LR into PC to return.
2645 DCHECK_NE(core_spill_mask_ & (1 << kLrCode), 0U);
2646 uint32_t pop_mask = (core_spill_mask_ & (~(1 << kLrCode))) | 1 << kPcCode;
2647 __ Pop(RegisterList(pop_mask));
2648 GetAssembler()->cfi().RestoreState();
2649 GetAssembler()->cfi().DefCFAOffset(GetFrameSize());
2650}
2651
2652void CodeGeneratorARMVIXL::Bind(HBasicBlock* block) {
2653 __ Bind(GetLabelOf(block));
2654}
2655
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002656Location InvokeDexCallingConventionVisitorARMVIXL::GetNextLocation(DataType::Type type) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002657 switch (type) {
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01002658 case DataType::Type::kReference:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002659 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01002660 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002661 case DataType::Type::kInt8:
2662 case DataType::Type::kUint16:
2663 case DataType::Type::kInt16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01002664 case DataType::Type::kInt32: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002665 uint32_t index = gp_index_++;
2666 uint32_t stack_index = stack_index_++;
2667 if (index < calling_convention.GetNumberOfRegisters()) {
2668 return LocationFrom(calling_convention.GetRegisterAt(index));
2669 } else {
2670 return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index));
2671 }
2672 }
2673
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002674 case DataType::Type::kInt64: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002675 uint32_t index = gp_index_;
2676 uint32_t stack_index = stack_index_;
2677 gp_index_ += 2;
2678 stack_index_ += 2;
2679 if (index + 1 < calling_convention.GetNumberOfRegisters()) {
2680 if (calling_convention.GetRegisterAt(index).Is(r1)) {
2681 // Skip R1, and use R2_R3 instead.
2682 gp_index_++;
2683 index++;
2684 }
2685 }
2686 if (index + 1 < calling_convention.GetNumberOfRegisters()) {
2687 DCHECK_EQ(calling_convention.GetRegisterAt(index).GetCode() + 1,
2688 calling_convention.GetRegisterAt(index + 1).GetCode());
2689
2690 return LocationFrom(calling_convention.GetRegisterAt(index),
2691 calling_convention.GetRegisterAt(index + 1));
2692 } else {
2693 return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index));
2694 }
2695 }
2696
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002697 case DataType::Type::kFloat32: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002698 uint32_t stack_index = stack_index_++;
2699 if (float_index_ % 2 == 0) {
2700 float_index_ = std::max(double_index_, float_index_);
2701 }
2702 if (float_index_ < calling_convention.GetNumberOfFpuRegisters()) {
2703 return LocationFrom(calling_convention.GetFpuRegisterAt(float_index_++));
2704 } else {
2705 return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index));
2706 }
2707 }
2708
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002709 case DataType::Type::kFloat64: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002710 double_index_ = std::max(double_index_, RoundUp(float_index_, 2));
2711 uint32_t stack_index = stack_index_;
2712 stack_index_ += 2;
2713 if (double_index_ + 1 < calling_convention.GetNumberOfFpuRegisters()) {
2714 uint32_t index = double_index_;
2715 double_index_ += 2;
2716 Location result = LocationFrom(
2717 calling_convention.GetFpuRegisterAt(index),
2718 calling_convention.GetFpuRegisterAt(index + 1));
2719 DCHECK(ExpectedPairLayout(result));
2720 return result;
2721 } else {
2722 return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index));
2723 }
2724 }
2725
Aart Bik66c158e2018-01-31 12:55:04 -08002726 case DataType::Type::kUint32:
2727 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002728 case DataType::Type::kVoid:
Artem Serovd4cc5b22016-11-04 11:19:09 +00002729 LOG(FATAL) << "Unexpected parameter type " << type;
2730 break;
2731 }
2732 return Location::NoLocation();
2733}
2734
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002735Location InvokeDexCallingConventionVisitorARMVIXL::GetReturnLocation(DataType::Type type) const {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002736 switch (type) {
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01002737 case DataType::Type::kReference:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002738 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01002739 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002740 case DataType::Type::kInt8:
2741 case DataType::Type::kUint16:
2742 case DataType::Type::kInt16:
Aart Bik66c158e2018-01-31 12:55:04 -08002743 case DataType::Type::kUint32:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01002744 case DataType::Type::kInt32: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002745 return LocationFrom(r0);
2746 }
2747
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002748 case DataType::Type::kFloat32: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002749 return LocationFrom(s0);
2750 }
2751
Aart Bik66c158e2018-01-31 12:55:04 -08002752 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002753 case DataType::Type::kInt64: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002754 return LocationFrom(r0, r1);
2755 }
2756
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002757 case DataType::Type::kFloat64: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002758 return LocationFrom(s0, s1);
2759 }
2760
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002761 case DataType::Type::kVoid:
Artem Serovd4cc5b22016-11-04 11:19:09 +00002762 return Location::NoLocation();
2763 }
2764
2765 UNREACHABLE();
2766}
2767
2768Location InvokeDexCallingConventionVisitorARMVIXL::GetMethodLocation() const {
2769 return LocationFrom(kMethodRegister);
2770}
2771
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002772void CodeGeneratorARMVIXL::Move32(Location destination, Location source) {
2773 if (source.Equals(destination)) {
2774 return;
2775 }
2776 if (destination.IsRegister()) {
2777 if (source.IsRegister()) {
2778 __ Mov(RegisterFrom(destination), RegisterFrom(source));
2779 } else if (source.IsFpuRegister()) {
2780 __ Vmov(RegisterFrom(destination), SRegisterFrom(source));
2781 } else {
2782 GetAssembler()->LoadFromOffset(kLoadWord,
2783 RegisterFrom(destination),
2784 sp,
2785 source.GetStackIndex());
2786 }
2787 } else if (destination.IsFpuRegister()) {
2788 if (source.IsRegister()) {
2789 __ Vmov(SRegisterFrom(destination), RegisterFrom(source));
2790 } else if (source.IsFpuRegister()) {
2791 __ Vmov(SRegisterFrom(destination), SRegisterFrom(source));
2792 } else {
2793 GetAssembler()->LoadSFromOffset(SRegisterFrom(destination), sp, source.GetStackIndex());
2794 }
2795 } else {
2796 DCHECK(destination.IsStackSlot()) << destination;
2797 if (source.IsRegister()) {
2798 GetAssembler()->StoreToOffset(kStoreWord,
2799 RegisterFrom(source),
2800 sp,
2801 destination.GetStackIndex());
2802 } else if (source.IsFpuRegister()) {
2803 GetAssembler()->StoreSToOffset(SRegisterFrom(source), sp, destination.GetStackIndex());
2804 } else {
2805 DCHECK(source.IsStackSlot()) << source;
2806 UseScratchRegisterScope temps(GetVIXLAssembler());
2807 vixl32::Register temp = temps.Acquire();
2808 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, source.GetStackIndex());
2809 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
2810 }
2811 }
2812}
2813
Artem Serovcfbe9132016-10-14 15:58:56 +01002814void CodeGeneratorARMVIXL::MoveConstant(Location location, int32_t value) {
2815 DCHECK(location.IsRegister());
2816 __ Mov(RegisterFrom(location), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01002817}
2818
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002819void CodeGeneratorARMVIXL::MoveLocation(Location dst, Location src, DataType::Type dst_type) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002820 // TODO(VIXL): Maybe refactor to have the 'move' implementation here and use it in
2821 // `ParallelMoveResolverARMVIXL::EmitMove`, as is done in the `arm64` backend.
Vladimir Markoca6fff82017-10-03 14:49:14 +01002822 HParallelMove move(GetGraph()->GetAllocator());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002823 move.AddMove(src, dst, dst_type, nullptr);
2824 GetMoveResolver()->EmitNativeCode(&move);
Scott Wakelingfe885462016-09-22 10:24:38 +01002825}
2826
Artem Serovcfbe9132016-10-14 15:58:56 +01002827void CodeGeneratorARMVIXL::AddLocationAsTemp(Location location, LocationSummary* locations) {
2828 if (location.IsRegister()) {
2829 locations->AddTemp(location);
2830 } else if (location.IsRegisterPair()) {
2831 locations->AddTemp(LocationFrom(LowRegisterFrom(location)));
2832 locations->AddTemp(LocationFrom(HighRegisterFrom(location)));
2833 } else {
2834 UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location;
2835 }
Scott Wakelingfe885462016-09-22 10:24:38 +01002836}
2837
2838void CodeGeneratorARMVIXL::InvokeRuntime(QuickEntrypointEnum entrypoint,
2839 HInstruction* instruction,
2840 uint32_t dex_pc,
2841 SlowPathCode* slow_path) {
2842 ValidateInvokeRuntime(entrypoint, instruction, slow_path);
Alexandre Rames374ddf32016-11-04 10:40:49 +00002843 __ Ldr(lr, MemOperand(tr, GetThreadOffset<kArmPointerSize>(entrypoint).Int32Value()));
2844 // Ensure the pc position is recorded immediately after the `blx` instruction.
2845 // 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 +00002846 ExactAssemblyScope aas(GetVIXLAssembler(),
2847 vixl32::k16BitT32InstructionSizeInBytes,
2848 CodeBufferCheckScope::kExactSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00002849 __ blx(lr);
Scott Wakelingfe885462016-09-22 10:24:38 +01002850 if (EntrypointRequiresStackMap(entrypoint)) {
2851 RecordPcInfo(instruction, dex_pc, slow_path);
2852 }
2853}
2854
2855void CodeGeneratorARMVIXL::InvokeRuntimeWithoutRecordingPcInfo(int32_t entry_point_offset,
2856 HInstruction* instruction,
2857 SlowPathCode* slow_path) {
2858 ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction, slow_path);
Alexandre Rames374ddf32016-11-04 10:40:49 +00002859 __ Ldr(lr, MemOperand(tr, entry_point_offset));
Scott Wakelingfe885462016-09-22 10:24:38 +01002860 __ Blx(lr);
2861}
2862
Scott Wakelingfe885462016-09-22 10:24:38 +01002863void InstructionCodeGeneratorARMVIXL::HandleGoto(HInstruction* got, HBasicBlock* successor) {
Aart Bika8b8e9b2018-01-09 11:01:02 -08002864 if (successor->IsExitBlock()) {
2865 DCHECK(got->GetPrevious()->AlwaysThrows());
2866 return; // no code needed
2867 }
2868
Scott Wakelingfe885462016-09-22 10:24:38 +01002869 HBasicBlock* block = got->GetBlock();
2870 HInstruction* previous = got->GetPrevious();
2871 HLoopInformation* info = block->GetLoopInformation();
2872
2873 if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) {
Nicolas Geoffray8d728322018-01-18 22:44:32 +00002874 if (codegen_->GetCompilerOptions().CountHotnessInCompiledCode()) {
2875 UseScratchRegisterScope temps(GetVIXLAssembler());
2876 vixl32::Register temp = temps.Acquire();
2877 __ Push(vixl32::Register(kMethodRegister));
2878 GetAssembler()->LoadFromOffset(kLoadWord, kMethodRegister, sp, kArmWordSize);
2879 __ Ldrh(temp, MemOperand(kMethodRegister, ArtMethod::HotnessCountOffset().Int32Value()));
2880 __ Add(temp, temp, 1);
2881 __ Strh(temp, MemOperand(kMethodRegister, ArtMethod::HotnessCountOffset().Int32Value()));
2882 __ Pop(vixl32::Register(kMethodRegister));
2883 }
Scott Wakelingfe885462016-09-22 10:24:38 +01002884 GenerateSuspendCheck(info->GetSuspendCheck(), successor);
2885 return;
2886 }
2887 if (block->IsEntryBlock() && (previous != nullptr) && previous->IsSuspendCheck()) {
2888 GenerateSuspendCheck(previous->AsSuspendCheck(), nullptr);
Roland Levillain5daa4952017-07-03 17:23:56 +01002889 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 2);
Scott Wakelingfe885462016-09-22 10:24:38 +01002890 }
2891 if (!codegen_->GoesToNextBlock(block, successor)) {
2892 __ B(codegen_->GetLabelOf(successor));
2893 }
2894}
2895
2896void LocationsBuilderARMVIXL::VisitGoto(HGoto* got) {
2897 got->SetLocations(nullptr);
2898}
2899
2900void InstructionCodeGeneratorARMVIXL::VisitGoto(HGoto* got) {
2901 HandleGoto(got, got->GetSuccessor());
2902}
2903
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002904void LocationsBuilderARMVIXL::VisitTryBoundary(HTryBoundary* try_boundary) {
2905 try_boundary->SetLocations(nullptr);
2906}
2907
2908void InstructionCodeGeneratorARMVIXL::VisitTryBoundary(HTryBoundary* try_boundary) {
2909 HBasicBlock* successor = try_boundary->GetNormalFlowSuccessor();
2910 if (!successor->IsExitBlock()) {
2911 HandleGoto(try_boundary, successor);
2912 }
2913}
2914
Scott Wakelingfe885462016-09-22 10:24:38 +01002915void LocationsBuilderARMVIXL::VisitExit(HExit* exit) {
2916 exit->SetLocations(nullptr);
2917}
2918
2919void InstructionCodeGeneratorARMVIXL::VisitExit(HExit* exit ATTRIBUTE_UNUSED) {
2920}
2921
Scott Wakelingfe885462016-09-22 10:24:38 +01002922void InstructionCodeGeneratorARMVIXL::GenerateCompareTestAndBranch(HCondition* condition,
Anton Kirilov23b752b2017-07-20 14:40:44 +01002923 vixl32::Label* true_target,
2924 vixl32::Label* false_target,
Anton Kirilovfd522532017-05-10 12:46:57 +01002925 bool is_far_target) {
Anton Kirilov23b752b2017-07-20 14:40:44 +01002926 if (true_target == false_target) {
2927 DCHECK(true_target != nullptr);
2928 __ B(true_target);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00002929 return;
2930 }
2931
Anton Kirilov23b752b2017-07-20 14:40:44 +01002932 vixl32::Label* non_fallthrough_target;
2933 bool invert;
2934 bool emit_both_branches;
Scott Wakelingfe885462016-09-22 10:24:38 +01002935
Anton Kirilov23b752b2017-07-20 14:40:44 +01002936 if (true_target == nullptr) {
2937 // The true target is fallthrough.
2938 DCHECK(false_target != nullptr);
2939 non_fallthrough_target = false_target;
2940 invert = true;
2941 emit_both_branches = false;
2942 } else {
2943 non_fallthrough_target = true_target;
2944 invert = false;
2945 // Either the false target is fallthrough, or there is no fallthrough
2946 // and both branches must be emitted.
2947 emit_both_branches = (false_target != nullptr);
Scott Wakelingfe885462016-09-22 10:24:38 +01002948 }
2949
Anton Kirilov23b752b2017-07-20 14:40:44 +01002950 const auto cond = GenerateTest(condition, invert, codegen_);
2951
2952 __ B(cond.first, non_fallthrough_target, is_far_target);
2953
2954 if (emit_both_branches) {
2955 // No target falls through, we need to branch.
2956 __ B(false_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01002957 }
2958}
2959
2960void InstructionCodeGeneratorARMVIXL::GenerateTestAndBranch(HInstruction* instruction,
2961 size_t condition_input_index,
2962 vixl32::Label* true_target,
xueliang.zhongf51bc622016-11-04 09:23:32 +00002963 vixl32::Label* false_target,
2964 bool far_target) {
Scott Wakelingfe885462016-09-22 10:24:38 +01002965 HInstruction* cond = instruction->InputAt(condition_input_index);
2966
2967 if (true_target == nullptr && false_target == nullptr) {
2968 // Nothing to do. The code always falls through.
2969 return;
2970 } else if (cond->IsIntConstant()) {
2971 // Constant condition, statically compared against "true" (integer value 1).
2972 if (cond->AsIntConstant()->IsTrue()) {
2973 if (true_target != nullptr) {
2974 __ B(true_target);
2975 }
2976 } else {
Anton Kirilov644032c2016-12-06 17:51:43 +00002977 DCHECK(cond->AsIntConstant()->IsFalse()) << Int32ConstantFrom(cond);
Scott Wakelingfe885462016-09-22 10:24:38 +01002978 if (false_target != nullptr) {
2979 __ B(false_target);
2980 }
2981 }
2982 return;
2983 }
2984
2985 // The following code generates these patterns:
2986 // (1) true_target == nullptr && false_target != nullptr
2987 // - opposite condition true => branch to false_target
2988 // (2) true_target != nullptr && false_target == nullptr
2989 // - condition true => branch to true_target
2990 // (3) true_target != nullptr && false_target != nullptr
2991 // - condition true => branch to true_target
2992 // - branch to false_target
2993 if (IsBooleanValueOrMaterializedCondition(cond)) {
2994 // Condition has been materialized, compare the output to 0.
2995 if (kIsDebugBuild) {
2996 Location cond_val = instruction->GetLocations()->InAt(condition_input_index);
2997 DCHECK(cond_val.IsRegister());
2998 }
2999 if (true_target == nullptr) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00003000 __ CompareAndBranchIfZero(InputRegisterAt(instruction, condition_input_index),
3001 false_target,
3002 far_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01003003 } else {
xueliang.zhongf51bc622016-11-04 09:23:32 +00003004 __ CompareAndBranchIfNonZero(InputRegisterAt(instruction, condition_input_index),
3005 true_target,
3006 far_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01003007 }
3008 } else {
3009 // Condition has not been materialized. Use its inputs as the comparison and
3010 // its condition as the branch condition.
3011 HCondition* condition = cond->AsCondition();
3012
3013 // If this is a long or FP comparison that has been folded into
3014 // the HCondition, generate the comparison directly.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003015 DataType::Type type = condition->InputAt(0)->GetType();
3016 if (type == DataType::Type::kInt64 || DataType::IsFloatingPointType(type)) {
Anton Kirilovfd522532017-05-10 12:46:57 +01003017 GenerateCompareTestAndBranch(condition, true_target, false_target, far_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01003018 return;
3019 }
3020
Donghui Bai426b49c2016-11-08 14:55:38 +08003021 vixl32::Label* non_fallthrough_target;
3022 vixl32::Condition arm_cond = vixl32::Condition::None();
3023 const vixl32::Register left = InputRegisterAt(cond, 0);
3024 const Operand right = InputOperandAt(cond, 1);
3025
Scott Wakelingfe885462016-09-22 10:24:38 +01003026 if (true_target == nullptr) {
Donghui Bai426b49c2016-11-08 14:55:38 +08003027 arm_cond = ARMCondition(condition->GetOppositeCondition());
3028 non_fallthrough_target = false_target;
Scott Wakelingfe885462016-09-22 10:24:38 +01003029 } else {
Donghui Bai426b49c2016-11-08 14:55:38 +08003030 arm_cond = ARMCondition(condition->GetCondition());
3031 non_fallthrough_target = true_target;
3032 }
3033
3034 if (right.IsImmediate() && right.GetImmediate() == 0 && (arm_cond.Is(ne) || arm_cond.Is(eq))) {
3035 if (arm_cond.Is(eq)) {
Anton Kirilovfd522532017-05-10 12:46:57 +01003036 __ CompareAndBranchIfZero(left, non_fallthrough_target, far_target);
Donghui Bai426b49c2016-11-08 14:55:38 +08003037 } else {
3038 DCHECK(arm_cond.Is(ne));
Anton Kirilovfd522532017-05-10 12:46:57 +01003039 __ CompareAndBranchIfNonZero(left, non_fallthrough_target, far_target);
Donghui Bai426b49c2016-11-08 14:55:38 +08003040 }
3041 } else {
3042 __ Cmp(left, right);
Anton Kirilovfd522532017-05-10 12:46:57 +01003043 __ B(arm_cond, non_fallthrough_target, far_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01003044 }
3045 }
3046
3047 // If neither branch falls through (case 3), the conditional branch to `true_target`
3048 // was already emitted (case 2) and we need to emit a jump to `false_target`.
3049 if (true_target != nullptr && false_target != nullptr) {
3050 __ B(false_target);
3051 }
3052}
3053
3054void LocationsBuilderARMVIXL::VisitIf(HIf* if_instr) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01003055 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(if_instr);
Scott Wakelingfe885462016-09-22 10:24:38 +01003056 if (IsBooleanValueOrMaterializedCondition(if_instr->InputAt(0))) {
3057 locations->SetInAt(0, Location::RequiresRegister());
3058 }
3059}
3060
3061void InstructionCodeGeneratorARMVIXL::VisitIf(HIf* if_instr) {
3062 HBasicBlock* true_successor = if_instr->IfTrueSuccessor();
3063 HBasicBlock* false_successor = if_instr->IfFalseSuccessor();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003064 vixl32::Label* true_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), true_successor) ?
3065 nullptr : codegen_->GetLabelOf(true_successor);
3066 vixl32::Label* false_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), false_successor) ?
3067 nullptr : codegen_->GetLabelOf(false_successor);
Scott Wakelingfe885462016-09-22 10:24:38 +01003068 GenerateTestAndBranch(if_instr, /* condition_input_index */ 0, true_target, false_target);
3069}
3070
Scott Wakelingc34dba72016-10-03 10:14:44 +01003071void LocationsBuilderARMVIXL::VisitDeoptimize(HDeoptimize* deoptimize) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01003072 LocationSummary* locations = new (GetGraph()->GetAllocator())
Scott Wakelingc34dba72016-10-03 10:14:44 +01003073 LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath);
Nicolas Geoffray4e92c3c2017-05-08 09:34:26 +01003074 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3075 RegisterSet caller_saves = RegisterSet::Empty();
3076 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0)));
3077 locations->SetCustomSlowPathCallerSaves(caller_saves);
Scott Wakelingc34dba72016-10-03 10:14:44 +01003078 if (IsBooleanValueOrMaterializedCondition(deoptimize->InputAt(0))) {
3079 locations->SetInAt(0, Location::RequiresRegister());
3080 }
3081}
3082
3083void InstructionCodeGeneratorARMVIXL::VisitDeoptimize(HDeoptimize* deoptimize) {
3084 SlowPathCodeARMVIXL* slow_path =
3085 deopt_slow_paths_.NewSlowPath<DeoptimizationSlowPathARMVIXL>(deoptimize);
3086 GenerateTestAndBranch(deoptimize,
3087 /* condition_input_index */ 0,
3088 slow_path->GetEntryLabel(),
3089 /* false_target */ nullptr);
3090}
3091
Artem Serovd4cc5b22016-11-04 11:19:09 +00003092void LocationsBuilderARMVIXL::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01003093 LocationSummary* locations = new (GetGraph()->GetAllocator())
Artem Serovd4cc5b22016-11-04 11:19:09 +00003094 LocationSummary(flag, LocationSummary::kNoCall);
3095 locations->SetOut(Location::RequiresRegister());
3096}
3097
3098void InstructionCodeGeneratorARMVIXL::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
3099 GetAssembler()->LoadFromOffset(kLoadWord,
3100 OutputRegister(flag),
3101 sp,
3102 codegen_->GetStackOffsetOfShouldDeoptimizeFlag());
3103}
3104
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003105void LocationsBuilderARMVIXL::VisitSelect(HSelect* select) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01003106 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(select);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003107 const bool is_floating_point = DataType::IsFloatingPointType(select->GetType());
Donghui Bai426b49c2016-11-08 14:55:38 +08003108
3109 if (is_floating_point) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003110 locations->SetInAt(0, Location::RequiresFpuRegister());
Donghui Bai426b49c2016-11-08 14:55:38 +08003111 locations->SetInAt(1, Location::FpuRegisterOrConstant(select->GetTrueValue()));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003112 } else {
3113 locations->SetInAt(0, Location::RequiresRegister());
Donghui Bai426b49c2016-11-08 14:55:38 +08003114 locations->SetInAt(1, Arm8BitEncodableConstantOrRegister(select->GetTrueValue()));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003115 }
Donghui Bai426b49c2016-11-08 14:55:38 +08003116
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003117 if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) {
Donghui Bai426b49c2016-11-08 14:55:38 +08003118 locations->SetInAt(2, Location::RegisterOrConstant(select->GetCondition()));
3119 // The code generator handles overlap with the values, but not with the condition.
3120 locations->SetOut(Location::SameAsFirstInput());
3121 } else if (is_floating_point) {
3122 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3123 } else {
3124 if (!locations->InAt(1).IsConstant()) {
3125 locations->SetInAt(0, Arm8BitEncodableConstantOrRegister(select->GetFalseValue()));
3126 }
3127
3128 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003129 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003130}
3131
3132void InstructionCodeGeneratorARMVIXL::VisitSelect(HSelect* select) {
Donghui Bai426b49c2016-11-08 14:55:38 +08003133 HInstruction* const condition = select->GetCondition();
3134 const LocationSummary* const locations = select->GetLocations();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003135 const DataType::Type type = select->GetType();
Donghui Bai426b49c2016-11-08 14:55:38 +08003136 const Location first = locations->InAt(0);
3137 const Location out = locations->Out();
3138 const Location second = locations->InAt(1);
3139 Location src;
3140
3141 if (condition->IsIntConstant()) {
3142 if (condition->AsIntConstant()->IsFalse()) {
3143 src = first;
3144 } else {
3145 src = second;
3146 }
3147
3148 codegen_->MoveLocation(out, src, type);
3149 return;
3150 }
3151
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003152 if (!DataType::IsFloatingPointType(type)) {
Donghui Bai426b49c2016-11-08 14:55:38 +08003153 bool invert = false;
3154
3155 if (out.Equals(second)) {
3156 src = first;
3157 invert = true;
3158 } else if (out.Equals(first)) {
3159 src = second;
3160 } else if (second.IsConstant()) {
3161 DCHECK(CanEncodeConstantAs8BitImmediate(second.GetConstant()));
3162 src = second;
3163 } else if (first.IsConstant()) {
3164 DCHECK(CanEncodeConstantAs8BitImmediate(first.GetConstant()));
3165 src = first;
3166 invert = true;
3167 } else {
3168 src = second;
3169 }
3170
3171 if (CanGenerateConditionalMove(out, src)) {
3172 if (!out.Equals(first) && !out.Equals(second)) {
3173 codegen_->MoveLocation(out, src.Equals(first) ? second : first, type);
3174 }
3175
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003176 std::pair<vixl32::Condition, vixl32::Condition> cond(eq, ne);
3177
3178 if (IsBooleanValueOrMaterializedCondition(condition)) {
3179 __ Cmp(InputRegisterAt(select, 2), 0);
3180 cond = invert ? std::make_pair(eq, ne) : std::make_pair(ne, eq);
3181 } else {
3182 cond = GenerateTest(condition->AsCondition(), invert, codegen_);
3183 }
3184
Donghui Bai426b49c2016-11-08 14:55:38 +08003185 const size_t instr_count = out.IsRegisterPair() ? 4 : 2;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003186 // We use the scope because of the IT block that follows.
Donghui Bai426b49c2016-11-08 14:55:38 +08003187 ExactAssemblyScope guard(GetVIXLAssembler(),
3188 instr_count * vixl32::k16BitT32InstructionSizeInBytes,
3189 CodeBufferCheckScope::kExactSize);
3190
3191 if (out.IsRegister()) {
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003192 __ it(cond.first);
3193 __ mov(cond.first, RegisterFrom(out), OperandFrom(src, type));
Donghui Bai426b49c2016-11-08 14:55:38 +08003194 } else {
3195 DCHECK(out.IsRegisterPair());
3196
3197 Operand operand_high(0);
3198 Operand operand_low(0);
3199
3200 if (src.IsConstant()) {
3201 const int64_t value = Int64ConstantFrom(src);
3202
3203 operand_high = High32Bits(value);
3204 operand_low = Low32Bits(value);
3205 } else {
3206 DCHECK(src.IsRegisterPair());
3207 operand_high = HighRegisterFrom(src);
3208 operand_low = LowRegisterFrom(src);
3209 }
3210
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003211 __ it(cond.first);
3212 __ mov(cond.first, LowRegisterFrom(out), operand_low);
3213 __ it(cond.first);
3214 __ mov(cond.first, HighRegisterFrom(out), operand_high);
Donghui Bai426b49c2016-11-08 14:55:38 +08003215 }
3216
3217 return;
3218 }
3219 }
3220
3221 vixl32::Label* false_target = nullptr;
3222 vixl32::Label* true_target = nullptr;
3223 vixl32::Label select_end;
3224 vixl32::Label* const target = codegen_->GetFinalLabel(select, &select_end);
3225
3226 if (out.Equals(second)) {
3227 true_target = target;
3228 src = first;
3229 } else {
3230 false_target = target;
3231 src = second;
3232
3233 if (!out.Equals(first)) {
3234 codegen_->MoveLocation(out, first, type);
3235 }
3236 }
3237
3238 GenerateTestAndBranch(select, 2, true_target, false_target, /* far_target */ false);
3239 codegen_->MoveLocation(out, src, type);
3240
3241 if (select_end.IsReferenced()) {
3242 __ Bind(&select_end);
3243 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003244}
3245
Artem Serov551b28f2016-10-18 19:11:30 +01003246void LocationsBuilderARMVIXL::VisitNativeDebugInfo(HNativeDebugInfo* info) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01003247 new (GetGraph()->GetAllocator()) LocationSummary(info);
Artem Serov551b28f2016-10-18 19:11:30 +01003248}
3249
3250void InstructionCodeGeneratorARMVIXL::VisitNativeDebugInfo(HNativeDebugInfo*) {
3251 // MaybeRecordNativeDebugInfo is already called implicitly in CodeGenerator::Compile.
3252}
3253
Scott Wakelingfe885462016-09-22 10:24:38 +01003254void CodeGeneratorARMVIXL::GenerateNop() {
3255 __ Nop();
3256}
3257
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003258// `temp` is an extra temporary register that is used for some conditions;
3259// callers may not specify it, in which case the method will use a scratch
3260// register instead.
3261void CodeGeneratorARMVIXL::GenerateConditionWithZero(IfCondition condition,
3262 vixl32::Register out,
3263 vixl32::Register in,
3264 vixl32::Register temp) {
3265 switch (condition) {
3266 case kCondEQ:
3267 // x <= 0 iff x == 0 when the comparison is unsigned.
3268 case kCondBE:
3269 if (!temp.IsValid() || (out.IsLow() && !out.Is(in))) {
3270 temp = out;
3271 }
3272
3273 // Avoid 32-bit instructions if possible; note that `in` and `temp` must be
3274 // different as well.
3275 if (in.IsLow() && temp.IsLow() && !in.Is(temp)) {
3276 // temp = - in; only 0 sets the carry flag.
3277 __ Rsbs(temp, in, 0);
3278
3279 if (out.Is(in)) {
3280 std::swap(in, temp);
3281 }
3282
3283 // out = - in + in + carry = carry
3284 __ Adc(out, temp, in);
3285 } else {
3286 // If `in` is 0, then it has 32 leading zeros, and less than that otherwise.
3287 __ Clz(out, in);
3288 // Any number less than 32 logically shifted right by 5 bits results in 0;
3289 // the same operation on 32 yields 1.
3290 __ Lsr(out, out, 5);
3291 }
3292
3293 break;
3294 case kCondNE:
3295 // x > 0 iff x != 0 when the comparison is unsigned.
3296 case kCondA: {
3297 UseScratchRegisterScope temps(GetVIXLAssembler());
3298
3299 if (out.Is(in)) {
3300 if (!temp.IsValid() || in.Is(temp)) {
3301 temp = temps.Acquire();
3302 }
3303 } else if (!temp.IsValid() || !temp.IsLow()) {
3304 temp = out;
3305 }
3306
3307 // temp = in - 1; only 0 does not set the carry flag.
3308 __ Subs(temp, in, 1);
3309 // out = in + ~temp + carry = in + (-(in - 1) - 1) + carry = in - in + 1 - 1 + carry = carry
3310 __ Sbc(out, in, temp);
3311 break;
3312 }
3313 case kCondGE:
3314 __ Mvn(out, in);
3315 in = out;
3316 FALLTHROUGH_INTENDED;
3317 case kCondLT:
3318 // We only care about the sign bit.
3319 __ Lsr(out, in, 31);
3320 break;
3321 case kCondAE:
3322 // Trivially true.
3323 __ Mov(out, 1);
3324 break;
3325 case kCondB:
3326 // Trivially false.
3327 __ Mov(out, 0);
3328 break;
3329 default:
3330 LOG(FATAL) << "Unexpected condition " << condition;
3331 UNREACHABLE();
3332 }
3333}
3334
Scott Wakelingfe885462016-09-22 10:24:38 +01003335void LocationsBuilderARMVIXL::HandleCondition(HCondition* cond) {
3336 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003337 new (GetGraph()->GetAllocator()) LocationSummary(cond, LocationSummary::kNoCall);
Scott Wakelingfe885462016-09-22 10:24:38 +01003338 // Handle the long/FP comparisons made in instruction simplification.
3339 switch (cond->InputAt(0)->GetType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003340 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003341 locations->SetInAt(0, Location::RequiresRegister());
3342 locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
3343 if (!cond->IsEmittedAtUseSite()) {
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003344 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Scott Wakelingfe885462016-09-22 10:24:38 +01003345 }
3346 break;
3347
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003348 case DataType::Type::kFloat32:
3349 case DataType::Type::kFloat64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003350 locations->SetInAt(0, Location::RequiresFpuRegister());
Artem Serov657022c2016-11-23 14:19:38 +00003351 locations->SetInAt(1, ArithmeticZeroOrFpuRegister(cond->InputAt(1)));
Scott Wakelingfe885462016-09-22 10:24:38 +01003352 if (!cond->IsEmittedAtUseSite()) {
3353 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3354 }
3355 break;
3356
3357 default:
3358 locations->SetInAt(0, Location::RequiresRegister());
3359 locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
3360 if (!cond->IsEmittedAtUseSite()) {
3361 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3362 }
3363 }
3364}
3365
3366void InstructionCodeGeneratorARMVIXL::HandleCondition(HCondition* cond) {
3367 if (cond->IsEmittedAtUseSite()) {
3368 return;
3369 }
3370
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003371 const DataType::Type type = cond->GetLeft()->GetType();
Scott Wakelingfe885462016-09-22 10:24:38 +01003372
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003373 if (DataType::IsFloatingPointType(type)) {
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003374 GenerateConditionGeneric(cond, codegen_);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003375 return;
Scott Wakelingfe885462016-09-22 10:24:38 +01003376 }
3377
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003378 DCHECK(DataType::IsIntegralType(type) || type == DataType::Type::kReference) << type;
Scott Wakelingfe885462016-09-22 10:24:38 +01003379
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003380 const IfCondition condition = cond->GetCondition();
Scott Wakelingfe885462016-09-22 10:24:38 +01003381
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003382 // A condition with only one boolean input, or two boolean inputs without being equality or
3383 // inequality results from transformations done by the instruction simplifier, and is handled
3384 // as a regular condition with integral inputs.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003385 if (type == DataType::Type::kBool &&
3386 cond->GetRight()->GetType() == DataType::Type::kBool &&
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003387 (condition == kCondEQ || condition == kCondNE)) {
3388 vixl32::Register left = InputRegisterAt(cond, 0);
3389 const vixl32::Register out = OutputRegister(cond);
3390 const Location right_loc = cond->GetLocations()->InAt(1);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003391
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003392 // The constant case is handled by the instruction simplifier.
3393 DCHECK(!right_loc.IsConstant());
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003394
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003395 vixl32::Register right = RegisterFrom(right_loc);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003396
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003397 // Avoid 32-bit instructions if possible.
3398 if (out.Is(right)) {
3399 std::swap(left, right);
3400 }
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003401
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003402 __ Eor(out, left, right);
3403
3404 if (condition == kCondEQ) {
3405 __ Eor(out, out, 1);
3406 }
3407
3408 return;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003409 }
Anton Kirilov6f644202017-02-27 18:29:45 +00003410
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003411 GenerateConditionIntegralOrNonPrimitive(cond, codegen_);
Scott Wakelingfe885462016-09-22 10:24:38 +01003412}
3413
3414void LocationsBuilderARMVIXL::VisitEqual(HEqual* comp) {
3415 HandleCondition(comp);
3416}
3417
3418void InstructionCodeGeneratorARMVIXL::VisitEqual(HEqual* comp) {
3419 HandleCondition(comp);
3420}
3421
3422void LocationsBuilderARMVIXL::VisitNotEqual(HNotEqual* comp) {
3423 HandleCondition(comp);
3424}
3425
3426void InstructionCodeGeneratorARMVIXL::VisitNotEqual(HNotEqual* comp) {
3427 HandleCondition(comp);
3428}
3429
3430void LocationsBuilderARMVIXL::VisitLessThan(HLessThan* comp) {
3431 HandleCondition(comp);
3432}
3433
3434void InstructionCodeGeneratorARMVIXL::VisitLessThan(HLessThan* comp) {
3435 HandleCondition(comp);
3436}
3437
3438void LocationsBuilderARMVIXL::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
3439 HandleCondition(comp);
3440}
3441
3442void InstructionCodeGeneratorARMVIXL::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
3443 HandleCondition(comp);
3444}
3445
3446void LocationsBuilderARMVIXL::VisitGreaterThan(HGreaterThan* comp) {
3447 HandleCondition(comp);
3448}
3449
3450void InstructionCodeGeneratorARMVIXL::VisitGreaterThan(HGreaterThan* comp) {
3451 HandleCondition(comp);
3452}
3453
3454void LocationsBuilderARMVIXL::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
3455 HandleCondition(comp);
3456}
3457
3458void InstructionCodeGeneratorARMVIXL::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
3459 HandleCondition(comp);
3460}
3461
3462void LocationsBuilderARMVIXL::VisitBelow(HBelow* comp) {
3463 HandleCondition(comp);
3464}
3465
3466void InstructionCodeGeneratorARMVIXL::VisitBelow(HBelow* comp) {
3467 HandleCondition(comp);
3468}
3469
3470void LocationsBuilderARMVIXL::VisitBelowOrEqual(HBelowOrEqual* comp) {
3471 HandleCondition(comp);
3472}
3473
3474void InstructionCodeGeneratorARMVIXL::VisitBelowOrEqual(HBelowOrEqual* comp) {
3475 HandleCondition(comp);
3476}
3477
3478void LocationsBuilderARMVIXL::VisitAbove(HAbove* comp) {
3479 HandleCondition(comp);
3480}
3481
3482void InstructionCodeGeneratorARMVIXL::VisitAbove(HAbove* comp) {
3483 HandleCondition(comp);
3484}
3485
3486void LocationsBuilderARMVIXL::VisitAboveOrEqual(HAboveOrEqual* comp) {
3487 HandleCondition(comp);
3488}
3489
3490void InstructionCodeGeneratorARMVIXL::VisitAboveOrEqual(HAboveOrEqual* comp) {
3491 HandleCondition(comp);
3492}
3493
3494void LocationsBuilderARMVIXL::VisitIntConstant(HIntConstant* constant) {
3495 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003496 new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
Scott Wakelingfe885462016-09-22 10:24:38 +01003497 locations->SetOut(Location::ConstantLocation(constant));
3498}
3499
3500void InstructionCodeGeneratorARMVIXL::VisitIntConstant(HIntConstant* constant ATTRIBUTE_UNUSED) {
3501 // Will be generated at use site.
3502}
3503
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003504void LocationsBuilderARMVIXL::VisitNullConstant(HNullConstant* constant) {
3505 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003506 new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003507 locations->SetOut(Location::ConstantLocation(constant));
3508}
3509
3510void InstructionCodeGeneratorARMVIXL::VisitNullConstant(HNullConstant* constant ATTRIBUTE_UNUSED) {
3511 // Will be generated at use site.
3512}
3513
Scott Wakelingfe885462016-09-22 10:24:38 +01003514void LocationsBuilderARMVIXL::VisitLongConstant(HLongConstant* constant) {
3515 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003516 new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
Scott Wakelingfe885462016-09-22 10:24:38 +01003517 locations->SetOut(Location::ConstantLocation(constant));
3518}
3519
3520void InstructionCodeGeneratorARMVIXL::VisitLongConstant(HLongConstant* constant ATTRIBUTE_UNUSED) {
3521 // Will be generated at use site.
3522}
3523
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01003524void LocationsBuilderARMVIXL::VisitFloatConstant(HFloatConstant* constant) {
3525 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003526 new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01003527 locations->SetOut(Location::ConstantLocation(constant));
3528}
3529
Scott Wakelingc34dba72016-10-03 10:14:44 +01003530void InstructionCodeGeneratorARMVIXL::VisitFloatConstant(
3531 HFloatConstant* constant ATTRIBUTE_UNUSED) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01003532 // Will be generated at use site.
3533}
3534
3535void LocationsBuilderARMVIXL::VisitDoubleConstant(HDoubleConstant* constant) {
3536 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003537 new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01003538 locations->SetOut(Location::ConstantLocation(constant));
3539}
3540
Scott Wakelingc34dba72016-10-03 10:14:44 +01003541void InstructionCodeGeneratorARMVIXL::VisitDoubleConstant(
3542 HDoubleConstant* constant ATTRIBUTE_UNUSED) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01003543 // Will be generated at use site.
3544}
3545
Igor Murashkind01745e2017-04-05 16:40:31 -07003546void LocationsBuilderARMVIXL::VisitConstructorFence(HConstructorFence* constructor_fence) {
3547 constructor_fence->SetLocations(nullptr);
3548}
3549
3550void InstructionCodeGeneratorARMVIXL::VisitConstructorFence(
3551 HConstructorFence* constructor_fence ATTRIBUTE_UNUSED) {
3552 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
3553}
3554
Scott Wakelingfe885462016-09-22 10:24:38 +01003555void LocationsBuilderARMVIXL::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
3556 memory_barrier->SetLocations(nullptr);
3557}
3558
3559void InstructionCodeGeneratorARMVIXL::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
3560 codegen_->GenerateMemoryBarrier(memory_barrier->GetBarrierKind());
3561}
3562
3563void LocationsBuilderARMVIXL::VisitReturnVoid(HReturnVoid* ret) {
3564 ret->SetLocations(nullptr);
3565}
3566
3567void InstructionCodeGeneratorARMVIXL::VisitReturnVoid(HReturnVoid* ret ATTRIBUTE_UNUSED) {
3568 codegen_->GenerateFrameExit();
3569}
3570
3571void LocationsBuilderARMVIXL::VisitReturn(HReturn* ret) {
3572 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003573 new (GetGraph()->GetAllocator()) LocationSummary(ret, LocationSummary::kNoCall);
Scott Wakelingfe885462016-09-22 10:24:38 +01003574 locations->SetInAt(0, parameter_visitor_.GetReturnLocation(ret->InputAt(0)->GetType()));
3575}
3576
3577void InstructionCodeGeneratorARMVIXL::VisitReturn(HReturn* ret ATTRIBUTE_UNUSED) {
3578 codegen_->GenerateFrameExit();
3579}
3580
Artem Serovcfbe9132016-10-14 15:58:56 +01003581void LocationsBuilderARMVIXL::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
3582 // The trampoline uses the same calling convention as dex calling conventions,
3583 // except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain
3584 // the method_idx.
3585 HandleInvoke(invoke);
3586}
3587
3588void InstructionCodeGeneratorARMVIXL::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
3589 codegen_->GenerateInvokeUnresolvedRuntimeCall(invoke);
Roland Levillain5daa4952017-07-03 17:23:56 +01003590 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 3);
Artem Serovcfbe9132016-10-14 15:58:56 +01003591}
3592
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003593void LocationsBuilderARMVIXL::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
3594 // Explicit clinit checks triggered by static invokes must have been pruned by
3595 // art::PrepareForRegisterAllocation.
3596 DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
3597
Anton Kirilov5ec62182016-10-13 20:16:02 +01003598 IntrinsicLocationsBuilderARMVIXL intrinsic(codegen_);
3599 if (intrinsic.TryDispatch(invoke)) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01003600 return;
3601 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003602
3603 HandleInvoke(invoke);
3604}
3605
Anton Kirilov5ec62182016-10-13 20:16:02 +01003606static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorARMVIXL* codegen) {
3607 if (invoke->GetLocations()->Intrinsified()) {
3608 IntrinsicCodeGeneratorARMVIXL intrinsic(codegen);
3609 intrinsic.Dispatch(invoke);
3610 return true;
3611 }
3612 return false;
3613}
3614
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003615void InstructionCodeGeneratorARMVIXL::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
3616 // Explicit clinit checks triggered by static invokes must have been pruned by
3617 // art::PrepareForRegisterAllocation.
3618 DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
3619
Anton Kirilov5ec62182016-10-13 20:16:02 +01003620 if (TryGenerateIntrinsicCode(invoke, codegen_)) {
Roland Levillain5daa4952017-07-03 17:23:56 +01003621 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 4);
Anton Kirilov5ec62182016-10-13 20:16:02 +01003622 return;
3623 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003624
3625 LocationSummary* locations = invoke->GetLocations();
Artem Serovd4cc5b22016-11-04 11:19:09 +00003626 codegen_->GenerateStaticOrDirectCall(
3627 invoke, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation());
Roland Levillain5daa4952017-07-03 17:23:56 +01003628
3629 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 5);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003630}
3631
3632void LocationsBuilderARMVIXL::HandleInvoke(HInvoke* invoke) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00003633 InvokeDexCallingConventionVisitorARMVIXL calling_convention_visitor;
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003634 CodeGenerator::CreateCommonInvokeLocationSummary(invoke, &calling_convention_visitor);
3635}
3636
3637void LocationsBuilderARMVIXL::VisitInvokeVirtual(HInvokeVirtual* invoke) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01003638 IntrinsicLocationsBuilderARMVIXL intrinsic(codegen_);
3639 if (intrinsic.TryDispatch(invoke)) {
3640 return;
3641 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003642
3643 HandleInvoke(invoke);
3644}
3645
3646void InstructionCodeGeneratorARMVIXL::VisitInvokeVirtual(HInvokeVirtual* invoke) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01003647 if (TryGenerateIntrinsicCode(invoke, codegen_)) {
Roland Levillain5daa4952017-07-03 17:23:56 +01003648 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 6);
Anton Kirilov5ec62182016-10-13 20:16:02 +01003649 return;
3650 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003651
3652 codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0));
Alexandre Rames374ddf32016-11-04 10:40:49 +00003653 DCHECK(!codegen_->IsLeafMethod());
Roland Levillain5daa4952017-07-03 17:23:56 +01003654
3655 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 7);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003656}
3657
Artem Serovcfbe9132016-10-14 15:58:56 +01003658void LocationsBuilderARMVIXL::VisitInvokeInterface(HInvokeInterface* invoke) {
3659 HandleInvoke(invoke);
3660 // Add the hidden argument.
3661 invoke->GetLocations()->AddTemp(LocationFrom(r12));
3662}
3663
3664void InstructionCodeGeneratorARMVIXL::VisitInvokeInterface(HInvokeInterface* invoke) {
3665 // TODO: b/18116999, our IMTs can miss an IncompatibleClassChangeError.
3666 LocationSummary* locations = invoke->GetLocations();
3667 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
3668 vixl32::Register hidden_reg = RegisterFrom(locations->GetTemp(1));
3669 Location receiver = locations->InAt(0);
3670 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
3671
3672 DCHECK(!receiver.IsStackSlot());
3673
Alexandre Rames374ddf32016-11-04 10:40:49 +00003674 // Ensure the pc position is recorded immediately after the `ldr` instruction.
3675 {
Artem Serov0fb37192016-12-06 18:13:40 +00003676 ExactAssemblyScope aas(GetVIXLAssembler(),
3677 vixl32::kMaxInstructionSizeInBytes,
3678 CodeBufferCheckScope::kMaximumSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00003679 // /* HeapReference<Class> */ temp = receiver->klass_
3680 __ ldr(temp, MemOperand(RegisterFrom(receiver), class_offset));
3681 codegen_->MaybeRecordImplicitNullCheck(invoke);
3682 }
Artem Serovcfbe9132016-10-14 15:58:56 +01003683 // Instead of simply (possibly) unpoisoning `temp` here, we should
3684 // emit a read barrier for the previous class reference load.
3685 // However this is not required in practice, as this is an
3686 // intermediate/temporary reference and because the current
3687 // concurrent copying collector keeps the from-space memory
3688 // intact/accessible until the end of the marking phase (the
3689 // concurrent copying collector may not in the future).
3690 GetAssembler()->MaybeUnpoisonHeapReference(temp);
3691 GetAssembler()->LoadFromOffset(kLoadWord,
3692 temp,
3693 temp,
3694 mirror::Class::ImtPtrOffset(kArmPointerSize).Uint32Value());
3695 uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement(
3696 invoke->GetImtIndex(), kArmPointerSize));
3697 // temp = temp->GetImtEntryAt(method_offset);
3698 GetAssembler()->LoadFromOffset(kLoadWord, temp, temp, method_offset);
3699 uint32_t entry_point =
3700 ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value();
3701 // LR = temp->GetEntryPoint();
3702 GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, entry_point);
3703
3704 // Set the hidden (in r12) argument. It is done here, right before a BLX to prevent other
3705 // instruction from clobbering it as they might use r12 as a scratch register.
3706 DCHECK(hidden_reg.Is(r12));
Scott Wakelingb77051e2016-11-21 19:46:00 +00003707
3708 {
3709 // The VIXL macro assembler may clobber any of the scratch registers that are available to it,
3710 // so it checks if the application is using them (by passing them to the macro assembler
3711 // methods). The following application of UseScratchRegisterScope corrects VIXL's notion of
3712 // what is available, and is the opposite of the standard usage: Instead of requesting a
3713 // temporary location, it imposes an external constraint (i.e. a specific register is reserved
3714 // for the hidden argument). Note that this works even if VIXL needs a scratch register itself
3715 // (to materialize the constant), since the destination register becomes available for such use
3716 // internally for the duration of the macro instruction.
3717 UseScratchRegisterScope temps(GetVIXLAssembler());
3718 temps.Exclude(hidden_reg);
3719 __ Mov(hidden_reg, invoke->GetDexMethodIndex());
3720 }
Artem Serovcfbe9132016-10-14 15:58:56 +01003721 {
Alexandre Rames374ddf32016-11-04 10:40:49 +00003722 // Ensure the pc position is recorded immediately after the `blx` instruction.
3723 // 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 +00003724 ExactAssemblyScope aas(GetVIXLAssembler(),
Alexandre Rames374ddf32016-11-04 10:40:49 +00003725 vixl32::k16BitT32InstructionSizeInBytes,
3726 CodeBufferCheckScope::kExactSize);
Artem Serovcfbe9132016-10-14 15:58:56 +01003727 // LR();
3728 __ blx(lr);
Artem Serovcfbe9132016-10-14 15:58:56 +01003729 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
Alexandre Rames374ddf32016-11-04 10:40:49 +00003730 DCHECK(!codegen_->IsLeafMethod());
Artem Serovcfbe9132016-10-14 15:58:56 +01003731 }
Roland Levillain5daa4952017-07-03 17:23:56 +01003732
3733 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 8);
Artem Serovcfbe9132016-10-14 15:58:56 +01003734}
3735
Orion Hodsonac141392017-01-13 11:53:47 +00003736void LocationsBuilderARMVIXL::VisitInvokePolymorphic(HInvokePolymorphic* invoke) {
3737 HandleInvoke(invoke);
3738}
3739
3740void InstructionCodeGeneratorARMVIXL::VisitInvokePolymorphic(HInvokePolymorphic* invoke) {
3741 codegen_->GenerateInvokePolymorphicCall(invoke);
Roland Levillain5daa4952017-07-03 17:23:56 +01003742 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 9);
Orion Hodsonac141392017-01-13 11:53:47 +00003743}
3744
Artem Serov02109dd2016-09-23 17:17:54 +01003745void LocationsBuilderARMVIXL::VisitNeg(HNeg* neg) {
3746 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003747 new (GetGraph()->GetAllocator()) LocationSummary(neg, LocationSummary::kNoCall);
Artem Serov02109dd2016-09-23 17:17:54 +01003748 switch (neg->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003749 case DataType::Type::kInt32: {
Artem Serov02109dd2016-09-23 17:17:54 +01003750 locations->SetInAt(0, Location::RequiresRegister());
3751 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3752 break;
3753 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003754 case DataType::Type::kInt64: {
Artem Serov02109dd2016-09-23 17:17:54 +01003755 locations->SetInAt(0, Location::RequiresRegister());
3756 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
3757 break;
3758 }
3759
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003760 case DataType::Type::kFloat32:
3761 case DataType::Type::kFloat64:
Artem Serov02109dd2016-09-23 17:17:54 +01003762 locations->SetInAt(0, Location::RequiresFpuRegister());
3763 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3764 break;
3765
3766 default:
3767 LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
3768 }
3769}
3770
3771void InstructionCodeGeneratorARMVIXL::VisitNeg(HNeg* neg) {
3772 LocationSummary* locations = neg->GetLocations();
3773 Location out = locations->Out();
3774 Location in = locations->InAt(0);
3775 switch (neg->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003776 case DataType::Type::kInt32:
Artem Serov02109dd2016-09-23 17:17:54 +01003777 __ Rsb(OutputRegister(neg), InputRegisterAt(neg, 0), 0);
3778 break;
3779
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003780 case DataType::Type::kInt64:
Artem Serov02109dd2016-09-23 17:17:54 +01003781 // out.lo = 0 - in.lo (and update the carry/borrow (C) flag)
3782 __ Rsbs(LowRegisterFrom(out), LowRegisterFrom(in), 0);
3783 // We cannot emit an RSC (Reverse Subtract with Carry)
3784 // instruction here, as it does not exist in the Thumb-2
3785 // instruction set. We use the following approach
3786 // using SBC and SUB instead.
3787 //
3788 // out.hi = -C
3789 __ Sbc(HighRegisterFrom(out), HighRegisterFrom(out), HighRegisterFrom(out));
3790 // out.hi = out.hi - in.hi
3791 __ Sub(HighRegisterFrom(out), HighRegisterFrom(out), HighRegisterFrom(in));
3792 break;
3793
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003794 case DataType::Type::kFloat32:
3795 case DataType::Type::kFloat64:
Anton Kirilov644032c2016-12-06 17:51:43 +00003796 __ Vneg(OutputVRegister(neg), InputVRegister(neg));
Artem Serov02109dd2016-09-23 17:17:54 +01003797 break;
3798
3799 default:
3800 LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
3801 }
3802}
3803
Scott Wakelingfe885462016-09-22 10:24:38 +01003804void LocationsBuilderARMVIXL::VisitTypeConversion(HTypeConversion* conversion) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003805 DataType::Type result_type = conversion->GetResultType();
3806 DataType::Type input_type = conversion->GetInputType();
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003807 DCHECK(!DataType::IsTypeConversionImplicit(input_type, result_type))
3808 << input_type << " -> " << result_type;
Scott Wakelingfe885462016-09-22 10:24:38 +01003809
3810 // The float-to-long, double-to-long and long-to-float type conversions
3811 // rely on a call to the runtime.
3812 LocationSummary::CallKind call_kind =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003813 (((input_type == DataType::Type::kFloat32 || input_type == DataType::Type::kFloat64)
3814 && result_type == DataType::Type::kInt64)
3815 || (input_type == DataType::Type::kInt64 && result_type == DataType::Type::kFloat32))
Scott Wakelingfe885462016-09-22 10:24:38 +01003816 ? LocationSummary::kCallOnMainOnly
3817 : LocationSummary::kNoCall;
3818 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003819 new (GetGraph()->GetAllocator()) LocationSummary(conversion, call_kind);
Scott Wakelingfe885462016-09-22 10:24:38 +01003820
Scott Wakelingfe885462016-09-22 10:24:38 +01003821 switch (result_type) {
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003822 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003823 case DataType::Type::kInt8:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003824 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003825 case DataType::Type::kInt16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003826 DCHECK(DataType::IsIntegralType(input_type)) << input_type;
3827 locations->SetInAt(0, Location::RequiresRegister());
3828 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Scott Wakelingfe885462016-09-22 10:24:38 +01003829 break;
3830
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003831 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003832 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003833 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003834 locations->SetInAt(0, Location::Any());
3835 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3836 break;
3837
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003838 case DataType::Type::kFloat32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003839 locations->SetInAt(0, Location::RequiresFpuRegister());
3840 locations->SetOut(Location::RequiresRegister());
3841 locations->AddTemp(Location::RequiresFpuRegister());
3842 break;
3843
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003844 case DataType::Type::kFloat64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003845 locations->SetInAt(0, Location::RequiresFpuRegister());
3846 locations->SetOut(Location::RequiresRegister());
3847 locations->AddTemp(Location::RequiresFpuRegister());
3848 break;
3849
3850 default:
3851 LOG(FATAL) << "Unexpected type conversion from " << input_type
3852 << " to " << result_type;
3853 }
3854 break;
3855
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003856 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003857 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003858 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003859 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003860 case DataType::Type::kInt8:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003861 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003862 case DataType::Type::kInt16:
3863 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003864 locations->SetInAt(0, Location::RequiresRegister());
3865 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3866 break;
3867
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003868 case DataType::Type::kFloat32: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003869 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3870 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
3871 locations->SetOut(LocationFrom(r0, r1));
Scott Wakelingfe885462016-09-22 10:24:38 +01003872 break;
3873 }
3874
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003875 case DataType::Type::kFloat64: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003876 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3877 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0),
3878 calling_convention.GetFpuRegisterAt(1)));
3879 locations->SetOut(LocationFrom(r0, r1));
Scott Wakelingfe885462016-09-22 10:24:38 +01003880 break;
3881 }
3882
3883 default:
3884 LOG(FATAL) << "Unexpected type conversion from " << input_type
3885 << " to " << result_type;
3886 }
3887 break;
3888
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003889 case DataType::Type::kFloat32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003890 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003891 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003892 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003893 case DataType::Type::kInt8:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003894 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003895 case DataType::Type::kInt16:
3896 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003897 locations->SetInAt(0, Location::RequiresRegister());
3898 locations->SetOut(Location::RequiresFpuRegister());
3899 break;
3900
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003901 case DataType::Type::kInt64: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003902 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3903 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0),
3904 calling_convention.GetRegisterAt(1)));
3905 locations->SetOut(LocationFrom(calling_convention.GetFpuRegisterAt(0)));
Scott Wakelingfe885462016-09-22 10:24:38 +01003906 break;
3907 }
3908
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003909 case DataType::Type::kFloat64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003910 locations->SetInAt(0, Location::RequiresFpuRegister());
3911 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3912 break;
3913
3914 default:
3915 LOG(FATAL) << "Unexpected type conversion from " << input_type
3916 << " to " << result_type;
Igor Murashkin2ffb7032017-11-08 13:35:21 -08003917 }
Scott Wakelingfe885462016-09-22 10:24:38 +01003918 break;
3919
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003920 case DataType::Type::kFloat64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003921 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003922 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003923 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003924 case DataType::Type::kInt8:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003925 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003926 case DataType::Type::kInt16:
3927 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003928 locations->SetInAt(0, Location::RequiresRegister());
3929 locations->SetOut(Location::RequiresFpuRegister());
3930 break;
3931
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003932 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003933 locations->SetInAt(0, Location::RequiresRegister());
3934 locations->SetOut(Location::RequiresFpuRegister());
3935 locations->AddTemp(Location::RequiresFpuRegister());
3936 locations->AddTemp(Location::RequiresFpuRegister());
3937 break;
3938
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003939 case DataType::Type::kFloat32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003940 locations->SetInAt(0, Location::RequiresFpuRegister());
3941 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3942 break;
3943
3944 default:
3945 LOG(FATAL) << "Unexpected type conversion from " << input_type
3946 << " to " << result_type;
Igor Murashkin2ffb7032017-11-08 13:35:21 -08003947 }
Scott Wakelingfe885462016-09-22 10:24:38 +01003948 break;
3949
3950 default:
3951 LOG(FATAL) << "Unexpected type conversion from " << input_type
3952 << " to " << result_type;
3953 }
3954}
3955
3956void InstructionCodeGeneratorARMVIXL::VisitTypeConversion(HTypeConversion* conversion) {
3957 LocationSummary* locations = conversion->GetLocations();
3958 Location out = locations->Out();
3959 Location in = locations->InAt(0);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003960 DataType::Type result_type = conversion->GetResultType();
3961 DataType::Type input_type = conversion->GetInputType();
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003962 DCHECK(!DataType::IsTypeConversionImplicit(input_type, result_type))
3963 << input_type << " -> " << result_type;
Scott Wakelingfe885462016-09-22 10:24:38 +01003964 switch (result_type) {
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003965 case DataType::Type::kUint8:
Scott Wakelingfe885462016-09-22 10:24:38 +01003966 switch (input_type) {
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003967 case DataType::Type::kInt8:
3968 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003969 case DataType::Type::kInt16:
3970 case DataType::Type::kInt32:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003971 __ Ubfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 8);
3972 break;
3973 case DataType::Type::kInt64:
3974 __ Ubfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 8);
3975 break;
3976
3977 default:
3978 LOG(FATAL) << "Unexpected type conversion from " << input_type
3979 << " to " << result_type;
3980 }
3981 break;
3982
3983 case DataType::Type::kInt8:
3984 switch (input_type) {
3985 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003986 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003987 case DataType::Type::kInt16:
3988 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003989 __ Sbfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 8);
3990 break;
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003991 case DataType::Type::kInt64:
3992 __ Sbfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 8);
3993 break;
3994
3995 default:
3996 LOG(FATAL) << "Unexpected type conversion from " << input_type
3997 << " to " << result_type;
3998 }
3999 break;
4000
4001 case DataType::Type::kUint16:
4002 switch (input_type) {
4003 case DataType::Type::kInt8:
4004 case DataType::Type::kInt16:
4005 case DataType::Type::kInt32:
4006 __ Ubfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 16);
4007 break;
4008 case DataType::Type::kInt64:
4009 __ Ubfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 16);
4010 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01004011
4012 default:
4013 LOG(FATAL) << "Unexpected type conversion from " << input_type
4014 << " to " << result_type;
4015 }
4016 break;
4017
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004018 case DataType::Type::kInt16:
Scott Wakelingfe885462016-09-22 10:24:38 +01004019 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004020 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01004021 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01004022 __ Sbfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 16);
4023 break;
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01004024 case DataType::Type::kInt64:
4025 __ Sbfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 16);
4026 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01004027
4028 default:
4029 LOG(FATAL) << "Unexpected type conversion from " << input_type
4030 << " to " << result_type;
4031 }
4032 break;
4033
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004034 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01004035 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004036 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01004037 DCHECK(out.IsRegister());
4038 if (in.IsRegisterPair()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004039 __ Mov(OutputRegister(conversion), LowRegisterFrom(in));
Scott Wakelingfe885462016-09-22 10:24:38 +01004040 } else if (in.IsDoubleStackSlot()) {
4041 GetAssembler()->LoadFromOffset(kLoadWord,
4042 OutputRegister(conversion),
4043 sp,
4044 in.GetStackIndex());
4045 } else {
4046 DCHECK(in.IsConstant());
4047 DCHECK(in.GetConstant()->IsLongConstant());
Vladimir Markoba1a48e2017-04-13 11:50:14 +01004048 int64_t value = in.GetConstant()->AsLongConstant()->GetValue();
4049 __ Mov(OutputRegister(conversion), static_cast<int32_t>(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01004050 }
4051 break;
4052
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004053 case DataType::Type::kFloat32: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004054 vixl32::SRegister temp = LowSRegisterFrom(locations->GetTemp(0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01004055 __ Vcvt(S32, F32, temp, InputSRegisterAt(conversion, 0));
Scott Wakelingfe885462016-09-22 10:24:38 +01004056 __ Vmov(OutputRegister(conversion), temp);
4057 break;
4058 }
4059
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004060 case DataType::Type::kFloat64: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004061 vixl32::SRegister temp_s = LowSRegisterFrom(locations->GetTemp(0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01004062 __ Vcvt(S32, F64, temp_s, DRegisterFrom(in));
Scott Wakelingfe885462016-09-22 10:24:38 +01004063 __ Vmov(OutputRegister(conversion), temp_s);
4064 break;
4065 }
4066
4067 default:
4068 LOG(FATAL) << "Unexpected type conversion from " << input_type
4069 << " to " << result_type;
4070 }
4071 break;
4072
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004073 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01004074 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004075 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01004076 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004077 case DataType::Type::kInt8:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01004078 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004079 case DataType::Type::kInt16:
4080 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01004081 DCHECK(out.IsRegisterPair());
4082 DCHECK(in.IsRegister());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004083 __ Mov(LowRegisterFrom(out), InputRegisterAt(conversion, 0));
Scott Wakelingfe885462016-09-22 10:24:38 +01004084 // Sign extension.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004085 __ Asr(HighRegisterFrom(out), LowRegisterFrom(out), 31);
Scott Wakelingfe885462016-09-22 10:24:38 +01004086 break;
4087
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004088 case DataType::Type::kFloat32:
Scott Wakelingfe885462016-09-22 10:24:38 +01004089 codegen_->InvokeRuntime(kQuickF2l, conversion, conversion->GetDexPc());
4090 CheckEntrypointTypes<kQuickF2l, int64_t, float>();
4091 break;
4092
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004093 case DataType::Type::kFloat64:
Scott Wakelingfe885462016-09-22 10:24:38 +01004094 codegen_->InvokeRuntime(kQuickD2l, conversion, conversion->GetDexPc());
4095 CheckEntrypointTypes<kQuickD2l, int64_t, double>();
4096 break;
4097
4098 default:
4099 LOG(FATAL) << "Unexpected type conversion from " << input_type
4100 << " to " << result_type;
4101 }
4102 break;
4103
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004104 case DataType::Type::kFloat32:
Scott Wakelingfe885462016-09-22 10:24:38 +01004105 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004106 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01004107 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004108 case DataType::Type::kInt8:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01004109 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004110 case DataType::Type::kInt16:
4111 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01004112 __ Vmov(OutputSRegister(conversion), InputRegisterAt(conversion, 0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01004113 __ Vcvt(F32, S32, OutputSRegister(conversion), OutputSRegister(conversion));
Scott Wakelingfe885462016-09-22 10:24:38 +01004114 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01004115
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004116 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01004117 codegen_->InvokeRuntime(kQuickL2f, conversion, conversion->GetDexPc());
4118 CheckEntrypointTypes<kQuickL2f, float, int64_t>();
4119 break;
4120
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004121 case DataType::Type::kFloat64:
Scott Wakelingc34dba72016-10-03 10:14:44 +01004122 __ Vcvt(F32, F64, OutputSRegister(conversion), DRegisterFrom(in));
Scott Wakelingfe885462016-09-22 10:24:38 +01004123 break;
4124
4125 default:
4126 LOG(FATAL) << "Unexpected type conversion from " << input_type
4127 << " to " << result_type;
Igor Murashkin2ffb7032017-11-08 13:35:21 -08004128 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004129 break;
4130
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004131 case DataType::Type::kFloat64:
Scott Wakelingfe885462016-09-22 10:24:38 +01004132 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004133 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01004134 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004135 case DataType::Type::kInt8:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01004136 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004137 case DataType::Type::kInt16:
4138 case DataType::Type::kInt32:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004139 __ Vmov(LowSRegisterFrom(out), InputRegisterAt(conversion, 0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01004140 __ Vcvt(F64, S32, DRegisterFrom(out), LowSRegisterFrom(out));
Scott Wakelingfe885462016-09-22 10:24:38 +01004141 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01004142
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004143 case DataType::Type::kInt64: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004144 vixl32::Register low = LowRegisterFrom(in);
4145 vixl32::Register high = HighRegisterFrom(in);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004146 vixl32::SRegister out_s = LowSRegisterFrom(out);
Scott Wakelingc34dba72016-10-03 10:14:44 +01004147 vixl32::DRegister out_d = DRegisterFrom(out);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004148 vixl32::SRegister temp_s = LowSRegisterFrom(locations->GetTemp(0));
Scott Wakelingc34dba72016-10-03 10:14:44 +01004149 vixl32::DRegister temp_d = DRegisterFrom(locations->GetTemp(0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01004150 vixl32::DRegister constant_d = DRegisterFrom(locations->GetTemp(1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004151
4152 // temp_d = int-to-double(high)
4153 __ Vmov(temp_s, high);
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01004154 __ Vcvt(F64, S32, temp_d, temp_s);
Scott Wakelingfe885462016-09-22 10:24:38 +01004155 // constant_d = k2Pow32EncodingForDouble
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004156 __ Vmov(constant_d, bit_cast<double, int64_t>(k2Pow32EncodingForDouble));
Scott Wakelingfe885462016-09-22 10:24:38 +01004157 // out_d = unsigned-to-double(low)
4158 __ Vmov(out_s, low);
4159 __ Vcvt(F64, U32, out_d, out_s);
4160 // out_d += temp_d * constant_d
4161 __ Vmla(F64, out_d, temp_d, constant_d);
4162 break;
4163 }
4164
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004165 case DataType::Type::kFloat32:
Scott Wakelingc34dba72016-10-03 10:14:44 +01004166 __ Vcvt(F64, F32, DRegisterFrom(out), InputSRegisterAt(conversion, 0));
Scott Wakelingfe885462016-09-22 10:24:38 +01004167 break;
4168
4169 default:
4170 LOG(FATAL) << "Unexpected type conversion from " << input_type
4171 << " to " << result_type;
Igor Murashkin2ffb7032017-11-08 13:35:21 -08004172 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004173 break;
4174
4175 default:
4176 LOG(FATAL) << "Unexpected type conversion from " << input_type
4177 << " to " << result_type;
4178 }
4179}
4180
4181void LocationsBuilderARMVIXL::VisitAdd(HAdd* add) {
4182 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01004183 new (GetGraph()->GetAllocator()) LocationSummary(add, LocationSummary::kNoCall);
Scott Wakelingfe885462016-09-22 10:24:38 +01004184 switch (add->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004185 case DataType::Type::kInt32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004186 locations->SetInAt(0, Location::RequiresRegister());
4187 locations->SetInAt(1, Location::RegisterOrConstant(add->InputAt(1)));
4188 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4189 break;
4190 }
4191
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004192 case DataType::Type::kInt64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004193 locations->SetInAt(0, Location::RequiresRegister());
Anton Kirilovdda43962016-11-21 19:55:20 +00004194 locations->SetInAt(1, ArmEncodableConstantOrRegister(add->InputAt(1), ADD));
Scott Wakelingfe885462016-09-22 10:24:38 +01004195 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4196 break;
4197 }
4198
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004199 case DataType::Type::kFloat32:
4200 case DataType::Type::kFloat64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004201 locations->SetInAt(0, Location::RequiresFpuRegister());
4202 locations->SetInAt(1, Location::RequiresFpuRegister());
4203 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4204 break;
4205 }
4206
4207 default:
4208 LOG(FATAL) << "Unexpected add type " << add->GetResultType();
4209 }
4210}
4211
4212void InstructionCodeGeneratorARMVIXL::VisitAdd(HAdd* add) {
4213 LocationSummary* locations = add->GetLocations();
4214 Location out = locations->Out();
4215 Location first = locations->InAt(0);
4216 Location second = locations->InAt(1);
4217
4218 switch (add->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004219 case DataType::Type::kInt32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004220 __ Add(OutputRegister(add), InputRegisterAt(add, 0), InputOperandAt(add, 1));
4221 }
4222 break;
4223
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004224 case DataType::Type::kInt64: {
Anton Kirilovdda43962016-11-21 19:55:20 +00004225 if (second.IsConstant()) {
4226 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
4227 GenerateAddLongConst(out, first, value);
4228 } else {
4229 DCHECK(second.IsRegisterPair());
4230 __ Adds(LowRegisterFrom(out), LowRegisterFrom(first), LowRegisterFrom(second));
4231 __ Adc(HighRegisterFrom(out), HighRegisterFrom(first), HighRegisterFrom(second));
4232 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004233 break;
4234 }
4235
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004236 case DataType::Type::kFloat32:
4237 case DataType::Type::kFloat64:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004238 __ Vadd(OutputVRegister(add), InputVRegisterAt(add, 0), InputVRegisterAt(add, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004239 break;
4240
4241 default:
4242 LOG(FATAL) << "Unexpected add type " << add->GetResultType();
4243 }
4244}
4245
4246void LocationsBuilderARMVIXL::VisitSub(HSub* sub) {
4247 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01004248 new (GetGraph()->GetAllocator()) LocationSummary(sub, LocationSummary::kNoCall);
Scott Wakelingfe885462016-09-22 10:24:38 +01004249 switch (sub->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004250 case DataType::Type::kInt32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004251 locations->SetInAt(0, Location::RequiresRegister());
4252 locations->SetInAt(1, Location::RegisterOrConstant(sub->InputAt(1)));
4253 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4254 break;
4255 }
4256
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004257 case DataType::Type::kInt64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004258 locations->SetInAt(0, Location::RequiresRegister());
Anton Kirilovdda43962016-11-21 19:55:20 +00004259 locations->SetInAt(1, ArmEncodableConstantOrRegister(sub->InputAt(1), SUB));
Scott Wakelingfe885462016-09-22 10:24:38 +01004260 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4261 break;
4262 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004263 case DataType::Type::kFloat32:
4264 case DataType::Type::kFloat64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004265 locations->SetInAt(0, Location::RequiresFpuRegister());
4266 locations->SetInAt(1, Location::RequiresFpuRegister());
4267 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4268 break;
4269 }
4270 default:
4271 LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
4272 }
4273}
4274
4275void InstructionCodeGeneratorARMVIXL::VisitSub(HSub* sub) {
4276 LocationSummary* locations = sub->GetLocations();
4277 Location out = locations->Out();
4278 Location first = locations->InAt(0);
4279 Location second = locations->InAt(1);
4280 switch (sub->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004281 case DataType::Type::kInt32: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004282 __ Sub(OutputRegister(sub), InputRegisterAt(sub, 0), InputOperandAt(sub, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004283 break;
4284 }
4285
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004286 case DataType::Type::kInt64: {
Anton Kirilovdda43962016-11-21 19:55:20 +00004287 if (second.IsConstant()) {
4288 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
4289 GenerateAddLongConst(out, first, -value);
4290 } else {
4291 DCHECK(second.IsRegisterPair());
4292 __ Subs(LowRegisterFrom(out), LowRegisterFrom(first), LowRegisterFrom(second));
4293 __ Sbc(HighRegisterFrom(out), HighRegisterFrom(first), HighRegisterFrom(second));
4294 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004295 break;
4296 }
4297
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004298 case DataType::Type::kFloat32:
4299 case DataType::Type::kFloat64:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004300 __ Vsub(OutputVRegister(sub), InputVRegisterAt(sub, 0), InputVRegisterAt(sub, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004301 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01004302
4303 default:
4304 LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
4305 }
4306}
4307
4308void LocationsBuilderARMVIXL::VisitMul(HMul* mul) {
4309 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01004310 new (GetGraph()->GetAllocator()) LocationSummary(mul, LocationSummary::kNoCall);
Scott Wakelingfe885462016-09-22 10:24:38 +01004311 switch (mul->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004312 case DataType::Type::kInt32:
4313 case DataType::Type::kInt64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004314 locations->SetInAt(0, Location::RequiresRegister());
4315 locations->SetInAt(1, Location::RequiresRegister());
4316 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4317 break;
4318 }
4319
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004320 case DataType::Type::kFloat32:
4321 case DataType::Type::kFloat64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004322 locations->SetInAt(0, Location::RequiresFpuRegister());
4323 locations->SetInAt(1, Location::RequiresFpuRegister());
4324 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4325 break;
4326 }
4327
4328 default:
4329 LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
4330 }
4331}
4332
4333void InstructionCodeGeneratorARMVIXL::VisitMul(HMul* mul) {
4334 LocationSummary* locations = mul->GetLocations();
4335 Location out = locations->Out();
4336 Location first = locations->InAt(0);
4337 Location second = locations->InAt(1);
4338 switch (mul->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004339 case DataType::Type::kInt32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004340 __ Mul(OutputRegister(mul), InputRegisterAt(mul, 0), InputRegisterAt(mul, 1));
4341 break;
4342 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004343 case DataType::Type::kInt64: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004344 vixl32::Register out_hi = HighRegisterFrom(out);
4345 vixl32::Register out_lo = LowRegisterFrom(out);
4346 vixl32::Register in1_hi = HighRegisterFrom(first);
4347 vixl32::Register in1_lo = LowRegisterFrom(first);
4348 vixl32::Register in2_hi = HighRegisterFrom(second);
4349 vixl32::Register in2_lo = LowRegisterFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01004350
4351 // Extra checks to protect caused by the existence of R1_R2.
4352 // The algorithm is wrong if out.hi is either in1.lo or in2.lo:
4353 // (e.g. in1=r0_r1, in2=r2_r3 and out=r1_r2);
Anton Kirilov644032c2016-12-06 17:51:43 +00004354 DCHECK(!out_hi.Is(in1_lo));
4355 DCHECK(!out_hi.Is(in2_lo));
Scott Wakelingfe885462016-09-22 10:24:38 +01004356
4357 // input: in1 - 64 bits, in2 - 64 bits
4358 // output: out
4359 // formula: out.hi : out.lo = (in1.lo * in2.hi + in1.hi * in2.lo)* 2^32 + in1.lo * in2.lo
4360 // parts: out.hi = in1.lo * in2.hi + in1.hi * in2.lo + (in1.lo * in2.lo)[63:32]
4361 // parts: out.lo = (in1.lo * in2.lo)[31:0]
4362
4363 UseScratchRegisterScope temps(GetVIXLAssembler());
4364 vixl32::Register temp = temps.Acquire();
4365 // temp <- in1.lo * in2.hi
4366 __ Mul(temp, in1_lo, in2_hi);
4367 // out.hi <- in1.lo * in2.hi + in1.hi * in2.lo
4368 __ Mla(out_hi, in1_hi, in2_lo, temp);
4369 // out.lo <- (in1.lo * in2.lo)[31:0];
4370 __ Umull(out_lo, temp, in1_lo, in2_lo);
4371 // out.hi <- in2.hi * in1.lo + in2.lo * in1.hi + (in1.lo * in2.lo)[63:32]
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004372 __ Add(out_hi, out_hi, temp);
Scott Wakelingfe885462016-09-22 10:24:38 +01004373 break;
4374 }
4375
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004376 case DataType::Type::kFloat32:
4377 case DataType::Type::kFloat64:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004378 __ Vmul(OutputVRegister(mul), InputVRegisterAt(mul, 0), InputVRegisterAt(mul, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004379 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01004380
4381 default:
4382 LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
4383 }
4384}
4385
Scott Wakelingfe885462016-09-22 10:24:38 +01004386void InstructionCodeGeneratorARMVIXL::DivRemOneOrMinusOne(HBinaryOperation* instruction) {
4387 DCHECK(instruction->IsDiv() || instruction->IsRem());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004388 DCHECK(instruction->GetResultType() == DataType::Type::kInt32);
Scott Wakelingfe885462016-09-22 10:24:38 +01004389
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004390 Location second = instruction->GetLocations()->InAt(1);
Scott Wakelingfe885462016-09-22 10:24:38 +01004391 DCHECK(second.IsConstant());
4392
4393 vixl32::Register out = OutputRegister(instruction);
4394 vixl32::Register dividend = InputRegisterAt(instruction, 0);
Anton Kirilov644032c2016-12-06 17:51:43 +00004395 int32_t imm = Int32ConstantFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01004396 DCHECK(imm == 1 || imm == -1);
4397
4398 if (instruction->IsRem()) {
4399 __ Mov(out, 0);
4400 } else {
4401 if (imm == 1) {
4402 __ Mov(out, dividend);
4403 } else {
4404 __ Rsb(out, dividend, 0);
4405 }
4406 }
4407}
4408
4409void InstructionCodeGeneratorARMVIXL::DivRemByPowerOfTwo(HBinaryOperation* instruction) {
4410 DCHECK(instruction->IsDiv() || instruction->IsRem());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004411 DCHECK(instruction->GetResultType() == DataType::Type::kInt32);
Scott Wakelingfe885462016-09-22 10:24:38 +01004412
4413 LocationSummary* locations = instruction->GetLocations();
4414 Location second = locations->InAt(1);
4415 DCHECK(second.IsConstant());
4416
4417 vixl32::Register out = OutputRegister(instruction);
4418 vixl32::Register dividend = InputRegisterAt(instruction, 0);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004419 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
Anton Kirilov644032c2016-12-06 17:51:43 +00004420 int32_t imm = Int32ConstantFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01004421 uint32_t abs_imm = static_cast<uint32_t>(AbsOrMin(imm));
4422 int ctz_imm = CTZ(abs_imm);
4423
4424 if (ctz_imm == 1) {
4425 __ Lsr(temp, dividend, 32 - ctz_imm);
4426 } else {
4427 __ Asr(temp, dividend, 31);
4428 __ Lsr(temp, temp, 32 - ctz_imm);
4429 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004430 __ Add(out, temp, dividend);
Scott Wakelingfe885462016-09-22 10:24:38 +01004431
4432 if (instruction->IsDiv()) {
4433 __ Asr(out, out, ctz_imm);
4434 if (imm < 0) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004435 __ Rsb(out, out, 0);
Scott Wakelingfe885462016-09-22 10:24:38 +01004436 }
4437 } else {
4438 __ Ubfx(out, out, 0, ctz_imm);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004439 __ Sub(out, out, temp);
Scott Wakelingfe885462016-09-22 10:24:38 +01004440 }
4441}
4442
4443void InstructionCodeGeneratorARMVIXL::GenerateDivRemWithAnyConstant(HBinaryOperation* instruction) {
4444 DCHECK(instruction->IsDiv() || instruction->IsRem());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004445 DCHECK(instruction->GetResultType() == DataType::Type::kInt32);
Scott Wakelingfe885462016-09-22 10:24:38 +01004446
4447 LocationSummary* locations = instruction->GetLocations();
4448 Location second = locations->InAt(1);
4449 DCHECK(second.IsConstant());
4450
4451 vixl32::Register out = OutputRegister(instruction);
4452 vixl32::Register dividend = InputRegisterAt(instruction, 0);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004453 vixl32::Register temp1 = RegisterFrom(locations->GetTemp(0));
4454 vixl32::Register temp2 = RegisterFrom(locations->GetTemp(1));
Scott Wakelingb77051e2016-11-21 19:46:00 +00004455 int32_t imm = Int32ConstantFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01004456
4457 int64_t magic;
4458 int shift;
4459 CalculateMagicAndShiftForDivRem(imm, false /* is_long */, &magic, &shift);
4460
Anton Kirilovdda43962016-11-21 19:55:20 +00004461 // TODO(VIXL): Change the static cast to Operand::From() after VIXL is fixed.
4462 __ Mov(temp1, static_cast<int32_t>(magic));
Scott Wakelingfe885462016-09-22 10:24:38 +01004463 __ Smull(temp2, temp1, dividend, temp1);
4464
4465 if (imm > 0 && magic < 0) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004466 __ Add(temp1, temp1, dividend);
Scott Wakelingfe885462016-09-22 10:24:38 +01004467 } else if (imm < 0 && magic > 0) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004468 __ Sub(temp1, temp1, dividend);
Scott Wakelingfe885462016-09-22 10:24:38 +01004469 }
4470
4471 if (shift != 0) {
4472 __ Asr(temp1, temp1, shift);
4473 }
4474
4475 if (instruction->IsDiv()) {
4476 __ Sub(out, temp1, Operand(temp1, vixl32::Shift(ASR), 31));
4477 } else {
4478 __ Sub(temp1, temp1, Operand(temp1, vixl32::Shift(ASR), 31));
4479 // TODO: Strength reduction for mls.
4480 __ Mov(temp2, imm);
4481 __ Mls(out, temp1, temp2, dividend);
4482 }
4483}
4484
4485void InstructionCodeGeneratorARMVIXL::GenerateDivRemConstantIntegral(
4486 HBinaryOperation* instruction) {
4487 DCHECK(instruction->IsDiv() || instruction->IsRem());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004488 DCHECK(instruction->GetResultType() == DataType::Type::kInt32);
Scott Wakelingfe885462016-09-22 10:24:38 +01004489
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004490 Location second = instruction->GetLocations()->InAt(1);
Scott Wakelingfe885462016-09-22 10:24:38 +01004491 DCHECK(second.IsConstant());
4492
Anton Kirilov644032c2016-12-06 17:51:43 +00004493 int32_t imm = Int32ConstantFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01004494 if (imm == 0) {
4495 // Do not generate anything. DivZeroCheck would prevent any code to be executed.
4496 } else if (imm == 1 || imm == -1) {
4497 DivRemOneOrMinusOne(instruction);
4498 } else if (IsPowerOfTwo(AbsOrMin(imm))) {
4499 DivRemByPowerOfTwo(instruction);
4500 } else {
4501 DCHECK(imm <= -2 || imm >= 2);
4502 GenerateDivRemWithAnyConstant(instruction);
4503 }
4504}
4505
4506void LocationsBuilderARMVIXL::VisitDiv(HDiv* div) {
4507 LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004508 if (div->GetResultType() == DataType::Type::kInt64) {
Scott Wakelingfe885462016-09-22 10:24:38 +01004509 // pLdiv runtime call.
4510 call_kind = LocationSummary::kCallOnMainOnly;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004511 } else if (div->GetResultType() == DataType::Type::kInt32 && div->InputAt(1)->IsConstant()) {
Scott Wakelingfe885462016-09-22 10:24:38 +01004512 // sdiv will be replaced by other instruction sequence.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004513 } else if (div->GetResultType() == DataType::Type::kInt32 &&
Scott Wakelingfe885462016-09-22 10:24:38 +01004514 !codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4515 // pIdivmod runtime call.
4516 call_kind = LocationSummary::kCallOnMainOnly;
4517 }
4518
Vladimir Markoca6fff82017-10-03 14:49:14 +01004519 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(div, call_kind);
Scott Wakelingfe885462016-09-22 10:24:38 +01004520
4521 switch (div->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004522 case DataType::Type::kInt32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004523 if (div->InputAt(1)->IsConstant()) {
4524 locations->SetInAt(0, Location::RequiresRegister());
4525 locations->SetInAt(1, Location::ConstantLocation(div->InputAt(1)->AsConstant()));
4526 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Anton Kirilov644032c2016-12-06 17:51:43 +00004527 int32_t value = Int32ConstantFrom(div->InputAt(1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004528 if (value == 1 || value == 0 || value == -1) {
4529 // No temp register required.
4530 } else {
4531 locations->AddTemp(Location::RequiresRegister());
4532 if (!IsPowerOfTwo(AbsOrMin(value))) {
4533 locations->AddTemp(Location::RequiresRegister());
4534 }
4535 }
4536 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4537 locations->SetInAt(0, Location::RequiresRegister());
4538 locations->SetInAt(1, Location::RequiresRegister());
4539 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4540 } else {
Artem Serov551b28f2016-10-18 19:11:30 +01004541 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4542 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
4543 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
Roland Levillain5e8d5f02016-10-18 18:03:43 +01004544 // Note: divmod will compute both the quotient and the remainder as the pair R0 and R1, but
Artem Serov551b28f2016-10-18 19:11:30 +01004545 // we only need the former.
4546 locations->SetOut(LocationFrom(r0));
Scott Wakelingfe885462016-09-22 10:24:38 +01004547 }
4548 break;
4549 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004550 case DataType::Type::kInt64: {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004551 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4552 locations->SetInAt(0, LocationFrom(
4553 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
4554 locations->SetInAt(1, LocationFrom(
4555 calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3)));
4556 locations->SetOut(LocationFrom(r0, r1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004557 break;
4558 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004559 case DataType::Type::kFloat32:
4560 case DataType::Type::kFloat64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004561 locations->SetInAt(0, Location::RequiresFpuRegister());
4562 locations->SetInAt(1, Location::RequiresFpuRegister());
4563 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4564 break;
4565 }
4566
4567 default:
4568 LOG(FATAL) << "Unexpected div type " << div->GetResultType();
4569 }
4570}
4571
4572void InstructionCodeGeneratorARMVIXL::VisitDiv(HDiv* div) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004573 Location lhs = div->GetLocations()->InAt(0);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004574 Location rhs = div->GetLocations()->InAt(1);
Scott Wakelingfe885462016-09-22 10:24:38 +01004575
4576 switch (div->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004577 case DataType::Type::kInt32: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004578 if (rhs.IsConstant()) {
Scott Wakelingfe885462016-09-22 10:24:38 +01004579 GenerateDivRemConstantIntegral(div);
4580 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4581 __ Sdiv(OutputRegister(div), InputRegisterAt(div, 0), InputRegisterAt(div, 1));
4582 } else {
Artem Serov551b28f2016-10-18 19:11:30 +01004583 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4584 DCHECK(calling_convention.GetRegisterAt(0).Is(RegisterFrom(lhs)));
4585 DCHECK(calling_convention.GetRegisterAt(1).Is(RegisterFrom(rhs)));
4586 DCHECK(r0.Is(OutputRegister(div)));
4587
4588 codegen_->InvokeRuntime(kQuickIdivmod, div, div->GetDexPc());
4589 CheckEntrypointTypes<kQuickIdivmod, int32_t, int32_t, int32_t>();
Scott Wakelingfe885462016-09-22 10:24:38 +01004590 }
4591 break;
4592 }
4593
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004594 case DataType::Type::kInt64: {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004595 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4596 DCHECK(calling_convention.GetRegisterAt(0).Is(LowRegisterFrom(lhs)));
4597 DCHECK(calling_convention.GetRegisterAt(1).Is(HighRegisterFrom(lhs)));
4598 DCHECK(calling_convention.GetRegisterAt(2).Is(LowRegisterFrom(rhs)));
4599 DCHECK(calling_convention.GetRegisterAt(3).Is(HighRegisterFrom(rhs)));
4600 DCHECK(LowRegisterFrom(div->GetLocations()->Out()).Is(r0));
4601 DCHECK(HighRegisterFrom(div->GetLocations()->Out()).Is(r1));
4602
4603 codegen_->InvokeRuntime(kQuickLdiv, div, div->GetDexPc());
4604 CheckEntrypointTypes<kQuickLdiv, int64_t, int64_t, int64_t>();
Scott Wakelingfe885462016-09-22 10:24:38 +01004605 break;
4606 }
4607
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004608 case DataType::Type::kFloat32:
4609 case DataType::Type::kFloat64:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004610 __ Vdiv(OutputVRegister(div), InputVRegisterAt(div, 0), InputVRegisterAt(div, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004611 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01004612
4613 default:
4614 LOG(FATAL) << "Unexpected div type " << div->GetResultType();
4615 }
4616}
4617
Artem Serov551b28f2016-10-18 19:11:30 +01004618void LocationsBuilderARMVIXL::VisitRem(HRem* rem) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004619 DataType::Type type = rem->GetResultType();
Artem Serov551b28f2016-10-18 19:11:30 +01004620
4621 // Most remainders are implemented in the runtime.
4622 LocationSummary::CallKind call_kind = LocationSummary::kCallOnMainOnly;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004623 if (rem->GetResultType() == DataType::Type::kInt32 && rem->InputAt(1)->IsConstant()) {
Artem Serov551b28f2016-10-18 19:11:30 +01004624 // sdiv will be replaced by other instruction sequence.
4625 call_kind = LocationSummary::kNoCall;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004626 } else if ((rem->GetResultType() == DataType::Type::kInt32)
Artem Serov551b28f2016-10-18 19:11:30 +01004627 && codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4628 // Have hardware divide instruction for int, do it with three instructions.
4629 call_kind = LocationSummary::kNoCall;
4630 }
4631
Vladimir Markoca6fff82017-10-03 14:49:14 +01004632 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(rem, call_kind);
Artem Serov551b28f2016-10-18 19:11:30 +01004633
4634 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004635 case DataType::Type::kInt32: {
Artem Serov551b28f2016-10-18 19:11:30 +01004636 if (rem->InputAt(1)->IsConstant()) {
4637 locations->SetInAt(0, Location::RequiresRegister());
4638 locations->SetInAt(1, Location::ConstantLocation(rem->InputAt(1)->AsConstant()));
4639 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Anton Kirilov644032c2016-12-06 17:51:43 +00004640 int32_t value = Int32ConstantFrom(rem->InputAt(1));
Artem Serov551b28f2016-10-18 19:11:30 +01004641 if (value == 1 || value == 0 || value == -1) {
4642 // No temp register required.
4643 } else {
4644 locations->AddTemp(Location::RequiresRegister());
4645 if (!IsPowerOfTwo(AbsOrMin(value))) {
4646 locations->AddTemp(Location::RequiresRegister());
4647 }
4648 }
4649 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4650 locations->SetInAt(0, Location::RequiresRegister());
4651 locations->SetInAt(1, Location::RequiresRegister());
4652 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4653 locations->AddTemp(Location::RequiresRegister());
4654 } else {
4655 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4656 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
4657 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
Roland Levillain5e8d5f02016-10-18 18:03:43 +01004658 // Note: divmod will compute both the quotient and the remainder as the pair R0 and R1, but
Artem Serov551b28f2016-10-18 19:11:30 +01004659 // we only need the latter.
4660 locations->SetOut(LocationFrom(r1));
4661 }
4662 break;
4663 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004664 case DataType::Type::kInt64: {
Artem Serov551b28f2016-10-18 19:11:30 +01004665 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4666 locations->SetInAt(0, LocationFrom(
4667 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
4668 locations->SetInAt(1, LocationFrom(
4669 calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3)));
4670 // The runtime helper puts the output in R2,R3.
4671 locations->SetOut(LocationFrom(r2, r3));
4672 break;
4673 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004674 case DataType::Type::kFloat32: {
Artem Serov551b28f2016-10-18 19:11:30 +01004675 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4676 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
4677 locations->SetInAt(1, LocationFrom(calling_convention.GetFpuRegisterAt(1)));
4678 locations->SetOut(LocationFrom(s0));
4679 break;
4680 }
4681
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004682 case DataType::Type::kFloat64: {
Artem Serov551b28f2016-10-18 19:11:30 +01004683 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4684 locations->SetInAt(0, LocationFrom(
4685 calling_convention.GetFpuRegisterAt(0), calling_convention.GetFpuRegisterAt(1)));
4686 locations->SetInAt(1, LocationFrom(
4687 calling_convention.GetFpuRegisterAt(2), calling_convention.GetFpuRegisterAt(3)));
4688 locations->SetOut(LocationFrom(s0, s1));
4689 break;
4690 }
4691
4692 default:
4693 LOG(FATAL) << "Unexpected rem type " << type;
4694 }
4695}
4696
4697void InstructionCodeGeneratorARMVIXL::VisitRem(HRem* rem) {
4698 LocationSummary* locations = rem->GetLocations();
4699 Location second = locations->InAt(1);
4700
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004701 DataType::Type type = rem->GetResultType();
Artem Serov551b28f2016-10-18 19:11:30 +01004702 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004703 case DataType::Type::kInt32: {
Artem Serov551b28f2016-10-18 19:11:30 +01004704 vixl32::Register reg1 = InputRegisterAt(rem, 0);
4705 vixl32::Register out_reg = OutputRegister(rem);
4706 if (second.IsConstant()) {
4707 GenerateDivRemConstantIntegral(rem);
4708 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4709 vixl32::Register reg2 = RegisterFrom(second);
4710 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
4711
4712 // temp = reg1 / reg2 (integer division)
4713 // dest = reg1 - temp * reg2
4714 __ Sdiv(temp, reg1, reg2);
4715 __ Mls(out_reg, temp, reg2, reg1);
4716 } else {
4717 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4718 DCHECK(reg1.Is(calling_convention.GetRegisterAt(0)));
4719 DCHECK(RegisterFrom(second).Is(calling_convention.GetRegisterAt(1)));
4720 DCHECK(out_reg.Is(r1));
4721
4722 codegen_->InvokeRuntime(kQuickIdivmod, rem, rem->GetDexPc());
4723 CheckEntrypointTypes<kQuickIdivmod, int32_t, int32_t, int32_t>();
4724 }
4725 break;
4726 }
4727
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004728 case DataType::Type::kInt64: {
Artem Serov551b28f2016-10-18 19:11:30 +01004729 codegen_->InvokeRuntime(kQuickLmod, rem, rem->GetDexPc());
4730 CheckEntrypointTypes<kQuickLmod, int64_t, int64_t, int64_t>();
4731 break;
4732 }
4733
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004734 case DataType::Type::kFloat32: {
Artem Serov551b28f2016-10-18 19:11:30 +01004735 codegen_->InvokeRuntime(kQuickFmodf, rem, rem->GetDexPc());
4736 CheckEntrypointTypes<kQuickFmodf, float, float, float>();
4737 break;
4738 }
4739
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004740 case DataType::Type::kFloat64: {
Artem Serov551b28f2016-10-18 19:11:30 +01004741 codegen_->InvokeRuntime(kQuickFmod, rem, rem->GetDexPc());
4742 CheckEntrypointTypes<kQuickFmod, double, double, double>();
4743 break;
4744 }
4745
4746 default:
4747 LOG(FATAL) << "Unexpected rem type " << type;
4748 }
4749}
4750
Aart Bik1f8d51b2018-02-15 10:42:37 -08004751static void CreateMinMaxLocations(ArenaAllocator* allocator, HBinaryOperation* minmax) {
4752 LocationSummary* locations = new (allocator) LocationSummary(minmax);
4753 switch (minmax->GetResultType()) {
4754 case DataType::Type::kInt32:
4755 locations->SetInAt(0, Location::RequiresRegister());
4756 locations->SetInAt(1, Location::RequiresRegister());
4757 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4758 break;
4759 case DataType::Type::kInt64:
4760 locations->SetInAt(0, Location::RequiresRegister());
4761 locations->SetInAt(1, Location::RequiresRegister());
4762 locations->SetOut(Location::SameAsFirstInput());
4763 break;
4764 case DataType::Type::kFloat32:
4765 locations->SetInAt(0, Location::RequiresFpuRegister());
4766 locations->SetInAt(1, Location::RequiresFpuRegister());
4767 locations->SetOut(Location::SameAsFirstInput());
4768 locations->AddTemp(Location::RequiresRegister());
4769 break;
4770 case DataType::Type::kFloat64:
4771 locations->SetInAt(0, Location::RequiresFpuRegister());
4772 locations->SetInAt(1, Location::RequiresFpuRegister());
4773 locations->SetOut(Location::SameAsFirstInput());
4774 break;
4775 default:
4776 LOG(FATAL) << "Unexpected type for HMinMax " << minmax->GetResultType();
4777 }
4778}
4779
Aart Bik351df3e2018-03-07 11:54:57 -08004780void InstructionCodeGeneratorARMVIXL::GenerateMinMaxInt(LocationSummary* locations, bool is_min) {
Aart Bik1f8d51b2018-02-15 10:42:37 -08004781 Location op1_loc = locations->InAt(0);
4782 Location op2_loc = locations->InAt(1);
4783 Location out_loc = locations->Out();
4784
4785 vixl32::Register op1 = RegisterFrom(op1_loc);
4786 vixl32::Register op2 = RegisterFrom(op2_loc);
4787 vixl32::Register out = RegisterFrom(out_loc);
4788
4789 __ Cmp(op1, op2);
4790
4791 {
4792 ExactAssemblyScope aas(GetVIXLAssembler(),
4793 3 * kMaxInstructionSizeInBytes,
4794 CodeBufferCheckScope::kMaximumSize);
4795
4796 __ ite(is_min ? lt : gt);
4797 __ mov(is_min ? lt : gt, out, op1);
4798 __ mov(is_min ? ge : le, out, op2);
4799 }
4800}
4801
4802void InstructionCodeGeneratorARMVIXL::GenerateMinMaxLong(LocationSummary* locations, bool is_min) {
4803 Location op1_loc = locations->InAt(0);
4804 Location op2_loc = locations->InAt(1);
4805 Location out_loc = locations->Out();
4806
4807 // Optimization: don't generate any code if inputs are the same.
4808 if (op1_loc.Equals(op2_loc)) {
4809 DCHECK(out_loc.Equals(op1_loc)); // out_loc is set as SameAsFirstInput() in location builder.
4810 return;
4811 }
4812
4813 vixl32::Register op1_lo = LowRegisterFrom(op1_loc);
4814 vixl32::Register op1_hi = HighRegisterFrom(op1_loc);
4815 vixl32::Register op2_lo = LowRegisterFrom(op2_loc);
4816 vixl32::Register op2_hi = HighRegisterFrom(op2_loc);
4817 vixl32::Register out_lo = LowRegisterFrom(out_loc);
4818 vixl32::Register out_hi = HighRegisterFrom(out_loc);
4819 UseScratchRegisterScope temps(GetVIXLAssembler());
4820 const vixl32::Register temp = temps.Acquire();
4821
4822 DCHECK(op1_lo.Is(out_lo));
4823 DCHECK(op1_hi.Is(out_hi));
4824
4825 // Compare op1 >= op2, or op1 < op2.
4826 __ Cmp(out_lo, op2_lo);
4827 __ Sbcs(temp, out_hi, op2_hi);
4828
4829 // Now GE/LT condition code is correct for the long comparison.
4830 {
4831 vixl32::ConditionType cond = is_min ? ge : lt;
4832 ExactAssemblyScope it_scope(GetVIXLAssembler(),
4833 3 * kMaxInstructionSizeInBytes,
4834 CodeBufferCheckScope::kMaximumSize);
4835 __ itt(cond);
4836 __ mov(cond, out_lo, op2_lo);
4837 __ mov(cond, out_hi, op2_hi);
4838 }
4839}
4840
Aart Bik351df3e2018-03-07 11:54:57 -08004841void InstructionCodeGeneratorARMVIXL::GenerateMinMaxFloat(HInstruction* minmax, bool is_min) {
4842 LocationSummary* locations = minmax->GetLocations();
Aart Bik1f8d51b2018-02-15 10:42:37 -08004843 Location op1_loc = locations->InAt(0);
4844 Location op2_loc = locations->InAt(1);
4845 Location out_loc = locations->Out();
4846
4847 // Optimization: don't generate any code if inputs are the same.
4848 if (op1_loc.Equals(op2_loc)) {
4849 DCHECK(out_loc.Equals(op1_loc)); // out_loc is set as SameAsFirstInput() in location builder.
4850 return;
4851 }
4852
4853 vixl32::SRegister op1 = SRegisterFrom(op1_loc);
4854 vixl32::SRegister op2 = SRegisterFrom(op2_loc);
4855 vixl32::SRegister out = SRegisterFrom(out_loc);
4856
4857 UseScratchRegisterScope temps(GetVIXLAssembler());
4858 const vixl32::Register temp1 = temps.Acquire();
4859 vixl32::Register temp2 = RegisterFrom(locations->GetTemp(0));
4860 vixl32::Label nan, done;
Aart Bik351df3e2018-03-07 11:54:57 -08004861 vixl32::Label* final_label = codegen_->GetFinalLabel(minmax, &done);
Aart Bik1f8d51b2018-02-15 10:42:37 -08004862
4863 DCHECK(op1.Is(out));
4864
4865 __ Vcmp(op1, op2);
4866 __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
4867 __ B(vs, &nan, /* far_target */ false); // if un-ordered, go to NaN handling.
4868
4869 // op1 <> op2
4870 vixl32::ConditionType cond = is_min ? gt : lt;
4871 {
4872 ExactAssemblyScope it_scope(GetVIXLAssembler(),
4873 2 * kMaxInstructionSizeInBytes,
4874 CodeBufferCheckScope::kMaximumSize);
4875 __ it(cond);
4876 __ vmov(cond, F32, out, op2);
4877 }
4878 // for <>(not equal), we've done min/max calculation.
4879 __ B(ne, final_label, /* far_target */ false);
4880
4881 // handle op1 == op2, max(+0.0,-0.0), min(+0.0,-0.0).
4882 __ Vmov(temp1, op1);
4883 __ Vmov(temp2, op2);
4884 if (is_min) {
4885 __ Orr(temp1, temp1, temp2);
4886 } else {
4887 __ And(temp1, temp1, temp2);
4888 }
4889 __ Vmov(out, temp1);
4890 __ B(final_label);
4891
4892 // handle NaN input.
4893 __ Bind(&nan);
4894 __ Movt(temp1, High16Bits(kNanFloat)); // 0x7FC0xxxx is a NaN.
4895 __ Vmov(out, temp1);
4896
4897 if (done.IsReferenced()) {
4898 __ Bind(&done);
4899 }
4900}
4901
Aart Bik351df3e2018-03-07 11:54:57 -08004902void InstructionCodeGeneratorARMVIXL::GenerateMinMaxDouble(HInstruction* minmax, bool is_min) {
4903 LocationSummary* locations = minmax->GetLocations();
Aart Bik1f8d51b2018-02-15 10:42:37 -08004904 Location op1_loc = locations->InAt(0);
4905 Location op2_loc = locations->InAt(1);
4906 Location out_loc = locations->Out();
4907
4908 // Optimization: don't generate any code if inputs are the same.
4909 if (op1_loc.Equals(op2_loc)) {
4910 DCHECK(out_loc.Equals(op1_loc)); // out_loc is set as SameAsFirstInput() in.
4911 return;
4912 }
4913
4914 vixl32::DRegister op1 = DRegisterFrom(op1_loc);
4915 vixl32::DRegister op2 = DRegisterFrom(op2_loc);
4916 vixl32::DRegister out = DRegisterFrom(out_loc);
4917 vixl32::Label handle_nan_eq, done;
Aart Bik351df3e2018-03-07 11:54:57 -08004918 vixl32::Label* final_label = codegen_->GetFinalLabel(minmax, &done);
Aart Bik1f8d51b2018-02-15 10:42:37 -08004919
4920 DCHECK(op1.Is(out));
4921
4922 __ Vcmp(op1, op2);
4923 __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
4924 __ B(vs, &handle_nan_eq, /* far_target */ false); // if un-ordered, go to NaN handling.
4925
4926 // op1 <> op2
4927 vixl32::ConditionType cond = is_min ? gt : lt;
4928 {
4929 ExactAssemblyScope it_scope(GetVIXLAssembler(),
4930 2 * kMaxInstructionSizeInBytes,
4931 CodeBufferCheckScope::kMaximumSize);
4932 __ it(cond);
4933 __ vmov(cond, F64, out, op2);
4934 }
4935 // for <>(not equal), we've done min/max calculation.
4936 __ B(ne, final_label, /* far_target */ false);
4937
4938 // handle op1 == op2, max(+0.0,-0.0).
4939 if (!is_min) {
4940 __ Vand(F64, out, op1, op2);
4941 __ B(final_label);
4942 }
4943
4944 // handle op1 == op2, min(+0.0,-0.0), NaN input.
4945 __ Bind(&handle_nan_eq);
4946 __ Vorr(F64, out, op1, op2); // assemble op1/-0.0/NaN.
4947
4948 if (done.IsReferenced()) {
4949 __ Bind(&done);
4950 }
4951}
4952
Aart Bik351df3e2018-03-07 11:54:57 -08004953void InstructionCodeGeneratorARMVIXL::GenerateMinMax(HBinaryOperation* minmax, bool is_min) {
4954 DataType::Type type = minmax->GetResultType();
4955 switch (type) {
4956 case DataType::Type::kInt32:
4957 GenerateMinMaxInt(minmax->GetLocations(), is_min);
4958 break;
4959 case DataType::Type::kInt64:
4960 GenerateMinMaxLong(minmax->GetLocations(), is_min);
4961 break;
4962 case DataType::Type::kFloat32:
4963 GenerateMinMaxFloat(minmax, is_min);
4964 break;
4965 case DataType::Type::kFloat64:
4966 GenerateMinMaxDouble(minmax, is_min);
4967 break;
4968 default:
4969 LOG(FATAL) << "Unexpected type for HMinMax " << type;
4970 }
4971}
4972
Aart Bik1f8d51b2018-02-15 10:42:37 -08004973void LocationsBuilderARMVIXL::VisitMin(HMin* min) {
4974 CreateMinMaxLocations(GetGraph()->GetAllocator(), min);
4975}
4976
4977void InstructionCodeGeneratorARMVIXL::VisitMin(HMin* min) {
Aart Bik351df3e2018-03-07 11:54:57 -08004978 GenerateMinMax(min, /*is_min*/ true);
Aart Bik1f8d51b2018-02-15 10:42:37 -08004979}
4980
4981void LocationsBuilderARMVIXL::VisitMax(HMax* max) {
4982 CreateMinMaxLocations(GetGraph()->GetAllocator(), max);
4983}
4984
4985void InstructionCodeGeneratorARMVIXL::VisitMax(HMax* max) {
Aart Bik351df3e2018-03-07 11:54:57 -08004986 GenerateMinMax(max, /*is_min*/ false);
Aart Bik1f8d51b2018-02-15 10:42:37 -08004987}
4988
Aart Bik3dad3412018-02-28 12:01:46 -08004989void LocationsBuilderARMVIXL::VisitAbs(HAbs* abs) {
4990 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(abs);
4991 switch (abs->GetResultType()) {
4992 case DataType::Type::kInt32:
4993 case DataType::Type::kInt64:
4994 locations->SetInAt(0, Location::RequiresRegister());
4995 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4996 locations->AddTemp(Location::RequiresRegister());
4997 break;
4998 case DataType::Type::kFloat32:
4999 case DataType::Type::kFloat64:
5000 locations->SetInAt(0, Location::RequiresFpuRegister());
5001 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
5002 break;
5003 default:
5004 LOG(FATAL) << "Unexpected type for abs operation " << abs->GetResultType();
5005 }
5006}
5007
5008void InstructionCodeGeneratorARMVIXL::VisitAbs(HAbs* abs) {
5009 LocationSummary* locations = abs->GetLocations();
5010 switch (abs->GetResultType()) {
5011 case DataType::Type::kInt32: {
5012 vixl32::Register in_reg = RegisterFrom(locations->InAt(0));
5013 vixl32::Register out_reg = RegisterFrom(locations->Out());
5014 vixl32::Register mask = RegisterFrom(locations->GetTemp(0));
5015 __ Asr(mask, in_reg, 31);
5016 __ Add(out_reg, in_reg, mask);
5017 __ Eor(out_reg, out_reg, mask);
5018 break;
5019 }
5020 case DataType::Type::kInt64: {
5021 Location in = locations->InAt(0);
5022 vixl32::Register in_reg_lo = LowRegisterFrom(in);
5023 vixl32::Register in_reg_hi = HighRegisterFrom(in);
5024 Location output = locations->Out();
5025 vixl32::Register out_reg_lo = LowRegisterFrom(output);
5026 vixl32::Register out_reg_hi = HighRegisterFrom(output);
5027 DCHECK(!out_reg_lo.Is(in_reg_hi)) << "Diagonal overlap unexpected.";
5028 vixl32::Register mask = RegisterFrom(locations->GetTemp(0));
5029 __ Asr(mask, in_reg_hi, 31);
5030 __ Adds(out_reg_lo, in_reg_lo, mask);
5031 __ Adc(out_reg_hi, in_reg_hi, mask);
5032 __ Eor(out_reg_lo, out_reg_lo, mask);
5033 __ Eor(out_reg_hi, out_reg_hi, mask);
5034 break;
5035 }
5036 case DataType::Type::kFloat32:
5037 case DataType::Type::kFloat64:
5038 __ Vabs(OutputVRegister(abs), InputVRegisterAt(abs, 0));
5039 break;
5040 default:
5041 LOG(FATAL) << "Unexpected type for abs operation " << abs->GetResultType();
5042 }
5043}
Artem Serov551b28f2016-10-18 19:11:30 +01005044
Scott Wakelingfe885462016-09-22 10:24:38 +01005045void LocationsBuilderARMVIXL::VisitDivZeroCheck(HDivZeroCheck* instruction) {
Artem Serov657022c2016-11-23 14:19:38 +00005046 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
Scott Wakelingfe885462016-09-22 10:24:38 +01005047 locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0)));
Scott Wakelingfe885462016-09-22 10:24:38 +01005048}
5049
5050void InstructionCodeGeneratorARMVIXL::VisitDivZeroCheck(HDivZeroCheck* instruction) {
5051 DivZeroCheckSlowPathARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01005052 new (codegen_->GetScopedAllocator()) DivZeroCheckSlowPathARMVIXL(instruction);
Scott Wakelingfe885462016-09-22 10:24:38 +01005053 codegen_->AddSlowPath(slow_path);
5054
5055 LocationSummary* locations = instruction->GetLocations();
5056 Location value = locations->InAt(0);
5057
5058 switch (instruction->GetType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005059 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005060 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005061 case DataType::Type::kInt8:
5062 case DataType::Type::kUint16:
5063 case DataType::Type::kInt16:
5064 case DataType::Type::kInt32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01005065 if (value.IsRegister()) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00005066 __ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
Scott Wakelingfe885462016-09-22 10:24:38 +01005067 } else {
5068 DCHECK(value.IsConstant()) << value;
Anton Kirilov644032c2016-12-06 17:51:43 +00005069 if (Int32ConstantFrom(value) == 0) {
Scott Wakelingfe885462016-09-22 10:24:38 +01005070 __ B(slow_path->GetEntryLabel());
5071 }
5072 }
5073 break;
5074 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005075 case DataType::Type::kInt64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01005076 if (value.IsRegisterPair()) {
5077 UseScratchRegisterScope temps(GetVIXLAssembler());
5078 vixl32::Register temp = temps.Acquire();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005079 __ Orrs(temp, LowRegisterFrom(value), HighRegisterFrom(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01005080 __ B(eq, slow_path->GetEntryLabel());
5081 } else {
5082 DCHECK(value.IsConstant()) << value;
Anton Kirilov644032c2016-12-06 17:51:43 +00005083 if (Int64ConstantFrom(value) == 0) {
Scott Wakelingfe885462016-09-22 10:24:38 +01005084 __ B(slow_path->GetEntryLabel());
5085 }
5086 }
5087 break;
5088 }
5089 default:
5090 LOG(FATAL) << "Unexpected type for HDivZeroCheck " << instruction->GetType();
5091 }
5092}
5093
Artem Serov02109dd2016-09-23 17:17:54 +01005094void InstructionCodeGeneratorARMVIXL::HandleIntegerRotate(HRor* ror) {
5095 LocationSummary* locations = ror->GetLocations();
5096 vixl32::Register in = InputRegisterAt(ror, 0);
5097 Location rhs = locations->InAt(1);
5098 vixl32::Register out = OutputRegister(ror);
5099
5100 if (rhs.IsConstant()) {
5101 // Arm32 and Thumb2 assemblers require a rotation on the interval [1,31],
5102 // so map all rotations to a +ve. equivalent in that range.
5103 // (e.g. left *or* right by -2 bits == 30 bits in the same direction.)
5104 uint32_t rot = CodeGenerator::GetInt32ValueOf(rhs.GetConstant()) & 0x1F;
5105 if (rot) {
5106 // Rotate, mapping left rotations to right equivalents if necessary.
5107 // (e.g. left by 2 bits == right by 30.)
5108 __ Ror(out, in, rot);
5109 } else if (!out.Is(in)) {
5110 __ Mov(out, in);
5111 }
5112 } else {
5113 __ Ror(out, in, RegisterFrom(rhs));
5114 }
5115}
5116
5117// Gain some speed by mapping all Long rotates onto equivalent pairs of Integer
5118// rotates by swapping input regs (effectively rotating by the first 32-bits of
5119// a larger rotation) or flipping direction (thus treating larger right/left
5120// rotations as sub-word sized rotations in the other direction) as appropriate.
5121void InstructionCodeGeneratorARMVIXL::HandleLongRotate(HRor* ror) {
5122 LocationSummary* locations = ror->GetLocations();
5123 vixl32::Register in_reg_lo = LowRegisterFrom(locations->InAt(0));
5124 vixl32::Register in_reg_hi = HighRegisterFrom(locations->InAt(0));
5125 Location rhs = locations->InAt(1);
5126 vixl32::Register out_reg_lo = LowRegisterFrom(locations->Out());
5127 vixl32::Register out_reg_hi = HighRegisterFrom(locations->Out());
5128
5129 if (rhs.IsConstant()) {
5130 uint64_t rot = CodeGenerator::GetInt64ValueOf(rhs.GetConstant());
5131 // Map all rotations to +ve. equivalents on the interval [0,63].
5132 rot &= kMaxLongShiftDistance;
5133 // For rotates over a word in size, 'pre-rotate' by 32-bits to keep rotate
5134 // logic below to a simple pair of binary orr.
5135 // (e.g. 34 bits == in_reg swap + 2 bits right.)
5136 if (rot >= kArmBitsPerWord) {
5137 rot -= kArmBitsPerWord;
5138 std::swap(in_reg_hi, in_reg_lo);
5139 }
5140 // Rotate, or mov to out for zero or word size rotations.
5141 if (rot != 0u) {
Scott Wakelingb77051e2016-11-21 19:46:00 +00005142 __ Lsr(out_reg_hi, in_reg_hi, Operand::From(rot));
Artem Serov02109dd2016-09-23 17:17:54 +01005143 __ Orr(out_reg_hi, out_reg_hi, Operand(in_reg_lo, ShiftType::LSL, kArmBitsPerWord - rot));
Scott Wakelingb77051e2016-11-21 19:46:00 +00005144 __ Lsr(out_reg_lo, in_reg_lo, Operand::From(rot));
Artem Serov02109dd2016-09-23 17:17:54 +01005145 __ Orr(out_reg_lo, out_reg_lo, Operand(in_reg_hi, ShiftType::LSL, kArmBitsPerWord - rot));
5146 } else {
5147 __ Mov(out_reg_lo, in_reg_lo);
5148 __ Mov(out_reg_hi, in_reg_hi);
5149 }
5150 } else {
5151 vixl32::Register shift_right = RegisterFrom(locations->GetTemp(0));
5152 vixl32::Register shift_left = RegisterFrom(locations->GetTemp(1));
5153 vixl32::Label end;
5154 vixl32::Label shift_by_32_plus_shift_right;
Anton Kirilov6f644202017-02-27 18:29:45 +00005155 vixl32::Label* final_label = codegen_->GetFinalLabel(ror, &end);
Artem Serov02109dd2016-09-23 17:17:54 +01005156
5157 __ And(shift_right, RegisterFrom(rhs), 0x1F);
5158 __ Lsrs(shift_left, RegisterFrom(rhs), 6);
Scott Wakelingbffdc702016-12-07 17:46:03 +00005159 __ Rsb(LeaveFlags, shift_left, shift_right, Operand::From(kArmBitsPerWord));
Artem Serov517d9f62016-12-12 15:51:15 +00005160 __ B(cc, &shift_by_32_plus_shift_right, /* far_target */ false);
Artem Serov02109dd2016-09-23 17:17:54 +01005161
5162 // out_reg_hi = (reg_hi << shift_left) | (reg_lo >> shift_right).
5163 // out_reg_lo = (reg_lo << shift_left) | (reg_hi >> shift_right).
5164 __ Lsl(out_reg_hi, in_reg_hi, shift_left);
5165 __ Lsr(out_reg_lo, in_reg_lo, shift_right);
5166 __ Add(out_reg_hi, out_reg_hi, out_reg_lo);
5167 __ Lsl(out_reg_lo, in_reg_lo, shift_left);
5168 __ Lsr(shift_left, in_reg_hi, shift_right);
5169 __ Add(out_reg_lo, out_reg_lo, shift_left);
Anton Kirilov6f644202017-02-27 18:29:45 +00005170 __ B(final_label);
Artem Serov02109dd2016-09-23 17:17:54 +01005171
5172 __ Bind(&shift_by_32_plus_shift_right); // Shift by 32+shift_right.
5173 // out_reg_hi = (reg_hi >> shift_right) | (reg_lo << shift_left).
5174 // out_reg_lo = (reg_lo >> shift_right) | (reg_hi << shift_left).
5175 __ Lsr(out_reg_hi, in_reg_hi, shift_right);
5176 __ Lsl(out_reg_lo, in_reg_lo, shift_left);
5177 __ Add(out_reg_hi, out_reg_hi, out_reg_lo);
5178 __ Lsr(out_reg_lo, in_reg_lo, shift_right);
5179 __ Lsl(shift_right, in_reg_hi, shift_left);
5180 __ Add(out_reg_lo, out_reg_lo, shift_right);
5181
Anton Kirilov6f644202017-02-27 18:29:45 +00005182 if (end.IsReferenced()) {
5183 __ Bind(&end);
5184 }
Artem Serov02109dd2016-09-23 17:17:54 +01005185 }
5186}
5187
5188void LocationsBuilderARMVIXL::VisitRor(HRor* ror) {
5189 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005190 new (GetGraph()->GetAllocator()) LocationSummary(ror, LocationSummary::kNoCall);
Artem Serov02109dd2016-09-23 17:17:54 +01005191 switch (ror->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005192 case DataType::Type::kInt32: {
Artem Serov02109dd2016-09-23 17:17:54 +01005193 locations->SetInAt(0, Location::RequiresRegister());
5194 locations->SetInAt(1, Location::RegisterOrConstant(ror->InputAt(1)));
5195 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5196 break;
5197 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005198 case DataType::Type::kInt64: {
Artem Serov02109dd2016-09-23 17:17:54 +01005199 locations->SetInAt(0, Location::RequiresRegister());
5200 if (ror->InputAt(1)->IsConstant()) {
5201 locations->SetInAt(1, Location::ConstantLocation(ror->InputAt(1)->AsConstant()));
5202 } else {
5203 locations->SetInAt(1, Location::RequiresRegister());
5204 locations->AddTemp(Location::RequiresRegister());
5205 locations->AddTemp(Location::RequiresRegister());
5206 }
5207 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
5208 break;
5209 }
5210 default:
5211 LOG(FATAL) << "Unexpected operation type " << ror->GetResultType();
5212 }
5213}
5214
5215void InstructionCodeGeneratorARMVIXL::VisitRor(HRor* ror) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005216 DataType::Type type = ror->GetResultType();
Artem Serov02109dd2016-09-23 17:17:54 +01005217 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005218 case DataType::Type::kInt32: {
Artem Serov02109dd2016-09-23 17:17:54 +01005219 HandleIntegerRotate(ror);
5220 break;
5221 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005222 case DataType::Type::kInt64: {
Artem Serov02109dd2016-09-23 17:17:54 +01005223 HandleLongRotate(ror);
5224 break;
5225 }
5226 default:
5227 LOG(FATAL) << "Unexpected operation type " << type;
5228 UNREACHABLE();
5229 }
5230}
5231
Artem Serov02d37832016-10-25 15:25:33 +01005232void LocationsBuilderARMVIXL::HandleShift(HBinaryOperation* op) {
5233 DCHECK(op->IsShl() || op->IsShr() || op->IsUShr());
5234
5235 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005236 new (GetGraph()->GetAllocator()) LocationSummary(op, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01005237
5238 switch (op->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005239 case DataType::Type::kInt32: {
Artem Serov02d37832016-10-25 15:25:33 +01005240 locations->SetInAt(0, Location::RequiresRegister());
5241 if (op->InputAt(1)->IsConstant()) {
5242 locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant()));
5243 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5244 } else {
5245 locations->SetInAt(1, Location::RequiresRegister());
5246 // Make the output overlap, as it will be used to hold the masked
5247 // second input.
5248 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
5249 }
5250 break;
5251 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005252 case DataType::Type::kInt64: {
Artem Serov02d37832016-10-25 15:25:33 +01005253 locations->SetInAt(0, Location::RequiresRegister());
5254 if (op->InputAt(1)->IsConstant()) {
5255 locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant()));
5256 // For simplicity, use kOutputOverlap even though we only require that low registers
5257 // don't clash with high registers which the register allocator currently guarantees.
5258 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
5259 } else {
5260 locations->SetInAt(1, Location::RequiresRegister());
5261 locations->AddTemp(Location::RequiresRegister());
5262 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
5263 }
5264 break;
5265 }
5266 default:
5267 LOG(FATAL) << "Unexpected operation type " << op->GetResultType();
5268 }
5269}
5270
5271void InstructionCodeGeneratorARMVIXL::HandleShift(HBinaryOperation* op) {
5272 DCHECK(op->IsShl() || op->IsShr() || op->IsUShr());
5273
5274 LocationSummary* locations = op->GetLocations();
5275 Location out = locations->Out();
5276 Location first = locations->InAt(0);
5277 Location second = locations->InAt(1);
5278
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005279 DataType::Type type = op->GetResultType();
Artem Serov02d37832016-10-25 15:25:33 +01005280 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005281 case DataType::Type::kInt32: {
Artem Serov02d37832016-10-25 15:25:33 +01005282 vixl32::Register out_reg = OutputRegister(op);
5283 vixl32::Register first_reg = InputRegisterAt(op, 0);
5284 if (second.IsRegister()) {
5285 vixl32::Register second_reg = RegisterFrom(second);
5286 // ARM doesn't mask the shift count so we need to do it ourselves.
5287 __ And(out_reg, second_reg, kMaxIntShiftDistance);
5288 if (op->IsShl()) {
5289 __ Lsl(out_reg, first_reg, out_reg);
5290 } else if (op->IsShr()) {
5291 __ Asr(out_reg, first_reg, out_reg);
5292 } else {
5293 __ Lsr(out_reg, first_reg, out_reg);
5294 }
5295 } else {
Anton Kirilov644032c2016-12-06 17:51:43 +00005296 int32_t cst = Int32ConstantFrom(second);
Artem Serov02d37832016-10-25 15:25:33 +01005297 uint32_t shift_value = cst & kMaxIntShiftDistance;
5298 if (shift_value == 0) { // ARM does not support shifting with 0 immediate.
5299 __ Mov(out_reg, first_reg);
5300 } else if (op->IsShl()) {
5301 __ Lsl(out_reg, first_reg, shift_value);
5302 } else if (op->IsShr()) {
5303 __ Asr(out_reg, first_reg, shift_value);
5304 } else {
5305 __ Lsr(out_reg, first_reg, shift_value);
5306 }
5307 }
5308 break;
5309 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005310 case DataType::Type::kInt64: {
Artem Serov02d37832016-10-25 15:25:33 +01005311 vixl32::Register o_h = HighRegisterFrom(out);
5312 vixl32::Register o_l = LowRegisterFrom(out);
5313
5314 vixl32::Register high = HighRegisterFrom(first);
5315 vixl32::Register low = LowRegisterFrom(first);
5316
5317 if (second.IsRegister()) {
5318 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
5319
5320 vixl32::Register second_reg = RegisterFrom(second);
5321
5322 if (op->IsShl()) {
5323 __ And(o_l, second_reg, kMaxLongShiftDistance);
5324 // Shift the high part
5325 __ Lsl(o_h, high, o_l);
5326 // Shift the low part and `or` what overflew on the high part
Scott Wakelingb77051e2016-11-21 19:46:00 +00005327 __ Rsb(temp, o_l, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01005328 __ Lsr(temp, low, temp);
5329 __ Orr(o_h, o_h, temp);
5330 // If the shift is > 32 bits, override the high part
Scott Wakelingb77051e2016-11-21 19:46:00 +00005331 __ Subs(temp, o_l, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01005332 {
Artem Serov0fb37192016-12-06 18:13:40 +00005333 ExactAssemblyScope guard(GetVIXLAssembler(),
5334 2 * vixl32::kMaxInstructionSizeInBytes,
5335 CodeBufferCheckScope::kMaximumSize);
Artem Serov02d37832016-10-25 15:25:33 +01005336 __ it(pl);
5337 __ lsl(pl, o_h, low, temp);
5338 }
5339 // Shift the low part
5340 __ Lsl(o_l, low, o_l);
5341 } else if (op->IsShr()) {
5342 __ And(o_h, second_reg, kMaxLongShiftDistance);
5343 // Shift the low part
5344 __ Lsr(o_l, low, o_h);
5345 // Shift the high part and `or` what underflew on the low part
Scott Wakelingb77051e2016-11-21 19:46:00 +00005346 __ Rsb(temp, o_h, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01005347 __ Lsl(temp, high, temp);
5348 __ Orr(o_l, o_l, temp);
5349 // If the shift is > 32 bits, override the low part
Scott Wakelingb77051e2016-11-21 19:46:00 +00005350 __ Subs(temp, o_h, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01005351 {
Artem Serov0fb37192016-12-06 18:13:40 +00005352 ExactAssemblyScope guard(GetVIXLAssembler(),
5353 2 * vixl32::kMaxInstructionSizeInBytes,
5354 CodeBufferCheckScope::kMaximumSize);
Artem Serov02d37832016-10-25 15:25:33 +01005355 __ it(pl);
5356 __ asr(pl, o_l, high, temp);
5357 }
5358 // Shift the high part
5359 __ Asr(o_h, high, o_h);
5360 } else {
5361 __ And(o_h, second_reg, kMaxLongShiftDistance);
5362 // same as Shr except we use `Lsr`s and not `Asr`s
5363 __ Lsr(o_l, low, o_h);
Scott Wakelingb77051e2016-11-21 19:46:00 +00005364 __ Rsb(temp, o_h, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01005365 __ Lsl(temp, high, temp);
5366 __ Orr(o_l, o_l, temp);
Scott Wakelingb77051e2016-11-21 19:46:00 +00005367 __ Subs(temp, o_h, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01005368 {
Artem Serov0fb37192016-12-06 18:13:40 +00005369 ExactAssemblyScope guard(GetVIXLAssembler(),
5370 2 * vixl32::kMaxInstructionSizeInBytes,
5371 CodeBufferCheckScope::kMaximumSize);
Artem Serov02d37832016-10-25 15:25:33 +01005372 __ it(pl);
5373 __ lsr(pl, o_l, high, temp);
5374 }
5375 __ Lsr(o_h, high, o_h);
5376 }
5377 } else {
5378 // Register allocator doesn't create partial overlap.
5379 DCHECK(!o_l.Is(high));
5380 DCHECK(!o_h.Is(low));
Anton Kirilov644032c2016-12-06 17:51:43 +00005381 int32_t cst = Int32ConstantFrom(second);
Artem Serov02d37832016-10-25 15:25:33 +01005382 uint32_t shift_value = cst & kMaxLongShiftDistance;
5383 if (shift_value > 32) {
5384 if (op->IsShl()) {
5385 __ Lsl(o_h, low, shift_value - 32);
5386 __ Mov(o_l, 0);
5387 } else if (op->IsShr()) {
5388 __ Asr(o_l, high, shift_value - 32);
5389 __ Asr(o_h, high, 31);
5390 } else {
5391 __ Lsr(o_l, high, shift_value - 32);
5392 __ Mov(o_h, 0);
5393 }
5394 } else if (shift_value == 32) {
5395 if (op->IsShl()) {
5396 __ Mov(o_h, low);
5397 __ Mov(o_l, 0);
5398 } else if (op->IsShr()) {
5399 __ Mov(o_l, high);
5400 __ Asr(o_h, high, 31);
5401 } else {
5402 __ Mov(o_l, high);
5403 __ Mov(o_h, 0);
5404 }
5405 } else if (shift_value == 1) {
5406 if (op->IsShl()) {
5407 __ Lsls(o_l, low, 1);
5408 __ Adc(o_h, high, high);
5409 } else if (op->IsShr()) {
5410 __ Asrs(o_h, high, 1);
5411 __ Rrx(o_l, low);
5412 } else {
5413 __ Lsrs(o_h, high, 1);
5414 __ Rrx(o_l, low);
5415 }
5416 } else {
5417 DCHECK(2 <= shift_value && shift_value < 32) << shift_value;
5418 if (op->IsShl()) {
5419 __ Lsl(o_h, high, shift_value);
5420 __ Orr(o_h, o_h, Operand(low, ShiftType::LSR, 32 - shift_value));
5421 __ Lsl(o_l, low, shift_value);
5422 } else if (op->IsShr()) {
5423 __ Lsr(o_l, low, shift_value);
5424 __ Orr(o_l, o_l, Operand(high, ShiftType::LSL, 32 - shift_value));
5425 __ Asr(o_h, high, shift_value);
5426 } else {
5427 __ Lsr(o_l, low, shift_value);
5428 __ Orr(o_l, o_l, Operand(high, ShiftType::LSL, 32 - shift_value));
5429 __ Lsr(o_h, high, shift_value);
5430 }
5431 }
5432 }
5433 break;
5434 }
5435 default:
5436 LOG(FATAL) << "Unexpected operation type " << type;
5437 UNREACHABLE();
5438 }
5439}
5440
5441void LocationsBuilderARMVIXL::VisitShl(HShl* shl) {
5442 HandleShift(shl);
5443}
5444
5445void InstructionCodeGeneratorARMVIXL::VisitShl(HShl* shl) {
5446 HandleShift(shl);
5447}
5448
5449void LocationsBuilderARMVIXL::VisitShr(HShr* shr) {
5450 HandleShift(shr);
5451}
5452
5453void InstructionCodeGeneratorARMVIXL::VisitShr(HShr* shr) {
5454 HandleShift(shr);
5455}
5456
5457void LocationsBuilderARMVIXL::VisitUShr(HUShr* ushr) {
5458 HandleShift(ushr);
5459}
5460
5461void InstructionCodeGeneratorARMVIXL::VisitUShr(HUShr* ushr) {
5462 HandleShift(ushr);
5463}
5464
5465void LocationsBuilderARMVIXL::VisitNewInstance(HNewInstance* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01005466 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
5467 instruction, LocationSummary::kCallOnMainOnly);
Artem Serov02d37832016-10-25 15:25:33 +01005468 if (instruction->IsStringAlloc()) {
5469 locations->AddTemp(LocationFrom(kMethodRegister));
5470 } else {
5471 InvokeRuntimeCallingConventionARMVIXL calling_convention;
5472 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
Artem Serov02d37832016-10-25 15:25:33 +01005473 }
5474 locations->SetOut(LocationFrom(r0));
5475}
5476
5477void InstructionCodeGeneratorARMVIXL::VisitNewInstance(HNewInstance* instruction) {
5478 // Note: if heap poisoning is enabled, the entry point takes cares
5479 // of poisoning the reference.
5480 if (instruction->IsStringAlloc()) {
5481 // String is allocated through StringFactory. Call NewEmptyString entry point.
5482 vixl32::Register temp = RegisterFrom(instruction->GetLocations()->GetTemp(0));
5483 MemberOffset code_offset = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize);
5484 GetAssembler()->LoadFromOffset(kLoadWord, temp, tr, QUICK_ENTRY_POINT(pNewEmptyString));
5485 GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, code_offset.Int32Value());
Alexandre Rames374ddf32016-11-04 10:40:49 +00005486 // 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 +00005487 ExactAssemblyScope aas(GetVIXLAssembler(),
5488 vixl32::k16BitT32InstructionSizeInBytes,
5489 CodeBufferCheckScope::kExactSize);
Artem Serov02d37832016-10-25 15:25:33 +01005490 __ blx(lr);
5491 codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
5492 } else {
5493 codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
Nicolas Geoffray0d3998b2017-01-12 15:35:12 +00005494 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
Artem Serov02d37832016-10-25 15:25:33 +01005495 }
Roland Levillain5daa4952017-07-03 17:23:56 +01005496 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 10);
Artem Serov02d37832016-10-25 15:25:33 +01005497}
5498
5499void LocationsBuilderARMVIXL::VisitNewArray(HNewArray* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01005500 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
5501 instruction, LocationSummary::kCallOnMainOnly);
Artem Serov02d37832016-10-25 15:25:33 +01005502 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Artem Serov02d37832016-10-25 15:25:33 +01005503 locations->SetOut(LocationFrom(r0));
Nicolas Geoffray8c7c4f12017-01-26 10:13:11 +00005504 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
5505 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
Artem Serov02d37832016-10-25 15:25:33 +01005506}
5507
5508void InstructionCodeGeneratorARMVIXL::VisitNewArray(HNewArray* instruction) {
Artem Serov02d37832016-10-25 15:25:33 +01005509 // Note: if heap poisoning is enabled, the entry point takes cares
5510 // of poisoning the reference.
Artem Serov7b3672e2017-02-03 17:30:34 +00005511 QuickEntrypointEnum entrypoint =
5512 CodeGenerator::GetArrayAllocationEntrypoint(instruction->GetLoadClass()->GetClass());
5513 codegen_->InvokeRuntime(entrypoint, instruction, instruction->GetDexPc());
Nicolas Geoffraye761bcc2017-01-19 08:59:37 +00005514 CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>();
Artem Serov7b3672e2017-02-03 17:30:34 +00005515 DCHECK(!codegen_->IsLeafMethod());
Roland Levillain5daa4952017-07-03 17:23:56 +01005516 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 11);
Artem Serov02d37832016-10-25 15:25:33 +01005517}
5518
5519void LocationsBuilderARMVIXL::VisitParameterValue(HParameterValue* instruction) {
5520 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005521 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01005522 Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
5523 if (location.IsStackSlot()) {
5524 location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
5525 } else if (location.IsDoubleStackSlot()) {
5526 location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
5527 }
5528 locations->SetOut(location);
5529}
5530
5531void InstructionCodeGeneratorARMVIXL::VisitParameterValue(
5532 HParameterValue* instruction ATTRIBUTE_UNUSED) {
5533 // Nothing to do, the parameter is already at its location.
5534}
5535
5536void LocationsBuilderARMVIXL::VisitCurrentMethod(HCurrentMethod* instruction) {
5537 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005538 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01005539 locations->SetOut(LocationFrom(kMethodRegister));
5540}
5541
5542void InstructionCodeGeneratorARMVIXL::VisitCurrentMethod(
5543 HCurrentMethod* instruction ATTRIBUTE_UNUSED) {
5544 // Nothing to do, the method is already at its location.
5545}
5546
5547void LocationsBuilderARMVIXL::VisitNot(HNot* not_) {
5548 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005549 new (GetGraph()->GetAllocator()) LocationSummary(not_, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01005550 locations->SetInAt(0, Location::RequiresRegister());
5551 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5552}
5553
5554void InstructionCodeGeneratorARMVIXL::VisitNot(HNot* not_) {
5555 LocationSummary* locations = not_->GetLocations();
5556 Location out = locations->Out();
5557 Location in = locations->InAt(0);
5558 switch (not_->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005559 case DataType::Type::kInt32:
Artem Serov02d37832016-10-25 15:25:33 +01005560 __ Mvn(OutputRegister(not_), InputRegisterAt(not_, 0));
5561 break;
5562
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005563 case DataType::Type::kInt64:
Artem Serov02d37832016-10-25 15:25:33 +01005564 __ Mvn(LowRegisterFrom(out), LowRegisterFrom(in));
5565 __ Mvn(HighRegisterFrom(out), HighRegisterFrom(in));
5566 break;
5567
5568 default:
5569 LOG(FATAL) << "Unimplemented type for not operation " << not_->GetResultType();
5570 }
5571}
5572
Scott Wakelingc34dba72016-10-03 10:14:44 +01005573void LocationsBuilderARMVIXL::VisitBooleanNot(HBooleanNot* bool_not) {
5574 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005575 new (GetGraph()->GetAllocator()) LocationSummary(bool_not, LocationSummary::kNoCall);
Scott Wakelingc34dba72016-10-03 10:14:44 +01005576 locations->SetInAt(0, Location::RequiresRegister());
5577 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5578}
5579
5580void InstructionCodeGeneratorARMVIXL::VisitBooleanNot(HBooleanNot* bool_not) {
5581 __ Eor(OutputRegister(bool_not), InputRegister(bool_not), 1);
5582}
5583
Artem Serov02d37832016-10-25 15:25:33 +01005584void LocationsBuilderARMVIXL::VisitCompare(HCompare* compare) {
5585 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005586 new (GetGraph()->GetAllocator()) LocationSummary(compare, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01005587 switch (compare->InputAt(0)->GetType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005588 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005589 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005590 case DataType::Type::kInt8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005591 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005592 case DataType::Type::kInt16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005593 case DataType::Type::kInt32:
5594 case DataType::Type::kInt64: {
Artem Serov02d37832016-10-25 15:25:33 +01005595 locations->SetInAt(0, Location::RequiresRegister());
5596 locations->SetInAt(1, Location::RequiresRegister());
5597 // Output overlaps because it is written before doing the low comparison.
5598 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
5599 break;
5600 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005601 case DataType::Type::kFloat32:
5602 case DataType::Type::kFloat64: {
Artem Serov02d37832016-10-25 15:25:33 +01005603 locations->SetInAt(0, Location::RequiresFpuRegister());
5604 locations->SetInAt(1, ArithmeticZeroOrFpuRegister(compare->InputAt(1)));
5605 locations->SetOut(Location::RequiresRegister());
5606 break;
5607 }
5608 default:
5609 LOG(FATAL) << "Unexpected type for compare operation " << compare->InputAt(0)->GetType();
5610 }
5611}
5612
5613void InstructionCodeGeneratorARMVIXL::VisitCompare(HCompare* compare) {
5614 LocationSummary* locations = compare->GetLocations();
5615 vixl32::Register out = OutputRegister(compare);
5616 Location left = locations->InAt(0);
5617 Location right = locations->InAt(1);
5618
5619 vixl32::Label less, greater, done;
Anton Kirilov6f644202017-02-27 18:29:45 +00005620 vixl32::Label* final_label = codegen_->GetFinalLabel(compare, &done);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005621 DataType::Type type = compare->InputAt(0)->GetType();
Vladimir Marko33bff252017-11-01 14:35:42 +00005622 vixl32::Condition less_cond = vixl32::Condition::None();
Artem Serov02d37832016-10-25 15:25:33 +01005623 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005624 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005625 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005626 case DataType::Type::kInt8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005627 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005628 case DataType::Type::kInt16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005629 case DataType::Type::kInt32: {
Artem Serov02d37832016-10-25 15:25:33 +01005630 // Emit move to `out` before the `Cmp`, as `Mov` might affect the status flags.
5631 __ Mov(out, 0);
5632 __ Cmp(RegisterFrom(left), RegisterFrom(right)); // Signed compare.
5633 less_cond = lt;
5634 break;
5635 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005636 case DataType::Type::kInt64: {
Artem Serov02d37832016-10-25 15:25:33 +01005637 __ Cmp(HighRegisterFrom(left), HighRegisterFrom(right)); // Signed compare.
Artem Serov517d9f62016-12-12 15:51:15 +00005638 __ B(lt, &less, /* far_target */ false);
5639 __ B(gt, &greater, /* far_target */ false);
Artem Serov02d37832016-10-25 15:25:33 +01005640 // Emit move to `out` before the last `Cmp`, as `Mov` might affect the status flags.
5641 __ Mov(out, 0);
5642 __ Cmp(LowRegisterFrom(left), LowRegisterFrom(right)); // Unsigned compare.
5643 less_cond = lo;
5644 break;
5645 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005646 case DataType::Type::kFloat32:
5647 case DataType::Type::kFloat64: {
Artem Serov02d37832016-10-25 15:25:33 +01005648 __ Mov(out, 0);
Donghui Bai426b49c2016-11-08 14:55:38 +08005649 GenerateVcmp(compare, codegen_);
Artem Serov02d37832016-10-25 15:25:33 +01005650 // To branch on the FP compare result we transfer FPSCR to APSR (encoded as PC in VMRS).
5651 __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
5652 less_cond = ARMFPCondition(kCondLT, compare->IsGtBias());
5653 break;
5654 }
5655 default:
5656 LOG(FATAL) << "Unexpected compare type " << type;
5657 UNREACHABLE();
5658 }
5659
Anton Kirilov6f644202017-02-27 18:29:45 +00005660 __ B(eq, final_label, /* far_target */ false);
Artem Serov517d9f62016-12-12 15:51:15 +00005661 __ B(less_cond, &less, /* far_target */ false);
Artem Serov02d37832016-10-25 15:25:33 +01005662
5663 __ Bind(&greater);
5664 __ Mov(out, 1);
Anton Kirilov6f644202017-02-27 18:29:45 +00005665 __ B(final_label);
Artem Serov02d37832016-10-25 15:25:33 +01005666
5667 __ Bind(&less);
5668 __ Mov(out, -1);
5669
Anton Kirilov6f644202017-02-27 18:29:45 +00005670 if (done.IsReferenced()) {
5671 __ Bind(&done);
5672 }
Artem Serov02d37832016-10-25 15:25:33 +01005673}
5674
5675void LocationsBuilderARMVIXL::VisitPhi(HPhi* instruction) {
5676 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005677 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01005678 for (size_t i = 0, e = locations->GetInputCount(); i < e; ++i) {
5679 locations->SetInAt(i, Location::Any());
5680 }
5681 locations->SetOut(Location::Any());
5682}
5683
5684void InstructionCodeGeneratorARMVIXL::VisitPhi(HPhi* instruction ATTRIBUTE_UNUSED) {
5685 LOG(FATAL) << "Unreachable";
5686}
5687
5688void CodeGeneratorARMVIXL::GenerateMemoryBarrier(MemBarrierKind kind) {
5689 // TODO (ported from quick): revisit ARM barrier kinds.
5690 DmbOptions flavor = DmbOptions::ISH; // Quiet C++ warnings.
5691 switch (kind) {
5692 case MemBarrierKind::kAnyStore:
5693 case MemBarrierKind::kLoadAny:
5694 case MemBarrierKind::kAnyAny: {
5695 flavor = DmbOptions::ISH;
5696 break;
5697 }
5698 case MemBarrierKind::kStoreStore: {
5699 flavor = DmbOptions::ISHST;
5700 break;
5701 }
5702 default:
5703 LOG(FATAL) << "Unexpected memory barrier " << kind;
5704 }
5705 __ Dmb(flavor);
5706}
5707
5708void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicLoad(vixl32::Register addr,
5709 uint32_t offset,
5710 vixl32::Register out_lo,
5711 vixl32::Register out_hi) {
5712 UseScratchRegisterScope temps(GetVIXLAssembler());
5713 if (offset != 0) {
5714 vixl32::Register temp = temps.Acquire();
5715 __ Add(temp, addr, offset);
5716 addr = temp;
5717 }
Scott Wakelingb77051e2016-11-21 19:46:00 +00005718 __ Ldrexd(out_lo, out_hi, MemOperand(addr));
Artem Serov02d37832016-10-25 15:25:33 +01005719}
5720
5721void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicStore(vixl32::Register addr,
5722 uint32_t offset,
5723 vixl32::Register value_lo,
5724 vixl32::Register value_hi,
5725 vixl32::Register temp1,
5726 vixl32::Register temp2,
5727 HInstruction* instruction) {
5728 UseScratchRegisterScope temps(GetVIXLAssembler());
5729 vixl32::Label fail;
5730 if (offset != 0) {
5731 vixl32::Register temp = temps.Acquire();
5732 __ Add(temp, addr, offset);
5733 addr = temp;
5734 }
5735 __ Bind(&fail);
Alexandre Rames374ddf32016-11-04 10:40:49 +00005736 {
5737 // Ensure the pc position is recorded immediately after the `ldrexd` instruction.
Artem Serov0fb37192016-12-06 18:13:40 +00005738 ExactAssemblyScope aas(GetVIXLAssembler(),
5739 vixl32::kMaxInstructionSizeInBytes,
5740 CodeBufferCheckScope::kMaximumSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00005741 // We need a load followed by store. (The address used in a STREX instruction must
5742 // be the same as the address in the most recently executed LDREX instruction.)
5743 __ ldrexd(temp1, temp2, MemOperand(addr));
5744 codegen_->MaybeRecordImplicitNullCheck(instruction);
5745 }
Scott Wakelingb77051e2016-11-21 19:46:00 +00005746 __ Strexd(temp1, value_lo, value_hi, MemOperand(addr));
xueliang.zhongf51bc622016-11-04 09:23:32 +00005747 __ CompareAndBranchIfNonZero(temp1, &fail);
Artem Serov02d37832016-10-25 15:25:33 +01005748}
Artem Serov02109dd2016-09-23 17:17:54 +01005749
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005750void LocationsBuilderARMVIXL::HandleFieldSet(
5751 HInstruction* instruction, const FieldInfo& field_info) {
5752 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
5753
5754 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005755 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005756 locations->SetInAt(0, Location::RequiresRegister());
5757
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005758 DataType::Type field_type = field_info.GetFieldType();
5759 if (DataType::IsFloatingPointType(field_type)) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005760 locations->SetInAt(1, Location::RequiresFpuRegister());
5761 } else {
5762 locations->SetInAt(1, Location::RequiresRegister());
5763 }
5764
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005765 bool is_wide = field_type == DataType::Type::kInt64 || field_type == DataType::Type::kFloat64;
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005766 bool generate_volatile = field_info.IsVolatile()
5767 && is_wide
5768 && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
5769 bool needs_write_barrier =
5770 CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1));
5771 // Temporary registers for the write barrier.
5772 // TODO: consider renaming StoreNeedsWriteBarrier to StoreNeedsGCMark.
5773 if (needs_write_barrier) {
5774 locations->AddTemp(Location::RequiresRegister()); // Possibly used for reference poisoning too.
5775 locations->AddTemp(Location::RequiresRegister());
5776 } else if (generate_volatile) {
5777 // ARM encoding have some additional constraints for ldrexd/strexd:
5778 // - registers need to be consecutive
5779 // - the first register should be even but not R14.
5780 // We don't test for ARM yet, and the assertion makes sure that we
5781 // revisit this if we ever enable ARM encoding.
5782 DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet());
5783
5784 locations->AddTemp(Location::RequiresRegister());
5785 locations->AddTemp(Location::RequiresRegister());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005786 if (field_type == DataType::Type::kFloat64) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005787 // For doubles we need two more registers to copy the value.
5788 locations->AddTemp(LocationFrom(r2));
5789 locations->AddTemp(LocationFrom(r3));
5790 }
5791 }
5792}
5793
5794void InstructionCodeGeneratorARMVIXL::HandleFieldSet(HInstruction* instruction,
5795 const FieldInfo& field_info,
5796 bool value_can_be_null) {
5797 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
5798
5799 LocationSummary* locations = instruction->GetLocations();
5800 vixl32::Register base = InputRegisterAt(instruction, 0);
5801 Location value = locations->InAt(1);
5802
5803 bool is_volatile = field_info.IsVolatile();
5804 bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005805 DataType::Type field_type = field_info.GetFieldType();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005806 uint32_t offset = field_info.GetFieldOffset().Uint32Value();
5807 bool needs_write_barrier =
5808 CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1));
5809
5810 if (is_volatile) {
5811 codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
5812 }
5813
5814 switch (field_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005815 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005816 case DataType::Type::kUint8:
5817 case DataType::Type::kInt8:
5818 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005819 case DataType::Type::kInt16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005820 case DataType::Type::kInt32: {
5821 StoreOperandType operand_type = GetStoreOperandType(field_type);
5822 GetAssembler()->StoreToOffset(operand_type, RegisterFrom(value), base, offset);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005823 break;
5824 }
5825
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005826 case DataType::Type::kReference: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005827 if (kPoisonHeapReferences && needs_write_barrier) {
5828 // Note that in the case where `value` is a null reference,
5829 // we do not enter this block, as a null reference does not
5830 // need poisoning.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005831 DCHECK_EQ(field_type, DataType::Type::kReference);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005832 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
5833 __ Mov(temp, RegisterFrom(value));
5834 GetAssembler()->PoisonHeapReference(temp);
5835 GetAssembler()->StoreToOffset(kStoreWord, temp, base, offset);
5836 } else {
5837 GetAssembler()->StoreToOffset(kStoreWord, RegisterFrom(value), base, offset);
5838 }
5839 break;
5840 }
5841
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005842 case DataType::Type::kInt64: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005843 if (is_volatile && !atomic_ldrd_strd) {
5844 GenerateWideAtomicStore(base,
5845 offset,
5846 LowRegisterFrom(value),
5847 HighRegisterFrom(value),
5848 RegisterFrom(locations->GetTemp(0)),
5849 RegisterFrom(locations->GetTemp(1)),
5850 instruction);
5851 } else {
5852 GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), base, offset);
5853 codegen_->MaybeRecordImplicitNullCheck(instruction);
5854 }
5855 break;
5856 }
5857
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005858 case DataType::Type::kFloat32: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005859 GetAssembler()->StoreSToOffset(SRegisterFrom(value), base, offset);
5860 break;
5861 }
5862
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005863 case DataType::Type::kFloat64: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01005864 vixl32::DRegister value_reg = DRegisterFrom(value);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005865 if (is_volatile && !atomic_ldrd_strd) {
5866 vixl32::Register value_reg_lo = RegisterFrom(locations->GetTemp(0));
5867 vixl32::Register value_reg_hi = RegisterFrom(locations->GetTemp(1));
5868
5869 __ Vmov(value_reg_lo, value_reg_hi, value_reg);
5870
5871 GenerateWideAtomicStore(base,
5872 offset,
5873 value_reg_lo,
5874 value_reg_hi,
5875 RegisterFrom(locations->GetTemp(2)),
5876 RegisterFrom(locations->GetTemp(3)),
5877 instruction);
5878 } else {
5879 GetAssembler()->StoreDToOffset(value_reg, base, offset);
5880 codegen_->MaybeRecordImplicitNullCheck(instruction);
5881 }
5882 break;
5883 }
5884
Aart Bik66c158e2018-01-31 12:55:04 -08005885 case DataType::Type::kUint32:
5886 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005887 case DataType::Type::kVoid:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005888 LOG(FATAL) << "Unreachable type " << field_type;
5889 UNREACHABLE();
5890 }
5891
5892 // Longs and doubles are handled in the switch.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005893 if (field_type != DataType::Type::kInt64 && field_type != DataType::Type::kFloat64) {
Alexandre Rames374ddf32016-11-04 10:40:49 +00005894 // TODO(VIXL): Here and for other calls to `MaybeRecordImplicitNullCheck` in this method, we
5895 // should use a scope and the assembler to emit the store instruction to guarantee that we
5896 // record the pc at the correct position. But the `Assembler` does not automatically handle
5897 // unencodable offsets. Practically, everything is fine because the helper and VIXL, at the time
5898 // of writing, do generate the store instruction last.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005899 codegen_->MaybeRecordImplicitNullCheck(instruction);
5900 }
5901
5902 if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
5903 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
5904 vixl32::Register card = RegisterFrom(locations->GetTemp(1));
5905 codegen_->MarkGCCard(temp, card, base, RegisterFrom(value), value_can_be_null);
5906 }
5907
5908 if (is_volatile) {
5909 codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
5910 }
5911}
5912
Artem Serov02d37832016-10-25 15:25:33 +01005913void LocationsBuilderARMVIXL::HandleFieldGet(HInstruction* instruction,
5914 const FieldInfo& field_info) {
5915 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
5916
5917 bool object_field_get_with_read_barrier =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005918 kEmitCompilerReadBarrier && (field_info.GetFieldType() == DataType::Type::kReference);
Artem Serov02d37832016-10-25 15:25:33 +01005919 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005920 new (GetGraph()->GetAllocator()) LocationSummary(instruction,
5921 object_field_get_with_read_barrier
5922 ? LocationSummary::kCallOnSlowPath
5923 : LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01005924 if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
5925 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
5926 }
5927 locations->SetInAt(0, Location::RequiresRegister());
5928
5929 bool volatile_for_double = field_info.IsVolatile()
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005930 && (field_info.GetFieldType() == DataType::Type::kFloat64)
Artem Serov02d37832016-10-25 15:25:33 +01005931 && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
5932 // The output overlaps in case of volatile long: we don't want the
5933 // code generated by GenerateWideAtomicLoad to overwrite the
5934 // object's location. Likewise, in the case of an object field get
5935 // with read barriers enabled, we do not want the load to overwrite
5936 // the object's location, as we need it to emit the read barrier.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005937 bool overlap =
5938 (field_info.IsVolatile() && (field_info.GetFieldType() == DataType::Type::kInt64)) ||
Artem Serov02d37832016-10-25 15:25:33 +01005939 object_field_get_with_read_barrier;
5940
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005941 if (DataType::IsFloatingPointType(instruction->GetType())) {
Artem Serov02d37832016-10-25 15:25:33 +01005942 locations->SetOut(Location::RequiresFpuRegister());
5943 } else {
5944 locations->SetOut(Location::RequiresRegister(),
5945 (overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap));
5946 }
5947 if (volatile_for_double) {
5948 // ARM encoding have some additional constraints for ldrexd/strexd:
5949 // - registers need to be consecutive
5950 // - the first register should be even but not R14.
5951 // We don't test for ARM yet, and the assertion makes sure that we
5952 // revisit this if we ever enable ARM encoding.
5953 DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet());
5954 locations->AddTemp(Location::RequiresRegister());
5955 locations->AddTemp(Location::RequiresRegister());
5956 } else if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
5957 // We need a temporary register for the read barrier marking slow
Artem Serovc5fcb442016-12-02 19:19:58 +00005958 // path in CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01005959 if (kBakerReadBarrierLinkTimeThunksEnableForFields &&
5960 !Runtime::Current()->UseJitCompilation()) {
5961 // If link-time thunks for the Baker read barrier are enabled, for AOT
5962 // loads we need a temporary only if the offset is too big.
5963 if (field_info.GetFieldOffset().Uint32Value() >= kReferenceLoadMinFarOffset) {
5964 locations->AddTemp(Location::RequiresRegister());
5965 }
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01005966 } else {
5967 locations->AddTemp(Location::RequiresRegister());
5968 }
Artem Serov02d37832016-10-25 15:25:33 +01005969 }
5970}
5971
5972Location LocationsBuilderARMVIXL::ArithmeticZeroOrFpuRegister(HInstruction* input) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005973 DCHECK(DataType::IsFloatingPointType(input->GetType())) << input->GetType();
Artem Serov02d37832016-10-25 15:25:33 +01005974 if ((input->IsFloatConstant() && (input->AsFloatConstant()->IsArithmeticZero())) ||
5975 (input->IsDoubleConstant() && (input->AsDoubleConstant()->IsArithmeticZero()))) {
5976 return Location::ConstantLocation(input->AsConstant());
5977 } else {
5978 return Location::RequiresFpuRegister();
5979 }
5980}
5981
Artem Serov02109dd2016-09-23 17:17:54 +01005982Location LocationsBuilderARMVIXL::ArmEncodableConstantOrRegister(HInstruction* constant,
5983 Opcode opcode) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005984 DCHECK(!DataType::IsFloatingPointType(constant->GetType()));
Artem Serov02109dd2016-09-23 17:17:54 +01005985 if (constant->IsConstant() &&
5986 CanEncodeConstantAsImmediate(constant->AsConstant(), opcode)) {
5987 return Location::ConstantLocation(constant->AsConstant());
5988 }
5989 return Location::RequiresRegister();
5990}
5991
Vladimir Markof0a6a1d2018-01-08 14:23:56 +00005992static bool CanEncode32BitConstantAsImmediate(
5993 CodeGeneratorARMVIXL* codegen,
5994 uint32_t value,
5995 Opcode opcode,
5996 vixl32::FlagsUpdate flags_update = vixl32::FlagsUpdate::DontCare) {
5997 ArmVIXLAssembler* assembler = codegen->GetAssembler();
5998 if (assembler->ShifterOperandCanHold(opcode, value, flags_update)) {
Artem Serov02109dd2016-09-23 17:17:54 +01005999 return true;
6000 }
6001 Opcode neg_opcode = kNoOperand;
Anton Kiriloveffd5bf2017-02-28 16:59:15 +00006002 uint32_t neg_value = 0;
Artem Serov02109dd2016-09-23 17:17:54 +01006003 switch (opcode) {
Anton Kiriloveffd5bf2017-02-28 16:59:15 +00006004 case AND: neg_opcode = BIC; neg_value = ~value; break;
6005 case ORR: neg_opcode = ORN; neg_value = ~value; break;
6006 case ADD: neg_opcode = SUB; neg_value = -value; break;
6007 case ADC: neg_opcode = SBC; neg_value = ~value; break;
6008 case SUB: neg_opcode = ADD; neg_value = -value; break;
6009 case SBC: neg_opcode = ADC; neg_value = ~value; break;
6010 case MOV: neg_opcode = MVN; neg_value = ~value; break;
Artem Serov02109dd2016-09-23 17:17:54 +01006011 default:
6012 return false;
6013 }
Anton Kiriloveffd5bf2017-02-28 16:59:15 +00006014
Vladimir Markof0a6a1d2018-01-08 14:23:56 +00006015 if (assembler->ShifterOperandCanHold(neg_opcode, neg_value, flags_update)) {
Anton Kiriloveffd5bf2017-02-28 16:59:15 +00006016 return true;
6017 }
6018
6019 return opcode == AND && IsPowerOfTwo(value + 1);
Artem Serov02109dd2016-09-23 17:17:54 +01006020}
6021
Vladimir Markof0a6a1d2018-01-08 14:23:56 +00006022bool LocationsBuilderARMVIXL::CanEncodeConstantAsImmediate(HConstant* input_cst, Opcode opcode) {
6023 uint64_t value = static_cast<uint64_t>(Int64FromConstant(input_cst));
6024 if (DataType::Is64BitType(input_cst->GetType())) {
6025 Opcode high_opcode = opcode;
6026 vixl32::FlagsUpdate low_flags_update = vixl32::FlagsUpdate::DontCare;
6027 switch (opcode) {
6028 case SUB:
6029 // Flip the operation to an ADD.
6030 value = -value;
6031 opcode = ADD;
6032 FALLTHROUGH_INTENDED;
6033 case ADD:
6034 if (Low32Bits(value) == 0u) {
6035 return CanEncode32BitConstantAsImmediate(codegen_, High32Bits(value), opcode);
6036 }
6037 high_opcode = ADC;
6038 low_flags_update = vixl32::FlagsUpdate::SetFlags;
6039 break;
6040 default:
6041 break;
6042 }
6043 return CanEncode32BitConstantAsImmediate(codegen_, High32Bits(value), high_opcode) &&
6044 CanEncode32BitConstantAsImmediate(codegen_, Low32Bits(value), opcode, low_flags_update);
6045 } else {
6046 return CanEncode32BitConstantAsImmediate(codegen_, Low32Bits(value), opcode);
6047 }
6048}
6049
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006050void InstructionCodeGeneratorARMVIXL::HandleFieldGet(HInstruction* instruction,
6051 const FieldInfo& field_info) {
6052 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
6053
6054 LocationSummary* locations = instruction->GetLocations();
6055 vixl32::Register base = InputRegisterAt(instruction, 0);
6056 Location out = locations->Out();
6057 bool is_volatile = field_info.IsVolatile();
6058 bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
Vladimir Marko61b92282017-10-11 13:23:17 +01006059 DCHECK_EQ(DataType::Size(field_info.GetFieldType()), DataType::Size(instruction->GetType()));
6060 DataType::Type load_type = instruction->GetType();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006061 uint32_t offset = field_info.GetFieldOffset().Uint32Value();
6062
Vladimir Marko61b92282017-10-11 13:23:17 +01006063 switch (load_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006064 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006065 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006066 case DataType::Type::kInt8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006067 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006068 case DataType::Type::kInt16:
6069 case DataType::Type::kInt32: {
Vladimir Marko61b92282017-10-11 13:23:17 +01006070 LoadOperandType operand_type = GetLoadOperandType(load_type);
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006071 GetAssembler()->LoadFromOffset(operand_type, RegisterFrom(out), base, offset);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006072 break;
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006073 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006074
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006075 case DataType::Type::kReference: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006076 // /* HeapReference<Object> */ out = *(base + offset)
6077 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
Vladimir Markodcd117e2018-04-19 11:54:00 +01006078 Location maybe_temp = (locations->GetTempCount() != 0) ? locations->GetTemp(0) : Location();
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006079 // Note that a potential implicit null check is handled in this
6080 // CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier call.
6081 codegen_->GenerateFieldLoadWithBakerReadBarrier(
Vladimir Markodcd117e2018-04-19 11:54:00 +01006082 instruction, out, base, offset, maybe_temp, /* needs_null_check */ true);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006083 if (is_volatile) {
6084 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
6085 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006086 } else {
6087 GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(out), base, offset);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006088 codegen_->MaybeRecordImplicitNullCheck(instruction);
6089 if (is_volatile) {
6090 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
6091 }
6092 // If read barriers are enabled, emit read barriers other than
6093 // Baker's using a slow path (and also unpoison the loaded
6094 // reference, if heap poisoning is enabled).
6095 codegen_->MaybeGenerateReadBarrierSlow(instruction, out, out, locations->InAt(0), offset);
6096 }
6097 break;
6098 }
6099
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006100 case DataType::Type::kInt64:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006101 if (is_volatile && !atomic_ldrd_strd) {
6102 GenerateWideAtomicLoad(base, offset, LowRegisterFrom(out), HighRegisterFrom(out));
6103 } else {
6104 GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out), base, offset);
6105 }
6106 break;
6107
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006108 case DataType::Type::kFloat32:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006109 GetAssembler()->LoadSFromOffset(SRegisterFrom(out), base, offset);
6110 break;
6111
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006112 case DataType::Type::kFloat64: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006113 vixl32::DRegister out_dreg = DRegisterFrom(out);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006114 if (is_volatile && !atomic_ldrd_strd) {
6115 vixl32::Register lo = RegisterFrom(locations->GetTemp(0));
6116 vixl32::Register hi = RegisterFrom(locations->GetTemp(1));
6117 GenerateWideAtomicLoad(base, offset, lo, hi);
6118 // TODO(VIXL): Do we need to be immediately after the ldrexd instruction? If so we need a
6119 // scope.
6120 codegen_->MaybeRecordImplicitNullCheck(instruction);
6121 __ Vmov(out_dreg, lo, hi);
6122 } else {
6123 GetAssembler()->LoadDFromOffset(out_dreg, base, offset);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006124 codegen_->MaybeRecordImplicitNullCheck(instruction);
6125 }
6126 break;
6127 }
6128
Aart Bik66c158e2018-01-31 12:55:04 -08006129 case DataType::Type::kUint32:
6130 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006131 case DataType::Type::kVoid:
Vladimir Marko61b92282017-10-11 13:23:17 +01006132 LOG(FATAL) << "Unreachable type " << load_type;
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006133 UNREACHABLE();
6134 }
6135
Vladimir Marko61b92282017-10-11 13:23:17 +01006136 if (load_type == DataType::Type::kReference || load_type == DataType::Type::kFloat64) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006137 // Potential implicit null checks, in the case of reference or
6138 // double fields, are handled in the previous switch statement.
6139 } else {
6140 // Address cases other than reference and double that may require an implicit null check.
Alexandre Rames374ddf32016-11-04 10:40:49 +00006141 // TODO(VIXL): Here and for other calls to `MaybeRecordImplicitNullCheck` in this method, we
6142 // should use a scope and the assembler to emit the load instruction to guarantee that we
6143 // record the pc at the correct position. But the `Assembler` does not automatically handle
6144 // unencodable offsets. Practically, everything is fine because the helper and VIXL, at the time
6145 // of writing, do generate the store instruction last.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006146 codegen_->MaybeRecordImplicitNullCheck(instruction);
6147 }
6148
6149 if (is_volatile) {
Vladimir Marko61b92282017-10-11 13:23:17 +01006150 if (load_type == DataType::Type::kReference) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006151 // Memory barriers, in the case of references, are also handled
6152 // in the previous switch statement.
6153 } else {
6154 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
6155 }
6156 }
6157}
6158
6159void LocationsBuilderARMVIXL::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
6160 HandleFieldSet(instruction, instruction->GetFieldInfo());
6161}
6162
6163void InstructionCodeGeneratorARMVIXL::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
6164 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
6165}
6166
6167void LocationsBuilderARMVIXL::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
6168 HandleFieldGet(instruction, instruction->GetFieldInfo());
6169}
6170
6171void InstructionCodeGeneratorARMVIXL::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
6172 HandleFieldGet(instruction, instruction->GetFieldInfo());
6173}
6174
6175void LocationsBuilderARMVIXL::VisitStaticFieldGet(HStaticFieldGet* instruction) {
6176 HandleFieldGet(instruction, instruction->GetFieldInfo());
6177}
6178
6179void InstructionCodeGeneratorARMVIXL::VisitStaticFieldGet(HStaticFieldGet* instruction) {
6180 HandleFieldGet(instruction, instruction->GetFieldInfo());
6181}
6182
Scott Wakelingc34dba72016-10-03 10:14:44 +01006183void LocationsBuilderARMVIXL::VisitStaticFieldSet(HStaticFieldSet* instruction) {
6184 HandleFieldSet(instruction, instruction->GetFieldInfo());
6185}
6186
6187void InstructionCodeGeneratorARMVIXL::VisitStaticFieldSet(HStaticFieldSet* instruction) {
6188 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
6189}
6190
Artem Serovcfbe9132016-10-14 15:58:56 +01006191void LocationsBuilderARMVIXL::VisitUnresolvedInstanceFieldGet(
6192 HUnresolvedInstanceFieldGet* instruction) {
6193 FieldAccessCallingConventionARMVIXL calling_convention;
6194 codegen_->CreateUnresolvedFieldLocationSummary(
6195 instruction, instruction->GetFieldType(), calling_convention);
6196}
6197
6198void InstructionCodeGeneratorARMVIXL::VisitUnresolvedInstanceFieldGet(
6199 HUnresolvedInstanceFieldGet* instruction) {
6200 FieldAccessCallingConventionARMVIXL calling_convention;
6201 codegen_->GenerateUnresolvedFieldAccess(instruction,
6202 instruction->GetFieldType(),
6203 instruction->GetFieldIndex(),
6204 instruction->GetDexPc(),
6205 calling_convention);
6206}
6207
6208void LocationsBuilderARMVIXL::VisitUnresolvedInstanceFieldSet(
6209 HUnresolvedInstanceFieldSet* instruction) {
6210 FieldAccessCallingConventionARMVIXL calling_convention;
6211 codegen_->CreateUnresolvedFieldLocationSummary(
6212 instruction, instruction->GetFieldType(), calling_convention);
6213}
6214
6215void InstructionCodeGeneratorARMVIXL::VisitUnresolvedInstanceFieldSet(
6216 HUnresolvedInstanceFieldSet* instruction) {
6217 FieldAccessCallingConventionARMVIXL calling_convention;
6218 codegen_->GenerateUnresolvedFieldAccess(instruction,
6219 instruction->GetFieldType(),
6220 instruction->GetFieldIndex(),
6221 instruction->GetDexPc(),
6222 calling_convention);
6223}
6224
6225void LocationsBuilderARMVIXL::VisitUnresolvedStaticFieldGet(
6226 HUnresolvedStaticFieldGet* instruction) {
6227 FieldAccessCallingConventionARMVIXL calling_convention;
6228 codegen_->CreateUnresolvedFieldLocationSummary(
6229 instruction, instruction->GetFieldType(), calling_convention);
6230}
6231
6232void InstructionCodeGeneratorARMVIXL::VisitUnresolvedStaticFieldGet(
6233 HUnresolvedStaticFieldGet* instruction) {
6234 FieldAccessCallingConventionARMVIXL calling_convention;
6235 codegen_->GenerateUnresolvedFieldAccess(instruction,
6236 instruction->GetFieldType(),
6237 instruction->GetFieldIndex(),
6238 instruction->GetDexPc(),
6239 calling_convention);
6240}
6241
6242void LocationsBuilderARMVIXL::VisitUnresolvedStaticFieldSet(
6243 HUnresolvedStaticFieldSet* instruction) {
6244 FieldAccessCallingConventionARMVIXL calling_convention;
6245 codegen_->CreateUnresolvedFieldLocationSummary(
6246 instruction, instruction->GetFieldType(), calling_convention);
6247}
6248
6249void InstructionCodeGeneratorARMVIXL::VisitUnresolvedStaticFieldSet(
6250 HUnresolvedStaticFieldSet* instruction) {
6251 FieldAccessCallingConventionARMVIXL calling_convention;
6252 codegen_->GenerateUnresolvedFieldAccess(instruction,
6253 instruction->GetFieldType(),
6254 instruction->GetFieldIndex(),
6255 instruction->GetDexPc(),
6256 calling_convention);
6257}
6258
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006259void LocationsBuilderARMVIXL::VisitNullCheck(HNullCheck* instruction) {
Artem Serov657022c2016-11-23 14:19:38 +00006260 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006261 locations->SetInAt(0, Location::RequiresRegister());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006262}
6263
6264void CodeGeneratorARMVIXL::GenerateImplicitNullCheck(HNullCheck* instruction) {
6265 if (CanMoveNullCheckToUser(instruction)) {
6266 return;
6267 }
6268
6269 UseScratchRegisterScope temps(GetVIXLAssembler());
Alexandre Rames374ddf32016-11-04 10:40:49 +00006270 // Ensure the pc position is recorded immediately after the `ldr` instruction.
Artem Serov0fb37192016-12-06 18:13:40 +00006271 ExactAssemblyScope aas(GetVIXLAssembler(),
6272 vixl32::kMaxInstructionSizeInBytes,
6273 CodeBufferCheckScope::kMaximumSize);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006274 __ ldr(temps.Acquire(), MemOperand(InputRegisterAt(instruction, 0)));
6275 RecordPcInfo(instruction, instruction->GetDexPc());
6276}
6277
6278void CodeGeneratorARMVIXL::GenerateExplicitNullCheck(HNullCheck* instruction) {
6279 NullCheckSlowPathARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01006280 new (GetScopedAllocator()) NullCheckSlowPathARMVIXL(instruction);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006281 AddSlowPath(slow_path);
xueliang.zhongf51bc622016-11-04 09:23:32 +00006282 __ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006283}
6284
6285void InstructionCodeGeneratorARMVIXL::VisitNullCheck(HNullCheck* instruction) {
6286 codegen_->GenerateNullCheck(instruction);
6287}
6288
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006289void CodeGeneratorARMVIXL::LoadFromShiftedRegOffset(DataType::Type type,
Scott Wakelingc34dba72016-10-03 10:14:44 +01006290 Location out_loc,
6291 vixl32::Register base,
6292 vixl32::Register reg_index,
6293 vixl32::Condition cond) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006294 uint32_t shift_count = DataType::SizeShift(type);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006295 MemOperand mem_address(base, reg_index, vixl32::LSL, shift_count);
6296
6297 switch (type) {
Vladimir Marko61b92282017-10-11 13:23:17 +01006298 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006299 case DataType::Type::kUint8:
Vladimir Marko61b92282017-10-11 13:23:17 +01006300 __ Ldrb(cond, RegisterFrom(out_loc), mem_address);
6301 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006302 case DataType::Type::kInt8:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006303 __ Ldrsb(cond, RegisterFrom(out_loc), mem_address);
6304 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006305 case DataType::Type::kUint16:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006306 __ Ldrh(cond, RegisterFrom(out_loc), mem_address);
6307 break;
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006308 case DataType::Type::kInt16:
6309 __ Ldrsh(cond, RegisterFrom(out_loc), mem_address);
6310 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006311 case DataType::Type::kReference:
6312 case DataType::Type::kInt32:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006313 __ Ldr(cond, RegisterFrom(out_loc), mem_address);
6314 break;
6315 // T32 doesn't support LoadFromShiftedRegOffset mem address mode for these types.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006316 case DataType::Type::kInt64:
6317 case DataType::Type::kFloat32:
6318 case DataType::Type::kFloat64:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006319 default:
6320 LOG(FATAL) << "Unreachable type " << type;
6321 UNREACHABLE();
6322 }
6323}
6324
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006325void CodeGeneratorARMVIXL::StoreToShiftedRegOffset(DataType::Type type,
Scott Wakelingc34dba72016-10-03 10:14:44 +01006326 Location loc,
6327 vixl32::Register base,
6328 vixl32::Register reg_index,
6329 vixl32::Condition cond) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006330 uint32_t shift_count = DataType::SizeShift(type);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006331 MemOperand mem_address(base, reg_index, vixl32::LSL, shift_count);
6332
6333 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006334 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006335 case DataType::Type::kUint8:
6336 case DataType::Type::kInt8:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006337 __ Strb(cond, RegisterFrom(loc), mem_address);
6338 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006339 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006340 case DataType::Type::kInt16:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006341 __ Strh(cond, RegisterFrom(loc), mem_address);
6342 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006343 case DataType::Type::kReference:
6344 case DataType::Type::kInt32:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006345 __ Str(cond, RegisterFrom(loc), mem_address);
6346 break;
6347 // T32 doesn't support StoreToShiftedRegOffset mem address mode for these types.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006348 case DataType::Type::kInt64:
6349 case DataType::Type::kFloat32:
6350 case DataType::Type::kFloat64:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006351 default:
6352 LOG(FATAL) << "Unreachable type " << type;
6353 UNREACHABLE();
6354 }
6355}
6356
6357void LocationsBuilderARMVIXL::VisitArrayGet(HArrayGet* instruction) {
6358 bool object_array_get_with_read_barrier =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006359 kEmitCompilerReadBarrier && (instruction->GetType() == DataType::Type::kReference);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006360 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01006361 new (GetGraph()->GetAllocator()) LocationSummary(instruction,
6362 object_array_get_with_read_barrier
6363 ? LocationSummary::kCallOnSlowPath
6364 : LocationSummary::kNoCall);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006365 if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006366 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Scott Wakelingc34dba72016-10-03 10:14:44 +01006367 }
6368 locations->SetInAt(0, Location::RequiresRegister());
6369 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006370 if (DataType::IsFloatingPointType(instruction->GetType())) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006371 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
6372 } else {
6373 // The output overlaps in the case of an object array get with
6374 // read barriers enabled: we do not want the move to overwrite the
6375 // array's location, as we need it to emit the read barrier.
6376 locations->SetOut(
6377 Location::RequiresRegister(),
6378 object_array_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap);
6379 }
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006380 if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006381 if (kBakerReadBarrierLinkTimeThunksEnableForFields &&
6382 !Runtime::Current()->UseJitCompilation() &&
6383 instruction->GetIndex()->IsConstant()) {
6384 // Array loads with constant index are treated as field loads.
6385 // If link-time thunks for the Baker read barrier are enabled, for AOT
6386 // constant index loads we need a temporary only if the offset is too big.
6387 uint32_t offset = CodeGenerator::GetArrayDataOffset(instruction);
6388 uint32_t index = instruction->GetIndex()->AsIntConstant()->GetValue();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006389 offset += index << DataType::SizeShift(DataType::Type::kReference);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006390 if (offset >= kReferenceLoadMinFarOffset) {
6391 locations->AddTemp(Location::RequiresRegister());
6392 }
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006393 } else {
Vladimir Markodcd117e2018-04-19 11:54:00 +01006394 // If using introspection, we need a non-scratch temporary for the array data pointer.
6395 // Otherwise, we need a temporary register for the read barrier marking slow
6396 // path in CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006397 locations->AddTemp(Location::RequiresRegister());
6398 }
6399 } else if (mirror::kUseStringCompression && instruction->IsStringCharAt()) {
6400 // Also need a temporary for String compression feature.
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006401 locations->AddTemp(Location::RequiresRegister());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006402 }
6403}
6404
6405void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006406 LocationSummary* locations = instruction->GetLocations();
6407 Location obj_loc = locations->InAt(0);
6408 vixl32::Register obj = InputRegisterAt(instruction, 0);
6409 Location index = locations->InAt(1);
6410 Location out_loc = locations->Out();
6411 uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006412 DataType::Type type = instruction->GetType();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006413 const bool maybe_compressed_char_at = mirror::kUseStringCompression &&
6414 instruction->IsStringCharAt();
6415 HInstruction* array_instr = instruction->GetArray();
6416 bool has_intermediate_address = array_instr->IsIntermediateAddress();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006417
6418 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006419 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006420 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006421 case DataType::Type::kInt8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006422 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006423 case DataType::Type::kInt16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006424 case DataType::Type::kInt32: {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01006425 vixl32::Register length;
6426 if (maybe_compressed_char_at) {
6427 length = RegisterFrom(locations->GetTemp(0));
6428 uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
6429 GetAssembler()->LoadFromOffset(kLoadWord, length, obj, count_offset);
6430 codegen_->MaybeRecordImplicitNullCheck(instruction);
6431 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006432 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006433 int32_t const_index = Int32ConstantFrom(index);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006434 if (maybe_compressed_char_at) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006435 vixl32::Label uncompressed_load, done;
Anton Kirilov6f644202017-02-27 18:29:45 +00006436 vixl32::Label* final_label = codegen_->GetFinalLabel(instruction, &done);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01006437 __ Lsrs(length, length, 1u); // LSRS has a 16-bit encoding, TST (immediate) does not.
6438 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
6439 "Expecting 0=compressed, 1=uncompressed");
Artem Serov517d9f62016-12-12 15:51:15 +00006440 __ B(cs, &uncompressed_load, /* far_target */ false);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006441 GetAssembler()->LoadFromOffset(kLoadUnsignedByte,
6442 RegisterFrom(out_loc),
6443 obj,
6444 data_offset + const_index);
Anton Kirilov6f644202017-02-27 18:29:45 +00006445 __ B(final_label);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006446 __ Bind(&uncompressed_load);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006447 GetAssembler()->LoadFromOffset(GetLoadOperandType(DataType::Type::kUint16),
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006448 RegisterFrom(out_loc),
6449 obj,
6450 data_offset + (const_index << 1));
Anton Kirilov6f644202017-02-27 18:29:45 +00006451 if (done.IsReferenced()) {
6452 __ Bind(&done);
6453 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006454 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006455 uint32_t full_offset = data_offset + (const_index << DataType::SizeShift(type));
Scott Wakelingc34dba72016-10-03 10:14:44 +01006456
6457 LoadOperandType load_type = GetLoadOperandType(type);
6458 GetAssembler()->LoadFromOffset(load_type, RegisterFrom(out_loc), obj, full_offset);
6459 }
6460 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006461 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006462 vixl32::Register temp = temps.Acquire();
6463
6464 if (has_intermediate_address) {
Artem Serov2bbc9532016-10-21 11:51:50 +01006465 // We do not need to compute the intermediate address from the array: the
6466 // input instruction has done it already. See the comment in
6467 // `TryExtractArrayAccessAddress()`.
6468 if (kIsDebugBuild) {
6469 HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
Anton Kirilov644032c2016-12-06 17:51:43 +00006470 DCHECK_EQ(Uint64ConstantFrom(tmp->GetOffset()), data_offset);
Artem Serov2bbc9532016-10-21 11:51:50 +01006471 }
6472 temp = obj;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006473 } else {
6474 __ Add(temp, obj, data_offset);
6475 }
6476 if (maybe_compressed_char_at) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006477 vixl32::Label uncompressed_load, done;
Anton Kirilov6f644202017-02-27 18:29:45 +00006478 vixl32::Label* final_label = codegen_->GetFinalLabel(instruction, &done);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01006479 __ Lsrs(length, length, 1u); // LSRS has a 16-bit encoding, TST (immediate) does not.
6480 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
6481 "Expecting 0=compressed, 1=uncompressed");
Artem Serov517d9f62016-12-12 15:51:15 +00006482 __ B(cs, &uncompressed_load, /* far_target */ false);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006483 __ Ldrb(RegisterFrom(out_loc), MemOperand(temp, RegisterFrom(index), vixl32::LSL, 0));
Anton Kirilov6f644202017-02-27 18:29:45 +00006484 __ B(final_label);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006485 __ Bind(&uncompressed_load);
6486 __ Ldrh(RegisterFrom(out_loc), MemOperand(temp, RegisterFrom(index), vixl32::LSL, 1));
Anton Kirilov6f644202017-02-27 18:29:45 +00006487 if (done.IsReferenced()) {
6488 __ Bind(&done);
6489 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006490 } else {
6491 codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index));
6492 }
6493 }
6494 break;
6495 }
6496
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006497 case DataType::Type::kReference: {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006498 // The read barrier instrumentation of object ArrayGet
6499 // instructions does not support the HIntermediateAddress
6500 // instruction.
6501 DCHECK(!(has_intermediate_address && kEmitCompilerReadBarrier));
6502
Scott Wakelingc34dba72016-10-03 10:14:44 +01006503 static_assert(
6504 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
6505 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
6506 // /* HeapReference<Object> */ out =
6507 // *(obj + data_offset + index * sizeof(HeapReference<Object>))
6508 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006509 // Note that a potential implicit null check is handled in this
6510 // CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier call.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006511 DCHECK(!instruction->CanDoImplicitNullCheckOn(instruction->InputAt(0)));
6512 if (index.IsConstant()) {
6513 // Array load with a constant index can be treated as a field load.
Vladimir Markodcd117e2018-04-19 11:54:00 +01006514 Location maybe_temp =
6515 (locations->GetTempCount() != 0) ? locations->GetTemp(0) : Location();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006516 data_offset += Int32ConstantFrom(index) << DataType::SizeShift(type);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006517 codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction,
6518 out_loc,
6519 obj,
6520 data_offset,
Vladimir Markodcd117e2018-04-19 11:54:00 +01006521 maybe_temp,
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006522 /* needs_null_check */ false);
6523 } else {
Vladimir Markodcd117e2018-04-19 11:54:00 +01006524 Location temp = locations->GetTemp(0);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006525 codegen_->GenerateArrayLoadWithBakerReadBarrier(
6526 instruction, out_loc, obj, data_offset, index, temp, /* needs_null_check */ false);
6527 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006528 } else {
6529 vixl32::Register out = OutputRegister(instruction);
6530 if (index.IsConstant()) {
6531 size_t offset =
Anton Kirilov644032c2016-12-06 17:51:43 +00006532 (Int32ConstantFrom(index) << TIMES_4) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006533 GetAssembler()->LoadFromOffset(kLoadWord, out, obj, offset);
Alexandre Rames374ddf32016-11-04 10:40:49 +00006534 // TODO(VIXL): Here and for other calls to `MaybeRecordImplicitNullCheck` in this method,
6535 // we should use a scope and the assembler to emit the load instruction to guarantee that
6536 // we record the pc at the correct position. But the `Assembler` does not automatically
6537 // handle unencodable offsets. Practically, everything is fine because the helper and
6538 // VIXL, at the time of writing, do generate the store instruction last.
Scott Wakelingc34dba72016-10-03 10:14:44 +01006539 codegen_->MaybeRecordImplicitNullCheck(instruction);
6540 // If read barriers are enabled, emit read barriers other than
6541 // Baker's using a slow path (and also unpoison the loaded
6542 // reference, if heap poisoning is enabled).
6543 codegen_->MaybeGenerateReadBarrierSlow(instruction, out_loc, out_loc, obj_loc, offset);
6544 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006545 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006546 vixl32::Register temp = temps.Acquire();
6547
6548 if (has_intermediate_address) {
Artem Serov2bbc9532016-10-21 11:51:50 +01006549 // We do not need to compute the intermediate address from the array: the
6550 // input instruction has done it already. See the comment in
6551 // `TryExtractArrayAccessAddress()`.
6552 if (kIsDebugBuild) {
6553 HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
Anton Kirilov644032c2016-12-06 17:51:43 +00006554 DCHECK_EQ(Uint64ConstantFrom(tmp->GetOffset()), data_offset);
Artem Serov2bbc9532016-10-21 11:51:50 +01006555 }
6556 temp = obj;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006557 } else {
6558 __ Add(temp, obj, data_offset);
6559 }
6560 codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index));
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006561 temps.Close();
Alexandre Rames374ddf32016-11-04 10:40:49 +00006562 // TODO(VIXL): Use a scope to ensure that we record the pc position immediately after the
6563 // load instruction. Practically, everything is fine because the helper and VIXL, at the
6564 // time of writing, do generate the store instruction last.
Scott Wakelingc34dba72016-10-03 10:14:44 +01006565 codegen_->MaybeRecordImplicitNullCheck(instruction);
6566 // If read barriers are enabled, emit read barriers other than
6567 // Baker's using a slow path (and also unpoison the loaded
6568 // reference, if heap poisoning is enabled).
6569 codegen_->MaybeGenerateReadBarrierSlow(
6570 instruction, out_loc, out_loc, obj_loc, data_offset, index);
6571 }
6572 }
6573 break;
6574 }
6575
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006576 case DataType::Type::kInt64: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006577 if (index.IsConstant()) {
6578 size_t offset =
Anton Kirilov644032c2016-12-06 17:51:43 +00006579 (Int32ConstantFrom(index) << TIMES_8) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006580 GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out_loc), obj, offset);
6581 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006582 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006583 vixl32::Register temp = temps.Acquire();
6584 __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
6585 GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out_loc), temp, data_offset);
6586 }
6587 break;
6588 }
6589
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006590 case DataType::Type::kFloat32: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006591 vixl32::SRegister out = SRegisterFrom(out_loc);
6592 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006593 size_t offset = (Int32ConstantFrom(index) << TIMES_4) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006594 GetAssembler()->LoadSFromOffset(out, obj, offset);
6595 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006596 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006597 vixl32::Register temp = temps.Acquire();
6598 __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_4));
6599 GetAssembler()->LoadSFromOffset(out, temp, data_offset);
6600 }
6601 break;
6602 }
6603
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006604 case DataType::Type::kFloat64: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006605 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006606 size_t offset = (Int32ConstantFrom(index) << TIMES_8) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006607 GetAssembler()->LoadDFromOffset(DRegisterFrom(out_loc), obj, offset);
6608 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006609 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006610 vixl32::Register temp = temps.Acquire();
6611 __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
6612 GetAssembler()->LoadDFromOffset(DRegisterFrom(out_loc), temp, data_offset);
6613 }
6614 break;
6615 }
6616
Aart Bik66c158e2018-01-31 12:55:04 -08006617 case DataType::Type::kUint32:
6618 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006619 case DataType::Type::kVoid:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006620 LOG(FATAL) << "Unreachable type " << type;
6621 UNREACHABLE();
6622 }
6623
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006624 if (type == DataType::Type::kReference) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006625 // Potential implicit null checks, in the case of reference
6626 // arrays, are handled in the previous switch statement.
6627 } else if (!maybe_compressed_char_at) {
Alexandre Rames374ddf32016-11-04 10:40:49 +00006628 // TODO(VIXL): Use a scope to ensure we record the pc info immediately after
6629 // the preceding load instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01006630 codegen_->MaybeRecordImplicitNullCheck(instruction);
6631 }
6632}
6633
6634void LocationsBuilderARMVIXL::VisitArraySet(HArraySet* instruction) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006635 DataType::Type value_type = instruction->GetComponentType();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006636
6637 bool needs_write_barrier =
6638 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
6639 bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck();
6640
Vladimir Markoca6fff82017-10-03 14:49:14 +01006641 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
Scott Wakelingc34dba72016-10-03 10:14:44 +01006642 instruction,
6643 may_need_runtime_call_for_type_check ?
6644 LocationSummary::kCallOnSlowPath :
6645 LocationSummary::kNoCall);
6646
6647 locations->SetInAt(0, Location::RequiresRegister());
6648 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006649 if (DataType::IsFloatingPointType(value_type)) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006650 locations->SetInAt(2, Location::RequiresFpuRegister());
6651 } else {
6652 locations->SetInAt(2, Location::RequiresRegister());
6653 }
6654 if (needs_write_barrier) {
6655 // Temporary registers for the write barrier.
6656 locations->AddTemp(Location::RequiresRegister()); // Possibly used for ref. poisoning too.
6657 locations->AddTemp(Location::RequiresRegister());
6658 }
6659}
6660
6661void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006662 LocationSummary* locations = instruction->GetLocations();
6663 vixl32::Register array = InputRegisterAt(instruction, 0);
6664 Location index = locations->InAt(1);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006665 DataType::Type value_type = instruction->GetComponentType();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006666 bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck();
6667 bool needs_write_barrier =
6668 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
6669 uint32_t data_offset =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006670 mirror::Array::DataOffset(DataType::Size(value_type)).Uint32Value();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006671 Location value_loc = locations->InAt(2);
6672 HInstruction* array_instr = instruction->GetArray();
6673 bool has_intermediate_address = array_instr->IsIntermediateAddress();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006674
6675 switch (value_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006676 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006677 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006678 case DataType::Type::kInt8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006679 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006680 case DataType::Type::kInt16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006681 case DataType::Type::kInt32: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006682 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006683 int32_t const_index = Int32ConstantFrom(index);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006684 uint32_t full_offset =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006685 data_offset + (const_index << DataType::SizeShift(value_type));
Scott Wakelingc34dba72016-10-03 10:14:44 +01006686 StoreOperandType store_type = GetStoreOperandType(value_type);
6687 GetAssembler()->StoreToOffset(store_type, RegisterFrom(value_loc), array, full_offset);
6688 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006689 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006690 vixl32::Register temp = temps.Acquire();
6691
6692 if (has_intermediate_address) {
Artem Serov2bbc9532016-10-21 11:51:50 +01006693 // We do not need to compute the intermediate address from the array: the
6694 // input instruction has done it already. See the comment in
6695 // `TryExtractArrayAccessAddress()`.
6696 if (kIsDebugBuild) {
6697 HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
Anton Kirilov644032c2016-12-06 17:51:43 +00006698 DCHECK_EQ(Uint64ConstantFrom(tmp->GetOffset()), data_offset);
Artem Serov2bbc9532016-10-21 11:51:50 +01006699 }
6700 temp = array;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006701 } else {
6702 __ Add(temp, array, data_offset);
6703 }
6704 codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
6705 }
6706 break;
6707 }
6708
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006709 case DataType::Type::kReference: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006710 vixl32::Register value = RegisterFrom(value_loc);
6711 // TryExtractArrayAccessAddress optimization is never applied for non-primitive ArraySet.
6712 // See the comment in instruction_simplifier_shared.cc.
6713 DCHECK(!has_intermediate_address);
6714
6715 if (instruction->InputAt(2)->IsNullConstant()) {
6716 // Just setting null.
6717 if (index.IsConstant()) {
6718 size_t offset =
Anton Kirilov644032c2016-12-06 17:51:43 +00006719 (Int32ConstantFrom(index) << TIMES_4) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006720 GetAssembler()->StoreToOffset(kStoreWord, value, array, offset);
6721 } else {
6722 DCHECK(index.IsRegister()) << index;
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006723 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006724 vixl32::Register temp = temps.Acquire();
6725 __ Add(temp, array, data_offset);
6726 codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
6727 }
Alexandre Rames374ddf32016-11-04 10:40:49 +00006728 // TODO(VIXL): Use a scope to ensure we record the pc info immediately after the preceding
6729 // store instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01006730 codegen_->MaybeRecordImplicitNullCheck(instruction);
6731 DCHECK(!needs_write_barrier);
6732 DCHECK(!may_need_runtime_call_for_type_check);
6733 break;
6734 }
6735
6736 DCHECK(needs_write_barrier);
6737 Location temp1_loc = locations->GetTemp(0);
6738 vixl32::Register temp1 = RegisterFrom(temp1_loc);
6739 Location temp2_loc = locations->GetTemp(1);
6740 vixl32::Register temp2 = RegisterFrom(temp2_loc);
6741 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
6742 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
6743 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
6744 vixl32::Label done;
Anton Kirilov6f644202017-02-27 18:29:45 +00006745 vixl32::Label* final_label = codegen_->GetFinalLabel(instruction, &done);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006746 SlowPathCodeARMVIXL* slow_path = nullptr;
6747
6748 if (may_need_runtime_call_for_type_check) {
Vladimir Marko174b2e22017-10-12 13:34:49 +01006749 slow_path = new (codegen_->GetScopedAllocator()) ArraySetSlowPathARMVIXL(instruction);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006750 codegen_->AddSlowPath(slow_path);
6751 if (instruction->GetValueCanBeNull()) {
6752 vixl32::Label non_zero;
xueliang.zhongf51bc622016-11-04 09:23:32 +00006753 __ CompareAndBranchIfNonZero(value, &non_zero);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006754 if (index.IsConstant()) {
6755 size_t offset =
Anton Kirilov644032c2016-12-06 17:51:43 +00006756 (Int32ConstantFrom(index) << TIMES_4) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006757 GetAssembler()->StoreToOffset(kStoreWord, value, array, offset);
6758 } else {
6759 DCHECK(index.IsRegister()) << index;
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006760 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006761 vixl32::Register temp = temps.Acquire();
6762 __ Add(temp, array, data_offset);
6763 codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
6764 }
Alexandre Rames374ddf32016-11-04 10:40:49 +00006765 // TODO(VIXL): Use a scope to ensure we record the pc info immediately after the preceding
6766 // store instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01006767 codegen_->MaybeRecordImplicitNullCheck(instruction);
Anton Kirilov6f644202017-02-27 18:29:45 +00006768 __ B(final_label);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006769 __ Bind(&non_zero);
6770 }
6771
6772 // Note that when read barriers are enabled, the type checks
6773 // are performed without read barriers. This is fine, even in
6774 // the case where a class object is in the from-space after
6775 // the flip, as a comparison involving such a type would not
6776 // produce a false positive; it may of course produce a false
6777 // negative, in which case we would take the ArraySet slow
6778 // path.
6779
Alexandre Rames374ddf32016-11-04 10:40:49 +00006780 {
6781 // Ensure we record the pc position immediately after the `ldr` instruction.
Artem Serov0fb37192016-12-06 18:13:40 +00006782 ExactAssemblyScope aas(GetVIXLAssembler(),
6783 vixl32::kMaxInstructionSizeInBytes,
6784 CodeBufferCheckScope::kMaximumSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00006785 // /* HeapReference<Class> */ temp1 = array->klass_
6786 __ ldr(temp1, MemOperand(array, class_offset));
6787 codegen_->MaybeRecordImplicitNullCheck(instruction);
6788 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006789 GetAssembler()->MaybeUnpoisonHeapReference(temp1);
6790
6791 // /* HeapReference<Class> */ temp1 = temp1->component_type_
6792 GetAssembler()->LoadFromOffset(kLoadWord, temp1, temp1, component_offset);
6793 // /* HeapReference<Class> */ temp2 = value->klass_
6794 GetAssembler()->LoadFromOffset(kLoadWord, temp2, value, class_offset);
6795 // If heap poisoning is enabled, no need to unpoison `temp1`
6796 // nor `temp2`, as we are comparing two poisoned references.
6797 __ Cmp(temp1, temp2);
6798
6799 if (instruction->StaticTypeOfArrayIsObjectArray()) {
6800 vixl32::Label do_put;
Artem Serov517d9f62016-12-12 15:51:15 +00006801 __ B(eq, &do_put, /* far_target */ false);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006802 // If heap poisoning is enabled, the `temp1` reference has
6803 // not been unpoisoned yet; unpoison it now.
6804 GetAssembler()->MaybeUnpoisonHeapReference(temp1);
6805
6806 // /* HeapReference<Class> */ temp1 = temp1->super_class_
6807 GetAssembler()->LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
6808 // If heap poisoning is enabled, no need to unpoison
6809 // `temp1`, as we are comparing against null below.
xueliang.zhongf51bc622016-11-04 09:23:32 +00006810 __ CompareAndBranchIfNonZero(temp1, slow_path->GetEntryLabel());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006811 __ Bind(&do_put);
6812 } else {
6813 __ B(ne, slow_path->GetEntryLabel());
6814 }
6815 }
6816
6817 vixl32::Register source = value;
6818 if (kPoisonHeapReferences) {
6819 // Note that in the case where `value` is a null reference,
6820 // we do not enter this block, as a null reference does not
6821 // need poisoning.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006822 DCHECK_EQ(value_type, DataType::Type::kReference);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006823 __ Mov(temp1, value);
6824 GetAssembler()->PoisonHeapReference(temp1);
6825 source = temp1;
6826 }
6827
6828 if (index.IsConstant()) {
6829 size_t offset =
Anton Kirilov644032c2016-12-06 17:51:43 +00006830 (Int32ConstantFrom(index) << TIMES_4) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006831 GetAssembler()->StoreToOffset(kStoreWord, source, array, offset);
6832 } else {
6833 DCHECK(index.IsRegister()) << index;
6834
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006835 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006836 vixl32::Register temp = temps.Acquire();
6837 __ Add(temp, array, data_offset);
6838 codegen_->StoreToShiftedRegOffset(value_type,
6839 LocationFrom(source),
6840 temp,
6841 RegisterFrom(index));
6842 }
6843
6844 if (!may_need_runtime_call_for_type_check) {
Alexandre Rames374ddf32016-11-04 10:40:49 +00006845 // TODO(VIXL): Ensure we record the pc position immediately after the preceding store
6846 // instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01006847 codegen_->MaybeRecordImplicitNullCheck(instruction);
6848 }
6849
6850 codegen_->MarkGCCard(temp1, temp2, array, value, instruction->GetValueCanBeNull());
6851
6852 if (done.IsReferenced()) {
6853 __ Bind(&done);
6854 }
6855
6856 if (slow_path != nullptr) {
6857 __ Bind(slow_path->GetExitLabel());
6858 }
6859
6860 break;
6861 }
6862
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006863 case DataType::Type::kInt64: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006864 Location value = locations->InAt(2);
6865 if (index.IsConstant()) {
6866 size_t offset =
Anton Kirilov644032c2016-12-06 17:51:43 +00006867 (Int32ConstantFrom(index) << TIMES_8) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006868 GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), array, offset);
6869 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006870 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006871 vixl32::Register temp = temps.Acquire();
6872 __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
6873 GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), temp, data_offset);
6874 }
6875 break;
6876 }
6877
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006878 case DataType::Type::kFloat32: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006879 Location value = locations->InAt(2);
6880 DCHECK(value.IsFpuRegister());
6881 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006882 size_t offset = (Int32ConstantFrom(index) << TIMES_4) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006883 GetAssembler()->StoreSToOffset(SRegisterFrom(value), array, offset);
6884 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006885 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006886 vixl32::Register temp = temps.Acquire();
6887 __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_4));
6888 GetAssembler()->StoreSToOffset(SRegisterFrom(value), temp, data_offset);
6889 }
6890 break;
6891 }
6892
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006893 case DataType::Type::kFloat64: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006894 Location value = locations->InAt(2);
6895 DCHECK(value.IsFpuRegisterPair());
6896 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006897 size_t offset = (Int32ConstantFrom(index) << TIMES_8) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006898 GetAssembler()->StoreDToOffset(DRegisterFrom(value), array, offset);
6899 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006900 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006901 vixl32::Register temp = temps.Acquire();
6902 __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
6903 GetAssembler()->StoreDToOffset(DRegisterFrom(value), temp, data_offset);
6904 }
6905 break;
6906 }
6907
Aart Bik66c158e2018-01-31 12:55:04 -08006908 case DataType::Type::kUint32:
6909 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006910 case DataType::Type::kVoid:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006911 LOG(FATAL) << "Unreachable type " << value_type;
6912 UNREACHABLE();
6913 }
6914
6915 // Objects are handled in the switch.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006916 if (value_type != DataType::Type::kReference) {
Alexandre Rames374ddf32016-11-04 10:40:49 +00006917 // TODO(VIXL): Ensure we record the pc position immediately after the preceding store
6918 // instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01006919 codegen_->MaybeRecordImplicitNullCheck(instruction);
6920 }
6921}
6922
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006923void LocationsBuilderARMVIXL::VisitArrayLength(HArrayLength* instruction) {
6924 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01006925 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006926 locations->SetInAt(0, Location::RequiresRegister());
6927 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
6928}
6929
6930void InstructionCodeGeneratorARMVIXL::VisitArrayLength(HArrayLength* instruction) {
6931 uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction);
6932 vixl32::Register obj = InputRegisterAt(instruction, 0);
6933 vixl32::Register out = OutputRegister(instruction);
Alexandre Rames374ddf32016-11-04 10:40:49 +00006934 {
Artem Serov0fb37192016-12-06 18:13:40 +00006935 ExactAssemblyScope aas(GetVIXLAssembler(),
6936 vixl32::kMaxInstructionSizeInBytes,
6937 CodeBufferCheckScope::kMaximumSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00006938 __ ldr(out, MemOperand(obj, offset));
6939 codegen_->MaybeRecordImplicitNullCheck(instruction);
6940 }
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006941 // Mask out compression flag from String's array length.
6942 if (mirror::kUseStringCompression && instruction->IsStringLength()) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01006943 __ Lsr(out, out, 1u);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006944 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006945}
6946
Artem Serov2bbc9532016-10-21 11:51:50 +01006947void LocationsBuilderARMVIXL::VisitIntermediateAddress(HIntermediateAddress* instruction) {
Artem Serov2bbc9532016-10-21 11:51:50 +01006948 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01006949 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Artem Serov2bbc9532016-10-21 11:51:50 +01006950
6951 locations->SetInAt(0, Location::RequiresRegister());
6952 locations->SetInAt(1, Location::RegisterOrConstant(instruction->GetOffset()));
6953 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
6954}
6955
6956void InstructionCodeGeneratorARMVIXL::VisitIntermediateAddress(HIntermediateAddress* instruction) {
6957 vixl32::Register out = OutputRegister(instruction);
6958 vixl32::Register first = InputRegisterAt(instruction, 0);
6959 Location second = instruction->GetLocations()->InAt(1);
6960
Artem Serov2bbc9532016-10-21 11:51:50 +01006961 if (second.IsRegister()) {
6962 __ Add(out, first, RegisterFrom(second));
6963 } else {
Anton Kirilov644032c2016-12-06 17:51:43 +00006964 __ Add(out, first, Int32ConstantFrom(second));
Artem Serov2bbc9532016-10-21 11:51:50 +01006965 }
6966}
6967
Artem Serove1811ed2017-04-27 16:50:47 +01006968void LocationsBuilderARMVIXL::VisitIntermediateAddressIndex(
6969 HIntermediateAddressIndex* instruction) {
6970 LOG(FATAL) << "Unreachable " << instruction->GetId();
6971}
6972
6973void InstructionCodeGeneratorARMVIXL::VisitIntermediateAddressIndex(
6974 HIntermediateAddressIndex* instruction) {
6975 LOG(FATAL) << "Unreachable " << instruction->GetId();
6976}
6977
Scott Wakelingc34dba72016-10-03 10:14:44 +01006978void LocationsBuilderARMVIXL::VisitBoundsCheck(HBoundsCheck* instruction) {
6979 RegisterSet caller_saves = RegisterSet::Empty();
6980 InvokeRuntimeCallingConventionARMVIXL calling_convention;
6981 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0)));
6982 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(1)));
6983 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction, caller_saves);
Artem Serov2dd053d2017-03-08 14:54:06 +00006984
6985 HInstruction* index = instruction->InputAt(0);
6986 HInstruction* length = instruction->InputAt(1);
6987 // If both index and length are constants we can statically check the bounds. But if at least one
6988 // of them is not encodable ArmEncodableConstantOrRegister will create
6989 // Location::RequiresRegister() which is not desired to happen. Instead we create constant
6990 // locations.
6991 bool both_const = index->IsConstant() && length->IsConstant();
6992 locations->SetInAt(0, both_const
6993 ? Location::ConstantLocation(index->AsConstant())
6994 : ArmEncodableConstantOrRegister(index, CMP));
6995 locations->SetInAt(1, both_const
6996 ? Location::ConstantLocation(length->AsConstant())
6997 : ArmEncodableConstantOrRegister(length, CMP));
Scott Wakelingc34dba72016-10-03 10:14:44 +01006998}
6999
7000void InstructionCodeGeneratorARMVIXL::VisitBoundsCheck(HBoundsCheck* instruction) {
Artem Serov2dd053d2017-03-08 14:54:06 +00007001 LocationSummary* locations = instruction->GetLocations();
7002 Location index_loc = locations->InAt(0);
7003 Location length_loc = locations->InAt(1);
Scott Wakelingc34dba72016-10-03 10:14:44 +01007004
Artem Serov2dd053d2017-03-08 14:54:06 +00007005 if (length_loc.IsConstant()) {
7006 int32_t length = Int32ConstantFrom(length_loc);
7007 if (index_loc.IsConstant()) {
7008 // BCE will remove the bounds check if we are guaranteed to pass.
7009 int32_t index = Int32ConstantFrom(index_loc);
7010 if (index < 0 || index >= length) {
7011 SlowPathCodeARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01007012 new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathARMVIXL(instruction);
Artem Serov2dd053d2017-03-08 14:54:06 +00007013 codegen_->AddSlowPath(slow_path);
7014 __ B(slow_path->GetEntryLabel());
7015 } else {
7016 // Some optimization after BCE may have generated this, and we should not
7017 // generate a bounds check if it is a valid range.
7018 }
7019 return;
7020 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01007021
Artem Serov2dd053d2017-03-08 14:54:06 +00007022 SlowPathCodeARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01007023 new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathARMVIXL(instruction);
Artem Serov2dd053d2017-03-08 14:54:06 +00007024 __ Cmp(RegisterFrom(index_loc), length);
7025 codegen_->AddSlowPath(slow_path);
7026 __ B(hs, slow_path->GetEntryLabel());
7027 } else {
7028 SlowPathCodeARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01007029 new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathARMVIXL(instruction);
Artem Serov2dd053d2017-03-08 14:54:06 +00007030 __ Cmp(RegisterFrom(length_loc), InputOperandAt(instruction, 0));
7031 codegen_->AddSlowPath(slow_path);
7032 __ B(ls, slow_path->GetEntryLabel());
7033 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01007034}
7035
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007036void CodeGeneratorARMVIXL::MarkGCCard(vixl32::Register temp,
7037 vixl32::Register card,
7038 vixl32::Register object,
7039 vixl32::Register value,
7040 bool can_be_null) {
7041 vixl32::Label is_null;
7042 if (can_be_null) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00007043 __ CompareAndBranchIfZero(value, &is_null);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007044 }
7045 GetAssembler()->LoadFromOffset(
7046 kLoadWord, card, tr, Thread::CardTableOffset<kArmPointerSize>().Int32Value());
Scott Wakelingb77051e2016-11-21 19:46:00 +00007047 __ Lsr(temp, object, Operand::From(gc::accounting::CardTable::kCardShift));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007048 __ Strb(card, MemOperand(card, temp));
7049 if (can_be_null) {
7050 __ Bind(&is_null);
7051 }
7052}
7053
Scott Wakelingfe885462016-09-22 10:24:38 +01007054void LocationsBuilderARMVIXL::VisitParallelMove(HParallelMove* instruction ATTRIBUTE_UNUSED) {
7055 LOG(FATAL) << "Unreachable";
7056}
7057
7058void InstructionCodeGeneratorARMVIXL::VisitParallelMove(HParallelMove* instruction) {
Vladimir Markobea75ff2017-10-11 20:39:54 +01007059 if (instruction->GetNext()->IsSuspendCheck() &&
7060 instruction->GetBlock()->GetLoopInformation() != nullptr) {
7061 HSuspendCheck* suspend_check = instruction->GetNext()->AsSuspendCheck();
7062 // The back edge will generate the suspend check.
7063 codegen_->ClearSpillSlotsFromLoopPhisInStackMap(suspend_check, instruction);
7064 }
7065
Scott Wakelingfe885462016-09-22 10:24:38 +01007066 codegen_->GetMoveResolver()->EmitNativeCode(instruction);
7067}
7068
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007069void LocationsBuilderARMVIXL::VisitSuspendCheck(HSuspendCheck* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01007070 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
7071 instruction, LocationSummary::kCallOnSlowPath);
Artem Serov657022c2016-11-23 14:19:38 +00007072 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007073}
7074
7075void InstructionCodeGeneratorARMVIXL::VisitSuspendCheck(HSuspendCheck* instruction) {
7076 HBasicBlock* block = instruction->GetBlock();
7077 if (block->GetLoopInformation() != nullptr) {
7078 DCHECK(block->GetLoopInformation()->GetSuspendCheck() == instruction);
7079 // The back edge will generate the suspend check.
7080 return;
7081 }
7082 if (block->IsEntryBlock() && instruction->GetNext()->IsGoto()) {
7083 // The goto will generate the suspend check.
7084 return;
7085 }
7086 GenerateSuspendCheck(instruction, nullptr);
Roland Levillain5daa4952017-07-03 17:23:56 +01007087 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 12);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007088}
7089
7090void InstructionCodeGeneratorARMVIXL::GenerateSuspendCheck(HSuspendCheck* instruction,
7091 HBasicBlock* successor) {
7092 SuspendCheckSlowPathARMVIXL* slow_path =
7093 down_cast<SuspendCheckSlowPathARMVIXL*>(instruction->GetSlowPath());
7094 if (slow_path == nullptr) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01007095 slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01007096 new (codegen_->GetScopedAllocator()) SuspendCheckSlowPathARMVIXL(instruction, successor);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007097 instruction->SetSlowPath(slow_path);
7098 codegen_->AddSlowPath(slow_path);
7099 if (successor != nullptr) {
7100 DCHECK(successor->IsLoopHeader());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007101 }
7102 } else {
7103 DCHECK_EQ(slow_path->GetSuccessor(), successor);
7104 }
7105
Anton Kirilovedb2ac32016-11-30 15:14:10 +00007106 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007107 vixl32::Register temp = temps.Acquire();
7108 GetAssembler()->LoadFromOffset(
7109 kLoadUnsignedHalfword, temp, tr, Thread::ThreadFlagsOffset<kArmPointerSize>().Int32Value());
7110 if (successor == nullptr) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00007111 __ CompareAndBranchIfNonZero(temp, slow_path->GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007112 __ Bind(slow_path->GetReturnLabel());
7113 } else {
xueliang.zhongf51bc622016-11-04 09:23:32 +00007114 __ CompareAndBranchIfZero(temp, codegen_->GetLabelOf(successor));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007115 __ B(slow_path->GetEntryLabel());
7116 }
7117}
7118
Scott Wakelingfe885462016-09-22 10:24:38 +01007119ArmVIXLAssembler* ParallelMoveResolverARMVIXL::GetAssembler() const {
7120 return codegen_->GetAssembler();
7121}
7122
7123void ParallelMoveResolverARMVIXL::EmitMove(size_t index) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007124 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
Scott Wakelingfe885462016-09-22 10:24:38 +01007125 MoveOperands* move = moves_[index];
7126 Location source = move->GetSource();
7127 Location destination = move->GetDestination();
7128
7129 if (source.IsRegister()) {
7130 if (destination.IsRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007131 __ Mov(RegisterFrom(destination), RegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01007132 } else if (destination.IsFpuRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007133 __ Vmov(SRegisterFrom(destination), RegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01007134 } else {
7135 DCHECK(destination.IsStackSlot());
7136 GetAssembler()->StoreToOffset(kStoreWord,
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007137 RegisterFrom(source),
Scott Wakelingfe885462016-09-22 10:24:38 +01007138 sp,
7139 destination.GetStackIndex());
7140 }
7141 } else if (source.IsStackSlot()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007142 if (destination.IsRegister()) {
7143 GetAssembler()->LoadFromOffset(kLoadWord,
7144 RegisterFrom(destination),
7145 sp,
7146 source.GetStackIndex());
7147 } else if (destination.IsFpuRegister()) {
7148 GetAssembler()->LoadSFromOffset(SRegisterFrom(destination), sp, source.GetStackIndex());
7149 } else {
7150 DCHECK(destination.IsStackSlot());
7151 vixl32::Register temp = temps.Acquire();
7152 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, source.GetStackIndex());
7153 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
7154 }
Scott Wakelingfe885462016-09-22 10:24:38 +01007155 } else if (source.IsFpuRegister()) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01007156 if (destination.IsRegister()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01007157 __ Vmov(RegisterFrom(destination), SRegisterFrom(source));
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01007158 } else if (destination.IsFpuRegister()) {
7159 __ Vmov(SRegisterFrom(destination), SRegisterFrom(source));
7160 } else {
7161 DCHECK(destination.IsStackSlot());
7162 GetAssembler()->StoreSToOffset(SRegisterFrom(source), sp, destination.GetStackIndex());
7163 }
Scott Wakelingfe885462016-09-22 10:24:38 +01007164 } else if (source.IsDoubleStackSlot()) {
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007165 if (destination.IsDoubleStackSlot()) {
7166 vixl32::DRegister temp = temps.AcquireD();
7167 GetAssembler()->LoadDFromOffset(temp, sp, source.GetStackIndex());
7168 GetAssembler()->StoreDToOffset(temp, sp, destination.GetStackIndex());
7169 } else if (destination.IsRegisterPair()) {
7170 DCHECK(ExpectedPairLayout(destination));
7171 GetAssembler()->LoadFromOffset(
7172 kLoadWordPair, LowRegisterFrom(destination), sp, source.GetStackIndex());
7173 } else {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01007174 DCHECK(destination.IsFpuRegisterPair()) << destination;
7175 GetAssembler()->LoadDFromOffset(DRegisterFrom(destination), sp, source.GetStackIndex());
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007176 }
Scott Wakelingfe885462016-09-22 10:24:38 +01007177 } else if (source.IsRegisterPair()) {
7178 if (destination.IsRegisterPair()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007179 __ Mov(LowRegisterFrom(destination), LowRegisterFrom(source));
7180 __ Mov(HighRegisterFrom(destination), HighRegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01007181 } else if (destination.IsFpuRegisterPair()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01007182 __ Vmov(DRegisterFrom(destination), LowRegisterFrom(source), HighRegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01007183 } else {
7184 DCHECK(destination.IsDoubleStackSlot()) << destination;
7185 DCHECK(ExpectedPairLayout(source));
7186 GetAssembler()->StoreToOffset(kStoreWordPair,
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007187 LowRegisterFrom(source),
Scott Wakelingfe885462016-09-22 10:24:38 +01007188 sp,
7189 destination.GetStackIndex());
7190 }
7191 } else if (source.IsFpuRegisterPair()) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01007192 if (destination.IsRegisterPair()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01007193 __ Vmov(LowRegisterFrom(destination), HighRegisterFrom(destination), DRegisterFrom(source));
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01007194 } else if (destination.IsFpuRegisterPair()) {
7195 __ Vmov(DRegisterFrom(destination), DRegisterFrom(source));
7196 } else {
7197 DCHECK(destination.IsDoubleStackSlot()) << destination;
7198 GetAssembler()->StoreDToOffset(DRegisterFrom(source), sp, destination.GetStackIndex());
7199 }
Scott Wakelingfe885462016-09-22 10:24:38 +01007200 } else {
7201 DCHECK(source.IsConstant()) << source;
7202 HConstant* constant = source.GetConstant();
7203 if (constant->IsIntConstant() || constant->IsNullConstant()) {
7204 int32_t value = CodeGenerator::GetInt32ValueOf(constant);
7205 if (destination.IsRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007206 __ Mov(RegisterFrom(destination), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01007207 } else {
7208 DCHECK(destination.IsStackSlot());
Scott Wakelingfe885462016-09-22 10:24:38 +01007209 vixl32::Register temp = temps.Acquire();
7210 __ Mov(temp, value);
7211 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
7212 }
7213 } else if (constant->IsLongConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00007214 int64_t value = Int64ConstantFrom(source);
Scott Wakelingfe885462016-09-22 10:24:38 +01007215 if (destination.IsRegisterPair()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007216 __ Mov(LowRegisterFrom(destination), Low32Bits(value));
7217 __ Mov(HighRegisterFrom(destination), High32Bits(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01007218 } else {
7219 DCHECK(destination.IsDoubleStackSlot()) << destination;
Scott Wakelingfe885462016-09-22 10:24:38 +01007220 vixl32::Register temp = temps.Acquire();
7221 __ Mov(temp, Low32Bits(value));
7222 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
7223 __ Mov(temp, High32Bits(value));
7224 GetAssembler()->StoreToOffset(kStoreWord,
7225 temp,
7226 sp,
7227 destination.GetHighStackIndex(kArmWordSize));
7228 }
7229 } else if (constant->IsDoubleConstant()) {
7230 double value = constant->AsDoubleConstant()->GetValue();
7231 if (destination.IsFpuRegisterPair()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01007232 __ Vmov(DRegisterFrom(destination), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01007233 } else {
7234 DCHECK(destination.IsDoubleStackSlot()) << destination;
7235 uint64_t int_value = bit_cast<uint64_t, double>(value);
Scott Wakelingfe885462016-09-22 10:24:38 +01007236 vixl32::Register temp = temps.Acquire();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007237 __ Mov(temp, Low32Bits(int_value));
Scott Wakelingfe885462016-09-22 10:24:38 +01007238 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007239 __ Mov(temp, High32Bits(int_value));
Scott Wakelingfe885462016-09-22 10:24:38 +01007240 GetAssembler()->StoreToOffset(kStoreWord,
7241 temp,
7242 sp,
7243 destination.GetHighStackIndex(kArmWordSize));
7244 }
7245 } else {
7246 DCHECK(constant->IsFloatConstant()) << constant->DebugName();
7247 float value = constant->AsFloatConstant()->GetValue();
7248 if (destination.IsFpuRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007249 __ Vmov(SRegisterFrom(destination), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01007250 } else {
7251 DCHECK(destination.IsStackSlot());
Scott Wakelingfe885462016-09-22 10:24:38 +01007252 vixl32::Register temp = temps.Acquire();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007253 __ Mov(temp, bit_cast<int32_t, float>(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01007254 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
7255 }
7256 }
7257 }
7258}
7259
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007260void ParallelMoveResolverARMVIXL::Exchange(vixl32::Register reg, int mem) {
7261 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
7262 vixl32::Register temp = temps.Acquire();
7263 __ Mov(temp, reg);
7264 GetAssembler()->LoadFromOffset(kLoadWord, reg, sp, mem);
7265 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, mem);
Scott Wakelingfe885462016-09-22 10:24:38 +01007266}
7267
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007268void ParallelMoveResolverARMVIXL::Exchange(int mem1, int mem2) {
7269 // TODO(VIXL32): Double check the performance of this implementation.
7270 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
Nicolas Geoffray13a797b2017-03-15 16:41:31 +00007271 vixl32::Register temp1 = temps.Acquire();
7272 ScratchRegisterScope ensure_scratch(
7273 this, temp1.GetCode(), r0.GetCode(), codegen_->GetNumberOfCoreRegisters());
7274 vixl32::Register temp2(ensure_scratch.GetRegister());
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007275
Nicolas Geoffray13a797b2017-03-15 16:41:31 +00007276 int stack_offset = ensure_scratch.IsSpilled() ? kArmWordSize : 0;
7277 GetAssembler()->LoadFromOffset(kLoadWord, temp1, sp, mem1 + stack_offset);
7278 GetAssembler()->LoadFromOffset(kLoadWord, temp2, sp, mem2 + stack_offset);
7279 GetAssembler()->StoreToOffset(kStoreWord, temp1, sp, mem2 + stack_offset);
7280 GetAssembler()->StoreToOffset(kStoreWord, temp2, sp, mem1 + stack_offset);
Scott Wakelingfe885462016-09-22 10:24:38 +01007281}
7282
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007283void ParallelMoveResolverARMVIXL::EmitSwap(size_t index) {
7284 MoveOperands* move = moves_[index];
7285 Location source = move->GetSource();
7286 Location destination = move->GetDestination();
7287 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
7288
7289 if (source.IsRegister() && destination.IsRegister()) {
7290 vixl32::Register temp = temps.Acquire();
7291 DCHECK(!RegisterFrom(source).Is(temp));
7292 DCHECK(!RegisterFrom(destination).Is(temp));
7293 __ Mov(temp, RegisterFrom(destination));
7294 __ Mov(RegisterFrom(destination), RegisterFrom(source));
7295 __ Mov(RegisterFrom(source), temp);
7296 } else if (source.IsRegister() && destination.IsStackSlot()) {
7297 Exchange(RegisterFrom(source), destination.GetStackIndex());
7298 } else if (source.IsStackSlot() && destination.IsRegister()) {
7299 Exchange(RegisterFrom(destination), source.GetStackIndex());
7300 } else if (source.IsStackSlot() && destination.IsStackSlot()) {
Anton Kirilovdda43962016-11-21 19:55:20 +00007301 Exchange(source.GetStackIndex(), destination.GetStackIndex());
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007302 } else if (source.IsFpuRegister() && destination.IsFpuRegister()) {
Nicolas Geoffray13a797b2017-03-15 16:41:31 +00007303 vixl32::Register temp = temps.Acquire();
Anton Kirilovdda43962016-11-21 19:55:20 +00007304 __ Vmov(temp, SRegisterFrom(source));
7305 __ Vmov(SRegisterFrom(source), SRegisterFrom(destination));
7306 __ Vmov(SRegisterFrom(destination), temp);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007307 } else if (source.IsRegisterPair() && destination.IsRegisterPair()) {
7308 vixl32::DRegister temp = temps.AcquireD();
7309 __ Vmov(temp, LowRegisterFrom(source), HighRegisterFrom(source));
7310 __ Mov(LowRegisterFrom(source), LowRegisterFrom(destination));
7311 __ Mov(HighRegisterFrom(source), HighRegisterFrom(destination));
7312 __ Vmov(LowRegisterFrom(destination), HighRegisterFrom(destination), temp);
7313 } else if (source.IsRegisterPair() || destination.IsRegisterPair()) {
7314 vixl32::Register low_reg = LowRegisterFrom(source.IsRegisterPair() ? source : destination);
7315 int mem = source.IsRegisterPair() ? destination.GetStackIndex() : source.GetStackIndex();
7316 DCHECK(ExpectedPairLayout(source.IsRegisterPair() ? source : destination));
7317 vixl32::DRegister temp = temps.AcquireD();
7318 __ Vmov(temp, low_reg, vixl32::Register(low_reg.GetCode() + 1));
7319 GetAssembler()->LoadFromOffset(kLoadWordPair, low_reg, sp, mem);
7320 GetAssembler()->StoreDToOffset(temp, sp, mem);
7321 } else if (source.IsFpuRegisterPair() && destination.IsFpuRegisterPair()) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007322 vixl32::DRegister first = DRegisterFrom(source);
7323 vixl32::DRegister second = DRegisterFrom(destination);
7324 vixl32::DRegister temp = temps.AcquireD();
7325 __ Vmov(temp, first);
7326 __ Vmov(first, second);
7327 __ Vmov(second, temp);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007328 } else if (source.IsFpuRegisterPair() || destination.IsFpuRegisterPair()) {
Anton Kirilovdda43962016-11-21 19:55:20 +00007329 vixl32::DRegister reg = source.IsFpuRegisterPair()
7330 ? DRegisterFrom(source)
7331 : DRegisterFrom(destination);
7332 int mem = source.IsFpuRegisterPair()
7333 ? destination.GetStackIndex()
7334 : source.GetStackIndex();
7335 vixl32::DRegister temp = temps.AcquireD();
7336 __ Vmov(temp, reg);
7337 GetAssembler()->LoadDFromOffset(reg, sp, mem);
7338 GetAssembler()->StoreDToOffset(temp, sp, mem);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007339 } else if (source.IsFpuRegister() || destination.IsFpuRegister()) {
Anton Kirilovdda43962016-11-21 19:55:20 +00007340 vixl32::SRegister reg = source.IsFpuRegister()
7341 ? SRegisterFrom(source)
7342 : SRegisterFrom(destination);
7343 int mem = source.IsFpuRegister()
7344 ? destination.GetStackIndex()
7345 : source.GetStackIndex();
7346 vixl32::Register temp = temps.Acquire();
7347 __ Vmov(temp, reg);
7348 GetAssembler()->LoadSFromOffset(reg, sp, mem);
7349 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, mem);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007350 } else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) {
7351 vixl32::DRegister temp1 = temps.AcquireD();
7352 vixl32::DRegister temp2 = temps.AcquireD();
7353 __ Vldr(temp1, MemOperand(sp, source.GetStackIndex()));
7354 __ Vldr(temp2, MemOperand(sp, destination.GetStackIndex()));
7355 __ Vstr(temp1, MemOperand(sp, destination.GetStackIndex()));
7356 __ Vstr(temp2, MemOperand(sp, source.GetStackIndex()));
7357 } else {
7358 LOG(FATAL) << "Unimplemented" << source << " <-> " << destination;
7359 }
Scott Wakelingfe885462016-09-22 10:24:38 +01007360}
7361
Nicolas Geoffray13a797b2017-03-15 16:41:31 +00007362void ParallelMoveResolverARMVIXL::SpillScratch(int reg) {
7363 __ Push(vixl32::Register(reg));
Scott Wakelingfe885462016-09-22 10:24:38 +01007364}
7365
Nicolas Geoffray13a797b2017-03-15 16:41:31 +00007366void ParallelMoveResolverARMVIXL::RestoreScratch(int reg) {
7367 __ Pop(vixl32::Register(reg));
Scott Wakelingfe885462016-09-22 10:24:38 +01007368}
7369
Artem Serov02d37832016-10-25 15:25:33 +01007370HLoadClass::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadClassKind(
Artem Serovd4cc5b22016-11-04 11:19:09 +00007371 HLoadClass::LoadKind desired_class_load_kind) {
7372 switch (desired_class_load_kind) {
Nicolas Geoffray83c8e272017-01-31 14:36:37 +00007373 case HLoadClass::LoadKind::kInvalid:
7374 LOG(FATAL) << "UNREACHABLE";
7375 UNREACHABLE();
Artem Serovd4cc5b22016-11-04 11:19:09 +00007376 case HLoadClass::LoadKind::kReferrersClass:
7377 break;
Artem Serovd4cc5b22016-11-04 11:19:09 +00007378 case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
Vladimir Markoe47f60c2018-02-21 13:43:28 +00007379 case HLoadClass::LoadKind::kBootImageRelRo:
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007380 case HLoadClass::LoadKind::kBssEntry:
7381 DCHECK(!Runtime::Current()->UseJitCompilation());
7382 break;
Nicolas Geoffray22384ae2016-12-12 22:33:36 +00007383 case HLoadClass::LoadKind::kJitTableAddress:
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007384 DCHECK(Runtime::Current()->UseJitCompilation());
Artem Serovc5fcb442016-12-02 19:19:58 +00007385 break;
Vladimir Marko764d4542017-05-16 10:31:41 +01007386 case HLoadClass::LoadKind::kBootImageAddress:
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007387 case HLoadClass::LoadKind::kRuntimeCall:
Artem Serovd4cc5b22016-11-04 11:19:09 +00007388 break;
7389 }
7390 return desired_class_load_kind;
Artem Serov02d37832016-10-25 15:25:33 +01007391}
7392
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007393void LocationsBuilderARMVIXL::VisitLoadClass(HLoadClass* cls) {
Vladimir Marko41559982017-01-06 14:04:23 +00007394 HLoadClass::LoadKind load_kind = cls->GetLoadKind();
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007395 if (load_kind == HLoadClass::LoadKind::kRuntimeCall) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007396 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Vladimir Marko41559982017-01-06 14:04:23 +00007397 CodeGenerator::CreateLoadClassRuntimeCallLocationSummary(
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007398 cls,
7399 LocationFrom(calling_convention.GetRegisterAt(0)),
Vladimir Marko41559982017-01-06 14:04:23 +00007400 LocationFrom(r0));
Vladimir Markoea4c1262017-02-06 19:59:33 +00007401 DCHECK(calling_convention.GetRegisterAt(0).Is(r0));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007402 return;
7403 }
Vladimir Marko41559982017-01-06 14:04:23 +00007404 DCHECK(!cls->NeedsAccessCheck());
Scott Wakelingfe885462016-09-22 10:24:38 +01007405
Artem Serovd4cc5b22016-11-04 11:19:09 +00007406 const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
7407 LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier)
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007408 ? LocationSummary::kCallOnSlowPath
7409 : LocationSummary::kNoCall;
Vladimir Markoca6fff82017-10-03 14:49:14 +01007410 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(cls, call_kind);
Artem Serovd4cc5b22016-11-04 11:19:09 +00007411 if (kUseBakerReadBarrier && requires_read_barrier && !cls->NeedsEnvironment()) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00007412 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Artem Serovd4cc5b22016-11-04 11:19:09 +00007413 }
7414
Vladimir Marko41559982017-01-06 14:04:23 +00007415 if (load_kind == HLoadClass::LoadKind::kReferrersClass) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007416 locations->SetInAt(0, Location::RequiresRegister());
7417 }
7418 locations->SetOut(Location::RequiresRegister());
Vladimir Markoea4c1262017-02-06 19:59:33 +00007419 if (load_kind == HLoadClass::LoadKind::kBssEntry) {
7420 if (!kUseReadBarrier || kUseBakerReadBarrier) {
7421 // Rely on the type resolution or initialization and marking to save everything we need.
Vladimir Markoea4c1262017-02-06 19:59:33 +00007422 RegisterSet caller_saves = RegisterSet::Empty();
7423 InvokeRuntimeCallingConventionARMVIXL calling_convention;
7424 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0)));
7425 // TODO: Add GetReturnLocation() to the calling convention so that we can DCHECK()
7426 // that the the kPrimNot result register is the same as the first argument register.
7427 locations->SetCustomSlowPathCallerSaves(caller_saves);
7428 } else {
7429 // For non-Baker read barrier we have a temp-clobbering call.
7430 }
7431 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007432}
7433
Nicolas Geoffray5247c082017-01-13 14:17:29 +00007434// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
7435// move.
7436void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFETY_ANALYSIS {
Vladimir Marko41559982017-01-06 14:04:23 +00007437 HLoadClass::LoadKind load_kind = cls->GetLoadKind();
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007438 if (load_kind == HLoadClass::LoadKind::kRuntimeCall) {
Vladimir Marko41559982017-01-06 14:04:23 +00007439 codegen_->GenerateLoadClassRuntimeCall(cls);
Roland Levillain5daa4952017-07-03 17:23:56 +01007440 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 13);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007441 return;
7442 }
Vladimir Marko41559982017-01-06 14:04:23 +00007443 DCHECK(!cls->NeedsAccessCheck());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007444
Vladimir Marko41559982017-01-06 14:04:23 +00007445 LocationSummary* locations = cls->GetLocations();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007446 Location out_loc = locations->Out();
7447 vixl32::Register out = OutputRegister(cls);
7448
Artem Serovd4cc5b22016-11-04 11:19:09 +00007449 const ReadBarrierOption read_barrier_option = cls->IsInBootImage()
7450 ? kWithoutReadBarrier
7451 : kCompilerReadBarrierOption;
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007452 bool generate_null_check = false;
Vladimir Marko41559982017-01-06 14:04:23 +00007453 switch (load_kind) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007454 case HLoadClass::LoadKind::kReferrersClass: {
7455 DCHECK(!cls->CanCallRuntime());
7456 DCHECK(!cls->MustGenerateClinitCheck());
7457 // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
7458 vixl32::Register current_method = InputRegisterAt(cls, 0);
Vladimir Markoca1e0382018-04-11 09:58:41 +00007459 codegen_->GenerateGcRootFieldLoad(cls,
7460 out_loc,
7461 current_method,
7462 ArtMethod::DeclaringClassOffset().Int32Value(),
7463 read_barrier_option);
Artem Serovd4cc5b22016-11-04 11:19:09 +00007464 break;
7465 }
Artem Serovd4cc5b22016-11-04 11:19:09 +00007466 case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: {
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007467 DCHECK(codegen_->GetCompilerOptions().IsBootImage());
Artem Serovd4cc5b22016-11-04 11:19:09 +00007468 DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
7469 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
Vladimir Marko59eb30f2018-02-20 11:52:34 +00007470 codegen_->NewBootImageTypePatch(cls->GetDexFile(), cls->GetTypeIndex());
Artem Serovd4cc5b22016-11-04 11:19:09 +00007471 codegen_->EmitMovwMovtPlaceholder(labels, out);
7472 break;
7473 }
7474 case HLoadClass::LoadKind::kBootImageAddress: {
Artem Serovc5fcb442016-12-02 19:19:58 +00007475 DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
Nicolas Geoffray5247c082017-01-13 14:17:29 +00007476 uint32_t address = dchecked_integral_cast<uint32_t>(
7477 reinterpret_cast<uintptr_t>(cls->GetClass().Get()));
7478 DCHECK_NE(address, 0u);
Artem Serovc5fcb442016-12-02 19:19:58 +00007479 __ Ldr(out, codegen_->DeduplicateBootImageAddressLiteral(address));
Artem Serovd4cc5b22016-11-04 11:19:09 +00007480 break;
7481 }
Vladimir Markoe47f60c2018-02-21 13:43:28 +00007482 case HLoadClass::LoadKind::kBootImageRelRo: {
Vladimir Marko94ec2db2017-09-06 17:21:03 +01007483 DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
7484 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
Vladimir Markoe47f60c2018-02-21 13:43:28 +00007485 codegen_->NewBootImageRelRoPatch(codegen_->GetBootImageOffset(cls));
Vladimir Marko94ec2db2017-09-06 17:21:03 +01007486 codegen_->EmitMovwMovtPlaceholder(labels, out);
7487 __ Ldr(out, MemOperand(out, /* offset */ 0));
Vladimir Marko94ec2db2017-09-06 17:21:03 +01007488 break;
7489 }
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007490 case HLoadClass::LoadKind::kBssEntry: {
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007491 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
Vladimir Marko1998cd02017-01-13 13:02:58 +00007492 codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex());
Vladimir Markof3c52b42017-11-17 17:32:12 +00007493 codegen_->EmitMovwMovtPlaceholder(labels, out);
Vladimir Markoca1e0382018-04-11 09:58:41 +00007494 codegen_->GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, read_barrier_option);
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007495 generate_null_check = true;
7496 break;
7497 }
Nicolas Geoffray22384ae2016-12-12 22:33:36 +00007498 case HLoadClass::LoadKind::kJitTableAddress: {
Artem Serovc5fcb442016-12-02 19:19:58 +00007499 __ Ldr(out, codegen_->DeduplicateJitClassLiteral(cls->GetDexFile(),
7500 cls->GetTypeIndex(),
Nicolas Geoffray5247c082017-01-13 14:17:29 +00007501 cls->GetClass()));
Artem Serovc5fcb442016-12-02 19:19:58 +00007502 // /* GcRoot<mirror::Class> */ out = *out
Vladimir Markoca1e0382018-04-11 09:58:41 +00007503 codegen_->GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, read_barrier_option);
Artem Serovd4cc5b22016-11-04 11:19:09 +00007504 break;
7505 }
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007506 case HLoadClass::LoadKind::kRuntimeCall:
Nicolas Geoffray83c8e272017-01-31 14:36:37 +00007507 case HLoadClass::LoadKind::kInvalid:
Vladimir Marko41559982017-01-06 14:04:23 +00007508 LOG(FATAL) << "UNREACHABLE";
7509 UNREACHABLE();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007510 }
7511
7512 if (generate_null_check || cls->MustGenerateClinitCheck()) {
7513 DCHECK(cls->CanCallRuntime());
Vladimir Marko174b2e22017-10-12 13:34:49 +01007514 LoadClassSlowPathARMVIXL* slow_path =
7515 new (codegen_->GetScopedAllocator()) LoadClassSlowPathARMVIXL(
7516 cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007517 codegen_->AddSlowPath(slow_path);
7518 if (generate_null_check) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00007519 __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007520 }
7521 if (cls->MustGenerateClinitCheck()) {
7522 GenerateClassInitializationCheck(slow_path, out);
7523 } else {
7524 __ Bind(slow_path->GetExitLabel());
7525 }
Roland Levillain5daa4952017-07-03 17:23:56 +01007526 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 14);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007527 }
7528}
7529
Artem Serov02d37832016-10-25 15:25:33 +01007530void LocationsBuilderARMVIXL::VisitClinitCheck(HClinitCheck* check) {
7531 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01007532 new (GetGraph()->GetAllocator()) LocationSummary(check, LocationSummary::kCallOnSlowPath);
Artem Serov02d37832016-10-25 15:25:33 +01007533 locations->SetInAt(0, Location::RequiresRegister());
7534 if (check->HasUses()) {
7535 locations->SetOut(Location::SameAsFirstInput());
7536 }
7537}
7538
7539void InstructionCodeGeneratorARMVIXL::VisitClinitCheck(HClinitCheck* check) {
7540 // We assume the class is not null.
7541 LoadClassSlowPathARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01007542 new (codegen_->GetScopedAllocator()) LoadClassSlowPathARMVIXL(check->GetLoadClass(),
7543 check,
7544 check->GetDexPc(),
7545 /* do_clinit */ true);
Artem Serov02d37832016-10-25 15:25:33 +01007546 codegen_->AddSlowPath(slow_path);
7547 GenerateClassInitializationCheck(slow_path, InputRegisterAt(check, 0));
7548}
7549
7550void InstructionCodeGeneratorARMVIXL::GenerateClassInitializationCheck(
7551 LoadClassSlowPathARMVIXL* slow_path, vixl32::Register class_reg) {
7552 UseScratchRegisterScope temps(GetVIXLAssembler());
7553 vixl32::Register temp = temps.Acquire();
Vladimir Markodc682aa2018-01-04 18:42:57 +00007554 constexpr size_t status_lsb_position = SubtypeCheckBits::BitStructSizeOf();
7555 const size_t status_byte_offset =
7556 mirror::Class::StatusOffset().SizeValue() + (status_lsb_position / kBitsPerByte);
7557 constexpr uint32_t shifted_initialized_value =
7558 enum_cast<uint32_t>(ClassStatus::kInitialized) << (status_lsb_position % kBitsPerByte);
7559
7560 GetAssembler()->LoadFromOffset(kLoadUnsignedByte, temp, class_reg, status_byte_offset);
7561 __ Cmp(temp, shifted_initialized_value);
Vladimir Marko2c64a832018-01-04 11:31:56 +00007562 __ B(lo, slow_path->GetEntryLabel());
Artem Serov02d37832016-10-25 15:25:33 +01007563 // Even if the initialized flag is set, we may be in a situation where caches are not synced
7564 // properly. Therefore, we do a memory fence.
7565 __ Dmb(ISH);
7566 __ Bind(slow_path->GetExitLabel());
7567}
7568
Vladimir Marko175e7862018-03-27 09:03:13 +00007569void InstructionCodeGeneratorARMVIXL::GenerateBitstringTypeCheckCompare(
7570 HTypeCheckInstruction* check,
7571 vixl32::Register temp,
7572 vixl32::FlagsUpdate flags_update) {
7573 uint32_t path_to_root = check->GetBitstringPathToRoot();
7574 uint32_t mask = check->GetBitstringMask();
7575 DCHECK(IsPowerOfTwo(mask + 1));
7576 size_t mask_bits = WhichPowerOf2(mask + 1);
7577
7578 // Note that HInstanceOf shall check for zero value in `temp` but HCheckCast needs
7579 // the Z flag for BNE. This is indicated by the `flags_update` parameter.
7580 if (mask_bits == 16u) {
7581 // Load only the bitstring part of the status word.
7582 __ Ldrh(temp, MemOperand(temp, mirror::Class::StatusOffset().Int32Value()));
7583 // Check if the bitstring bits are equal to `path_to_root`.
7584 if (flags_update == SetFlags) {
7585 __ Cmp(temp, path_to_root);
7586 } else {
7587 __ Sub(temp, temp, path_to_root);
7588 }
7589 } else {
7590 // /* uint32_t */ temp = temp->status_
7591 __ Ldr(temp, MemOperand(temp, mirror::Class::StatusOffset().Int32Value()));
7592 if (GetAssembler()->ShifterOperandCanHold(SUB, path_to_root)) {
7593 // Compare the bitstring bits using SUB.
7594 __ Sub(temp, temp, path_to_root);
7595 // Shift out bits that do not contribute to the comparison.
7596 __ Lsl(flags_update, temp, temp, dchecked_integral_cast<uint32_t>(32u - mask_bits));
7597 } else if (IsUint<16>(path_to_root)) {
7598 if (temp.IsLow()) {
7599 // Note: Optimized for size but contains one more dependent instruction than necessary.
7600 // MOVW+SUB(register) would be 8 bytes unless we find a low-reg temporary but the
7601 // macro assembler would use the high reg IP for the constant by default.
7602 // Compare the bitstring bits using SUB.
7603 __ Sub(temp, temp, path_to_root & 0x00ffu); // 16-bit SUB (immediate) T2
7604 __ Sub(temp, temp, path_to_root & 0xff00u); // 32-bit SUB (immediate) T3
7605 // Shift out bits that do not contribute to the comparison.
7606 __ Lsl(flags_update, temp, temp, dchecked_integral_cast<uint32_t>(32u - mask_bits));
7607 } else {
7608 // Extract the bitstring bits.
7609 __ Ubfx(temp, temp, 0, mask_bits);
7610 // Check if the bitstring bits are equal to `path_to_root`.
7611 if (flags_update == SetFlags) {
7612 __ Cmp(temp, path_to_root);
7613 } else {
7614 __ Sub(temp, temp, path_to_root);
7615 }
7616 }
7617 } else {
7618 // Shift out bits that do not contribute to the comparison.
7619 __ Lsl(temp, temp, dchecked_integral_cast<uint32_t>(32u - mask_bits));
7620 // Check if the shifted bitstring bits are equal to `path_to_root << (32u - mask_bits)`.
7621 if (flags_update == SetFlags) {
7622 __ Cmp(temp, path_to_root << (32u - mask_bits));
7623 } else {
7624 __ Sub(temp, temp, path_to_root << (32u - mask_bits));
7625 }
7626 }
7627 }
7628}
7629
Artem Serov02d37832016-10-25 15:25:33 +01007630HLoadString::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadStringKind(
Artem Serovd4cc5b22016-11-04 11:19:09 +00007631 HLoadString::LoadKind desired_string_load_kind) {
7632 switch (desired_string_load_kind) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00007633 case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
Vladimir Markoe47f60c2018-02-21 13:43:28 +00007634 case HLoadString::LoadKind::kBootImageRelRo:
Artem Serovd4cc5b22016-11-04 11:19:09 +00007635 case HLoadString::LoadKind::kBssEntry:
7636 DCHECK(!Runtime::Current()->UseJitCompilation());
7637 break;
7638 case HLoadString::LoadKind::kJitTableAddress:
7639 DCHECK(Runtime::Current()->UseJitCompilation());
Artem Serovc5fcb442016-12-02 19:19:58 +00007640 break;
Vladimir Marko764d4542017-05-16 10:31:41 +01007641 case HLoadString::LoadKind::kBootImageAddress:
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007642 case HLoadString::LoadKind::kRuntimeCall:
Artem Serovd4cc5b22016-11-04 11:19:09 +00007643 break;
7644 }
7645 return desired_string_load_kind;
Artem Serov02d37832016-10-25 15:25:33 +01007646}
7647
7648void LocationsBuilderARMVIXL::VisitLoadString(HLoadString* load) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00007649 LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load);
Vladimir Markoca6fff82017-10-03 14:49:14 +01007650 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(load, call_kind);
Artem Serov02d37832016-10-25 15:25:33 +01007651 HLoadString::LoadKind load_kind = load->GetLoadKind();
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007652 if (load_kind == HLoadString::LoadKind::kRuntimeCall) {
Artem Serov02d37832016-10-25 15:25:33 +01007653 locations->SetOut(LocationFrom(r0));
7654 } else {
7655 locations->SetOut(Location::RequiresRegister());
Artem Serovd4cc5b22016-11-04 11:19:09 +00007656 if (load_kind == HLoadString::LoadKind::kBssEntry) {
7657 if (!kUseReadBarrier || kUseBakerReadBarrier) {
Vladimir Markoea4c1262017-02-06 19:59:33 +00007658 // Rely on the pResolveString and marking to save everything we need, including temps.
Artem Serovd4cc5b22016-11-04 11:19:09 +00007659 RegisterSet caller_saves = RegisterSet::Empty();
7660 InvokeRuntimeCallingConventionARMVIXL calling_convention;
7661 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0)));
7662 // TODO: Add GetReturnLocation() to the calling convention so that we can DCHECK()
7663 // that the the kPrimNot result register is the same as the first argument register.
7664 locations->SetCustomSlowPathCallerSaves(caller_saves);
7665 } else {
7666 // For non-Baker read barrier we have a temp-clobbering call.
7667 }
7668 }
Artem Serov02d37832016-10-25 15:25:33 +01007669 }
7670}
7671
Nicolas Geoffrayf0acfe72017-01-09 20:54:52 +00007672// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
7673// move.
7674void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) NO_THREAD_SAFETY_ANALYSIS {
Artem Serovd4cc5b22016-11-04 11:19:09 +00007675 LocationSummary* locations = load->GetLocations();
7676 Location out_loc = locations->Out();
7677 vixl32::Register out = OutputRegister(load);
7678 HLoadString::LoadKind load_kind = load->GetLoadKind();
7679
7680 switch (load_kind) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00007681 case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
7682 DCHECK(codegen_->GetCompilerOptions().IsBootImage());
7683 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
Vladimir Marko59eb30f2018-02-20 11:52:34 +00007684 codegen_->NewBootImageStringPatch(load->GetDexFile(), load->GetStringIndex());
Artem Serovd4cc5b22016-11-04 11:19:09 +00007685 codegen_->EmitMovwMovtPlaceholder(labels, out);
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01007686 return;
Artem Serovd4cc5b22016-11-04 11:19:09 +00007687 }
7688 case HLoadString::LoadKind::kBootImageAddress: {
Nicolas Geoffrayf0acfe72017-01-09 20:54:52 +00007689 uint32_t address = dchecked_integral_cast<uint32_t>(
7690 reinterpret_cast<uintptr_t>(load->GetString().Get()));
7691 DCHECK_NE(address, 0u);
Artem Serovc5fcb442016-12-02 19:19:58 +00007692 __ Ldr(out, codegen_->DeduplicateBootImageAddressLiteral(address));
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01007693 return;
7694 }
Vladimir Markoe47f60c2018-02-21 13:43:28 +00007695 case HLoadString::LoadKind::kBootImageRelRo: {
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01007696 DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
7697 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
Vladimir Markoe47f60c2018-02-21 13:43:28 +00007698 codegen_->NewBootImageRelRoPatch(codegen_->GetBootImageOffset(load));
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01007699 codegen_->EmitMovwMovtPlaceholder(labels, out);
7700 __ Ldr(out, MemOperand(out, /* offset */ 0));
7701 return;
Artem Serovd4cc5b22016-11-04 11:19:09 +00007702 }
7703 case HLoadString::LoadKind::kBssEntry: {
7704 DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
Artem Serovd4cc5b22016-11-04 11:19:09 +00007705 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01007706 codegen_->NewStringBssEntryPatch(load->GetDexFile(), load->GetStringIndex());
Vladimir Markof3c52b42017-11-17 17:32:12 +00007707 codegen_->EmitMovwMovtPlaceholder(labels, out);
Vladimir Markoca1e0382018-04-11 09:58:41 +00007708 codegen_->GenerateGcRootFieldLoad(
7709 load, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption);
Artem Serovd4cc5b22016-11-04 11:19:09 +00007710 LoadStringSlowPathARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01007711 new (codegen_->GetScopedAllocator()) LoadStringSlowPathARMVIXL(load);
Artem Serovd4cc5b22016-11-04 11:19:09 +00007712 codegen_->AddSlowPath(slow_path);
7713 __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
7714 __ Bind(slow_path->GetExitLabel());
Roland Levillain5daa4952017-07-03 17:23:56 +01007715 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 15);
Artem Serovd4cc5b22016-11-04 11:19:09 +00007716 return;
7717 }
7718 case HLoadString::LoadKind::kJitTableAddress: {
Artem Serovc5fcb442016-12-02 19:19:58 +00007719 __ Ldr(out, codegen_->DeduplicateJitStringLiteral(load->GetDexFile(),
Nicolas Geoffrayf0acfe72017-01-09 20:54:52 +00007720 load->GetStringIndex(),
7721 load->GetString()));
Artem Serovc5fcb442016-12-02 19:19:58 +00007722 // /* GcRoot<mirror::String> */ out = *out
Vladimir Markoca1e0382018-04-11 09:58:41 +00007723 codegen_->GenerateGcRootFieldLoad(
7724 load, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption);
Artem Serovc5fcb442016-12-02 19:19:58 +00007725 return;
Artem Serovd4cc5b22016-11-04 11:19:09 +00007726 }
7727 default:
7728 break;
7729 }
Artem Serov02d37832016-10-25 15:25:33 +01007730
7731 // TODO: Re-add the compiler code to do string dex cache lookup again.
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007732 DCHECK_EQ(load->GetLoadKind(), HLoadString::LoadKind::kRuntimeCall);
Artem Serov02d37832016-10-25 15:25:33 +01007733 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Andreas Gampe8a0128a2016-11-28 07:38:35 -08007734 __ Mov(calling_convention.GetRegisterAt(0), load->GetStringIndex().index_);
Artem Serov02d37832016-10-25 15:25:33 +01007735 codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc());
7736 CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
Roland Levillain5daa4952017-07-03 17:23:56 +01007737 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 16);
Artem Serov02d37832016-10-25 15:25:33 +01007738}
7739
7740static int32_t GetExceptionTlsOffset() {
7741 return Thread::ExceptionOffset<kArmPointerSize>().Int32Value();
7742}
7743
7744void LocationsBuilderARMVIXL::VisitLoadException(HLoadException* load) {
7745 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01007746 new (GetGraph()->GetAllocator()) LocationSummary(load, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01007747 locations->SetOut(Location::RequiresRegister());
7748}
7749
7750void InstructionCodeGeneratorARMVIXL::VisitLoadException(HLoadException* load) {
7751 vixl32::Register out = OutputRegister(load);
7752 GetAssembler()->LoadFromOffset(kLoadWord, out, tr, GetExceptionTlsOffset());
7753}
7754
7755
7756void LocationsBuilderARMVIXL::VisitClearException(HClearException* clear) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01007757 new (GetGraph()->GetAllocator()) LocationSummary(clear, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01007758}
7759
7760void InstructionCodeGeneratorARMVIXL::VisitClearException(HClearException* clear ATTRIBUTE_UNUSED) {
7761 UseScratchRegisterScope temps(GetVIXLAssembler());
7762 vixl32::Register temp = temps.Acquire();
7763 __ Mov(temp, 0);
7764 GetAssembler()->StoreToOffset(kStoreWord, temp, tr, GetExceptionTlsOffset());
7765}
7766
7767void LocationsBuilderARMVIXL::VisitThrow(HThrow* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01007768 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
7769 instruction, LocationSummary::kCallOnMainOnly);
Artem Serov02d37832016-10-25 15:25:33 +01007770 InvokeRuntimeCallingConventionARMVIXL calling_convention;
7771 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
7772}
7773
7774void InstructionCodeGeneratorARMVIXL::VisitThrow(HThrow* instruction) {
7775 codegen_->InvokeRuntime(kQuickDeliverException, instruction, instruction->GetDexPc());
7776 CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
7777}
7778
Artem Serov657022c2016-11-23 14:19:38 +00007779// Temp is used for read barrier.
7780static size_t NumberOfInstanceOfTemps(TypeCheckKind type_check_kind) {
7781 if (kEmitCompilerReadBarrier &&
7782 (kUseBakerReadBarrier ||
7783 type_check_kind == TypeCheckKind::kAbstractClassCheck ||
7784 type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
7785 type_check_kind == TypeCheckKind::kArrayObjectCheck)) {
7786 return 1;
7787 }
7788 return 0;
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007789}
7790
Artem Serov657022c2016-11-23 14:19:38 +00007791// Interface case has 3 temps, one for holding the number of interfaces, one for the current
7792// interface pointer, one for loading the current interface.
7793// The other checks have one temp for loading the object's class.
7794static size_t NumberOfCheckCastTemps(TypeCheckKind type_check_kind) {
7795 if (type_check_kind == TypeCheckKind::kInterfaceCheck) {
7796 return 3;
7797 }
7798 return 1 + NumberOfInstanceOfTemps(type_check_kind);
7799}
Artem Serovcfbe9132016-10-14 15:58:56 +01007800
7801void LocationsBuilderARMVIXL::VisitInstanceOf(HInstanceOf* instruction) {
7802 LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
7803 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
7804 bool baker_read_barrier_slow_path = false;
7805 switch (type_check_kind) {
7806 case TypeCheckKind::kExactCheck:
7807 case TypeCheckKind::kAbstractClassCheck:
7808 case TypeCheckKind::kClassHierarchyCheck:
Vladimir Marko87584542017-12-12 17:47:52 +00007809 case TypeCheckKind::kArrayObjectCheck: {
7810 bool needs_read_barrier = CodeGenerator::InstanceOfNeedsReadBarrier(instruction);
7811 call_kind = needs_read_barrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall;
7812 baker_read_barrier_slow_path = kUseBakerReadBarrier && needs_read_barrier;
Artem Serovcfbe9132016-10-14 15:58:56 +01007813 break;
Vladimir Marko87584542017-12-12 17:47:52 +00007814 }
Artem Serovcfbe9132016-10-14 15:58:56 +01007815 case TypeCheckKind::kArrayCheck:
7816 case TypeCheckKind::kUnresolvedCheck:
7817 case TypeCheckKind::kInterfaceCheck:
7818 call_kind = LocationSummary::kCallOnSlowPath;
7819 break;
Vladimir Marko175e7862018-03-27 09:03:13 +00007820 case TypeCheckKind::kBitstringCheck:
7821 break;
Artem Serovcfbe9132016-10-14 15:58:56 +01007822 }
7823
Vladimir Markoca6fff82017-10-03 14:49:14 +01007824 LocationSummary* locations =
7825 new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
Artem Serovcfbe9132016-10-14 15:58:56 +01007826 if (baker_read_barrier_slow_path) {
7827 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
7828 }
7829 locations->SetInAt(0, Location::RequiresRegister());
Vladimir Marko175e7862018-03-27 09:03:13 +00007830 if (type_check_kind == TypeCheckKind::kBitstringCheck) {
7831 locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
7832 locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
7833 locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
7834 } else {
7835 locations->SetInAt(1, Location::RequiresRegister());
7836 }
Artem Serovcfbe9132016-10-14 15:58:56 +01007837 // The "out" register is used as a temporary, so it overlaps with the inputs.
7838 // Note that TypeCheckSlowPathARM uses this register too.
7839 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
Artem Serov657022c2016-11-23 14:19:38 +00007840 locations->AddRegisterTemps(NumberOfInstanceOfTemps(type_check_kind));
Artem Serovcfbe9132016-10-14 15:58:56 +01007841}
7842
7843void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) {
7844 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
7845 LocationSummary* locations = instruction->GetLocations();
7846 Location obj_loc = locations->InAt(0);
7847 vixl32::Register obj = InputRegisterAt(instruction, 0);
Vladimir Marko175e7862018-03-27 09:03:13 +00007848 vixl32::Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck)
7849 ? vixl32::Register()
7850 : InputRegisterAt(instruction, 1);
Artem Serovcfbe9132016-10-14 15:58:56 +01007851 Location out_loc = locations->Out();
7852 vixl32::Register out = OutputRegister(instruction);
Artem Serov657022c2016-11-23 14:19:38 +00007853 const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind);
7854 DCHECK_LE(num_temps, 1u);
7855 Location maybe_temp_loc = (num_temps >= 1) ? locations->GetTemp(0) : Location::NoLocation();
Artem Serovcfbe9132016-10-14 15:58:56 +01007856 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
7857 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
7858 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
7859 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007860 vixl32::Label done;
7861 vixl32::Label* const final_label = codegen_->GetFinalLabel(instruction, &done);
Artem Serovcfbe9132016-10-14 15:58:56 +01007862 SlowPathCodeARMVIXL* slow_path = nullptr;
7863
7864 // Return 0 if `obj` is null.
7865 // avoid null check if we know obj is not null.
7866 if (instruction->MustDoNullCheck()) {
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007867 DCHECK(!out.Is(obj));
7868 __ Mov(out, 0);
7869 __ CompareAndBranchIfZero(obj, final_label, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007870 }
7871
Artem Serovcfbe9132016-10-14 15:58:56 +01007872 switch (type_check_kind) {
7873 case TypeCheckKind::kExactCheck: {
Vladimir Marko87584542017-12-12 17:47:52 +00007874 ReadBarrierOption read_barrier_option =
7875 CodeGenerator::ReadBarrierOptionForInstanceOf(instruction);
Mathieu Chartier6beced42016-11-15 15:51:31 -08007876 // /* HeapReference<Class> */ out = obj->klass_
7877 GenerateReferenceLoadTwoRegisters(instruction,
7878 out_loc,
7879 obj_loc,
7880 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00007881 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00007882 read_barrier_option);
Artem Serovcfbe9132016-10-14 15:58:56 +01007883 // Classes must be equal for the instanceof to succeed.
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007884 __ Cmp(out, cls);
7885 // We speculatively set the result to false without changing the condition
7886 // flags, which allows us to avoid some branching later.
7887 __ Mov(LeaveFlags, out, 0);
7888
7889 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
7890 // we check that the output is in a low register, so that a 16-bit MOV
7891 // encoding can be used.
7892 if (out.IsLow()) {
7893 // We use the scope because of the IT block that follows.
7894 ExactAssemblyScope guard(GetVIXLAssembler(),
7895 2 * vixl32::k16BitT32InstructionSizeInBytes,
7896 CodeBufferCheckScope::kExactSize);
7897
7898 __ it(eq);
7899 __ mov(eq, out, 1);
7900 } else {
7901 __ B(ne, final_label, /* far_target */ false);
7902 __ Mov(out, 1);
7903 }
7904
Artem Serovcfbe9132016-10-14 15:58:56 +01007905 break;
7906 }
7907
7908 case TypeCheckKind::kAbstractClassCheck: {
Vladimir Marko87584542017-12-12 17:47:52 +00007909 ReadBarrierOption read_barrier_option =
7910 CodeGenerator::ReadBarrierOptionForInstanceOf(instruction);
Mathieu Chartier6beced42016-11-15 15:51:31 -08007911 // /* HeapReference<Class> */ out = obj->klass_
7912 GenerateReferenceLoadTwoRegisters(instruction,
7913 out_loc,
7914 obj_loc,
7915 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00007916 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00007917 read_barrier_option);
Artem Serovcfbe9132016-10-14 15:58:56 +01007918 // If the class is abstract, we eagerly fetch the super class of the
7919 // object to avoid doing a comparison we know will fail.
7920 vixl32::Label loop;
7921 __ Bind(&loop);
7922 // /* HeapReference<Class> */ out = out->super_class_
Artem Serov657022c2016-11-23 14:19:38 +00007923 GenerateReferenceLoadOneRegister(instruction,
7924 out_loc,
7925 super_offset,
7926 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00007927 read_barrier_option);
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007928 // If `out` is null, we use it for the result, and jump to the final label.
Anton Kirilov6f644202017-02-27 18:29:45 +00007929 __ CompareAndBranchIfZero(out, final_label, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007930 __ Cmp(out, cls);
Artem Serov517d9f62016-12-12 15:51:15 +00007931 __ B(ne, &loop, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007932 __ Mov(out, 1);
Artem Serovcfbe9132016-10-14 15:58:56 +01007933 break;
7934 }
7935
7936 case TypeCheckKind::kClassHierarchyCheck: {
Vladimir Marko87584542017-12-12 17:47:52 +00007937 ReadBarrierOption read_barrier_option =
7938 CodeGenerator::ReadBarrierOptionForInstanceOf(instruction);
Mathieu Chartier6beced42016-11-15 15:51:31 -08007939 // /* HeapReference<Class> */ out = obj->klass_
7940 GenerateReferenceLoadTwoRegisters(instruction,
7941 out_loc,
7942 obj_loc,
7943 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00007944 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00007945 read_barrier_option);
Artem Serovcfbe9132016-10-14 15:58:56 +01007946 // Walk over the class hierarchy to find a match.
7947 vixl32::Label loop, success;
7948 __ Bind(&loop);
7949 __ Cmp(out, cls);
Artem Serov517d9f62016-12-12 15:51:15 +00007950 __ B(eq, &success, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007951 // /* HeapReference<Class> */ out = out->super_class_
Artem Serov657022c2016-11-23 14:19:38 +00007952 GenerateReferenceLoadOneRegister(instruction,
7953 out_loc,
7954 super_offset,
7955 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00007956 read_barrier_option);
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007957 // This is essentially a null check, but it sets the condition flags to the
7958 // proper value for the code that follows the loop, i.e. not `eq`.
7959 __ Cmp(out, 1);
7960 __ B(hs, &loop, /* far_target */ false);
7961
7962 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
7963 // we check that the output is in a low register, so that a 16-bit MOV
7964 // encoding can be used.
7965 if (out.IsLow()) {
7966 // If `out` is null, we use it for the result, and the condition flags
7967 // have already been set to `ne`, so the IT block that comes afterwards
7968 // (and which handles the successful case) turns into a NOP (instead of
7969 // overwriting `out`).
7970 __ Bind(&success);
7971
7972 // We use the scope because of the IT block that follows.
7973 ExactAssemblyScope guard(GetVIXLAssembler(),
7974 2 * vixl32::k16BitT32InstructionSizeInBytes,
7975 CodeBufferCheckScope::kExactSize);
7976
7977 // There is only one branch to the `success` label (which is bound to this
7978 // IT block), and it has the same condition, `eq`, so in that case the MOV
7979 // is executed.
7980 __ it(eq);
7981 __ mov(eq, out, 1);
7982 } else {
7983 // If `out` is null, we use it for the result, and jump to the final label.
Anton Kirilov6f644202017-02-27 18:29:45 +00007984 __ B(final_label);
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007985 __ Bind(&success);
7986 __ Mov(out, 1);
Artem Serovcfbe9132016-10-14 15:58:56 +01007987 }
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007988
Artem Serovcfbe9132016-10-14 15:58:56 +01007989 break;
7990 }
7991
7992 case TypeCheckKind::kArrayObjectCheck: {
Vladimir Marko87584542017-12-12 17:47:52 +00007993 ReadBarrierOption read_barrier_option =
7994 CodeGenerator::ReadBarrierOptionForInstanceOf(instruction);
Mathieu Chartier6beced42016-11-15 15:51:31 -08007995 // /* HeapReference<Class> */ out = obj->klass_
7996 GenerateReferenceLoadTwoRegisters(instruction,
7997 out_loc,
7998 obj_loc,
7999 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00008000 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00008001 read_barrier_option);
Artem Serovcfbe9132016-10-14 15:58:56 +01008002 // Do an exact check.
8003 vixl32::Label exact_check;
8004 __ Cmp(out, cls);
Artem Serov517d9f62016-12-12 15:51:15 +00008005 __ B(eq, &exact_check, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01008006 // Otherwise, we need to check that the object's class is a non-primitive array.
8007 // /* HeapReference<Class> */ out = out->component_type_
Artem Serov657022c2016-11-23 14:19:38 +00008008 GenerateReferenceLoadOneRegister(instruction,
8009 out_loc,
8010 component_offset,
8011 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00008012 read_barrier_option);
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00008013 // If `out` is null, we use it for the result, and jump to the final label.
Anton Kirilov6f644202017-02-27 18:29:45 +00008014 __ CompareAndBranchIfZero(out, final_label, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01008015 GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset);
8016 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00008017 __ Cmp(out, 0);
8018 // We speculatively set the result to false without changing the condition
8019 // flags, which allows us to avoid some branching later.
8020 __ Mov(LeaveFlags, out, 0);
8021
8022 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
8023 // we check that the output is in a low register, so that a 16-bit MOV
8024 // encoding can be used.
8025 if (out.IsLow()) {
8026 __ Bind(&exact_check);
8027
8028 // We use the scope because of the IT block that follows.
8029 ExactAssemblyScope guard(GetVIXLAssembler(),
8030 2 * vixl32::k16BitT32InstructionSizeInBytes,
8031 CodeBufferCheckScope::kExactSize);
8032
8033 __ it(eq);
8034 __ mov(eq, out, 1);
8035 } else {
8036 __ B(ne, final_label, /* far_target */ false);
8037 __ Bind(&exact_check);
8038 __ Mov(out, 1);
8039 }
8040
Artem Serovcfbe9132016-10-14 15:58:56 +01008041 break;
8042 }
8043
8044 case TypeCheckKind::kArrayCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00008045 // No read barrier since the slow path will retry upon failure.
Mathieu Chartier6beced42016-11-15 15:51:31 -08008046 // /* HeapReference<Class> */ out = obj->klass_
8047 GenerateReferenceLoadTwoRegisters(instruction,
8048 out_loc,
8049 obj_loc,
8050 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00008051 maybe_temp_loc,
8052 kWithoutReadBarrier);
Artem Serovcfbe9132016-10-14 15:58:56 +01008053 __ Cmp(out, cls);
8054 DCHECK(locations->OnlyCallsOnSlowPath());
Vladimir Marko174b2e22017-10-12 13:34:49 +01008055 slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathARMVIXL(
8056 instruction, /* is_fatal */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01008057 codegen_->AddSlowPath(slow_path);
8058 __ B(ne, slow_path->GetEntryLabel());
8059 __ Mov(out, 1);
Artem Serovcfbe9132016-10-14 15:58:56 +01008060 break;
8061 }
8062
8063 case TypeCheckKind::kUnresolvedCheck:
8064 case TypeCheckKind::kInterfaceCheck: {
8065 // Note that we indeed only call on slow path, but we always go
8066 // into the slow path for the unresolved and interface check
8067 // cases.
8068 //
8069 // We cannot directly call the InstanceofNonTrivial runtime
8070 // entry point without resorting to a type checking slow path
8071 // here (i.e. by calling InvokeRuntime directly), as it would
8072 // require to assign fixed registers for the inputs of this
8073 // HInstanceOf instruction (following the runtime calling
8074 // convention), which might be cluttered by the potential first
8075 // read barrier emission at the beginning of this method.
8076 //
8077 // TODO: Introduce a new runtime entry point taking the object
8078 // to test (instead of its class) as argument, and let it deal
8079 // with the read barrier issues. This will let us refactor this
8080 // case of the `switch` code as it was previously (with a direct
8081 // call to the runtime not using a type checking slow path).
8082 // This should also be beneficial for the other cases above.
8083 DCHECK(locations->OnlyCallsOnSlowPath());
Vladimir Marko174b2e22017-10-12 13:34:49 +01008084 slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathARMVIXL(
8085 instruction, /* is_fatal */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01008086 codegen_->AddSlowPath(slow_path);
8087 __ B(slow_path->GetEntryLabel());
Artem Serovcfbe9132016-10-14 15:58:56 +01008088 break;
8089 }
Vladimir Marko175e7862018-03-27 09:03:13 +00008090
8091 case TypeCheckKind::kBitstringCheck: {
8092 // /* HeapReference<Class> */ temp = obj->klass_
8093 GenerateReferenceLoadTwoRegisters(instruction,
8094 out_loc,
8095 obj_loc,
8096 class_offset,
8097 maybe_temp_loc,
8098 kWithoutReadBarrier);
8099
8100 GenerateBitstringTypeCheckCompare(instruction, out, DontCare);
8101 // If `out` is a low reg and we would have another low reg temp, we could
8102 // optimize this as RSBS+ADC, see GenerateConditionWithZero().
8103 //
8104 // Also, in some cases when `out` is a low reg and we're loading a constant to IP
8105 // it would make sense to use CMP+MOV+IT+MOV instead of SUB+CLZ+LSR as the code size
8106 // would be the same and we would have fewer direct data dependencies.
8107 codegen_->GenerateConditionWithZero(kCondEQ, out, out); // CLZ+LSR
8108 break;
8109 }
Artem Serovcfbe9132016-10-14 15:58:56 +01008110 }
8111
Artem Serovcfbe9132016-10-14 15:58:56 +01008112 if (done.IsReferenced()) {
8113 __ Bind(&done);
8114 }
8115
8116 if (slow_path != nullptr) {
8117 __ Bind(slow_path->GetExitLabel());
8118 }
8119}
8120
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008121void LocationsBuilderARMVIXL::VisitCheckCast(HCheckCast* instruction) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008122 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
Vladimir Marko87584542017-12-12 17:47:52 +00008123 LocationSummary::CallKind call_kind = CodeGenerator::GetCheckCastCallKind(instruction);
Vladimir Markoca6fff82017-10-03 14:49:14 +01008124 LocationSummary* locations =
8125 new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008126 locations->SetInAt(0, Location::RequiresRegister());
Vladimir Marko175e7862018-03-27 09:03:13 +00008127 if (type_check_kind == TypeCheckKind::kBitstringCheck) {
8128 locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
8129 locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
8130 locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
8131 } else {
8132 locations->SetInAt(1, Location::RequiresRegister());
8133 }
Artem Serov657022c2016-11-23 14:19:38 +00008134 locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind));
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008135}
8136
8137void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) {
8138 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
8139 LocationSummary* locations = instruction->GetLocations();
8140 Location obj_loc = locations->InAt(0);
8141 vixl32::Register obj = InputRegisterAt(instruction, 0);
Vladimir Marko175e7862018-03-27 09:03:13 +00008142 vixl32::Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck)
8143 ? vixl32::Register()
8144 : InputRegisterAt(instruction, 1);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008145 Location temp_loc = locations->GetTemp(0);
8146 vixl32::Register temp = RegisterFrom(temp_loc);
Artem Serov657022c2016-11-23 14:19:38 +00008147 const size_t num_temps = NumberOfCheckCastTemps(type_check_kind);
8148 DCHECK_LE(num_temps, 3u);
8149 Location maybe_temp2_loc = (num_temps >= 2) ? locations->GetTemp(1) : Location::NoLocation();
8150 Location maybe_temp3_loc = (num_temps >= 3) ? locations->GetTemp(2) : Location::NoLocation();
8151 const uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
8152 const uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
8153 const uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
8154 const uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
8155 const uint32_t iftable_offset = mirror::Class::IfTableOffset().Uint32Value();
8156 const uint32_t array_length_offset = mirror::Array::LengthOffset().Uint32Value();
8157 const uint32_t object_array_data_offset =
8158 mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value();
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008159
Vladimir Marko87584542017-12-12 17:47:52 +00008160 bool is_type_check_slow_path_fatal = CodeGenerator::IsTypeCheckSlowPathFatal(instruction);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008161 SlowPathCodeARMVIXL* type_check_slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01008162 new (codegen_->GetScopedAllocator()) TypeCheckSlowPathARMVIXL(
8163 instruction, is_type_check_slow_path_fatal);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008164 codegen_->AddSlowPath(type_check_slow_path);
8165
8166 vixl32::Label done;
Anton Kirilov6f644202017-02-27 18:29:45 +00008167 vixl32::Label* final_label = codegen_->GetFinalLabel(instruction, &done);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008168 // Avoid null check if we know obj is not null.
8169 if (instruction->MustDoNullCheck()) {
Anton Kirilov6f644202017-02-27 18:29:45 +00008170 __ CompareAndBranchIfZero(obj, final_label, /* far_target */ false);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008171 }
8172
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008173 switch (type_check_kind) {
8174 case TypeCheckKind::kExactCheck:
8175 case TypeCheckKind::kArrayCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00008176 // /* HeapReference<Class> */ temp = obj->klass_
8177 GenerateReferenceLoadTwoRegisters(instruction,
8178 temp_loc,
8179 obj_loc,
8180 class_offset,
8181 maybe_temp2_loc,
8182 kWithoutReadBarrier);
8183
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008184 __ Cmp(temp, cls);
8185 // Jump to slow path for throwing the exception or doing a
8186 // more involved array check.
8187 __ B(ne, type_check_slow_path->GetEntryLabel());
8188 break;
8189 }
8190
8191 case TypeCheckKind::kAbstractClassCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00008192 // /* HeapReference<Class> */ temp = obj->klass_
8193 GenerateReferenceLoadTwoRegisters(instruction,
8194 temp_loc,
8195 obj_loc,
8196 class_offset,
8197 maybe_temp2_loc,
8198 kWithoutReadBarrier);
8199
Artem Serovcfbe9132016-10-14 15:58:56 +01008200 // If the class is abstract, we eagerly fetch the super class of the
8201 // object to avoid doing a comparison we know will fail.
8202 vixl32::Label loop;
8203 __ Bind(&loop);
8204 // /* HeapReference<Class> */ temp = temp->super_class_
Artem Serov657022c2016-11-23 14:19:38 +00008205 GenerateReferenceLoadOneRegister(instruction,
8206 temp_loc,
8207 super_offset,
8208 maybe_temp2_loc,
8209 kWithoutReadBarrier);
Artem Serovcfbe9132016-10-14 15:58:56 +01008210
8211 // If the class reference currently in `temp` is null, jump to the slow path to throw the
8212 // exception.
xueliang.zhongf51bc622016-11-04 09:23:32 +00008213 __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
Artem Serovcfbe9132016-10-14 15:58:56 +01008214
8215 // Otherwise, compare the classes.
8216 __ Cmp(temp, cls);
Artem Serov517d9f62016-12-12 15:51:15 +00008217 __ B(ne, &loop, /* far_target */ false);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008218 break;
8219 }
8220
8221 case TypeCheckKind::kClassHierarchyCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00008222 // /* HeapReference<Class> */ temp = obj->klass_
8223 GenerateReferenceLoadTwoRegisters(instruction,
8224 temp_loc,
8225 obj_loc,
8226 class_offset,
8227 maybe_temp2_loc,
8228 kWithoutReadBarrier);
8229
Artem Serovcfbe9132016-10-14 15:58:56 +01008230 // Walk over the class hierarchy to find a match.
8231 vixl32::Label loop;
8232 __ Bind(&loop);
8233 __ Cmp(temp, cls);
Anton Kirilov6f644202017-02-27 18:29:45 +00008234 __ B(eq, final_label, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01008235
8236 // /* HeapReference<Class> */ temp = temp->super_class_
Artem Serov657022c2016-11-23 14:19:38 +00008237 GenerateReferenceLoadOneRegister(instruction,
8238 temp_loc,
8239 super_offset,
8240 maybe_temp2_loc,
8241 kWithoutReadBarrier);
Artem Serovcfbe9132016-10-14 15:58:56 +01008242
8243 // If the class reference currently in `temp` is null, jump to the slow path to throw the
8244 // exception.
xueliang.zhongf51bc622016-11-04 09:23:32 +00008245 __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
Artem Serovcfbe9132016-10-14 15:58:56 +01008246 // Otherwise, jump to the beginning of the loop.
8247 __ B(&loop);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008248 break;
8249 }
8250
Artem Serovcfbe9132016-10-14 15:58:56 +01008251 case TypeCheckKind::kArrayObjectCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00008252 // /* HeapReference<Class> */ temp = obj->klass_
8253 GenerateReferenceLoadTwoRegisters(instruction,
8254 temp_loc,
8255 obj_loc,
8256 class_offset,
8257 maybe_temp2_loc,
8258 kWithoutReadBarrier);
8259
Artem Serovcfbe9132016-10-14 15:58:56 +01008260 // Do an exact check.
8261 __ Cmp(temp, cls);
Anton Kirilov6f644202017-02-27 18:29:45 +00008262 __ B(eq, final_label, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01008263
8264 // Otherwise, we need to check that the object's class is a non-primitive array.
8265 // /* HeapReference<Class> */ temp = temp->component_type_
Artem Serov657022c2016-11-23 14:19:38 +00008266 GenerateReferenceLoadOneRegister(instruction,
8267 temp_loc,
8268 component_offset,
8269 maybe_temp2_loc,
8270 kWithoutReadBarrier);
Artem Serovcfbe9132016-10-14 15:58:56 +01008271 // If the component type is null, jump to the slow path to throw the exception.
xueliang.zhongf51bc622016-11-04 09:23:32 +00008272 __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
Artem Serovcfbe9132016-10-14 15:58:56 +01008273 // Otherwise,the object is indeed an array, jump to label `check_non_primitive_component_type`
8274 // to further check that this component type is not a primitive type.
8275 GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, temp, temp, primitive_offset);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008276 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
xueliang.zhongf51bc622016-11-04 09:23:32 +00008277 __ CompareAndBranchIfNonZero(temp, type_check_slow_path->GetEntryLabel());
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008278 break;
8279 }
8280
8281 case TypeCheckKind::kUnresolvedCheck:
Artem Serov657022c2016-11-23 14:19:38 +00008282 // We always go into the type check slow path for the unresolved check case.
Artem Serovcfbe9132016-10-14 15:58:56 +01008283 // We cannot directly call the CheckCast runtime entry point
8284 // without resorting to a type checking slow path here (i.e. by
8285 // calling InvokeRuntime directly), as it would require to
8286 // assign fixed registers for the inputs of this HInstanceOf
8287 // instruction (following the runtime calling convention), which
8288 // might be cluttered by the potential first read barrier
8289 // emission at the beginning of this method.
Artem Serov657022c2016-11-23 14:19:38 +00008290
Artem Serovcfbe9132016-10-14 15:58:56 +01008291 __ B(type_check_slow_path->GetEntryLabel());
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008292 break;
Artem Serov657022c2016-11-23 14:19:38 +00008293
8294 case TypeCheckKind::kInterfaceCheck: {
8295 // Avoid read barriers to improve performance of the fast path. We can not get false
8296 // positives by doing this.
8297 // /* HeapReference<Class> */ temp = obj->klass_
8298 GenerateReferenceLoadTwoRegisters(instruction,
8299 temp_loc,
8300 obj_loc,
8301 class_offset,
8302 maybe_temp2_loc,
8303 kWithoutReadBarrier);
8304
8305 // /* HeapReference<Class> */ temp = temp->iftable_
8306 GenerateReferenceLoadTwoRegisters(instruction,
8307 temp_loc,
8308 temp_loc,
8309 iftable_offset,
8310 maybe_temp2_loc,
8311 kWithoutReadBarrier);
8312 // Iftable is never null.
8313 __ Ldr(RegisterFrom(maybe_temp2_loc), MemOperand(temp, array_length_offset));
8314 // Loop through the iftable and check if any class matches.
8315 vixl32::Label start_loop;
8316 __ Bind(&start_loop);
8317 __ CompareAndBranchIfZero(RegisterFrom(maybe_temp2_loc),
8318 type_check_slow_path->GetEntryLabel());
8319 __ Ldr(RegisterFrom(maybe_temp3_loc), MemOperand(temp, object_array_data_offset));
8320 GetAssembler()->MaybeUnpoisonHeapReference(RegisterFrom(maybe_temp3_loc));
8321 // Go to next interface.
8322 __ Add(temp, temp, Operand::From(2 * kHeapReferenceSize));
8323 __ Sub(RegisterFrom(maybe_temp2_loc), RegisterFrom(maybe_temp2_loc), 2);
8324 // Compare the classes and continue the loop if they do not match.
8325 __ Cmp(cls, RegisterFrom(maybe_temp3_loc));
Artem Serov517d9f62016-12-12 15:51:15 +00008326 __ B(ne, &start_loop, /* far_target */ false);
Artem Serov657022c2016-11-23 14:19:38 +00008327 break;
8328 }
Vladimir Marko175e7862018-03-27 09:03:13 +00008329
8330 case TypeCheckKind::kBitstringCheck: {
8331 // /* HeapReference<Class> */ temp = obj->klass_
8332 GenerateReferenceLoadTwoRegisters(instruction,
8333 temp_loc,
8334 obj_loc,
8335 class_offset,
8336 maybe_temp2_loc,
8337 kWithoutReadBarrier);
8338
8339 GenerateBitstringTypeCheckCompare(instruction, temp, SetFlags);
8340 __ B(ne, type_check_slow_path->GetEntryLabel());
8341 break;
8342 }
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008343 }
Anton Kirilov6f644202017-02-27 18:29:45 +00008344 if (done.IsReferenced()) {
8345 __ Bind(&done);
8346 }
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008347
8348 __ Bind(type_check_slow_path->GetExitLabel());
8349}
8350
Artem Serov551b28f2016-10-18 19:11:30 +01008351void LocationsBuilderARMVIXL::VisitMonitorOperation(HMonitorOperation* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01008352 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
8353 instruction, LocationSummary::kCallOnMainOnly);
Artem Serov551b28f2016-10-18 19:11:30 +01008354 InvokeRuntimeCallingConventionARMVIXL calling_convention;
8355 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
8356}
8357
8358void InstructionCodeGeneratorARMVIXL::VisitMonitorOperation(HMonitorOperation* instruction) {
8359 codegen_->InvokeRuntime(instruction->IsEnter() ? kQuickLockObject : kQuickUnlockObject,
8360 instruction,
8361 instruction->GetDexPc());
8362 if (instruction->IsEnter()) {
8363 CheckEntrypointTypes<kQuickLockObject, void, mirror::Object*>();
8364 } else {
8365 CheckEntrypointTypes<kQuickUnlockObject, void, mirror::Object*>();
8366 }
Roland Levillain5daa4952017-07-03 17:23:56 +01008367 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 17);
Artem Serov551b28f2016-10-18 19:11:30 +01008368}
8369
Artem Serov02109dd2016-09-23 17:17:54 +01008370void LocationsBuilderARMVIXL::VisitAnd(HAnd* instruction) {
8371 HandleBitwiseOperation(instruction, AND);
8372}
8373
8374void LocationsBuilderARMVIXL::VisitOr(HOr* instruction) {
8375 HandleBitwiseOperation(instruction, ORR);
8376}
8377
8378void LocationsBuilderARMVIXL::VisitXor(HXor* instruction) {
8379 HandleBitwiseOperation(instruction, EOR);
8380}
8381
8382void LocationsBuilderARMVIXL::HandleBitwiseOperation(HBinaryOperation* instruction, Opcode opcode) {
8383 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01008384 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008385 DCHECK(instruction->GetResultType() == DataType::Type::kInt32
8386 || instruction->GetResultType() == DataType::Type::kInt64);
Artem Serov02109dd2016-09-23 17:17:54 +01008387 // Note: GVN reorders commutative operations to have the constant on the right hand side.
8388 locations->SetInAt(0, Location::RequiresRegister());
8389 locations->SetInAt(1, ArmEncodableConstantOrRegister(instruction->InputAt(1), opcode));
8390 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
8391}
8392
8393void InstructionCodeGeneratorARMVIXL::VisitAnd(HAnd* instruction) {
8394 HandleBitwiseOperation(instruction);
8395}
8396
8397void InstructionCodeGeneratorARMVIXL::VisitOr(HOr* instruction) {
8398 HandleBitwiseOperation(instruction);
8399}
8400
8401void InstructionCodeGeneratorARMVIXL::VisitXor(HXor* instruction) {
8402 HandleBitwiseOperation(instruction);
8403}
8404
Artem Serov2bbc9532016-10-21 11:51:50 +01008405void LocationsBuilderARMVIXL::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) {
8406 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01008407 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008408 DCHECK(instruction->GetResultType() == DataType::Type::kInt32
8409 || instruction->GetResultType() == DataType::Type::kInt64);
Artem Serov2bbc9532016-10-21 11:51:50 +01008410
8411 locations->SetInAt(0, Location::RequiresRegister());
8412 locations->SetInAt(1, Location::RequiresRegister());
8413 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
8414}
8415
8416void InstructionCodeGeneratorARMVIXL::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) {
8417 LocationSummary* locations = instruction->GetLocations();
8418 Location first = locations->InAt(0);
8419 Location second = locations->InAt(1);
8420 Location out = locations->Out();
8421
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008422 if (instruction->GetResultType() == DataType::Type::kInt32) {
Artem Serov2bbc9532016-10-21 11:51:50 +01008423 vixl32::Register first_reg = RegisterFrom(first);
8424 vixl32::Register second_reg = RegisterFrom(second);
8425 vixl32::Register out_reg = RegisterFrom(out);
8426
8427 switch (instruction->GetOpKind()) {
8428 case HInstruction::kAnd:
8429 __ Bic(out_reg, first_reg, second_reg);
8430 break;
8431 case HInstruction::kOr:
8432 __ Orn(out_reg, first_reg, second_reg);
8433 break;
8434 // There is no EON on arm.
8435 case HInstruction::kXor:
8436 default:
8437 LOG(FATAL) << "Unexpected instruction " << instruction->DebugName();
8438 UNREACHABLE();
8439 }
8440 return;
8441
8442 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008443 DCHECK_EQ(instruction->GetResultType(), DataType::Type::kInt64);
Artem Serov2bbc9532016-10-21 11:51:50 +01008444 vixl32::Register first_low = LowRegisterFrom(first);
8445 vixl32::Register first_high = HighRegisterFrom(first);
8446 vixl32::Register second_low = LowRegisterFrom(second);
8447 vixl32::Register second_high = HighRegisterFrom(second);
8448 vixl32::Register out_low = LowRegisterFrom(out);
8449 vixl32::Register out_high = HighRegisterFrom(out);
8450
8451 switch (instruction->GetOpKind()) {
8452 case HInstruction::kAnd:
8453 __ Bic(out_low, first_low, second_low);
8454 __ Bic(out_high, first_high, second_high);
8455 break;
8456 case HInstruction::kOr:
8457 __ Orn(out_low, first_low, second_low);
8458 __ Orn(out_high, first_high, second_high);
8459 break;
8460 // There is no EON on arm.
8461 case HInstruction::kXor:
8462 default:
8463 LOG(FATAL) << "Unexpected instruction " << instruction->DebugName();
8464 UNREACHABLE();
8465 }
8466 }
8467}
8468
Anton Kirilov74234da2017-01-13 14:42:47 +00008469void LocationsBuilderARMVIXL::VisitDataProcWithShifterOp(
8470 HDataProcWithShifterOp* instruction) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008471 DCHECK(instruction->GetType() == DataType::Type::kInt32 ||
8472 instruction->GetType() == DataType::Type::kInt64);
Anton Kirilov74234da2017-01-13 14:42:47 +00008473 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01008474 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008475 const bool overlap = instruction->GetType() == DataType::Type::kInt64 &&
Anton Kirilov74234da2017-01-13 14:42:47 +00008476 HDataProcWithShifterOp::IsExtensionOp(instruction->GetOpKind());
8477
8478 locations->SetInAt(0, Location::RequiresRegister());
8479 locations->SetInAt(1, Location::RequiresRegister());
8480 locations->SetOut(Location::RequiresRegister(),
8481 overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap);
8482}
8483
8484void InstructionCodeGeneratorARMVIXL::VisitDataProcWithShifterOp(
8485 HDataProcWithShifterOp* instruction) {
8486 const LocationSummary* const locations = instruction->GetLocations();
8487 const HInstruction::InstructionKind kind = instruction->GetInstrKind();
8488 const HDataProcWithShifterOp::OpKind op_kind = instruction->GetOpKind();
8489
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008490 if (instruction->GetType() == DataType::Type::kInt32) {
Anton Kirilov420ee302017-02-21 18:10:26 +00008491 const vixl32::Register first = InputRegisterAt(instruction, 0);
8492 const vixl32::Register output = OutputRegister(instruction);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008493 const vixl32::Register second = instruction->InputAt(1)->GetType() == DataType::Type::kInt64
Anton Kirilov74234da2017-01-13 14:42:47 +00008494 ? LowRegisterFrom(locations->InAt(1))
8495 : InputRegisterAt(instruction, 1);
8496
Anton Kirilov420ee302017-02-21 18:10:26 +00008497 if (HDataProcWithShifterOp::IsExtensionOp(op_kind)) {
8498 DCHECK_EQ(kind, HInstruction::kAdd);
8499
8500 switch (op_kind) {
8501 case HDataProcWithShifterOp::kUXTB:
8502 __ Uxtab(output, first, second);
8503 break;
8504 case HDataProcWithShifterOp::kUXTH:
8505 __ Uxtah(output, first, second);
8506 break;
8507 case HDataProcWithShifterOp::kSXTB:
8508 __ Sxtab(output, first, second);
8509 break;
8510 case HDataProcWithShifterOp::kSXTH:
8511 __ Sxtah(output, first, second);
8512 break;
8513 default:
8514 LOG(FATAL) << "Unexpected operation kind: " << op_kind;
8515 UNREACHABLE();
8516 }
8517 } else {
8518 GenerateDataProcInstruction(kind,
8519 output,
8520 first,
8521 Operand(second,
8522 ShiftFromOpKind(op_kind),
8523 instruction->GetShiftAmount()),
8524 codegen_);
8525 }
Anton Kirilov74234da2017-01-13 14:42:47 +00008526 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008527 DCHECK_EQ(instruction->GetType(), DataType::Type::kInt64);
Anton Kirilov74234da2017-01-13 14:42:47 +00008528
8529 if (HDataProcWithShifterOp::IsExtensionOp(op_kind)) {
8530 const vixl32::Register second = InputRegisterAt(instruction, 1);
8531
8532 DCHECK(!LowRegisterFrom(locations->Out()).Is(second));
8533 GenerateDataProc(kind,
8534 locations->Out(),
8535 locations->InAt(0),
8536 second,
8537 Operand(second, ShiftType::ASR, 31),
8538 codegen_);
8539 } else {
8540 GenerateLongDataProc(instruction, codegen_);
8541 }
8542 }
8543}
8544
Artem Serov02109dd2016-09-23 17:17:54 +01008545// TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
8546void InstructionCodeGeneratorARMVIXL::GenerateAndConst(vixl32::Register out,
8547 vixl32::Register first,
8548 uint32_t value) {
8549 // Optimize special cases for individual halfs of `and-long` (`and` is simplified earlier).
8550 if (value == 0xffffffffu) {
8551 if (!out.Is(first)) {
8552 __ Mov(out, first);
8553 }
8554 return;
8555 }
8556 if (value == 0u) {
8557 __ Mov(out, 0);
8558 return;
8559 }
8560 if (GetAssembler()->ShifterOperandCanHold(AND, value)) {
Anton Kiriloveffd5bf2017-02-28 16:59:15 +00008561 __ And(out, first, value);
8562 } else if (GetAssembler()->ShifterOperandCanHold(BIC, ~value)) {
8563 __ Bic(out, first, ~value);
Artem Serov02109dd2016-09-23 17:17:54 +01008564 } else {
Anton Kiriloveffd5bf2017-02-28 16:59:15 +00008565 DCHECK(IsPowerOfTwo(value + 1));
8566 __ Ubfx(out, first, 0, WhichPowerOf2(value + 1));
Artem Serov02109dd2016-09-23 17:17:54 +01008567 }
8568}
8569
8570// TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
8571void InstructionCodeGeneratorARMVIXL::GenerateOrrConst(vixl32::Register out,
8572 vixl32::Register first,
8573 uint32_t value) {
8574 // Optimize special cases for individual halfs of `or-long` (`or` is simplified earlier).
8575 if (value == 0u) {
8576 if (!out.Is(first)) {
8577 __ Mov(out, first);
8578 }
8579 return;
8580 }
8581 if (value == 0xffffffffu) {
8582 __ Mvn(out, 0);
8583 return;
8584 }
8585 if (GetAssembler()->ShifterOperandCanHold(ORR, value)) {
8586 __ Orr(out, first, value);
8587 } else {
8588 DCHECK(GetAssembler()->ShifterOperandCanHold(ORN, ~value));
8589 __ Orn(out, first, ~value);
8590 }
8591}
8592
8593// TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
8594void InstructionCodeGeneratorARMVIXL::GenerateEorConst(vixl32::Register out,
8595 vixl32::Register first,
8596 uint32_t value) {
8597 // Optimize special case for individual halfs of `xor-long` (`xor` is simplified earlier).
8598 if (value == 0u) {
8599 if (!out.Is(first)) {
8600 __ Mov(out, first);
8601 }
8602 return;
8603 }
8604 __ Eor(out, first, value);
8605}
8606
Anton Kirilovdda43962016-11-21 19:55:20 +00008607void InstructionCodeGeneratorARMVIXL::GenerateAddLongConst(Location out,
8608 Location first,
8609 uint64_t value) {
8610 vixl32::Register out_low = LowRegisterFrom(out);
8611 vixl32::Register out_high = HighRegisterFrom(out);
8612 vixl32::Register first_low = LowRegisterFrom(first);
8613 vixl32::Register first_high = HighRegisterFrom(first);
8614 uint32_t value_low = Low32Bits(value);
8615 uint32_t value_high = High32Bits(value);
8616 if (value_low == 0u) {
8617 if (!out_low.Is(first_low)) {
8618 __ Mov(out_low, first_low);
8619 }
8620 __ Add(out_high, first_high, value_high);
8621 return;
8622 }
8623 __ Adds(out_low, first_low, value_low);
Vladimir Markof0a6a1d2018-01-08 14:23:56 +00008624 if (GetAssembler()->ShifterOperandCanHold(ADC, value_high)) {
Anton Kirilovdda43962016-11-21 19:55:20 +00008625 __ Adc(out_high, first_high, value_high);
Anton Kirilovdda43962016-11-21 19:55:20 +00008626 } else {
Vladimir Markof0a6a1d2018-01-08 14:23:56 +00008627 DCHECK(GetAssembler()->ShifterOperandCanHold(SBC, ~value_high));
8628 __ Sbc(out_high, first_high, ~value_high);
Anton Kirilovdda43962016-11-21 19:55:20 +00008629 }
8630}
8631
Artem Serov02109dd2016-09-23 17:17:54 +01008632void InstructionCodeGeneratorARMVIXL::HandleBitwiseOperation(HBinaryOperation* instruction) {
8633 LocationSummary* locations = instruction->GetLocations();
8634 Location first = locations->InAt(0);
8635 Location second = locations->InAt(1);
8636 Location out = locations->Out();
8637
8638 if (second.IsConstant()) {
8639 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
8640 uint32_t value_low = Low32Bits(value);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008641 if (instruction->GetResultType() == DataType::Type::kInt32) {
Artem Serov02109dd2016-09-23 17:17:54 +01008642 vixl32::Register first_reg = InputRegisterAt(instruction, 0);
8643 vixl32::Register out_reg = OutputRegister(instruction);
8644 if (instruction->IsAnd()) {
8645 GenerateAndConst(out_reg, first_reg, value_low);
8646 } else if (instruction->IsOr()) {
8647 GenerateOrrConst(out_reg, first_reg, value_low);
8648 } else {
8649 DCHECK(instruction->IsXor());
8650 GenerateEorConst(out_reg, first_reg, value_low);
8651 }
8652 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008653 DCHECK_EQ(instruction->GetResultType(), DataType::Type::kInt64);
Artem Serov02109dd2016-09-23 17:17:54 +01008654 uint32_t value_high = High32Bits(value);
8655 vixl32::Register first_low = LowRegisterFrom(first);
8656 vixl32::Register first_high = HighRegisterFrom(first);
8657 vixl32::Register out_low = LowRegisterFrom(out);
8658 vixl32::Register out_high = HighRegisterFrom(out);
8659 if (instruction->IsAnd()) {
8660 GenerateAndConst(out_low, first_low, value_low);
8661 GenerateAndConst(out_high, first_high, value_high);
8662 } else if (instruction->IsOr()) {
8663 GenerateOrrConst(out_low, first_low, value_low);
8664 GenerateOrrConst(out_high, first_high, value_high);
8665 } else {
8666 DCHECK(instruction->IsXor());
8667 GenerateEorConst(out_low, first_low, value_low);
8668 GenerateEorConst(out_high, first_high, value_high);
8669 }
8670 }
8671 return;
8672 }
8673
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008674 if (instruction->GetResultType() == DataType::Type::kInt32) {
Artem Serov02109dd2016-09-23 17:17:54 +01008675 vixl32::Register first_reg = InputRegisterAt(instruction, 0);
8676 vixl32::Register second_reg = InputRegisterAt(instruction, 1);
8677 vixl32::Register out_reg = OutputRegister(instruction);
8678 if (instruction->IsAnd()) {
8679 __ And(out_reg, first_reg, second_reg);
8680 } else if (instruction->IsOr()) {
8681 __ Orr(out_reg, first_reg, second_reg);
8682 } else {
8683 DCHECK(instruction->IsXor());
8684 __ Eor(out_reg, first_reg, second_reg);
8685 }
8686 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008687 DCHECK_EQ(instruction->GetResultType(), DataType::Type::kInt64);
Artem Serov02109dd2016-09-23 17:17:54 +01008688 vixl32::Register first_low = LowRegisterFrom(first);
8689 vixl32::Register first_high = HighRegisterFrom(first);
8690 vixl32::Register second_low = LowRegisterFrom(second);
8691 vixl32::Register second_high = HighRegisterFrom(second);
8692 vixl32::Register out_low = LowRegisterFrom(out);
8693 vixl32::Register out_high = HighRegisterFrom(out);
8694 if (instruction->IsAnd()) {
8695 __ And(out_low, first_low, second_low);
8696 __ And(out_high, first_high, second_high);
8697 } else if (instruction->IsOr()) {
8698 __ Orr(out_low, first_low, second_low);
8699 __ Orr(out_high, first_high, second_high);
8700 } else {
8701 DCHECK(instruction->IsXor());
8702 __ Eor(out_low, first_low, second_low);
8703 __ Eor(out_high, first_high, second_high);
8704 }
8705 }
8706}
8707
Artem Serovcfbe9132016-10-14 15:58:56 +01008708void InstructionCodeGeneratorARMVIXL::GenerateReferenceLoadOneRegister(
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008709 HInstruction* instruction,
Artem Serovcfbe9132016-10-14 15:58:56 +01008710 Location out,
8711 uint32_t offset,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008712 Location maybe_temp,
8713 ReadBarrierOption read_barrier_option) {
Artem Serovcfbe9132016-10-14 15:58:56 +01008714 vixl32::Register out_reg = RegisterFrom(out);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008715 if (read_barrier_option == kWithReadBarrier) {
8716 CHECK(kEmitCompilerReadBarrier);
8717 DCHECK(maybe_temp.IsRegister()) << maybe_temp;
8718 if (kUseBakerReadBarrier) {
8719 // Load with fast path based Baker's read barrier.
8720 // /* HeapReference<Object> */ out = *(out + offset)
8721 codegen_->GenerateFieldLoadWithBakerReadBarrier(
8722 instruction, out, out_reg, offset, maybe_temp, /* needs_null_check */ false);
8723 } else {
8724 // Load with slow path based read barrier.
8725 // Save the value of `out` into `maybe_temp` before overwriting it
8726 // in the following move operation, as we will need it for the
8727 // read barrier below.
8728 __ Mov(RegisterFrom(maybe_temp), out_reg);
8729 // /* HeapReference<Object> */ out = *(out + offset)
8730 GetAssembler()->LoadFromOffset(kLoadWord, out_reg, out_reg, offset);
8731 codegen_->GenerateReadBarrierSlow(instruction, out, out, maybe_temp, offset);
8732 }
Artem Serovcfbe9132016-10-14 15:58:56 +01008733 } else {
8734 // Plain load with no read barrier.
8735 // /* HeapReference<Object> */ out = *(out + offset)
8736 GetAssembler()->LoadFromOffset(kLoadWord, out_reg, out_reg, offset);
8737 GetAssembler()->MaybeUnpoisonHeapReference(out_reg);
8738 }
8739}
8740
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008741void InstructionCodeGeneratorARMVIXL::GenerateReferenceLoadTwoRegisters(
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008742 HInstruction* instruction,
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008743 Location out,
8744 Location obj,
8745 uint32_t offset,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008746 Location maybe_temp,
8747 ReadBarrierOption read_barrier_option) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008748 vixl32::Register out_reg = RegisterFrom(out);
8749 vixl32::Register obj_reg = RegisterFrom(obj);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008750 if (read_barrier_option == kWithReadBarrier) {
8751 CHECK(kEmitCompilerReadBarrier);
8752 if (kUseBakerReadBarrier) {
8753 DCHECK(maybe_temp.IsRegister()) << maybe_temp;
8754 // Load with fast path based Baker's read barrier.
8755 // /* HeapReference<Object> */ out = *(obj + offset)
8756 codegen_->GenerateFieldLoadWithBakerReadBarrier(
8757 instruction, out, obj_reg, offset, maybe_temp, /* needs_null_check */ false);
8758 } else {
8759 // Load with slow path based read barrier.
8760 // /* HeapReference<Object> */ out = *(obj + offset)
8761 GetAssembler()->LoadFromOffset(kLoadWord, out_reg, obj_reg, offset);
8762 codegen_->GenerateReadBarrierSlow(instruction, out, out, obj, offset);
8763 }
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008764 } else {
8765 // Plain load with no read barrier.
8766 // /* HeapReference<Object> */ out = *(obj + offset)
8767 GetAssembler()->LoadFromOffset(kLoadWord, out_reg, obj_reg, offset);
8768 GetAssembler()->MaybeUnpoisonHeapReference(out_reg);
8769 }
8770}
8771
Vladimir Markoca1e0382018-04-11 09:58:41 +00008772void CodeGeneratorARMVIXL::GenerateGcRootFieldLoad(
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008773 HInstruction* instruction,
Scott Wakelinga7812ae2016-10-17 10:03:36 +01008774 Location root,
8775 vixl32::Register obj,
8776 uint32_t offset,
Artem Serovd4cc5b22016-11-04 11:19:09 +00008777 ReadBarrierOption read_barrier_option) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01008778 vixl32::Register root_reg = RegisterFrom(root);
Artem Serovd4cc5b22016-11-04 11:19:09 +00008779 if (read_barrier_option == kWithReadBarrier) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008780 DCHECK(kEmitCompilerReadBarrier);
8781 if (kUseBakerReadBarrier) {
8782 // Fast path implementation of art::ReadBarrier::BarrierForRoot when
Roland Levillainba650a42017-03-06 13:52:32 +00008783 // Baker's read barrier are used.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008784 if (kBakerReadBarrierLinkTimeThunksEnableForGcRoots &&
8785 !Runtime::Current()->UseJitCompilation()) {
Roland Levillain6d729a72017-06-30 18:34:01 +01008786 // Query `art::Thread::Current()->GetIsGcMarking()` (stored in
8787 // the Marking Register) to decide whether we need to enter
8788 // the slow path to mark the GC root.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008789 //
8790 // We use link-time generated thunks for the slow path. That thunk
8791 // checks the reference and jumps to the entrypoint if needed.
8792 //
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008793 // lr = &return_address;
8794 // GcRoot<mirror::Object> root = *(obj+offset); // Original reference load.
Roland Levillain6d729a72017-06-30 18:34:01 +01008795 // if (mr) { // Thread::Current()->GetIsGcMarking()
8796 // goto gc_root_thunk<root_reg>(lr)
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008797 // }
8798 // return_address:
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008799
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008800 UseScratchRegisterScope temps(GetVIXLAssembler());
Vladimir Markodcd117e2018-04-19 11:54:00 +01008801 temps.Exclude(ip);
Vladimir Marko88abba22017-05-03 17:09:25 +01008802 bool narrow = CanEmitNarrowLdr(root_reg, obj, offset);
Vladimir Markoca1e0382018-04-11 09:58:41 +00008803 uint32_t custom_data = EncodeBakerReadBarrierGcRootData(root_reg.GetCode(), narrow);
8804 vixl32::Label* bne_label = NewBakerReadBarrierPatch(custom_data);
Roland Levillainba650a42017-03-06 13:52:32 +00008805
Roland Levillain6d729a72017-06-30 18:34:01 +01008806 vixl::EmissionCheckScope guard(GetVIXLAssembler(), 4 * vixl32::kMaxInstructionSizeInBytes);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008807 vixl32::Label return_address;
8808 EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
Roland Levillain6d729a72017-06-30 18:34:01 +01008809 __ cmp(mr, Operand(0));
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008810 // Currently the offset is always within range. If that changes,
8811 // we shall have to split the load the same way as for fields.
8812 DCHECK_LT(offset, kReferenceLoadMinFarOffset);
Vladimir Marko88abba22017-05-03 17:09:25 +01008813 ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
8814 __ ldr(EncodingSize(narrow ? Narrow : Wide), root_reg, MemOperand(obj, offset));
Vladimir Markoca1e0382018-04-11 09:58:41 +00008815 EmitPlaceholderBne(this, bne_label);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008816 __ Bind(&return_address);
Vladimir Marko88abba22017-05-03 17:09:25 +01008817 DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
8818 narrow ? BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_OFFSET
8819 : BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_OFFSET);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008820 } else {
Roland Levillain6d729a72017-06-30 18:34:01 +01008821 // Query `art::Thread::Current()->GetIsGcMarking()` (stored in
8822 // the Marking Register) to decide whether we need to enter
8823 // the slow path to mark the GC root.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008824 //
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008825 // GcRoot<mirror::Object> root = *(obj+offset); // Original reference load.
Roland Levillain6d729a72017-06-30 18:34:01 +01008826 // if (mr) { // Thread::Current()->GetIsGcMarking()
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008827 // // Slow path.
Roland Levillain6d729a72017-06-30 18:34:01 +01008828 // entrypoint = Thread::Current()->pReadBarrierMarkReg ## root.reg()
8829 // root = entrypoint(root); // root = ReadBarrier::Mark(root); // Entry point call.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008830 // }
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008831
Roland Levillain6d729a72017-06-30 18:34:01 +01008832 // Slow path marking the GC root `root`. The entrypoint will
8833 // be loaded by the slow path code.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008834 SlowPathCodeARMVIXL* slow_path =
Vladimir Markoca1e0382018-04-11 09:58:41 +00008835 new (GetScopedAllocator()) ReadBarrierMarkSlowPathARMVIXL(instruction, root);
8836 AddSlowPath(slow_path);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008837
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008838 // /* GcRoot<mirror::Object> */ root = *(obj + offset)
8839 GetAssembler()->LoadFromOffset(kLoadWord, root_reg, obj, offset);
8840 static_assert(
8841 sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(GcRoot<mirror::Object>),
8842 "art::mirror::CompressedReference<mirror::Object> and art::GcRoot<mirror::Object> "
8843 "have different sizes.");
8844 static_assert(sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(int32_t),
8845 "art::mirror::CompressedReference<mirror::Object> and int32_t "
8846 "have different sizes.");
8847
Roland Levillain6d729a72017-06-30 18:34:01 +01008848 __ CompareAndBranchIfNonZero(mr, slow_path->GetEntryLabel());
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008849 __ Bind(slow_path->GetExitLabel());
8850 }
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008851 } else {
8852 // GC root loaded through a slow path for read barriers other
8853 // than Baker's.
8854 // /* GcRoot<mirror::Object>* */ root = obj + offset
8855 __ Add(root_reg, obj, offset);
8856 // /* mirror::Object* */ root = root->Read()
Vladimir Markoca1e0382018-04-11 09:58:41 +00008857 GenerateReadBarrierForRootSlow(instruction, root, root);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008858 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01008859 } else {
8860 // Plain GC root load with no read barrier.
8861 // /* GcRoot<mirror::Object> */ root = *(obj + offset)
8862 GetAssembler()->LoadFromOffset(kLoadWord, root_reg, obj, offset);
8863 // Note that GC roots are not affected by heap poisoning, thus we
8864 // do not have to unpoison `root_reg` here.
8865 }
Vladimir Markoca1e0382018-04-11 09:58:41 +00008866 MaybeGenerateMarkingRegisterCheck(/* code */ 18);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01008867}
8868
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008869void CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
8870 Location ref,
8871 vixl32::Register obj,
8872 uint32_t offset,
8873 Location temp,
8874 bool needs_null_check) {
8875 DCHECK(kEmitCompilerReadBarrier);
8876 DCHECK(kUseBakerReadBarrier);
8877
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008878 if (kBakerReadBarrierLinkTimeThunksEnableForFields &&
8879 !Runtime::Current()->UseJitCompilation()) {
Roland Levillain6d729a72017-06-30 18:34:01 +01008880 // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
8881 // Marking Register) to decide whether we need to enter the slow
8882 // path to mark the reference. Then, in the slow path, check the
8883 // gray bit in the lock word of the reference's holder (`obj`) to
8884 // decide whether to mark `ref` or not.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008885 //
8886 // We use link-time generated thunks for the slow path. That thunk checks
8887 // the holder and jumps to the entrypoint if needed. If the holder is not
8888 // gray, it creates a fake dependency and returns to the LDR instruction.
8889 //
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008890 // lr = &gray_return_address;
Roland Levillain6d729a72017-06-30 18:34:01 +01008891 // if (mr) { // Thread::Current()->GetIsGcMarking()
8892 // goto field_thunk<holder_reg, base_reg>(lr)
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008893 // }
8894 // not_gray_return_address:
8895 // // Original reference load. If the offset is too large to fit
8896 // // into LDR, we use an adjusted base register here.
Vladimir Marko88abba22017-05-03 17:09:25 +01008897 // HeapReference<mirror::Object> reference = *(obj+offset);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008898 // gray_return_address:
8899
8900 DCHECK_ALIGNED(offset, sizeof(mirror::HeapReference<mirror::Object>));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008901 vixl32::Register ref_reg = RegisterFrom(ref, DataType::Type::kReference);
Vladimir Marko88abba22017-05-03 17:09:25 +01008902 bool narrow = CanEmitNarrowLdr(ref_reg, obj, offset);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008903 vixl32::Register base = obj;
8904 if (offset >= kReferenceLoadMinFarOffset) {
8905 base = RegisterFrom(temp);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008906 static_assert(IsPowerOfTwo(kReferenceLoadMinFarOffset), "Expecting a power of 2.");
8907 __ Add(base, obj, Operand(offset & ~(kReferenceLoadMinFarOffset - 1u)));
8908 offset &= (kReferenceLoadMinFarOffset - 1u);
Vladimir Marko88abba22017-05-03 17:09:25 +01008909 // Use narrow LDR only for small offsets. Generating narrow encoding LDR for the large
8910 // offsets with `(offset & (kReferenceLoadMinFarOffset - 1u)) < 32u` would most likely
8911 // increase the overall code size when taking the generated thunks into account.
8912 DCHECK(!narrow);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008913 }
8914 UseScratchRegisterScope temps(GetVIXLAssembler());
Vladimir Markodcd117e2018-04-19 11:54:00 +01008915 temps.Exclude(ip);
Vladimir Markoca1e0382018-04-11 09:58:41 +00008916 uint32_t custom_data = EncodeBakerReadBarrierFieldData(base.GetCode(), obj.GetCode(), narrow);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008917 vixl32::Label* bne_label = NewBakerReadBarrierPatch(custom_data);
8918
Roland Levillain5daa4952017-07-03 17:23:56 +01008919 {
8920 vixl::EmissionCheckScope guard(
8921 GetVIXLAssembler(),
8922 (kPoisonHeapReferences ? 5u : 4u) * vixl32::kMaxInstructionSizeInBytes);
8923 vixl32::Label return_address;
8924 EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
8925 __ cmp(mr, Operand(0));
8926 EmitPlaceholderBne(this, bne_label);
8927 ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
8928 __ ldr(EncodingSize(narrow ? Narrow : Wide), ref_reg, MemOperand(base, offset));
8929 if (needs_null_check) {
8930 MaybeRecordImplicitNullCheck(instruction);
Vladimir Marko88abba22017-05-03 17:09:25 +01008931 }
Roland Levillain5daa4952017-07-03 17:23:56 +01008932 // Note: We need a specific width for the unpoisoning NEG.
8933 if (kPoisonHeapReferences) {
8934 if (narrow) {
8935 // The only 16-bit encoding is T1 which sets flags outside IT block (i.e. RSBS, not RSB).
8936 __ rsbs(EncodingSize(Narrow), ref_reg, ref_reg, Operand(0));
8937 } else {
8938 __ rsb(EncodingSize(Wide), ref_reg, ref_reg, Operand(0));
8939 }
8940 }
8941 __ Bind(&return_address);
8942 DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
8943 narrow ? BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET
8944 : BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008945 }
Roland Levillain5daa4952017-07-03 17:23:56 +01008946 MaybeGenerateMarkingRegisterCheck(/* code */ 19, /* temp_loc */ LocationFrom(ip));
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008947 return;
8948 }
8949
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008950 // /* HeapReference<Object> */ ref = *(obj + offset)
8951 Location no_index = Location::NoLocation();
8952 ScaleFactor no_scale_factor = TIMES_1;
8953 GenerateReferenceLoadWithBakerReadBarrier(
8954 instruction, ref, obj, offset, no_index, no_scale_factor, temp, needs_null_check);
Roland Levillain6070e882016-11-03 17:51:58 +00008955}
8956
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008957void CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction,
8958 Location ref,
8959 vixl32::Register obj,
8960 uint32_t data_offset,
8961 Location index,
8962 Location temp,
8963 bool needs_null_check) {
8964 DCHECK(kEmitCompilerReadBarrier);
8965 DCHECK(kUseBakerReadBarrier);
8966
8967 static_assert(
8968 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
8969 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008970 ScaleFactor scale_factor = TIMES_4;
8971
8972 if (kBakerReadBarrierLinkTimeThunksEnableForArrays &&
8973 !Runtime::Current()->UseJitCompilation()) {
Roland Levillain6d729a72017-06-30 18:34:01 +01008974 // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
8975 // Marking Register) to decide whether we need to enter the slow
8976 // path to mark the reference. Then, in the slow path, check the
8977 // gray bit in the lock word of the reference's holder (`obj`) to
8978 // decide whether to mark `ref` or not.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008979 //
8980 // We use link-time generated thunks for the slow path. That thunk checks
8981 // the holder and jumps to the entrypoint if needed. If the holder is not
8982 // gray, it creates a fake dependency and returns to the LDR instruction.
8983 //
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008984 // lr = &gray_return_address;
Roland Levillain6d729a72017-06-30 18:34:01 +01008985 // if (mr) { // Thread::Current()->GetIsGcMarking()
8986 // goto array_thunk<base_reg>(lr)
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008987 // }
8988 // not_gray_return_address:
8989 // // Original reference load. If the offset is too large to fit
8990 // // into LDR, we use an adjusted base register here.
Vladimir Marko88abba22017-05-03 17:09:25 +01008991 // HeapReference<mirror::Object> reference = data[index];
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008992 // gray_return_address:
8993
8994 DCHECK(index.IsValid());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008995 vixl32::Register index_reg = RegisterFrom(index, DataType::Type::kInt32);
8996 vixl32::Register ref_reg = RegisterFrom(ref, DataType::Type::kReference);
8997 vixl32::Register data_reg = RegisterFrom(temp, DataType::Type::kInt32); // Raw pointer.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008998
8999 UseScratchRegisterScope temps(GetVIXLAssembler());
Vladimir Markodcd117e2018-04-19 11:54:00 +01009000 temps.Exclude(ip);
Vladimir Markoca1e0382018-04-11 09:58:41 +00009001 uint32_t custom_data = EncodeBakerReadBarrierArrayData(data_reg.GetCode());
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009002 vixl32::Label* bne_label = NewBakerReadBarrierPatch(custom_data);
9003
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009004 __ Add(data_reg, obj, Operand(data_offset));
Roland Levillain5daa4952017-07-03 17:23:56 +01009005 {
9006 vixl::EmissionCheckScope guard(
9007 GetVIXLAssembler(),
9008 (kPoisonHeapReferences ? 5u : 4u) * vixl32::kMaxInstructionSizeInBytes);
9009 vixl32::Label return_address;
9010 EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
9011 __ cmp(mr, Operand(0));
9012 EmitPlaceholderBne(this, bne_label);
9013 ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
9014 __ ldr(ref_reg, MemOperand(data_reg, index_reg, vixl32::LSL, scale_factor));
9015 DCHECK(!needs_null_check); // The thunk cannot handle the null check.
9016 // Note: We need a Wide NEG for the unpoisoning.
9017 if (kPoisonHeapReferences) {
9018 __ rsb(EncodingSize(Wide), ref_reg, ref_reg, Operand(0));
9019 }
9020 __ Bind(&return_address);
9021 DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
9022 BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009023 }
Roland Levillain5daa4952017-07-03 17:23:56 +01009024 MaybeGenerateMarkingRegisterCheck(/* code */ 20, /* temp_loc */ LocationFrom(ip));
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009025 return;
9026 }
9027
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009028 // /* HeapReference<Object> */ ref =
9029 // *(obj + data_offset + index * sizeof(HeapReference<Object>))
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009030 GenerateReferenceLoadWithBakerReadBarrier(
9031 instruction, ref, obj, data_offset, index, scale_factor, temp, needs_null_check);
Roland Levillain6070e882016-11-03 17:51:58 +00009032}
9033
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009034void CodeGeneratorARMVIXL::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
9035 Location ref,
9036 vixl32::Register obj,
9037 uint32_t offset,
9038 Location index,
9039 ScaleFactor scale_factor,
9040 Location temp,
Roland Levillainff487002017-03-07 16:50:01 +00009041 bool needs_null_check) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009042 DCHECK(kEmitCompilerReadBarrier);
9043 DCHECK(kUseBakerReadBarrier);
9044
Roland Levillain6d729a72017-06-30 18:34:01 +01009045 // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
9046 // Marking Register) to decide whether we need to enter the slow
9047 // path to mark the reference. Then, in the slow path, check the
9048 // gray bit in the lock word of the reference's holder (`obj`) to
9049 // decide whether to mark `ref` or not.
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009050 //
Roland Levillain6d729a72017-06-30 18:34:01 +01009051 // if (mr) { // Thread::Current()->GetIsGcMarking()
Roland Levillainff487002017-03-07 16:50:01 +00009052 // // Slow path.
9053 // uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
9054 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
9055 // HeapReference<mirror::Object> ref = *src; // Original reference load.
9056 // bool is_gray = (rb_state == ReadBarrier::GrayState());
9057 // if (is_gray) {
Roland Levillain6d729a72017-06-30 18:34:01 +01009058 // entrypoint = Thread::Current()->pReadBarrierMarkReg ## root.reg()
9059 // ref = entrypoint(ref); // ref = ReadBarrier::Mark(ref); // Runtime entry point call.
Roland Levillainff487002017-03-07 16:50:01 +00009060 // }
9061 // } else {
9062 // HeapReference<mirror::Object> ref = *src; // Original reference load.
9063 // }
9064
9065 vixl32::Register temp_reg = RegisterFrom(temp);
9066
9067 // Slow path marking the object `ref` when the GC is marking. The
Roland Levillain6d729a72017-06-30 18:34:01 +01009068 // entrypoint will be loaded by the slow path code.
Roland Levillainff487002017-03-07 16:50:01 +00009069 SlowPathCodeARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01009070 new (GetScopedAllocator()) LoadReferenceWithBakerReadBarrierSlowPathARMVIXL(
Roland Levillain6d729a72017-06-30 18:34:01 +01009071 instruction, ref, obj, offset, index, scale_factor, needs_null_check, temp_reg);
Roland Levillainff487002017-03-07 16:50:01 +00009072 AddSlowPath(slow_path);
9073
Roland Levillain6d729a72017-06-30 18:34:01 +01009074 __ CompareAndBranchIfNonZero(mr, slow_path->GetEntryLabel());
Roland Levillainff487002017-03-07 16:50:01 +00009075 // Fast path: the GC is not marking: just load the reference.
9076 GenerateRawReferenceLoad(instruction, ref, obj, offset, index, scale_factor, needs_null_check);
9077 __ Bind(slow_path->GetExitLabel());
Roland Levillain5daa4952017-07-03 17:23:56 +01009078 MaybeGenerateMarkingRegisterCheck(/* code */ 21);
Roland Levillainff487002017-03-07 16:50:01 +00009079}
9080
9081void CodeGeneratorARMVIXL::UpdateReferenceFieldWithBakerReadBarrier(HInstruction* instruction,
9082 Location ref,
9083 vixl32::Register obj,
9084 Location field_offset,
9085 Location temp,
9086 bool needs_null_check,
9087 vixl32::Register temp2) {
9088 DCHECK(kEmitCompilerReadBarrier);
9089 DCHECK(kUseBakerReadBarrier);
9090
Roland Levillain6d729a72017-06-30 18:34:01 +01009091 // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
9092 // Marking Register) to decide whether we need to enter the slow
9093 // path to update the reference field within `obj`. Then, in the
9094 // slow path, check the gray bit in the lock word of the reference's
9095 // holder (`obj`) to decide whether to mark `ref` and update the
9096 // field or not.
Roland Levillainff487002017-03-07 16:50:01 +00009097 //
Roland Levillain6d729a72017-06-30 18:34:01 +01009098 // if (mr) { // Thread::Current()->GetIsGcMarking()
Roland Levillainba650a42017-03-06 13:52:32 +00009099 // // Slow path.
Roland Levillain54f869e2017-03-06 13:54:11 +00009100 // uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
9101 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
Roland Levillainff487002017-03-07 16:50:01 +00009102 // HeapReference<mirror::Object> ref = *(obj + field_offset); // Reference load.
Roland Levillain54f869e2017-03-06 13:54:11 +00009103 // bool is_gray = (rb_state == ReadBarrier::GrayState());
9104 // if (is_gray) {
Roland Levillainff487002017-03-07 16:50:01 +00009105 // old_ref = ref;
Roland Levillain6d729a72017-06-30 18:34:01 +01009106 // entrypoint = Thread::Current()->pReadBarrierMarkReg ## root.reg()
9107 // ref = entrypoint(ref); // ref = ReadBarrier::Mark(ref); // Runtime entry point call.
Roland Levillainff487002017-03-07 16:50:01 +00009108 // compareAndSwapObject(obj, field_offset, old_ref, ref);
Roland Levillain54f869e2017-03-06 13:54:11 +00009109 // }
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009110 // }
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009111
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009112 vixl32::Register temp_reg = RegisterFrom(temp);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009113
Roland Levillainff487002017-03-07 16:50:01 +00009114 // Slow path updating the object reference at address `obj + field_offset`
Roland Levillain6d729a72017-06-30 18:34:01 +01009115 // when the GC is marking. The entrypoint will be loaded by the slow path code.
Vladimir Marko174b2e22017-10-12 13:34:49 +01009116 SlowPathCodeARMVIXL* slow_path =
9117 new (GetScopedAllocator()) LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARMVIXL(
Roland Levillainff487002017-03-07 16:50:01 +00009118 instruction,
9119 ref,
9120 obj,
9121 /* offset */ 0u,
9122 /* index */ field_offset,
9123 /* scale_factor */ ScaleFactor::TIMES_1,
9124 needs_null_check,
9125 temp_reg,
Roland Levillain6d729a72017-06-30 18:34:01 +01009126 temp2);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009127 AddSlowPath(slow_path);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009128
Roland Levillain6d729a72017-06-30 18:34:01 +01009129 __ CompareAndBranchIfNonZero(mr, slow_path->GetEntryLabel());
Roland Levillainff487002017-03-07 16:50:01 +00009130 // Fast path: the GC is not marking: nothing to do (the field is
9131 // up-to-date, and we don't need to load the reference).
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009132 __ Bind(slow_path->GetExitLabel());
Roland Levillain5daa4952017-07-03 17:23:56 +01009133 MaybeGenerateMarkingRegisterCheck(/* code */ 22);
Roland Levillain844e6532016-11-03 16:09:47 +00009134}
Scott Wakelingfe885462016-09-22 10:24:38 +01009135
Roland Levillainba650a42017-03-06 13:52:32 +00009136void CodeGeneratorARMVIXL::GenerateRawReferenceLoad(HInstruction* instruction,
9137 Location ref,
Vladimir Markoca1e0382018-04-11 09:58:41 +00009138 vixl32::Register obj,
Roland Levillainba650a42017-03-06 13:52:32 +00009139 uint32_t offset,
9140 Location index,
9141 ScaleFactor scale_factor,
9142 bool needs_null_check) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01009143 DataType::Type type = DataType::Type::kReference;
Roland Levillainba650a42017-03-06 13:52:32 +00009144 vixl32::Register ref_reg = RegisterFrom(ref, type);
9145
9146 // If needed, vixl::EmissionCheckScope guards are used to ensure
9147 // that no pools are emitted between the load (macro) instruction
9148 // and MaybeRecordImplicitNullCheck.
9149
Scott Wakelingfe885462016-09-22 10:24:38 +01009150 if (index.IsValid()) {
9151 // Load types involving an "index": ArrayGet,
9152 // UnsafeGetObject/UnsafeGetObjectVolatile and UnsafeCASObject
9153 // intrinsics.
Roland Levillainba650a42017-03-06 13:52:32 +00009154 // /* HeapReference<mirror::Object> */ ref = *(obj + offset + (index << scale_factor))
Scott Wakelingfe885462016-09-22 10:24:38 +01009155 if (index.IsConstant()) {
9156 size_t computed_offset =
9157 (Int32ConstantFrom(index) << scale_factor) + offset;
Roland Levillainba650a42017-03-06 13:52:32 +00009158 vixl::EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
Scott Wakelingfe885462016-09-22 10:24:38 +01009159 GetAssembler()->LoadFromOffset(kLoadWord, ref_reg, obj, computed_offset);
Roland Levillainba650a42017-03-06 13:52:32 +00009160 if (needs_null_check) {
9161 MaybeRecordImplicitNullCheck(instruction);
9162 }
Scott Wakelingfe885462016-09-22 10:24:38 +01009163 } else {
9164 // Handle the special case of the
9165 // UnsafeGetObject/UnsafeGetObjectVolatile and UnsafeCASObject
9166 // intrinsics, which use a register pair as index ("long
9167 // offset"), of which only the low part contains data.
9168 vixl32::Register index_reg = index.IsRegisterPair()
9169 ? LowRegisterFrom(index)
9170 : RegisterFrom(index);
9171 UseScratchRegisterScope temps(GetVIXLAssembler());
Roland Levillainba650a42017-03-06 13:52:32 +00009172 vixl32::Register temp = temps.Acquire();
9173 __ Add(temp, obj, Operand(index_reg, ShiftType::LSL, scale_factor));
9174 {
9175 vixl::EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
9176 GetAssembler()->LoadFromOffset(kLoadWord, ref_reg, temp, offset);
9177 if (needs_null_check) {
9178 MaybeRecordImplicitNullCheck(instruction);
9179 }
9180 }
Scott Wakelingfe885462016-09-22 10:24:38 +01009181 }
9182 } else {
Roland Levillainba650a42017-03-06 13:52:32 +00009183 // /* HeapReference<mirror::Object> */ ref = *(obj + offset)
9184 vixl::EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
Scott Wakelingfe885462016-09-22 10:24:38 +01009185 GetAssembler()->LoadFromOffset(kLoadWord, ref_reg, obj, offset);
Roland Levillainba650a42017-03-06 13:52:32 +00009186 if (needs_null_check) {
9187 MaybeRecordImplicitNullCheck(instruction);
9188 }
Scott Wakelingfe885462016-09-22 10:24:38 +01009189 }
9190
Roland Levillain844e6532016-11-03 16:09:47 +00009191 // Object* ref = ref_addr->AsMirrorPtr()
9192 GetAssembler()->MaybeUnpoisonHeapReference(ref_reg);
Roland Levillain844e6532016-11-03 16:09:47 +00009193}
9194
Roland Levillain5daa4952017-07-03 17:23:56 +01009195void CodeGeneratorARMVIXL::MaybeGenerateMarkingRegisterCheck(int code, Location temp_loc) {
9196 // The following condition is a compile-time one, so it does not have a run-time cost.
9197 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier && kIsDebugBuild) {
9198 // The following condition is a run-time one; it is executed after the
9199 // previous compile-time test, to avoid penalizing non-debug builds.
9200 if (GetCompilerOptions().EmitRunTimeChecksInDebugMode()) {
9201 UseScratchRegisterScope temps(GetVIXLAssembler());
9202 vixl32::Register temp = temp_loc.IsValid() ? RegisterFrom(temp_loc) : temps.Acquire();
9203 GetAssembler()->GenerateMarkingRegisterCheck(temp,
9204 kMarkingRegisterCheckBreakCodeBaseCode + code);
9205 }
9206 }
9207}
9208
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009209void CodeGeneratorARMVIXL::GenerateReadBarrierSlow(HInstruction* instruction,
9210 Location out,
9211 Location ref,
9212 Location obj,
9213 uint32_t offset,
9214 Location index) {
9215 DCHECK(kEmitCompilerReadBarrier);
9216
9217 // Insert a slow path based read barrier *after* the reference load.
9218 //
9219 // If heap poisoning is enabled, the unpoisoning of the loaded
9220 // reference will be carried out by the runtime within the slow
9221 // path.
9222 //
9223 // Note that `ref` currently does not get unpoisoned (when heap
9224 // poisoning is enabled), which is alright as the `ref` argument is
9225 // not used by the artReadBarrierSlow entry point.
9226 //
9227 // TODO: Unpoison `ref` when it is used by artReadBarrierSlow.
Vladimir Marko174b2e22017-10-12 13:34:49 +01009228 SlowPathCodeARMVIXL* slow_path = new (GetScopedAllocator())
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009229 ReadBarrierForHeapReferenceSlowPathARMVIXL(instruction, out, ref, obj, offset, index);
9230 AddSlowPath(slow_path);
9231
9232 __ B(slow_path->GetEntryLabel());
9233 __ Bind(slow_path->GetExitLabel());
9234}
9235
9236void CodeGeneratorARMVIXL::MaybeGenerateReadBarrierSlow(HInstruction* instruction,
Artem Serov02d37832016-10-25 15:25:33 +01009237 Location out,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009238 Location ref,
9239 Location obj,
9240 uint32_t offset,
9241 Location index) {
Artem Serov02d37832016-10-25 15:25:33 +01009242 if (kEmitCompilerReadBarrier) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009243 // Baker's read barriers shall be handled by the fast path
Roland Levillain9983e302017-07-14 14:34:22 +01009244 // (CodeGeneratorARMVIXL::GenerateReferenceLoadWithBakerReadBarrier).
Artem Serov02d37832016-10-25 15:25:33 +01009245 DCHECK(!kUseBakerReadBarrier);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009246 // If heap poisoning is enabled, unpoisoning will be taken care of
9247 // by the runtime within the slow path.
9248 GenerateReadBarrierSlow(instruction, out, ref, obj, offset, index);
Artem Serov02d37832016-10-25 15:25:33 +01009249 } else if (kPoisonHeapReferences) {
9250 GetAssembler()->UnpoisonHeapReference(RegisterFrom(out));
9251 }
9252}
9253
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009254void CodeGeneratorARMVIXL::GenerateReadBarrierForRootSlow(HInstruction* instruction,
9255 Location out,
9256 Location root) {
9257 DCHECK(kEmitCompilerReadBarrier);
9258
9259 // Insert a slow path based read barrier *after* the GC root load.
9260 //
9261 // Note that GC roots are not affected by heap poisoning, so we do
9262 // not need to do anything special for this here.
9263 SlowPathCodeARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01009264 new (GetScopedAllocator()) ReadBarrierForRootSlowPathARMVIXL(instruction, out, root);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009265 AddSlowPath(slow_path);
9266
9267 __ B(slow_path->GetEntryLabel());
9268 __ Bind(slow_path->GetExitLabel());
9269}
9270
Artem Serov02d37832016-10-25 15:25:33 +01009271// Check if the desired_dispatch_info is supported. If it is, return it,
9272// otherwise return a fall-back info that should be used instead.
9273HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARMVIXL::GetSupportedInvokeStaticOrDirectDispatch(
Artem Serovd4cc5b22016-11-04 11:19:09 +00009274 const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
Nicolas Geoffrayc1a42cf2016-12-18 15:52:36 +00009275 HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
Nicolas Geoffraye807ff72017-01-23 09:03:12 +00009276 return desired_dispatch_info;
Artem Serov02d37832016-10-25 15:25:33 +01009277}
9278
Scott Wakelingfe885462016-09-22 10:24:38 +01009279vixl32::Register CodeGeneratorARMVIXL::GetInvokeStaticOrDirectExtraParameter(
9280 HInvokeStaticOrDirect* invoke, vixl32::Register temp) {
9281 DCHECK_EQ(invoke->InputCount(), invoke->GetNumberOfArguments() + 1u);
9282 Location location = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
9283 if (!invoke->GetLocations()->Intrinsified()) {
9284 return RegisterFrom(location);
9285 }
9286 // For intrinsics we allow any location, so it may be on the stack.
9287 if (!location.IsRegister()) {
9288 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, location.GetStackIndex());
9289 return temp;
9290 }
9291 // For register locations, check if the register was saved. If so, get it from the stack.
9292 // Note: There is a chance that the register was saved but not overwritten, so we could
9293 // save one load. However, since this is just an intrinsic slow path we prefer this
9294 // simple and more robust approach rather that trying to determine if that's the case.
9295 SlowPathCode* slow_path = GetCurrentSlowPath();
Scott Wakelingd5cd4972017-02-03 11:38:35 +00009296 if (slow_path != nullptr && slow_path->IsCoreRegisterSaved(RegisterFrom(location).GetCode())) {
Scott Wakelingfe885462016-09-22 10:24:38 +01009297 int stack_offset = slow_path->GetStackOffsetOfCoreRegister(RegisterFrom(location).GetCode());
9298 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, stack_offset);
9299 return temp;
9300 }
9301 return RegisterFrom(location);
9302}
9303
Vladimir Markod254f5c2017-06-02 15:18:36 +00009304void CodeGeneratorARMVIXL::GenerateStaticOrDirectCall(
Vladimir Markoe7197bf2017-06-02 17:00:23 +01009305 HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00009306 Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp.
Scott Wakelingfe885462016-09-22 10:24:38 +01009307 switch (invoke->GetMethodLoadKind()) {
9308 case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: {
9309 uint32_t offset =
9310 GetThreadOffset<kArmPointerSize>(invoke->GetStringInitEntryPoint()).Int32Value();
9311 // temp = thread->string_init_entrypoint
Artem Serovd4cc5b22016-11-04 11:19:09 +00009312 GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(temp), tr, offset);
9313 break;
9314 }
9315 case HInvokeStaticOrDirect::MethodLoadKind::kRecursive:
9316 callee_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
9317 break;
Vladimir Marko65979462017-05-19 17:25:12 +01009318 case HInvokeStaticOrDirect::MethodLoadKind::kBootImageLinkTimePcRelative: {
9319 DCHECK(GetCompilerOptions().IsBootImage());
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009320 PcRelativePatchInfo* labels = NewBootImageMethodPatch(invoke->GetTargetMethod());
Vladimir Marko65979462017-05-19 17:25:12 +01009321 vixl32::Register temp_reg = RegisterFrom(temp);
9322 EmitMovwMovtPlaceholder(labels, temp_reg);
9323 break;
9324 }
Artem Serovd4cc5b22016-11-04 11:19:09 +00009325 case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress:
9326 __ Mov(RegisterFrom(temp), Operand::From(invoke->GetMethodAddress()));
9327 break;
Vladimir Markob066d432018-01-03 13:14:37 +00009328 case HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo: {
Vladimir Markoe47f60c2018-02-21 13:43:28 +00009329 uint32_t boot_image_offset = GetBootImageOffset(invoke);
Vladimir Markob066d432018-01-03 13:14:37 +00009330 PcRelativePatchInfo* labels = NewBootImageRelRoPatch(boot_image_offset);
9331 vixl32::Register temp_reg = RegisterFrom(temp);
9332 EmitMovwMovtPlaceholder(labels, temp_reg);
9333 GetAssembler()->LoadFromOffset(kLoadWord, temp_reg, temp_reg, /* offset*/ 0);
9334 break;
9335 }
Vladimir Marko0eb882b2017-05-15 13:39:18 +01009336 case HInvokeStaticOrDirect::MethodLoadKind::kBssEntry: {
9337 PcRelativePatchInfo* labels = NewMethodBssEntryPatch(
9338 MethodReference(&GetGraph()->GetDexFile(), invoke->GetDexMethodIndex()));
9339 vixl32::Register temp_reg = RegisterFrom(temp);
9340 EmitMovwMovtPlaceholder(labels, temp_reg);
9341 GetAssembler()->LoadFromOffset(kLoadWord, temp_reg, temp_reg, /* offset*/ 0);
Scott Wakelingfe885462016-09-22 10:24:38 +01009342 break;
9343 }
Vladimir Markoe7197bf2017-06-02 17:00:23 +01009344 case HInvokeStaticOrDirect::MethodLoadKind::kRuntimeCall: {
9345 GenerateInvokeStaticOrDirectRuntimeCall(invoke, temp, slow_path);
9346 return; // No code pointer retrieval; the runtime performs the call directly.
Scott Wakelingfe885462016-09-22 10:24:38 +01009347 }
Scott Wakelingfe885462016-09-22 10:24:38 +01009348 }
9349
Artem Serovd4cc5b22016-11-04 11:19:09 +00009350 switch (invoke->GetCodePtrLocation()) {
9351 case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf:
Vladimir Markoe7197bf2017-06-02 17:00:23 +01009352 {
9353 // Use a scope to help guarantee that `RecordPcInfo()` records the correct pc.
9354 ExactAssemblyScope aas(GetVIXLAssembler(),
9355 vixl32::k32BitT32InstructionSizeInBytes,
9356 CodeBufferCheckScope::kMaximumSize);
9357 __ bl(GetFrameEntryLabel());
9358 RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
9359 }
Artem Serovd4cc5b22016-11-04 11:19:09 +00009360 break;
Artem Serovd4cc5b22016-11-04 11:19:09 +00009361 case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod:
9362 // LR = callee_method->entry_point_from_quick_compiled_code_
9363 GetAssembler()->LoadFromOffset(
9364 kLoadWord,
9365 lr,
9366 RegisterFrom(callee_method),
9367 ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value());
Alexandre Rames374ddf32016-11-04 10:40:49 +00009368 {
Vladimir Markoe7197bf2017-06-02 17:00:23 +01009369 // Use a scope to help guarantee that `RecordPcInfo()` records the correct pc.
Alexandre Rames374ddf32016-11-04 10:40:49 +00009370 // 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 +00009371 ExactAssemblyScope aas(GetVIXLAssembler(),
9372 vixl32::k16BitT32InstructionSizeInBytes,
9373 CodeBufferCheckScope::kExactSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00009374 // LR()
9375 __ blx(lr);
Vladimir Markoe7197bf2017-06-02 17:00:23 +01009376 RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
Alexandre Rames374ddf32016-11-04 10:40:49 +00009377 }
Artem Serovd4cc5b22016-11-04 11:19:09 +00009378 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01009379 }
9380
Scott Wakelingfe885462016-09-22 10:24:38 +01009381 DCHECK(!IsLeafMethod());
9382}
9383
Vladimir Markoe7197bf2017-06-02 17:00:23 +01009384void CodeGeneratorARMVIXL::GenerateVirtualCall(
9385 HInvokeVirtual* invoke, Location temp_location, SlowPathCode* slow_path) {
Scott Wakelingfe885462016-09-22 10:24:38 +01009386 vixl32::Register temp = RegisterFrom(temp_location);
9387 uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
9388 invoke->GetVTableIndex(), kArmPointerSize).Uint32Value();
9389
9390 // Use the calling convention instead of the location of the receiver, as
9391 // intrinsics may have put the receiver in a different register. In the intrinsics
9392 // slow path, the arguments have been moved to the right place, so here we are
9393 // guaranteed that the receiver is the first register of the calling convention.
9394 InvokeDexCallingConventionARMVIXL calling_convention;
9395 vixl32::Register receiver = calling_convention.GetRegisterAt(0);
9396 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
Alexandre Rames374ddf32016-11-04 10:40:49 +00009397 {
9398 // Make sure the pc is recorded immediately after the `ldr` instruction.
Artem Serov0fb37192016-12-06 18:13:40 +00009399 ExactAssemblyScope aas(GetVIXLAssembler(),
9400 vixl32::kMaxInstructionSizeInBytes,
9401 CodeBufferCheckScope::kMaximumSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00009402 // /* HeapReference<Class> */ temp = receiver->klass_
9403 __ ldr(temp, MemOperand(receiver, class_offset));
9404 MaybeRecordImplicitNullCheck(invoke);
9405 }
Scott Wakelingfe885462016-09-22 10:24:38 +01009406 // Instead of simply (possibly) unpoisoning `temp` here, we should
9407 // emit a read barrier for the previous class reference load.
9408 // However this is not required in practice, as this is an
9409 // intermediate/temporary reference and because the current
9410 // concurrent copying collector keeps the from-space memory
9411 // intact/accessible until the end of the marking phase (the
9412 // concurrent copying collector may not in the future).
9413 GetAssembler()->MaybeUnpoisonHeapReference(temp);
9414
9415 // temp = temp->GetMethodAt(method_offset);
9416 uint32_t entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(
9417 kArmPointerSize).Int32Value();
9418 GetAssembler()->LoadFromOffset(kLoadWord, temp, temp, method_offset);
9419 // LR = temp->GetEntryPoint();
9420 GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, entry_point);
Vladimir Markoe7197bf2017-06-02 17:00:23 +01009421 {
9422 // Use a scope to help guarantee that `RecordPcInfo()` records the correct pc.
9423 // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
9424 ExactAssemblyScope aas(GetVIXLAssembler(),
9425 vixl32::k16BitT32InstructionSizeInBytes,
9426 CodeBufferCheckScope::kExactSize);
9427 // LR();
9428 __ blx(lr);
9429 RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
9430 }
Scott Wakelingfe885462016-09-22 10:24:38 +01009431}
9432
Vladimir Markob066d432018-01-03 13:14:37 +00009433CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewBootImageRelRoPatch(
9434 uint32_t boot_image_offset) {
9435 return NewPcRelativePatch(/* dex_file */ nullptr,
9436 boot_image_offset,
9437 &boot_image_method_patches_);
9438}
9439
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009440CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewBootImageMethodPatch(
Vladimir Marko65979462017-05-19 17:25:12 +01009441 MethodReference target_method) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009442 return NewPcRelativePatch(
9443 target_method.dex_file, target_method.index, &boot_image_method_patches_);
Artem Serovd4cc5b22016-11-04 11:19:09 +00009444}
9445
Vladimir Marko0eb882b2017-05-15 13:39:18 +01009446CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewMethodBssEntryPatch(
9447 MethodReference target_method) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009448 return NewPcRelativePatch(
9449 target_method.dex_file, target_method.index, &method_bss_entry_patches_);
Vladimir Marko0eb882b2017-05-15 13:39:18 +01009450}
9451
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009452CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewBootImageTypePatch(
Artem Serovd4cc5b22016-11-04 11:19:09 +00009453 const DexFile& dex_file, dex::TypeIndex type_index) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009454 return NewPcRelativePatch(&dex_file, type_index.index_, &boot_image_type_patches_);
Artem Serovd4cc5b22016-11-04 11:19:09 +00009455}
9456
Vladimir Marko1998cd02017-01-13 13:02:58 +00009457CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewTypeBssEntryPatch(
9458 const DexFile& dex_file, dex::TypeIndex type_index) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009459 return NewPcRelativePatch(&dex_file, type_index.index_, &type_bss_entry_patches_);
Vladimir Marko1998cd02017-01-13 13:02:58 +00009460}
9461
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009462CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewBootImageStringPatch(
Vladimir Marko65979462017-05-19 17:25:12 +01009463 const DexFile& dex_file, dex::StringIndex string_index) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009464 return NewPcRelativePatch(&dex_file, string_index.index_, &boot_image_string_patches_);
Vladimir Marko65979462017-05-19 17:25:12 +01009465}
9466
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01009467CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewStringBssEntryPatch(
9468 const DexFile& dex_file, dex::StringIndex string_index) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009469 return NewPcRelativePatch(&dex_file, string_index.index_, &string_bss_entry_patches_);
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01009470}
9471
Artem Serovd4cc5b22016-11-04 11:19:09 +00009472CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewPcRelativePatch(
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009473 const DexFile* dex_file, uint32_t offset_or_index, ArenaDeque<PcRelativePatchInfo>* patches) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00009474 patches->emplace_back(dex_file, offset_or_index);
9475 return &patches->back();
9476}
9477
Vladimir Markoca1e0382018-04-11 09:58:41 +00009478vixl32::Label* CodeGeneratorARMVIXL::NewBakerReadBarrierPatch(uint32_t custom_data) {
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009479 baker_read_barrier_patches_.emplace_back(custom_data);
9480 return &baker_read_barrier_patches_.back().label;
9481}
9482
Artem Serovc5fcb442016-12-02 19:19:58 +00009483VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateBootImageAddressLiteral(uint32_t address) {
Richard Uhlerc52f3032017-03-02 13:45:45 +00009484 return DeduplicateUint32Literal(dchecked_integral_cast<uint32_t>(address), &uint32_literals_);
Artem Serovc5fcb442016-12-02 19:19:58 +00009485}
9486
Nicolas Geoffrayf0acfe72017-01-09 20:54:52 +00009487VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateJitStringLiteral(
9488 const DexFile& dex_file,
9489 dex::StringIndex string_index,
9490 Handle<mirror::String> handle) {
Vladimir Marko174b2e22017-10-12 13:34:49 +01009491 ReserveJitStringRoot(StringReference(&dex_file, string_index), handle);
Artem Serovc5fcb442016-12-02 19:19:58 +00009492 return jit_string_patches_.GetOrCreate(
9493 StringReference(&dex_file, string_index),
9494 [this]() {
9495 return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u);
9496 });
9497}
9498
9499VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateJitClassLiteral(const DexFile& dex_file,
9500 dex::TypeIndex type_index,
Nicolas Geoffray5247c082017-01-13 14:17:29 +00009501 Handle<mirror::Class> handle) {
Vladimir Marko174b2e22017-10-12 13:34:49 +01009502 ReserveJitClassRoot(TypeReference(&dex_file, type_index), handle);
Artem Serovc5fcb442016-12-02 19:19:58 +00009503 return jit_class_patches_.GetOrCreate(
9504 TypeReference(&dex_file, type_index),
9505 [this]() {
9506 return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u);
9507 });
9508}
9509
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009510template <linker::LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)>
Artem Serovd4cc5b22016-11-04 11:19:09 +00009511inline void CodeGeneratorARMVIXL::EmitPcRelativeLinkerPatches(
9512 const ArenaDeque<PcRelativePatchInfo>& infos,
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009513 ArenaVector<linker::LinkerPatch>* linker_patches) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00009514 for (const PcRelativePatchInfo& info : infos) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009515 const DexFile* dex_file = info.target_dex_file;
Artem Serovd4cc5b22016-11-04 11:19:09 +00009516 size_t offset_or_index = info.offset_or_index;
9517 DCHECK(info.add_pc_label.IsBound());
9518 uint32_t add_pc_offset = dchecked_integral_cast<uint32_t>(info.add_pc_label.GetLocation());
9519 // Add MOVW patch.
9520 DCHECK(info.movw_label.IsBound());
9521 uint32_t movw_offset = dchecked_integral_cast<uint32_t>(info.movw_label.GetLocation());
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009522 linker_patches->push_back(Factory(movw_offset, dex_file, add_pc_offset, offset_or_index));
Artem Serovd4cc5b22016-11-04 11:19:09 +00009523 // Add MOVT patch.
9524 DCHECK(info.movt_label.IsBound());
9525 uint32_t movt_offset = dchecked_integral_cast<uint32_t>(info.movt_label.GetLocation());
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009526 linker_patches->push_back(Factory(movt_offset, dex_file, add_pc_offset, offset_or_index));
Artem Serovd4cc5b22016-11-04 11:19:09 +00009527 }
9528}
9529
Vladimir Markob066d432018-01-03 13:14:37 +00009530linker::LinkerPatch DataBimgRelRoPatchAdapter(size_t literal_offset,
9531 const DexFile* target_dex_file,
9532 uint32_t pc_insn_offset,
9533 uint32_t boot_image_offset) {
9534 DCHECK(target_dex_file == nullptr); // Unused for DataBimgRelRoPatch(), should be null.
9535 return linker::LinkerPatch::DataBimgRelRoPatch(literal_offset, pc_insn_offset, boot_image_offset);
9536}
9537
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009538void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* linker_patches) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00009539 DCHECK(linker_patches->empty());
9540 size_t size =
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009541 /* MOVW+MOVT for each entry */ 2u * boot_image_method_patches_.size() +
Vladimir Marko0eb882b2017-05-15 13:39:18 +01009542 /* MOVW+MOVT for each entry */ 2u * method_bss_entry_patches_.size() +
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009543 /* MOVW+MOVT for each entry */ 2u * boot_image_type_patches_.size() +
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009544 /* MOVW+MOVT for each entry */ 2u * type_bss_entry_patches_.size() +
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009545 /* MOVW+MOVT for each entry */ 2u * boot_image_string_patches_.size() +
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01009546 /* MOVW+MOVT for each entry */ 2u * string_bss_entry_patches_.size() +
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009547 baker_read_barrier_patches_.size();
Artem Serovd4cc5b22016-11-04 11:19:09 +00009548 linker_patches->reserve(size);
Vladimir Marko65979462017-05-19 17:25:12 +01009549 if (GetCompilerOptions().IsBootImage()) {
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009550 EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeMethodPatch>(
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009551 boot_image_method_patches_, linker_patches);
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009552 EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeTypePatch>(
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009553 boot_image_type_patches_, linker_patches);
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009554 EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeStringPatch>(
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009555 boot_image_string_patches_, linker_patches);
Vladimir Marko65979462017-05-19 17:25:12 +01009556 } else {
Vladimir Markob066d432018-01-03 13:14:37 +00009557 EmitPcRelativeLinkerPatches<DataBimgRelRoPatchAdapter>(
9558 boot_image_method_patches_, linker_patches);
Vladimir Markoe47f60c2018-02-21 13:43:28 +00009559 DCHECK(boot_image_type_patches_.empty());
9560 DCHECK(boot_image_string_patches_.empty());
Artem Serovd4cc5b22016-11-04 11:19:09 +00009561 }
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009562 EmitPcRelativeLinkerPatches<linker::LinkerPatch::MethodBssEntryPatch>(
9563 method_bss_entry_patches_, linker_patches);
9564 EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeBssEntryPatch>(
9565 type_bss_entry_patches_, linker_patches);
9566 EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringBssEntryPatch>(
9567 string_bss_entry_patches_, linker_patches);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009568 for (const BakerReadBarrierPatchInfo& info : baker_read_barrier_patches_) {
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009569 linker_patches->push_back(linker::LinkerPatch::BakerReadBarrierBranchPatch(
9570 info.label.GetLocation(), info.custom_data));
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009571 }
Vladimir Marko1998cd02017-01-13 13:02:58 +00009572 DCHECK_EQ(size, linker_patches->size());
Artem Serovc5fcb442016-12-02 19:19:58 +00009573}
9574
Vladimir Markoca1e0382018-04-11 09:58:41 +00009575bool CodeGeneratorARMVIXL::NeedsThunkCode(const linker::LinkerPatch& patch) const {
9576 return patch.GetType() == linker::LinkerPatch::Type::kBakerReadBarrierBranch ||
9577 patch.GetType() == linker::LinkerPatch::Type::kCallRelative;
9578}
9579
9580void CodeGeneratorARMVIXL::EmitThunkCode(const linker::LinkerPatch& patch,
9581 /*out*/ ArenaVector<uint8_t>* code,
9582 /*out*/ std::string* debug_name) {
9583 arm::ArmVIXLAssembler assembler(GetGraph()->GetAllocator());
9584 switch (patch.GetType()) {
9585 case linker::LinkerPatch::Type::kCallRelative:
9586 // The thunk just uses the entry point in the ArtMethod. This works even for calls
9587 // to the generic JNI and interpreter trampolines.
9588 assembler.LoadFromOffset(
9589 arm::kLoadWord,
9590 vixl32::pc,
9591 vixl32::r0,
9592 ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value());
9593 assembler.GetVIXLAssembler()->Bkpt(0);
9594 if (GetCompilerOptions().GenerateAnyDebugInfo()) {
9595 *debug_name = "MethodCallThunk";
9596 }
9597 break;
9598 case linker::LinkerPatch::Type::kBakerReadBarrierBranch:
9599 DCHECK_EQ(patch.GetBakerCustomValue2(), 0u);
9600 CompileBakerReadBarrierThunk(assembler, patch.GetBakerCustomValue1(), debug_name);
9601 break;
9602 default:
9603 LOG(FATAL) << "Unexpected patch type " << patch.GetType();
9604 UNREACHABLE();
9605 }
9606
9607 // Ensure we emit the literal pool if any.
9608 assembler.FinalizeCode();
9609 code->resize(assembler.CodeSize());
9610 MemoryRegion code_region(code->data(), code->size());
9611 assembler.FinalizeInstructions(code_region);
9612}
9613
Artem Serovc5fcb442016-12-02 19:19:58 +00009614VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateUint32Literal(
9615 uint32_t value,
9616 Uint32ToLiteralMap* map) {
9617 return map->GetOrCreate(
9618 value,
9619 [this, value]() {
9620 return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ value);
9621 });
9622}
9623
Artem Serov2bbc9532016-10-21 11:51:50 +01009624void LocationsBuilderARMVIXL::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
9625 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01009626 new (GetGraph()->GetAllocator()) LocationSummary(instr, LocationSummary::kNoCall);
Artem Serov2bbc9532016-10-21 11:51:50 +01009627 locations->SetInAt(HMultiplyAccumulate::kInputAccumulatorIndex,
9628 Location::RequiresRegister());
9629 locations->SetInAt(HMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister());
9630 locations->SetInAt(HMultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister());
9631 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
9632}
9633
9634void InstructionCodeGeneratorARMVIXL::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
9635 vixl32::Register res = OutputRegister(instr);
9636 vixl32::Register accumulator =
9637 InputRegisterAt(instr, HMultiplyAccumulate::kInputAccumulatorIndex);
9638 vixl32::Register mul_left =
9639 InputRegisterAt(instr, HMultiplyAccumulate::kInputMulLeftIndex);
9640 vixl32::Register mul_right =
9641 InputRegisterAt(instr, HMultiplyAccumulate::kInputMulRightIndex);
9642
9643 if (instr->GetOpKind() == HInstruction::kAdd) {
9644 __ Mla(res, mul_left, mul_right, accumulator);
9645 } else {
9646 __ Mls(res, mul_left, mul_right, accumulator);
9647 }
9648}
9649
Artem Serov551b28f2016-10-18 19:11:30 +01009650void LocationsBuilderARMVIXL::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
9651 // Nothing to do, this should be removed during prepare for register allocator.
9652 LOG(FATAL) << "Unreachable";
9653}
9654
9655void InstructionCodeGeneratorARMVIXL::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
9656 // Nothing to do, this should be removed during prepare for register allocator.
9657 LOG(FATAL) << "Unreachable";
9658}
9659
9660// Simple implementation of packed switch - generate cascaded compare/jumps.
9661void LocationsBuilderARMVIXL::VisitPackedSwitch(HPackedSwitch* switch_instr) {
9662 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01009663 new (GetGraph()->GetAllocator()) LocationSummary(switch_instr, LocationSummary::kNoCall);
Artem Serov551b28f2016-10-18 19:11:30 +01009664 locations->SetInAt(0, Location::RequiresRegister());
9665 if (switch_instr->GetNumEntries() > kPackedSwitchCompareJumpThreshold &&
9666 codegen_->GetAssembler()->GetVIXLAssembler()->IsUsingT32()) {
9667 locations->AddTemp(Location::RequiresRegister()); // We need a temp for the table base.
9668 if (switch_instr->GetStartValue() != 0) {
9669 locations->AddTemp(Location::RequiresRegister()); // We need a temp for the bias.
9670 }
9671 }
9672}
9673
9674// TODO(VIXL): Investigate and reach the parity with old arm codegen.
9675void InstructionCodeGeneratorARMVIXL::VisitPackedSwitch(HPackedSwitch* switch_instr) {
9676 int32_t lower_bound = switch_instr->GetStartValue();
9677 uint32_t num_entries = switch_instr->GetNumEntries();
9678 LocationSummary* locations = switch_instr->GetLocations();
9679 vixl32::Register value_reg = InputRegisterAt(switch_instr, 0);
9680 HBasicBlock* default_block = switch_instr->GetDefaultBlock();
9681
9682 if (num_entries <= kPackedSwitchCompareJumpThreshold ||
9683 !codegen_->GetAssembler()->GetVIXLAssembler()->IsUsingT32()) {
9684 // Create a series of compare/jumps.
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009685 UseScratchRegisterScope temps(GetVIXLAssembler());
Artem Serov551b28f2016-10-18 19:11:30 +01009686 vixl32::Register temp_reg = temps.Acquire();
9687 // Note: It is fine for the below AddConstantSetFlags() using IP register to temporarily store
9688 // the immediate, because IP is used as the destination register. For the other
9689 // AddConstantSetFlags() and GenerateCompareWithImmediate(), the immediate values are constant,
9690 // and they can be encoded in the instruction without making use of IP register.
9691 __ Adds(temp_reg, value_reg, -lower_bound);
9692
9693 const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors();
9694 // Jump to successors[0] if value == lower_bound.
9695 __ B(eq, codegen_->GetLabelOf(successors[0]));
9696 int32_t last_index = 0;
9697 for (; num_entries - last_index > 2; last_index += 2) {
9698 __ Adds(temp_reg, temp_reg, -2);
9699 // Jump to successors[last_index + 1] if value < case_value[last_index + 2].
9700 __ B(lo, codegen_->GetLabelOf(successors[last_index + 1]));
9701 // Jump to successors[last_index + 2] if value == case_value[last_index + 2].
9702 __ B(eq, codegen_->GetLabelOf(successors[last_index + 2]));
9703 }
9704 if (num_entries - last_index == 2) {
9705 // The last missing case_value.
9706 __ Cmp(temp_reg, 1);
9707 __ B(eq, codegen_->GetLabelOf(successors[last_index + 1]));
9708 }
9709
9710 // And the default for any other value.
9711 if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) {
9712 __ B(codegen_->GetLabelOf(default_block));
9713 }
9714 } else {
9715 // Create a table lookup.
9716 vixl32::Register table_base = RegisterFrom(locations->GetTemp(0));
9717
9718 JumpTableARMVIXL* jump_table = codegen_->CreateJumpTable(switch_instr);
9719
9720 // Remove the bias.
9721 vixl32::Register key_reg;
9722 if (lower_bound != 0) {
9723 key_reg = RegisterFrom(locations->GetTemp(1));
9724 __ Sub(key_reg, value_reg, lower_bound);
9725 } else {
9726 key_reg = value_reg;
9727 }
9728
9729 // Check whether the value is in the table, jump to default block if not.
9730 __ Cmp(key_reg, num_entries - 1);
9731 __ B(hi, codegen_->GetLabelOf(default_block));
9732
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009733 UseScratchRegisterScope temps(GetVIXLAssembler());
Artem Serov551b28f2016-10-18 19:11:30 +01009734 vixl32::Register jump_offset = temps.Acquire();
9735
9736 // Load jump offset from the table.
Scott Wakeling86e9d262017-01-18 15:59:24 +00009737 {
9738 const size_t jump_size = switch_instr->GetNumEntries() * sizeof(int32_t);
9739 ExactAssemblyScope aas(GetVIXLAssembler(),
9740 (vixl32::kMaxInstructionSizeInBytes * 4) + jump_size,
9741 CodeBufferCheckScope::kMaximumSize);
9742 __ adr(table_base, jump_table->GetTableStartLabel());
9743 __ ldr(jump_offset, MemOperand(table_base, key_reg, vixl32::LSL, 2));
Artem Serov551b28f2016-10-18 19:11:30 +01009744
Scott Wakeling86e9d262017-01-18 15:59:24 +00009745 // Jump to target block by branching to table_base(pc related) + offset.
9746 vixl32::Register target_address = table_base;
9747 __ add(target_address, table_base, jump_offset);
9748 __ bx(target_address);
Artem Serov09a940d2016-11-11 16:15:11 +00009749
Scott Wakeling86e9d262017-01-18 15:59:24 +00009750 jump_table->EmitTable(codegen_);
9751 }
Artem Serov551b28f2016-10-18 19:11:30 +01009752 }
9753}
9754
Artem Serov02d37832016-10-25 15:25:33 +01009755// Copy the result of a call into the given target.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01009756void CodeGeneratorARMVIXL::MoveFromReturnRegister(Location trg, DataType::Type type) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009757 if (!trg.IsValid()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01009758 DCHECK_EQ(type, DataType::Type::kVoid);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009759 return;
9760 }
9761
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01009762 DCHECK_NE(type, DataType::Type::kVoid);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009763
Artem Serovd4cc5b22016-11-04 11:19:09 +00009764 Location return_loc = InvokeDexCallingConventionVisitorARMVIXL().GetReturnLocation(type);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009765 if (return_loc.Equals(trg)) {
9766 return;
9767 }
9768
9769 // TODO: Consider pairs in the parallel move resolver, then this could be nicely merged
9770 // with the last branch.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01009771 if (type == DataType::Type::kInt64) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009772 TODO_VIXL32(FATAL);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01009773 } else if (type == DataType::Type::kFloat64) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009774 TODO_VIXL32(FATAL);
9775 } else {
9776 // Let the parallel move resolver take care of all of this.
Vladimir Markoca6fff82017-10-03 14:49:14 +01009777 HParallelMove parallel_move(GetGraph()->GetAllocator());
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009778 parallel_move.AddMove(return_loc, trg, type, nullptr);
9779 GetMoveResolver()->EmitNativeCode(&parallel_move);
9780 }
Scott Wakelingfe885462016-09-22 10:24:38 +01009781}
9782
xueliang.zhong8d2c4592016-11-23 17:05:25 +00009783void LocationsBuilderARMVIXL::VisitClassTableGet(HClassTableGet* instruction) {
9784 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01009785 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
xueliang.zhong8d2c4592016-11-23 17:05:25 +00009786 locations->SetInAt(0, Location::RequiresRegister());
9787 locations->SetOut(Location::RequiresRegister());
Artem Serov551b28f2016-10-18 19:11:30 +01009788}
9789
xueliang.zhong8d2c4592016-11-23 17:05:25 +00009790void InstructionCodeGeneratorARMVIXL::VisitClassTableGet(HClassTableGet* instruction) {
9791 if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) {
9792 uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
9793 instruction->GetIndex(), kArmPointerSize).SizeValue();
9794 GetAssembler()->LoadFromOffset(kLoadWord,
9795 OutputRegister(instruction),
9796 InputRegisterAt(instruction, 0),
9797 method_offset);
9798 } else {
9799 uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement(
9800 instruction->GetIndex(), kArmPointerSize));
9801 GetAssembler()->LoadFromOffset(kLoadWord,
9802 OutputRegister(instruction),
9803 InputRegisterAt(instruction, 0),
9804 mirror::Class::ImtPtrOffset(kArmPointerSize).Uint32Value());
9805 GetAssembler()->LoadFromOffset(kLoadWord,
9806 OutputRegister(instruction),
9807 OutputRegister(instruction),
9808 method_offset);
9809 }
Artem Serov551b28f2016-10-18 19:11:30 +01009810}
9811
Artem Serovc5fcb442016-12-02 19:19:58 +00009812static void PatchJitRootUse(uint8_t* code,
9813 const uint8_t* roots_data,
9814 VIXLUInt32Literal* literal,
9815 uint64_t index_in_table) {
9816 DCHECK(literal->IsBound());
9817 uint32_t literal_offset = literal->GetLocation();
9818 uintptr_t address =
9819 reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>);
9820 uint8_t* data = code + literal_offset;
9821 reinterpret_cast<uint32_t*>(data)[0] = dchecked_integral_cast<uint32_t>(address);
9822}
9823
9824void CodeGeneratorARMVIXL::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) {
9825 for (const auto& entry : jit_string_patches_) {
Vladimir Marko7d157fc2017-05-10 16:29:23 +01009826 const StringReference& string_reference = entry.first;
9827 VIXLUInt32Literal* table_entry_literal = entry.second;
Vladimir Marko174b2e22017-10-12 13:34:49 +01009828 uint64_t index_in_table = GetJitStringRootIndex(string_reference);
Vladimir Marko7d157fc2017-05-10 16:29:23 +01009829 PatchJitRootUse(code, roots_data, table_entry_literal, index_in_table);
Artem Serovc5fcb442016-12-02 19:19:58 +00009830 }
9831 for (const auto& entry : jit_class_patches_) {
Vladimir Marko7d157fc2017-05-10 16:29:23 +01009832 const TypeReference& type_reference = entry.first;
9833 VIXLUInt32Literal* table_entry_literal = entry.second;
Vladimir Marko174b2e22017-10-12 13:34:49 +01009834 uint64_t index_in_table = GetJitClassRootIndex(type_reference);
Vladimir Marko7d157fc2017-05-10 16:29:23 +01009835 PatchJitRootUse(code, roots_data, table_entry_literal, index_in_table);
Artem Serovc5fcb442016-12-02 19:19:58 +00009836 }
9837}
9838
Artem Serovd4cc5b22016-11-04 11:19:09 +00009839void CodeGeneratorARMVIXL::EmitMovwMovtPlaceholder(
9840 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels,
9841 vixl32::Register out) {
Artem Serov0fb37192016-12-06 18:13:40 +00009842 ExactAssemblyScope aas(GetVIXLAssembler(),
9843 3 * vixl32::kMaxInstructionSizeInBytes,
9844 CodeBufferCheckScope::kMaximumSize);
Artem Serovd4cc5b22016-11-04 11:19:09 +00009845 // TODO(VIXL): Think about using mov instead of movw.
9846 __ bind(&labels->movw_label);
9847 __ movw(out, /* placeholder */ 0u);
9848 __ bind(&labels->movt_label);
9849 __ movt(out, /* placeholder */ 0u);
9850 __ bind(&labels->add_pc_label);
9851 __ add(out, out, pc);
9852}
9853
Scott Wakelingfe885462016-09-22 10:24:38 +01009854#undef __
9855#undef QUICK_ENTRY_POINT
9856#undef TODO_VIXL32
9857
Vladimir Markoca1e0382018-04-11 09:58:41 +00009858#define __ assembler.GetVIXLAssembler()->
9859
9860static void EmitGrayCheckAndFastPath(ArmVIXLAssembler& assembler,
9861 vixl32::Register base_reg,
9862 vixl32::MemOperand& lock_word,
9863 vixl32::Label* slow_path,
Vladimir Marko7a695052018-04-12 10:26:50 +01009864 int32_t raw_ldr_offset,
9865 vixl32::Label* throw_npe = nullptr) {
Vladimir Markoca1e0382018-04-11 09:58:41 +00009866 // Load the lock word containing the rb_state.
9867 __ Ldr(ip, lock_word);
9868 // Given the numeric representation, it's enough to check the low bit of the rb_state.
9869 static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
9870 static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
9871 __ Tst(ip, Operand(LockWord::kReadBarrierStateMaskShifted));
9872 __ B(ne, slow_path, /* is_far_target */ false);
Vladimir Marko7a695052018-04-12 10:26:50 +01009873 // To throw NPE, we return to the fast path; the artificial dependence below does not matter.
9874 if (throw_npe != nullptr) {
9875 __ Bind(throw_npe);
9876 }
Vladimir Markoca1e0382018-04-11 09:58:41 +00009877 __ Add(lr, lr, raw_ldr_offset);
9878 // Introduce a dependency on the lock_word including rb_state,
9879 // to prevent load-load reordering, and without using
9880 // a memory barrier (which would be more expensive).
9881 __ Add(base_reg, base_reg, Operand(ip, LSR, 32));
9882 __ Bx(lr); // And return back to the function.
9883 // Note: The fake dependency is unnecessary for the slow path.
9884}
9885
9886// Load the read barrier introspection entrypoint in register `entrypoint`
Vladimir Markodcd117e2018-04-19 11:54:00 +01009887static vixl32::Register LoadReadBarrierMarkIntrospectionEntrypoint(ArmVIXLAssembler& assembler) {
Vladimir Markoca1e0382018-04-11 09:58:41 +00009888 // The register where the read barrier introspection entrypoint is loaded
Vladimir Markodcd117e2018-04-19 11:54:00 +01009889 // is the marking register. We clobber it here and the entrypoint restores it to 1.
9890 vixl32::Register entrypoint = mr;
Vladimir Markoca1e0382018-04-11 09:58:41 +00009891 // entrypoint = Thread::Current()->pReadBarrierMarkReg12, i.e. pReadBarrierMarkIntrospection.
9892 DCHECK_EQ(ip.GetCode(), 12u);
9893 const int32_t entry_point_offset =
9894 Thread::ReadBarrierMarkEntryPointsOffset<kArmPointerSize>(ip.GetCode());
9895 __ Ldr(entrypoint, MemOperand(tr, entry_point_offset));
Vladimir Markodcd117e2018-04-19 11:54:00 +01009896 return entrypoint;
Vladimir Markoca1e0382018-04-11 09:58:41 +00009897}
9898
9899void CodeGeneratorARMVIXL::CompileBakerReadBarrierThunk(ArmVIXLAssembler& assembler,
9900 uint32_t encoded_data,
9901 /*out*/ std::string* debug_name) {
9902 BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data);
9903 switch (kind) {
9904 case BakerReadBarrierKind::kField: {
Vladimir Markoca1e0382018-04-11 09:58:41 +00009905 vixl32::Register base_reg(BakerReadBarrierFirstRegField::Decode(encoded_data));
9906 CheckValidReg(base_reg.GetCode());
9907 vixl32::Register holder_reg(BakerReadBarrierSecondRegField::Decode(encoded_data));
9908 CheckValidReg(holder_reg.GetCode());
9909 BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data);
9910 UseScratchRegisterScope temps(assembler.GetVIXLAssembler());
9911 temps.Exclude(ip);
Vladimir Marko7a695052018-04-12 10:26:50 +01009912 // If base_reg differs from holder_reg, the offset was too large and we must have emitted
9913 // an explicit null check before the load. Otherwise, for implicit null checks, we need to
9914 // null-check the holder as we do not necessarily do that check before going to the thunk.
9915 vixl32::Label throw_npe_label;
9916 vixl32::Label* throw_npe = nullptr;
9917 if (GetCompilerOptions().GetImplicitNullChecks() && holder_reg.Is(base_reg)) {
9918 throw_npe = &throw_npe_label;
9919 __ CompareAndBranchIfZero(holder_reg, throw_npe, /* is_far_target */ false);
Vladimir Markoca1e0382018-04-11 09:58:41 +00009920 }
Vladimir Marko7a695052018-04-12 10:26:50 +01009921 // Check if the holder is gray and, if not, add fake dependency to the base register
9922 // and return to the LDR instruction to load the reference. Otherwise, use introspection
9923 // to load the reference and call the entrypoint that performs further checks on the
9924 // reference and marks it if needed.
Vladimir Markoca1e0382018-04-11 09:58:41 +00009925 vixl32::Label slow_path;
9926 MemOperand lock_word(holder_reg, mirror::Object::MonitorOffset().Int32Value());
9927 const int32_t raw_ldr_offset = (width == BakerReadBarrierWidth::kWide)
9928 ? BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET
9929 : BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET;
Vladimir Marko7a695052018-04-12 10:26:50 +01009930 EmitGrayCheckAndFastPath(
9931 assembler, base_reg, lock_word, &slow_path, raw_ldr_offset, throw_npe);
Vladimir Markoca1e0382018-04-11 09:58:41 +00009932 __ Bind(&slow_path);
9933 const int32_t ldr_offset = /* Thumb state adjustment (LR contains Thumb state). */ -1 +
9934 raw_ldr_offset;
Vladimir Markodcd117e2018-04-19 11:54:00 +01009935 vixl32::Register ep_reg = LoadReadBarrierMarkIntrospectionEntrypoint(assembler);
Vladimir Markoca1e0382018-04-11 09:58:41 +00009936 if (width == BakerReadBarrierWidth::kWide) {
9937 MemOperand ldr_half_address(lr, ldr_offset + 2);
9938 __ Ldrh(ip, ldr_half_address); // Load the LDR immediate half-word with "Rt | imm12".
9939 __ Ubfx(ip, ip, 0, 12); // Extract the offset imm12.
9940 __ Ldr(ip, MemOperand(base_reg, ip)); // Load the reference.
9941 } else {
9942 MemOperand ldr_address(lr, ldr_offset);
9943 __ Ldrh(ip, ldr_address); // Load the LDR immediate, encoding T1.
9944 __ Add(ep_reg, // Adjust the entrypoint address to the entrypoint
9945 ep_reg, // for narrow LDR.
9946 Operand(BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_ENTRYPOINT_OFFSET));
9947 __ Ubfx(ip, ip, 6, 5); // Extract the imm5, i.e. offset / 4.
9948 __ Ldr(ip, MemOperand(base_reg, ip, LSL, 2)); // Load the reference.
9949 }
9950 // Do not unpoison. With heap poisoning enabled, the entrypoint expects a poisoned reference.
9951 __ Bx(ep_reg); // Jump to the entrypoint.
Vladimir Markoca1e0382018-04-11 09:58:41 +00009952 break;
9953 }
9954 case BakerReadBarrierKind::kArray: {
9955 vixl32::Register base_reg(BakerReadBarrierFirstRegField::Decode(encoded_data));
9956 CheckValidReg(base_reg.GetCode());
9957 DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg,
9958 BakerReadBarrierSecondRegField::Decode(encoded_data));
9959 DCHECK(BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide);
9960 UseScratchRegisterScope temps(assembler.GetVIXLAssembler());
9961 temps.Exclude(ip);
9962 vixl32::Label slow_path;
9963 int32_t data_offset =
9964 mirror::Array::DataOffset(Primitive::ComponentSize(Primitive::kPrimNot)).Int32Value();
9965 MemOperand lock_word(base_reg, mirror::Object::MonitorOffset().Int32Value() - data_offset);
9966 DCHECK_LT(lock_word.GetOffsetImmediate(), 0);
9967 const int32_t raw_ldr_offset = BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET;
9968 EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path, raw_ldr_offset);
9969 __ Bind(&slow_path);
9970 const int32_t ldr_offset = /* Thumb state adjustment (LR contains Thumb state). */ -1 +
9971 raw_ldr_offset;
9972 MemOperand ldr_address(lr, ldr_offset + 2);
9973 __ Ldrb(ip, ldr_address); // Load the LDR (register) byte with "00 | imm2 | Rm",
9974 // i.e. Rm+32 because the scale in imm2 is 2.
Vladimir Markodcd117e2018-04-19 11:54:00 +01009975 vixl32::Register ep_reg = LoadReadBarrierMarkIntrospectionEntrypoint(assembler);
Vladimir Markoca1e0382018-04-11 09:58:41 +00009976 __ Bfi(ep_reg, ip, 3, 6); // Insert ip to the entrypoint address to create
9977 // a switch case target based on the index register.
9978 __ Mov(ip, base_reg); // Move the base register to ip0.
9979 __ Bx(ep_reg); // Jump to the entrypoint's array switch case.
9980 break;
9981 }
9982 case BakerReadBarrierKind::kGcRoot: {
9983 // Check if the reference needs to be marked and if so (i.e. not null, not marked yet
9984 // and it does not have a forwarding address), call the correct introspection entrypoint;
9985 // otherwise return the reference (or the extracted forwarding address).
9986 // There is no gray bit check for GC roots.
9987 vixl32::Register root_reg(BakerReadBarrierFirstRegField::Decode(encoded_data));
9988 CheckValidReg(root_reg.GetCode());
9989 DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg,
9990 BakerReadBarrierSecondRegField::Decode(encoded_data));
9991 BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data);
9992 UseScratchRegisterScope temps(assembler.GetVIXLAssembler());
9993 temps.Exclude(ip);
9994 vixl32::Label return_label, not_marked, forwarding_address;
9995 __ CompareAndBranchIfZero(root_reg, &return_label, /* is_far_target */ false);
9996 MemOperand lock_word(root_reg, mirror::Object::MonitorOffset().Int32Value());
9997 __ Ldr(ip, lock_word);
9998 __ Tst(ip, LockWord::kMarkBitStateMaskShifted);
9999 __ B(eq, &not_marked);
10000 __ Bind(&return_label);
10001 __ Bx(lr);
10002 __ Bind(&not_marked);
10003 static_assert(LockWord::kStateShift == 30 && LockWord::kStateForwardingAddress == 3,
10004 "To use 'CMP ip, #modified-immediate; BHS', we need the lock word state in "
10005 " the highest bits and the 'forwarding address' state to have all bits set");
10006 __ Cmp(ip, Operand(0xc0000000));
10007 __ B(hs, &forwarding_address);
Vladimir Markodcd117e2018-04-19 11:54:00 +010010008 vixl32::Register ep_reg = LoadReadBarrierMarkIntrospectionEntrypoint(assembler);
Vladimir Markoca1e0382018-04-11 09:58:41 +000010009 // Adjust the art_quick_read_barrier_mark_introspection address in kBakerCcEntrypointRegister
10010 // to art_quick_read_barrier_mark_introspection_gc_roots.
10011 int32_t entrypoint_offset = (width == BakerReadBarrierWidth::kWide)
10012 ? BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_ENTRYPOINT_OFFSET
10013 : BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_ENTRYPOINT_OFFSET;
10014 __ Add(ep_reg, ep_reg, Operand(entrypoint_offset));
10015 __ Mov(ip, root_reg);
10016 __ Bx(ep_reg);
10017 __ Bind(&forwarding_address);
10018 __ Lsl(root_reg, ip, LockWord::kForwardingAddressShift);
10019 __ Bx(lr);
10020 break;
10021 }
10022 default:
10023 LOG(FATAL) << "Unexpected kind: " << static_cast<uint32_t>(kind);
10024 UNREACHABLE();
10025 }
10026
10027 if (GetCompilerOptions().GenerateAnyDebugInfo()) {
10028 std::ostringstream oss;
10029 oss << "BakerReadBarrierThunk";
10030 switch (kind) {
10031 case BakerReadBarrierKind::kField:
10032 oss << "Field";
10033 if (BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide) {
10034 oss << "Wide";
10035 }
10036 oss << "_r" << BakerReadBarrierFirstRegField::Decode(encoded_data)
10037 << "_r" << BakerReadBarrierSecondRegField::Decode(encoded_data);
10038 break;
10039 case BakerReadBarrierKind::kArray:
10040 oss << "Array_r" << BakerReadBarrierFirstRegField::Decode(encoded_data);
10041 DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg,
10042 BakerReadBarrierSecondRegField::Decode(encoded_data));
10043 DCHECK(BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide);
10044 break;
10045 case BakerReadBarrierKind::kGcRoot:
10046 oss << "GcRoot";
10047 if (BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide) {
10048 oss << "Wide";
10049 }
10050 oss << "_r" << BakerReadBarrierFirstRegField::Decode(encoded_data);
10051 DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg,
10052 BakerReadBarrierSecondRegField::Decode(encoded_data));
10053 break;
10054 }
10055 *debug_name = oss.str();
10056 }
10057}
10058
10059#undef __
10060
Scott Wakelingfe885462016-09-22 10:24:38 +010010061} // namespace arm
10062} // namespace art