blob: 9e1ef4002e526a6b7fbed985597790fb8dc16c23 [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"
Vladimir Markoeebb8212018-06-05 14:57:24 +010030#include "gc/space/image_space.h"
Andreas Gampe09659c22017-09-18 18:23:32 -070031#include "heap_poisoning.h"
Vladimir Marko6fd16062018-06-26 11:02:04 +010032#include "intrinsics.h"
Anton Kirilov5ec62182016-10-13 20:16:02 +010033#include "intrinsics_arm_vixl.h"
Vladimir Markod8dbc8d2017-09-20 13:37:47 +010034#include "linker/linker_patch.h"
Scott Wakelingfe885462016-09-22 10:24:38 +010035#include "mirror/array-inl.h"
36#include "mirror/class-inl.h"
37#include "thread.h"
38#include "utils/arm/assembler_arm_vixl.h"
39#include "utils/arm/managed_register_arm.h"
40#include "utils/assembler.h"
41#include "utils/stack_checks.h"
42
43namespace art {
44namespace arm {
45
46namespace vixl32 = vixl::aarch32;
47using namespace vixl32; // NOLINT(build/namespaces)
48
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +010049using helpers::DRegisterFrom;
Scott Wakelingfe885462016-09-22 10:24:38 +010050using helpers::DWARFReg;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010051using helpers::HighDRegisterFrom;
52using helpers::HighRegisterFrom;
Donghui Bai426b49c2016-11-08 14:55:38 +080053using helpers::InputDRegisterAt;
Scott Wakelingfe885462016-09-22 10:24:38 +010054using helpers::InputOperandAt;
Scott Wakelingc34dba72016-10-03 10:14:44 +010055using helpers::InputRegister;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010056using helpers::InputRegisterAt;
Scott Wakelingfe885462016-09-22 10:24:38 +010057using helpers::InputSRegisterAt;
Anton Kirilov644032c2016-12-06 17:51:43 +000058using helpers::InputVRegister;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010059using helpers::InputVRegisterAt;
Scott Wakelingb77051e2016-11-21 19:46:00 +000060using helpers::Int32ConstantFrom;
Anton Kirilov644032c2016-12-06 17:51:43 +000061using helpers::Int64ConstantFrom;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010062using helpers::LocationFrom;
63using helpers::LowRegisterFrom;
64using helpers::LowSRegisterFrom;
Donghui Bai426b49c2016-11-08 14:55:38 +080065using helpers::OperandFrom;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010066using helpers::OutputRegister;
67using helpers::OutputSRegister;
68using helpers::OutputVRegister;
69using helpers::RegisterFrom;
70using helpers::SRegisterFrom;
Anton Kirilov644032c2016-12-06 17:51:43 +000071using helpers::Uint64ConstantFrom;
Scott Wakelingfe885462016-09-22 10:24:38 +010072
Artem Serov0fb37192016-12-06 18:13:40 +000073using vixl::ExactAssemblyScope;
74using vixl::CodeBufferCheckScope;
75
Scott Wakelingfe885462016-09-22 10:24:38 +010076using RegisterList = vixl32::RegisterList;
77
78static bool ExpectedPairLayout(Location location) {
79 // We expected this for both core and fpu register pairs.
80 return ((location.low() & 1) == 0) && (location.low() + 1 == location.high());
81}
Artem Serovd4cc5b22016-11-04 11:19:09 +000082// Use a local definition to prevent copying mistakes.
83static constexpr size_t kArmWordSize = static_cast<size_t>(kArmPointerSize);
84static constexpr size_t kArmBitsPerWord = kArmWordSize * kBitsPerByte;
Artem Serov551b28f2016-10-18 19:11:30 +010085static constexpr uint32_t kPackedSwitchCompareJumpThreshold = 7;
Scott Wakelingfe885462016-09-22 10:24:38 +010086
Vladimir Markoeee1c0e2017-04-21 17:58:41 +010087// Reference load (except object array loads) is using LDR Rt, [Rn, #offset] which can handle
88// offset < 4KiB. For offsets >= 4KiB, the load shall be emitted as two or more instructions.
89// For the Baker read barrier implementation using link-generated thunks we need to split
90// the offset explicitly.
91constexpr uint32_t kReferenceLoadMinFarOffset = 4 * KB;
92
93// Flags controlling the use of link-time generated thunks for Baker read barriers.
94constexpr bool kBakerReadBarrierLinkTimeThunksEnableForFields = true;
95constexpr bool kBakerReadBarrierLinkTimeThunksEnableForArrays = true;
96constexpr bool kBakerReadBarrierLinkTimeThunksEnableForGcRoots = true;
97
Roland Levillain5daa4952017-07-03 17:23:56 +010098// Using a base helps identify when we hit Marking Register check breakpoints.
99constexpr int kMarkingRegisterCheckBreakCodeBaseCode = 0x10;
100
Scott Wakelingfe885462016-09-22 10:24:38 +0100101#ifdef __
102#error "ARM Codegen VIXL macro-assembler macro already defined."
103#endif
104
Scott Wakelingfe885462016-09-22 10:24:38 +0100105// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
106#define __ down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler()-> // NOLINT
107#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArmPointerSize, x).Int32Value()
108
109// Marker that code is yet to be, and must, be implemented.
110#define TODO_VIXL32(level) LOG(level) << __PRETTY_FUNCTION__ << " unimplemented "
111
Vladimir Markoeee1c0e2017-04-21 17:58:41 +0100112static inline void EmitPlaceholderBne(CodeGeneratorARMVIXL* codegen, vixl32::Label* patch_label) {
113 ExactAssemblyScope eas(codegen->GetVIXLAssembler(), kMaxInstructionSizeInBytes);
114 __ bind(patch_label);
115 vixl32::Label placeholder_label;
116 __ b(ne, EncodingSize(Wide), &placeholder_label); // Placeholder, patched at link-time.
117 __ bind(&placeholder_label);
118}
119
Vladimir Marko88abba22017-05-03 17:09:25 +0100120static inline bool CanEmitNarrowLdr(vixl32::Register rt, vixl32::Register rn, uint32_t offset) {
121 return rt.IsLow() && rn.IsLow() && offset < 32u;
122}
123
Vladimir Markoeee1c0e2017-04-21 17:58:41 +0100124class EmitAdrCode {
125 public:
126 EmitAdrCode(ArmVIXLMacroAssembler* assembler, vixl32::Register rd, vixl32::Label* label)
127 : assembler_(assembler), rd_(rd), label_(label) {
128 ExactAssemblyScope aas(assembler, kMaxInstructionSizeInBytes);
129 adr_location_ = assembler->GetCursorOffset();
130 assembler->adr(EncodingSize(Wide), rd, label);
131 }
132
133 ~EmitAdrCode() {
134 DCHECK(label_->IsBound());
135 // The ADR emitted by the assembler does not set the Thumb mode bit we need.
136 // TODO: Maybe extend VIXL to allow ADR for return address?
137 uint8_t* raw_adr = assembler_->GetBuffer()->GetOffsetAddress<uint8_t*>(adr_location_);
138 // Expecting ADR encoding T3 with `(offset & 1) == 0`.
139 DCHECK_EQ(raw_adr[1] & 0xfbu, 0xf2u); // Check bits 24-31, except 26.
140 DCHECK_EQ(raw_adr[0] & 0xffu, 0x0fu); // Check bits 16-23.
141 DCHECK_EQ(raw_adr[3] & 0x8fu, rd_.GetCode()); // Check bits 8-11 and 15.
142 DCHECK_EQ(raw_adr[2] & 0x01u, 0x00u); // Check bit 0, i.e. the `offset & 1`.
143 // Add the Thumb mode bit.
144 raw_adr[2] |= 0x01u;
145 }
146
147 private:
148 ArmVIXLMacroAssembler* const assembler_;
149 vixl32::Register rd_;
150 vixl32::Label* const label_;
151 int32_t adr_location_;
152};
153
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100154// SaveLiveRegisters and RestoreLiveRegisters from SlowPathCodeARM operate on sets of S registers,
155// for each live D registers they treat two corresponding S registers as live ones.
156//
157// Two following functions (SaveContiguousSRegisterList, RestoreContiguousSRegisterList) build
158// from a list of contiguous S registers a list of contiguous D registers (processing first/last
159// S registers corner cases) and save/restore this new list treating them as D registers.
160// - decreasing code size
161// - avoiding hazards on Cortex-A57, when a pair of S registers for an actual live D register is
162// restored and then used in regular non SlowPath code as D register.
163//
164// For the following example (v means the S register is live):
165// D names: | D0 | D1 | D2 | D4 | ...
166// S names: | S0 | S1 | S2 | S3 | S4 | S5 | S6 | S7 | ...
167// Live? | | v | v | v | v | v | v | | ...
168//
169// S1 and S6 will be saved/restored independently; D registers list (D1, D2) will be processed
170// as D registers.
171//
172// TODO(VIXL): All this code should be unnecessary once the VIXL AArch32 backend provides helpers
173// for lists of floating-point registers.
174static size_t SaveContiguousSRegisterList(size_t first,
175 size_t last,
176 CodeGenerator* codegen,
177 size_t stack_offset) {
178 static_assert(kSRegSizeInBytes == kArmWordSize, "Broken assumption on reg/word sizes.");
179 static_assert(kDRegSizeInBytes == 2 * kArmWordSize, "Broken assumption on reg/word sizes.");
180 DCHECK_LE(first, last);
181 if ((first == last) && (first == 0)) {
182 __ Vstr(vixl32::SRegister(first), MemOperand(sp, stack_offset));
183 return stack_offset + kSRegSizeInBytes;
184 }
185 if (first % 2 == 1) {
186 __ Vstr(vixl32::SRegister(first++), MemOperand(sp, stack_offset));
187 stack_offset += kSRegSizeInBytes;
188 }
189
190 bool save_last = false;
191 if (last % 2 == 0) {
192 save_last = true;
193 --last;
194 }
195
196 if (first < last) {
197 vixl32::DRegister d_reg = vixl32::DRegister(first / 2);
198 DCHECK_EQ((last - first + 1) % 2, 0u);
199 size_t number_of_d_regs = (last - first + 1) / 2;
200
201 if (number_of_d_regs == 1) {
202 __ Vstr(d_reg, MemOperand(sp, stack_offset));
203 } else if (number_of_d_regs > 1) {
204 UseScratchRegisterScope temps(down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler());
205 vixl32::Register base = sp;
206 if (stack_offset != 0) {
207 base = temps.Acquire();
Scott Wakelingb77051e2016-11-21 19:46:00 +0000208 __ Add(base, sp, Operand::From(stack_offset));
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100209 }
210 __ Vstm(F64, base, NO_WRITE_BACK, DRegisterList(d_reg, number_of_d_regs));
211 }
212 stack_offset += number_of_d_regs * kDRegSizeInBytes;
213 }
214
215 if (save_last) {
216 __ Vstr(vixl32::SRegister(last + 1), MemOperand(sp, stack_offset));
217 stack_offset += kSRegSizeInBytes;
218 }
219
220 return stack_offset;
221}
222
223static size_t RestoreContiguousSRegisterList(size_t first,
224 size_t last,
225 CodeGenerator* codegen,
226 size_t stack_offset) {
227 static_assert(kSRegSizeInBytes == kArmWordSize, "Broken assumption on reg/word sizes.");
228 static_assert(kDRegSizeInBytes == 2 * kArmWordSize, "Broken assumption on reg/word sizes.");
229 DCHECK_LE(first, last);
230 if ((first == last) && (first == 0)) {
231 __ Vldr(vixl32::SRegister(first), MemOperand(sp, stack_offset));
232 return stack_offset + kSRegSizeInBytes;
233 }
234 if (first % 2 == 1) {
235 __ Vldr(vixl32::SRegister(first++), MemOperand(sp, stack_offset));
236 stack_offset += kSRegSizeInBytes;
237 }
238
239 bool restore_last = false;
240 if (last % 2 == 0) {
241 restore_last = true;
242 --last;
243 }
244
245 if (first < last) {
246 vixl32::DRegister d_reg = vixl32::DRegister(first / 2);
247 DCHECK_EQ((last - first + 1) % 2, 0u);
248 size_t number_of_d_regs = (last - first + 1) / 2;
249 if (number_of_d_regs == 1) {
250 __ Vldr(d_reg, MemOperand(sp, stack_offset));
251 } else if (number_of_d_regs > 1) {
252 UseScratchRegisterScope temps(down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler());
253 vixl32::Register base = sp;
254 if (stack_offset != 0) {
255 base = temps.Acquire();
Scott Wakelingb77051e2016-11-21 19:46:00 +0000256 __ Add(base, sp, Operand::From(stack_offset));
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100257 }
258 __ Vldm(F64, base, NO_WRITE_BACK, DRegisterList(d_reg, number_of_d_regs));
259 }
260 stack_offset += number_of_d_regs * kDRegSizeInBytes;
261 }
262
263 if (restore_last) {
264 __ Vldr(vixl32::SRegister(last + 1), MemOperand(sp, stack_offset));
265 stack_offset += kSRegSizeInBytes;
266 }
267
268 return stack_offset;
269}
270
Vladimir Markod5d2f2c2017-09-26 12:37:26 +0100271static LoadOperandType GetLoadOperandType(DataType::Type type) {
272 switch (type) {
273 case DataType::Type::kReference:
274 return kLoadWord;
275 case DataType::Type::kBool:
276 case DataType::Type::kUint8:
277 return kLoadUnsignedByte;
278 case DataType::Type::kInt8:
279 return kLoadSignedByte;
280 case DataType::Type::kUint16:
281 return kLoadUnsignedHalfword;
282 case DataType::Type::kInt16:
283 return kLoadSignedHalfword;
284 case DataType::Type::kInt32:
285 return kLoadWord;
286 case DataType::Type::kInt64:
287 return kLoadWordPair;
288 case DataType::Type::kFloat32:
289 return kLoadSWord;
290 case DataType::Type::kFloat64:
291 return kLoadDWord;
292 default:
293 LOG(FATAL) << "Unreachable type " << type;
294 UNREACHABLE();
295 }
296}
297
298static StoreOperandType GetStoreOperandType(DataType::Type type) {
299 switch (type) {
300 case DataType::Type::kReference:
301 return kStoreWord;
302 case DataType::Type::kBool:
303 case DataType::Type::kUint8:
304 case DataType::Type::kInt8:
305 return kStoreByte;
306 case DataType::Type::kUint16:
307 case DataType::Type::kInt16:
308 return kStoreHalfword;
309 case DataType::Type::kInt32:
310 return kStoreWord;
311 case DataType::Type::kInt64:
312 return kStoreWordPair;
313 case DataType::Type::kFloat32:
314 return kStoreSWord;
315 case DataType::Type::kFloat64:
316 return kStoreDWord;
317 default:
318 LOG(FATAL) << "Unreachable type " << type;
319 UNREACHABLE();
320 }
321}
322
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100323void SlowPathCodeARMVIXL::SaveLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
324 size_t stack_offset = codegen->GetFirstRegisterSlotInSlowPath();
325 size_t orig_offset = stack_offset;
326
327 const uint32_t core_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ true);
328 for (uint32_t i : LowToHighBits(core_spills)) {
329 // If the register holds an object, update the stack mask.
330 if (locations->RegisterContainsObject(i)) {
331 locations->SetStackBit(stack_offset / kVRegSize);
332 }
333 DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
334 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
335 saved_core_stack_offsets_[i] = stack_offset;
336 stack_offset += kArmWordSize;
337 }
338
339 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
340 arm_codegen->GetAssembler()->StoreRegisterList(core_spills, orig_offset);
341
342 uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ false);
343 orig_offset = stack_offset;
344 for (uint32_t i : LowToHighBits(fp_spills)) {
345 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
346 saved_fpu_stack_offsets_[i] = stack_offset;
347 stack_offset += kArmWordSize;
348 }
349
350 stack_offset = orig_offset;
351 while (fp_spills != 0u) {
352 uint32_t begin = CTZ(fp_spills);
353 uint32_t tmp = fp_spills + (1u << begin);
354 fp_spills &= tmp; // Clear the contiguous range of 1s.
355 uint32_t end = (tmp == 0u) ? 32u : CTZ(tmp); // CTZ(0) is undefined.
356 stack_offset = SaveContiguousSRegisterList(begin, end - 1, codegen, stack_offset);
357 }
358 DCHECK_LE(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
359}
360
361void SlowPathCodeARMVIXL::RestoreLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
362 size_t stack_offset = codegen->GetFirstRegisterSlotInSlowPath();
363 size_t orig_offset = stack_offset;
364
365 const uint32_t core_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ true);
366 for (uint32_t i : LowToHighBits(core_spills)) {
367 DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
368 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
369 stack_offset += kArmWordSize;
370 }
371
372 // TODO(VIXL): Check the coherency of stack_offset after this with a test.
373 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
374 arm_codegen->GetAssembler()->LoadRegisterList(core_spills, orig_offset);
375
376 uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ false);
377 while (fp_spills != 0u) {
378 uint32_t begin = CTZ(fp_spills);
379 uint32_t tmp = fp_spills + (1u << begin);
380 fp_spills &= tmp; // Clear the contiguous range of 1s.
381 uint32_t end = (tmp == 0u) ? 32u : CTZ(tmp); // CTZ(0) is undefined.
382 stack_offset = RestoreContiguousSRegisterList(begin, end - 1, codegen, stack_offset);
383 }
384 DCHECK_LE(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
385}
386
387class NullCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
388 public:
389 explicit NullCheckSlowPathARMVIXL(HNullCheck* instruction) : SlowPathCodeARMVIXL(instruction) {}
390
391 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
392 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
393 __ Bind(GetEntryLabel());
394 if (instruction_->CanThrowIntoCatchBlock()) {
395 // Live registers will be restored in the catch block if caught.
396 SaveLiveRegisters(codegen, instruction_->GetLocations());
397 }
398 arm_codegen->InvokeRuntime(kQuickThrowNullPointer,
399 instruction_,
400 instruction_->GetDexPc(),
401 this);
402 CheckEntrypointTypes<kQuickThrowNullPointer, void, void>();
403 }
404
405 bool IsFatal() const OVERRIDE { return true; }
406
407 const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathARMVIXL"; }
408
409 private:
410 DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARMVIXL);
411};
412
Scott Wakelingfe885462016-09-22 10:24:38 +0100413class DivZeroCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
414 public:
415 explicit DivZeroCheckSlowPathARMVIXL(HDivZeroCheck* instruction)
416 : SlowPathCodeARMVIXL(instruction) {}
417
418 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100419 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
Scott Wakelingfe885462016-09-22 10:24:38 +0100420 __ Bind(GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100421 arm_codegen->InvokeRuntime(kQuickThrowDivZero, instruction_, instruction_->GetDexPc(), this);
Scott Wakelingfe885462016-09-22 10:24:38 +0100422 CheckEntrypointTypes<kQuickThrowDivZero, void, void>();
423 }
424
425 bool IsFatal() const OVERRIDE { return true; }
426
427 const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathARMVIXL"; }
428
429 private:
430 DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathARMVIXL);
431};
432
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100433class SuspendCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
434 public:
435 SuspendCheckSlowPathARMVIXL(HSuspendCheck* instruction, HBasicBlock* successor)
436 : SlowPathCodeARMVIXL(instruction), successor_(successor) {}
437
438 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
439 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
440 __ Bind(GetEntryLabel());
441 arm_codegen->InvokeRuntime(kQuickTestSuspend, instruction_, instruction_->GetDexPc(), this);
442 CheckEntrypointTypes<kQuickTestSuspend, void, void>();
443 if (successor_ == nullptr) {
444 __ B(GetReturnLabel());
445 } else {
446 __ B(arm_codegen->GetLabelOf(successor_));
447 }
448 }
449
450 vixl32::Label* GetReturnLabel() {
451 DCHECK(successor_ == nullptr);
452 return &return_label_;
453 }
454
455 HBasicBlock* GetSuccessor() const {
456 return successor_;
457 }
458
459 const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathARMVIXL"; }
460
461 private:
462 // If not null, the block to branch to after the suspend check.
463 HBasicBlock* const successor_;
464
465 // If `successor_` is null, the label to branch to after the suspend check.
466 vixl32::Label return_label_;
467
468 DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathARMVIXL);
469};
470
Scott Wakelingc34dba72016-10-03 10:14:44 +0100471class BoundsCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
472 public:
473 explicit BoundsCheckSlowPathARMVIXL(HBoundsCheck* instruction)
474 : SlowPathCodeARMVIXL(instruction) {}
475
476 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
477 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
478 LocationSummary* locations = instruction_->GetLocations();
479
480 __ Bind(GetEntryLabel());
481 if (instruction_->CanThrowIntoCatchBlock()) {
482 // Live registers will be restored in the catch block if caught.
483 SaveLiveRegisters(codegen, instruction_->GetLocations());
484 }
485 // We're moving two locations to locations that could overlap, so we need a parallel
486 // move resolver.
487 InvokeRuntimeCallingConventionARMVIXL calling_convention;
488 codegen->EmitParallelMoves(
489 locations->InAt(0),
490 LocationFrom(calling_convention.GetRegisterAt(0)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100491 DataType::Type::kInt32,
Scott Wakelingc34dba72016-10-03 10:14:44 +0100492 locations->InAt(1),
493 LocationFrom(calling_convention.GetRegisterAt(1)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100494 DataType::Type::kInt32);
Scott Wakelingc34dba72016-10-03 10:14:44 +0100495 QuickEntrypointEnum entrypoint = instruction_->AsBoundsCheck()->IsStringCharAt()
496 ? kQuickThrowStringBounds
497 : kQuickThrowArrayBounds;
498 arm_codegen->InvokeRuntime(entrypoint, instruction_, instruction_->GetDexPc(), this);
499 CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>();
500 CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>();
501 }
502
503 bool IsFatal() const OVERRIDE { return true; }
504
505 const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathARMVIXL"; }
506
507 private:
508 DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARMVIXL);
509};
510
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100511class LoadClassSlowPathARMVIXL : public SlowPathCodeARMVIXL {
512 public:
513 LoadClassSlowPathARMVIXL(HLoadClass* cls, HInstruction* at, uint32_t dex_pc, bool do_clinit)
Vladimir Marko6bec91c2017-01-09 15:03:12 +0000514 : SlowPathCodeARMVIXL(at), cls_(cls), dex_pc_(dex_pc), do_clinit_(do_clinit) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100515 DCHECK(at->IsLoadClass() || at->IsClinitCheck());
516 }
517
518 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
Vladimir Marko6bec91c2017-01-09 15:03:12 +0000519 LocationSummary* locations = instruction_->GetLocations();
Vladimir Markoea4c1262017-02-06 19:59:33 +0000520 Location out = locations->Out();
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100521
522 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
523 __ Bind(GetEntryLabel());
524 SaveLiveRegisters(codegen, locations);
525
526 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Vladimir Marko6bec91c2017-01-09 15:03:12 +0000527 dex::TypeIndex type_index = cls_->GetTypeIndex();
528 __ Mov(calling_convention.GetRegisterAt(0), type_index.index_);
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100529 QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage
530 : kQuickInitializeType;
Vladimir Marko6bec91c2017-01-09 15:03:12 +0000531 arm_codegen->InvokeRuntime(entrypoint, instruction_, dex_pc_, this);
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100532 if (do_clinit_) {
533 CheckEntrypointTypes<kQuickInitializeStaticStorage, void*, uint32_t>();
534 } else {
535 CheckEntrypointTypes<kQuickInitializeType, void*, uint32_t>();
536 }
537
538 // Move the class to the desired location.
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100539 if (out.IsValid()) {
540 DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
541 arm_codegen->Move32(locations->Out(), LocationFrom(r0));
542 }
543 RestoreLiveRegisters(codegen, locations);
544 __ B(GetExitLabel());
545 }
546
547 const char* GetDescription() const OVERRIDE { return "LoadClassSlowPathARMVIXL"; }
548
549 private:
550 // The class this slow path will load.
551 HLoadClass* const cls_;
552
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100553 // The dex PC of `at_`.
554 const uint32_t dex_pc_;
555
556 // Whether to initialize the class.
557 const bool do_clinit_;
558
559 DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathARMVIXL);
560};
561
Artem Serovd4cc5b22016-11-04 11:19:09 +0000562class LoadStringSlowPathARMVIXL : public SlowPathCodeARMVIXL {
563 public:
564 explicit LoadStringSlowPathARMVIXL(HLoadString* instruction)
565 : SlowPathCodeARMVIXL(instruction) {}
566
567 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
Vladimir Markoea4c1262017-02-06 19:59:33 +0000568 DCHECK(instruction_->IsLoadString());
569 DCHECK_EQ(instruction_->AsLoadString()->GetLoadKind(), HLoadString::LoadKind::kBssEntry);
Artem Serovd4cc5b22016-11-04 11:19:09 +0000570 LocationSummary* locations = instruction_->GetLocations();
571 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
Vladimir Markof3c52b42017-11-17 17:32:12 +0000572 const dex::StringIndex string_index = instruction_->AsLoadString()->GetStringIndex();
Artem Serovd4cc5b22016-11-04 11:19:09 +0000573
574 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
575 __ Bind(GetEntryLabel());
576 SaveLiveRegisters(codegen, locations);
577
578 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Vladimir Marko6bec91c2017-01-09 15:03:12 +0000579 __ Mov(calling_convention.GetRegisterAt(0), string_index.index_);
Artem Serovd4cc5b22016-11-04 11:19:09 +0000580 arm_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), this);
581 CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
582
Artem Serovd4cc5b22016-11-04 11:19:09 +0000583 arm_codegen->Move32(locations->Out(), LocationFrom(r0));
584 RestoreLiveRegisters(codegen, locations);
585
586 __ B(GetExitLabel());
587 }
588
589 const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathARMVIXL"; }
590
591 private:
592 DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathARMVIXL);
593};
594
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100595class TypeCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
596 public:
597 TypeCheckSlowPathARMVIXL(HInstruction* instruction, bool is_fatal)
598 : SlowPathCodeARMVIXL(instruction), is_fatal_(is_fatal) {}
599
600 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
601 LocationSummary* locations = instruction_->GetLocations();
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100602 DCHECK(instruction_->IsCheckCast()
603 || !locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
604
605 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
606 __ Bind(GetEntryLabel());
607
Vladimir Marko87584542017-12-12 17:47:52 +0000608 if (!is_fatal_ || instruction_->CanThrowIntoCatchBlock()) {
Artem Serovcfbe9132016-10-14 15:58:56 +0100609 SaveLiveRegisters(codegen, locations);
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100610 }
611
612 // We're moving two locations to locations that could overlap, so we need a parallel
613 // move resolver.
614 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100615
Mathieu Chartier9fd8c602016-11-14 14:38:53 -0800616 codegen->EmitParallelMoves(locations->InAt(0),
Mathieu Chartierb99f4d62016-11-07 16:17:26 -0800617 LocationFrom(calling_convention.GetRegisterAt(0)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100618 DataType::Type::kReference,
Mathieu Chartier9fd8c602016-11-14 14:38:53 -0800619 locations->InAt(1),
Mathieu Chartierb99f4d62016-11-07 16:17:26 -0800620 LocationFrom(calling_convention.GetRegisterAt(1)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100621 DataType::Type::kReference);
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100622 if (instruction_->IsInstanceOf()) {
Artem Serovcfbe9132016-10-14 15:58:56 +0100623 arm_codegen->InvokeRuntime(kQuickInstanceofNonTrivial,
624 instruction_,
625 instruction_->GetDexPc(),
626 this);
Mathieu Chartier9fd8c602016-11-14 14:38:53 -0800627 CheckEntrypointTypes<kQuickInstanceofNonTrivial, size_t, mirror::Object*, mirror::Class*>();
Artem Serovcfbe9132016-10-14 15:58:56 +0100628 arm_codegen->Move32(locations->Out(), LocationFrom(r0));
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100629 } else {
630 DCHECK(instruction_->IsCheckCast());
Mathieu Chartierb99f4d62016-11-07 16:17:26 -0800631 arm_codegen->InvokeRuntime(kQuickCheckInstanceOf,
632 instruction_,
633 instruction_->GetDexPc(),
634 this);
635 CheckEntrypointTypes<kQuickCheckInstanceOf, void, mirror::Object*, mirror::Class*>();
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100636 }
637
638 if (!is_fatal_) {
Artem Serovcfbe9132016-10-14 15:58:56 +0100639 RestoreLiveRegisters(codegen, locations);
640 __ B(GetExitLabel());
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100641 }
642 }
643
644 const char* GetDescription() const OVERRIDE { return "TypeCheckSlowPathARMVIXL"; }
645
646 bool IsFatal() const OVERRIDE { return is_fatal_; }
647
648 private:
649 const bool is_fatal_;
650
651 DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathARMVIXL);
652};
653
Scott Wakelingc34dba72016-10-03 10:14:44 +0100654class DeoptimizationSlowPathARMVIXL : public SlowPathCodeARMVIXL {
655 public:
656 explicit DeoptimizationSlowPathARMVIXL(HDeoptimize* instruction)
657 : SlowPathCodeARMVIXL(instruction) {}
658
659 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
660 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
661 __ Bind(GetEntryLabel());
Nicolas Geoffray4e92c3c2017-05-08 09:34:26 +0100662 LocationSummary* locations = instruction_->GetLocations();
663 SaveLiveRegisters(codegen, locations);
664 InvokeRuntimeCallingConventionARMVIXL calling_convention;
665 __ Mov(calling_convention.GetRegisterAt(0),
666 static_cast<uint32_t>(instruction_->AsDeoptimize()->GetDeoptimizationKind()));
667
Scott Wakelingc34dba72016-10-03 10:14:44 +0100668 arm_codegen->InvokeRuntime(kQuickDeoptimize, instruction_, instruction_->GetDexPc(), this);
Nicolas Geoffray4e92c3c2017-05-08 09:34:26 +0100669 CheckEntrypointTypes<kQuickDeoptimize, void, DeoptimizationKind>();
Scott Wakelingc34dba72016-10-03 10:14:44 +0100670 }
671
672 const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathARMVIXL"; }
673
674 private:
675 DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathARMVIXL);
676};
677
678class ArraySetSlowPathARMVIXL : public SlowPathCodeARMVIXL {
679 public:
680 explicit ArraySetSlowPathARMVIXL(HInstruction* instruction) : SlowPathCodeARMVIXL(instruction) {}
681
682 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
683 LocationSummary* locations = instruction_->GetLocations();
684 __ Bind(GetEntryLabel());
685 SaveLiveRegisters(codegen, locations);
686
687 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Vladimir Markoca6fff82017-10-03 14:49:14 +0100688 HParallelMove parallel_move(codegen->GetGraph()->GetAllocator());
Scott Wakelingc34dba72016-10-03 10:14:44 +0100689 parallel_move.AddMove(
690 locations->InAt(0),
691 LocationFrom(calling_convention.GetRegisterAt(0)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100692 DataType::Type::kReference,
Scott Wakelingc34dba72016-10-03 10:14:44 +0100693 nullptr);
694 parallel_move.AddMove(
695 locations->InAt(1),
696 LocationFrom(calling_convention.GetRegisterAt(1)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100697 DataType::Type::kInt32,
Scott Wakelingc34dba72016-10-03 10:14:44 +0100698 nullptr);
699 parallel_move.AddMove(
700 locations->InAt(2),
701 LocationFrom(calling_convention.GetRegisterAt(2)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100702 DataType::Type::kReference,
Scott Wakelingc34dba72016-10-03 10:14:44 +0100703 nullptr);
704 codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
705
706 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
707 arm_codegen->InvokeRuntime(kQuickAputObject, instruction_, instruction_->GetDexPc(), this);
708 CheckEntrypointTypes<kQuickAputObject, void, mirror::Array*, int32_t, mirror::Object*>();
709 RestoreLiveRegisters(codegen, locations);
710 __ B(GetExitLabel());
711 }
712
713 const char* GetDescription() const OVERRIDE { return "ArraySetSlowPathARMVIXL"; }
714
715 private:
716 DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathARMVIXL);
717};
718
Roland Levillain54f869e2017-03-06 13:54:11 +0000719// Abstract base class for read barrier slow paths marking a reference
720// `ref`.
Roland Levillain27b1f9c2017-01-17 16:56:34 +0000721//
Roland Levillain54f869e2017-03-06 13:54:11 +0000722// Argument `entrypoint` must be a register location holding the read
Roland Levillain6d729a72017-06-30 18:34:01 +0100723// barrier marking runtime entry point to be invoked or an empty
724// location; in the latter case, the read barrier marking runtime
725// entry point will be loaded by the slow path code itself.
Roland Levillain54f869e2017-03-06 13:54:11 +0000726class ReadBarrierMarkSlowPathBaseARMVIXL : public SlowPathCodeARMVIXL {
727 protected:
728 ReadBarrierMarkSlowPathBaseARMVIXL(HInstruction* instruction, Location ref, Location entrypoint)
Roland Levillain27b1f9c2017-01-17 16:56:34 +0000729 : SlowPathCodeARMVIXL(instruction), ref_(ref), entrypoint_(entrypoint) {
730 DCHECK(kEmitCompilerReadBarrier);
731 }
732
Roland Levillain54f869e2017-03-06 13:54:11 +0000733 const char* GetDescription() const OVERRIDE { return "ReadBarrierMarkSlowPathBaseARMVIXL"; }
Roland Levillain27b1f9c2017-01-17 16:56:34 +0000734
Roland Levillain54f869e2017-03-06 13:54:11 +0000735 // Generate assembly code calling the read barrier marking runtime
736 // entry point (ReadBarrierMarkRegX).
737 void GenerateReadBarrierMarkRuntimeCall(CodeGenerator* codegen) {
Roland Levillain27b1f9c2017-01-17 16:56:34 +0000738 vixl32::Register ref_reg = RegisterFrom(ref_);
Roland Levillain47b3ab22017-02-27 14:31:35 +0000739
Roland Levillain47b3ab22017-02-27 14:31:35 +0000740 // No need to save live registers; it's taken care of by the
741 // entrypoint. Also, there is no need to update the stack mask,
742 // as this runtime call will not trigger a garbage collection.
743 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
744 DCHECK(!ref_reg.Is(sp));
745 DCHECK(!ref_reg.Is(lr));
746 DCHECK(!ref_reg.Is(pc));
747 // IP is used internally by the ReadBarrierMarkRegX entry point
748 // as a temporary, it cannot be the entry point's input/output.
749 DCHECK(!ref_reg.Is(ip));
750 DCHECK(ref_reg.IsRegister()) << ref_reg;
751 // "Compact" slow path, saving two moves.
752 //
753 // Instead of using the standard runtime calling convention (input
754 // and output in R0):
755 //
756 // R0 <- ref
757 // R0 <- ReadBarrierMark(R0)
758 // ref <- R0
759 //
760 // we just use rX (the register containing `ref`) as input and output
761 // of a dedicated entrypoint:
762 //
763 // rX <- ReadBarrierMarkRegX(rX)
764 //
765 if (entrypoint_.IsValid()) {
766 arm_codegen->ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction_, this);
767 __ Blx(RegisterFrom(entrypoint_));
768 } else {
Roland Levillain54f869e2017-03-06 13:54:11 +0000769 // Entrypoint is not already loaded, load from the thread.
Roland Levillain47b3ab22017-02-27 14:31:35 +0000770 int32_t entry_point_offset =
Roland Levillain97c46462017-05-11 14:04:03 +0100771 Thread::ReadBarrierMarkEntryPointsOffset<kArmPointerSize>(ref_reg.GetCode());
Roland Levillain47b3ab22017-02-27 14:31:35 +0000772 // This runtime call does not require a stack map.
773 arm_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
774 }
Roland Levillain47b3ab22017-02-27 14:31:35 +0000775 }
776
Roland Levillain47b3ab22017-02-27 14:31:35 +0000777 // The location (register) of the marked object reference.
778 const Location ref_;
779
780 // The location of the entrypoint if already loaded.
781 const Location entrypoint_;
782
Roland Levillain54f869e2017-03-06 13:54:11 +0000783 private:
784 DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkSlowPathBaseARMVIXL);
Roland Levillain47b3ab22017-02-27 14:31:35 +0000785};
786
Scott Wakelingc34dba72016-10-03 10:14:44 +0100787// Slow path marking an object reference `ref` during a read
788// barrier. The field `obj.field` in the object `obj` holding this
Roland Levillain54f869e2017-03-06 13:54:11 +0000789// reference does not get updated by this slow path after marking.
Roland Levillain47b3ab22017-02-27 14:31:35 +0000790//
Scott Wakelingc34dba72016-10-03 10:14:44 +0100791// This means that after the execution of this slow path, `ref` will
Roland Levillain27b1f9c2017-01-17 16:56:34 +0000792// always be up-to-date, but `obj.field` may not; i.e., after the
793// flip, `ref` will be a to-space reference, but `obj.field` will
794// probably still be a from-space reference (unless it gets updated by
795// another thread, or if another thread installed another object
796// reference (different from `ref`) in `obj.field`).
Roland Levillainba650a42017-03-06 13:52:32 +0000797//
Roland Levillain6d729a72017-06-30 18:34:01 +0100798// Argument `entrypoint` must be a register location holding the read
799// barrier marking runtime entry point to be invoked or an empty
800// location; in the latter case, the read barrier marking runtime
801// entry point will be loaded by the slow path code itself.
Roland Levillain54f869e2017-03-06 13:54:11 +0000802class ReadBarrierMarkSlowPathARMVIXL : public ReadBarrierMarkSlowPathBaseARMVIXL {
Roland Levillain47b3ab22017-02-27 14:31:35 +0000803 public:
Roland Levillain27b1f9c2017-01-17 16:56:34 +0000804 ReadBarrierMarkSlowPathARMVIXL(HInstruction* instruction,
805 Location ref,
806 Location entrypoint = Location::NoLocation())
Roland Levillain54f869e2017-03-06 13:54:11 +0000807 : ReadBarrierMarkSlowPathBaseARMVIXL(instruction, ref, entrypoint) {
Roland Levillain47b3ab22017-02-27 14:31:35 +0000808 DCHECK(kEmitCompilerReadBarrier);
809 }
810
Roland Levillain47b3ab22017-02-27 14:31:35 +0000811 const char* GetDescription() const OVERRIDE { return "ReadBarrierMarkSlowPathARMVIXL"; }
812
813 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
814 LocationSummary* locations = instruction_->GetLocations();
Roland Levillain54f869e2017-03-06 13:54:11 +0000815 DCHECK(locations->CanCall());
816 DCHECK(ref_.IsRegister()) << ref_;
817 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_.reg())) << ref_.reg();
818 DCHECK(instruction_->IsLoadClass() || instruction_->IsLoadString())
819 << "Unexpected instruction in read barrier marking slow path: "
820 << instruction_->DebugName();
821
822 __ Bind(GetEntryLabel());
823 GenerateReadBarrierMarkRuntimeCall(codegen);
824 __ B(GetExitLabel());
825 }
826
827 private:
828 DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkSlowPathARMVIXL);
829};
830
831// Slow path loading `obj`'s lock word, loading a reference from
832// object `*(obj + offset + (index << scale_factor))` into `ref`, and
833// marking `ref` if `obj` is gray according to the lock word (Baker
834// read barrier). The field `obj.field` in the object `obj` holding
835// this reference does not get updated by this slow path after marking
836// (see LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARMVIXL
837// below for that).
838//
839// This means that after the execution of this slow path, `ref` will
840// always be up-to-date, but `obj.field` may not; i.e., after the
841// flip, `ref` will be a to-space reference, but `obj.field` will
842// probably still be a from-space reference (unless it gets updated by
843// another thread, or if another thread installed another object
844// reference (different from `ref`) in `obj.field`).
845//
846// Argument `entrypoint` must be a register location holding the read
Roland Levillain6d729a72017-06-30 18:34:01 +0100847// barrier marking runtime entry point to be invoked or an empty
848// location; in the latter case, the read barrier marking runtime
849// entry point will be loaded by the slow path code itself.
Roland Levillain54f869e2017-03-06 13:54:11 +0000850class LoadReferenceWithBakerReadBarrierSlowPathARMVIXL : public ReadBarrierMarkSlowPathBaseARMVIXL {
851 public:
852 LoadReferenceWithBakerReadBarrierSlowPathARMVIXL(HInstruction* instruction,
853 Location ref,
854 vixl32::Register obj,
855 uint32_t offset,
856 Location index,
857 ScaleFactor scale_factor,
858 bool needs_null_check,
859 vixl32::Register temp,
Roland Levillain6d729a72017-06-30 18:34:01 +0100860 Location entrypoint = Location::NoLocation())
Roland Levillain54f869e2017-03-06 13:54:11 +0000861 : ReadBarrierMarkSlowPathBaseARMVIXL(instruction, ref, entrypoint),
862 obj_(obj),
863 offset_(offset),
864 index_(index),
865 scale_factor_(scale_factor),
866 needs_null_check_(needs_null_check),
867 temp_(temp) {
868 DCHECK(kEmitCompilerReadBarrier);
869 DCHECK(kUseBakerReadBarrier);
870 }
871
Roland Levillain47b3ab22017-02-27 14:31:35 +0000872 const char* GetDescription() const OVERRIDE {
Roland Levillain54f869e2017-03-06 13:54:11 +0000873 return "LoadReferenceWithBakerReadBarrierSlowPathARMVIXL";
Roland Levillain47b3ab22017-02-27 14:31:35 +0000874 }
875
876 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
877 LocationSummary* locations = instruction_->GetLocations();
878 vixl32::Register ref_reg = RegisterFrom(ref_);
879 DCHECK(locations->CanCall());
880 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_reg.GetCode())) << ref_reg;
Roland Levillain47b3ab22017-02-27 14:31:35 +0000881 DCHECK(instruction_->IsInstanceFieldGet() ||
882 instruction_->IsStaticFieldGet() ||
883 instruction_->IsArrayGet() ||
884 instruction_->IsArraySet() ||
Roland Levillain47b3ab22017-02-27 14:31:35 +0000885 instruction_->IsInstanceOf() ||
886 instruction_->IsCheckCast() ||
887 (instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()) ||
888 (instruction_->IsInvokeStaticOrDirect() && instruction_->GetLocations()->Intrinsified()))
889 << "Unexpected instruction in read barrier marking slow path: "
890 << instruction_->DebugName();
891 // The read barrier instrumentation of object ArrayGet
892 // instructions does not support the HIntermediateAddress
893 // instruction.
894 DCHECK(!(instruction_->IsArrayGet() &&
895 instruction_->AsArrayGet()->GetArray()->IsIntermediateAddress()));
896
Roland Levillain54f869e2017-03-06 13:54:11 +0000897 // Temporary register `temp_`, used to store the lock word, must
898 // not be IP, as we may use it to emit the reference load (in the
899 // call to GenerateRawReferenceLoad below), and we need the lock
900 // word to still be in `temp_` after the reference load.
901 DCHECK(!temp_.Is(ip));
902
Roland Levillain47b3ab22017-02-27 14:31:35 +0000903 __ Bind(GetEntryLabel());
Roland Levillain54f869e2017-03-06 13:54:11 +0000904
905 // When using MaybeGenerateReadBarrierSlow, the read barrier call is
906 // inserted after the original load. However, in fast path based
907 // Baker's read barriers, we need to perform the load of
908 // mirror::Object::monitor_ *before* the original reference load.
909 // This load-load ordering is required by the read barrier.
Roland Levillainff487002017-03-07 16:50:01 +0000910 // The slow path (for Baker's algorithm) should look like:
Roland Levillain54f869e2017-03-06 13:54:11 +0000911 //
912 // uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
913 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
914 // HeapReference<mirror::Object> ref = *src; // Original reference load.
915 // bool is_gray = (rb_state == ReadBarrier::GrayState());
916 // if (is_gray) {
917 // ref = entrypoint(ref); // ref = ReadBarrier::Mark(ref); // Runtime entry point call.
918 // }
919 //
920 // Note: the original implementation in ReadBarrier::Barrier is
921 // slightly more complex as it performs additional checks that we do
922 // not do here for performance reasons.
923
Roland Levillain47b3ab22017-02-27 14:31:35 +0000924 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
Roland Levillain54f869e2017-03-06 13:54:11 +0000925
926 // /* int32_t */ monitor = obj->monitor_
927 uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
928 arm_codegen->GetAssembler()->LoadFromOffset(kLoadWord, temp_, obj_, monitor_offset);
929 if (needs_null_check_) {
930 codegen->MaybeRecordImplicitNullCheck(instruction_);
Roland Levillain47b3ab22017-02-27 14:31:35 +0000931 }
Roland Levillain54f869e2017-03-06 13:54:11 +0000932 // /* LockWord */ lock_word = LockWord(monitor)
933 static_assert(sizeof(LockWord) == sizeof(int32_t),
934 "art::LockWord and int32_t have different sizes.");
935
936 // Introduce a dependency on the lock_word including the rb_state,
937 // which shall prevent load-load reordering without using
938 // a memory barrier (which would be more expensive).
939 // `obj` is unchanged by this operation, but its value now depends
940 // on `temp`.
941 __ Add(obj_, obj_, Operand(temp_, ShiftType::LSR, 32));
942
943 // The actual reference load.
944 // A possible implicit null check has already been handled above.
945 arm_codegen->GenerateRawReferenceLoad(
946 instruction_, ref_, obj_, offset_, index_, scale_factor_, /* needs_null_check */ false);
947
948 // Mark the object `ref` when `obj` is gray.
949 //
950 // if (rb_state == ReadBarrier::GrayState())
951 // ref = ReadBarrier::Mark(ref);
952 //
953 // Given the numeric representation, it's enough to check the low bit of the
954 // rb_state. We do that by shifting the bit out of the lock word with LSRS
955 // which can be a 16-bit instruction unlike the TST immediate.
956 static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
957 static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
958 __ Lsrs(temp_, temp_, LockWord::kReadBarrierStateShift + 1);
959 __ B(cc, GetExitLabel()); // Carry flag is the last bit shifted out by LSRS.
960 GenerateReadBarrierMarkRuntimeCall(codegen);
961
Roland Levillain47b3ab22017-02-27 14:31:35 +0000962 __ B(GetExitLabel());
963 }
964
965 private:
Roland Levillain54f869e2017-03-06 13:54:11 +0000966 // The register containing the object holding the marked object reference field.
967 vixl32::Register obj_;
968 // The offset, index and scale factor to access the reference in `obj_`.
969 uint32_t offset_;
970 Location index_;
971 ScaleFactor scale_factor_;
972 // Is a null check required?
973 bool needs_null_check_;
974 // A temporary register used to hold the lock word of `obj_`.
975 vixl32::Register temp_;
Roland Levillain47b3ab22017-02-27 14:31:35 +0000976
Roland Levillain54f869e2017-03-06 13:54:11 +0000977 DISALLOW_COPY_AND_ASSIGN(LoadReferenceWithBakerReadBarrierSlowPathARMVIXL);
Roland Levillain47b3ab22017-02-27 14:31:35 +0000978};
979
Roland Levillain54f869e2017-03-06 13:54:11 +0000980// Slow path loading `obj`'s lock word, loading a reference from
981// object `*(obj + offset + (index << scale_factor))` into `ref`, and
982// marking `ref` if `obj` is gray according to the lock word (Baker
983// read barrier). If needed, this slow path also atomically updates
984// the field `obj.field` in the object `obj` holding this reference
985// after marking (contrary to
986// LoadReferenceWithBakerReadBarrierSlowPathARMVIXL above, which never
987// tries to update `obj.field`).
Roland Levillain47b3ab22017-02-27 14:31:35 +0000988//
989// This means that after the execution of this slow path, both `ref`
990// and `obj.field` will be up-to-date; i.e., after the flip, both will
991// hold the same to-space reference (unless another thread installed
992// another object reference (different from `ref`) in `obj.field`).
Roland Levillainba650a42017-03-06 13:52:32 +0000993//
Roland Levillain54f869e2017-03-06 13:54:11 +0000994// Argument `entrypoint` must be a register location holding the read
Roland Levillain6d729a72017-06-30 18:34:01 +0100995// barrier marking runtime entry point to be invoked or an empty
996// location; in the latter case, the read barrier marking runtime
997// entry point will be loaded by the slow path code itself.
Roland Levillain54f869e2017-03-06 13:54:11 +0000998class LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARMVIXL
999 : public ReadBarrierMarkSlowPathBaseARMVIXL {
Roland Levillain47b3ab22017-02-27 14:31:35 +00001000 public:
Roland Levillain6d729a72017-06-30 18:34:01 +01001001 LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARMVIXL(
1002 HInstruction* instruction,
1003 Location ref,
1004 vixl32::Register obj,
1005 uint32_t offset,
1006 Location index,
1007 ScaleFactor scale_factor,
1008 bool needs_null_check,
1009 vixl32::Register temp1,
1010 vixl32::Register temp2,
1011 Location entrypoint = Location::NoLocation())
Roland Levillain54f869e2017-03-06 13:54:11 +00001012 : ReadBarrierMarkSlowPathBaseARMVIXL(instruction, ref, entrypoint),
Roland Levillain47b3ab22017-02-27 14:31:35 +00001013 obj_(obj),
Roland Levillain54f869e2017-03-06 13:54:11 +00001014 offset_(offset),
1015 index_(index),
1016 scale_factor_(scale_factor),
1017 needs_null_check_(needs_null_check),
Roland Levillain47b3ab22017-02-27 14:31:35 +00001018 temp1_(temp1),
Roland Levillain54f869e2017-03-06 13:54:11 +00001019 temp2_(temp2) {
Roland Levillain47b3ab22017-02-27 14:31:35 +00001020 DCHECK(kEmitCompilerReadBarrier);
Roland Levillain54f869e2017-03-06 13:54:11 +00001021 DCHECK(kUseBakerReadBarrier);
Roland Levillain47b3ab22017-02-27 14:31:35 +00001022 }
1023
1024 const char* GetDescription() const OVERRIDE {
Roland Levillain54f869e2017-03-06 13:54:11 +00001025 return "LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARMVIXL";
Roland Levillain47b3ab22017-02-27 14:31:35 +00001026 }
1027
1028 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
1029 LocationSummary* locations = instruction_->GetLocations();
1030 vixl32::Register ref_reg = RegisterFrom(ref_);
1031 DCHECK(locations->CanCall());
1032 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_reg.GetCode())) << ref_reg;
Roland Levillain54f869e2017-03-06 13:54:11 +00001033 DCHECK_NE(ref_.reg(), LocationFrom(temp1_).reg());
1034
1035 // This slow path is only used by the UnsafeCASObject intrinsic at the moment.
Roland Levillain47b3ab22017-02-27 14:31:35 +00001036 DCHECK((instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()))
1037 << "Unexpected instruction in read barrier marking and field updating slow path: "
1038 << instruction_->DebugName();
1039 DCHECK(instruction_->GetLocations()->Intrinsified());
1040 DCHECK_EQ(instruction_->AsInvoke()->GetIntrinsic(), Intrinsics::kUnsafeCASObject);
Roland Levillain54f869e2017-03-06 13:54:11 +00001041 DCHECK_EQ(offset_, 0u);
1042 DCHECK_EQ(scale_factor_, ScaleFactor::TIMES_1);
1043 Location field_offset = index_;
1044 DCHECK(field_offset.IsRegisterPair()) << field_offset;
1045
1046 // Temporary register `temp1_`, used to store the lock word, must
1047 // not be IP, as we may use it to emit the reference load (in the
1048 // call to GenerateRawReferenceLoad below), and we need the lock
1049 // word to still be in `temp1_` after the reference load.
1050 DCHECK(!temp1_.Is(ip));
Roland Levillain47b3ab22017-02-27 14:31:35 +00001051
1052 __ Bind(GetEntryLabel());
1053
Roland Levillainff487002017-03-07 16:50:01 +00001054 // The implementation is similar to LoadReferenceWithBakerReadBarrierSlowPathARMVIXL's:
1055 //
1056 // uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
1057 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
1058 // HeapReference<mirror::Object> ref = *src; // Original reference load.
1059 // bool is_gray = (rb_state == ReadBarrier::GrayState());
1060 // if (is_gray) {
1061 // old_ref = ref;
1062 // ref = entrypoint(ref); // ref = ReadBarrier::Mark(ref); // Runtime entry point call.
1063 // compareAndSwapObject(obj, field_offset, old_ref, ref);
1064 // }
1065
Roland Levillain54f869e2017-03-06 13:54:11 +00001066 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
1067
1068 // /* int32_t */ monitor = obj->monitor_
1069 uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
1070 arm_codegen->GetAssembler()->LoadFromOffset(kLoadWord, temp1_, obj_, monitor_offset);
1071 if (needs_null_check_) {
1072 codegen->MaybeRecordImplicitNullCheck(instruction_);
1073 }
1074 // /* LockWord */ lock_word = LockWord(monitor)
1075 static_assert(sizeof(LockWord) == sizeof(int32_t),
1076 "art::LockWord and int32_t have different sizes.");
1077
1078 // Introduce a dependency on the lock_word including the rb_state,
1079 // which shall prevent load-load reordering without using
1080 // a memory barrier (which would be more expensive).
1081 // `obj` is unchanged by this operation, but its value now depends
1082 // on `temp`.
1083 __ Add(obj_, obj_, Operand(temp1_, ShiftType::LSR, 32));
1084
1085 // The actual reference load.
1086 // A possible implicit null check has already been handled above.
1087 arm_codegen->GenerateRawReferenceLoad(
1088 instruction_, ref_, obj_, offset_, index_, scale_factor_, /* needs_null_check */ false);
1089
1090 // Mark the object `ref` when `obj` is gray.
1091 //
1092 // if (rb_state == ReadBarrier::GrayState())
1093 // ref = ReadBarrier::Mark(ref);
1094 //
1095 // Given the numeric representation, it's enough to check the low bit of the
1096 // rb_state. We do that by shifting the bit out of the lock word with LSRS
1097 // which can be a 16-bit instruction unlike the TST immediate.
1098 static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
1099 static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
1100 __ Lsrs(temp1_, temp1_, LockWord::kReadBarrierStateShift + 1);
1101 __ B(cc, GetExitLabel()); // Carry flag is the last bit shifted out by LSRS.
1102
1103 // Save the old value of the reference before marking it.
Roland Levillain47b3ab22017-02-27 14:31:35 +00001104 // Note that we cannot use IP to save the old reference, as IP is
1105 // used internally by the ReadBarrierMarkRegX entry point, and we
1106 // need the old reference after the call to that entry point.
1107 DCHECK(!temp1_.Is(ip));
1108 __ Mov(temp1_, ref_reg);
Roland Levillain27b1f9c2017-01-17 16:56:34 +00001109
Roland Levillain54f869e2017-03-06 13:54:11 +00001110 GenerateReadBarrierMarkRuntimeCall(codegen);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001111
1112 // If the new reference is different from the old reference,
Roland Levillain54f869e2017-03-06 13:54:11 +00001113 // update the field in the holder (`*(obj_ + field_offset)`).
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001114 //
1115 // Note that this field could also hold a different object, if
1116 // another thread had concurrently changed it. In that case, the
Anton Kirilov349e61f2017-12-15 17:11:33 +00001117 // LDREX/CMP/BNE sequence of instructions in the compare-and-set
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001118 // (CAS) operation below would abort the CAS, leaving the field
1119 // as-is.
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001120 __ Cmp(temp1_, ref_reg);
Roland Levillain54f869e2017-03-06 13:54:11 +00001121 __ B(eq, GetExitLabel());
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001122
1123 // Update the the holder's field atomically. This may fail if
1124 // mutator updates before us, but it's OK. This is achieved
1125 // using a strong compare-and-set (CAS) operation with relaxed
1126 // memory synchronization ordering, where the expected value is
1127 // the old reference and the desired value is the new reference.
1128
1129 UseScratchRegisterScope temps(arm_codegen->GetVIXLAssembler());
1130 // Convenience aliases.
1131 vixl32::Register base = obj_;
1132 // The UnsafeCASObject intrinsic uses a register pair as field
1133 // offset ("long offset"), of which only the low part contains
1134 // data.
Roland Levillain54f869e2017-03-06 13:54:11 +00001135 vixl32::Register offset = LowRegisterFrom(field_offset);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001136 vixl32::Register expected = temp1_;
1137 vixl32::Register value = ref_reg;
1138 vixl32::Register tmp_ptr = temps.Acquire(); // Pointer to actual memory.
1139 vixl32::Register tmp = temp2_; // Value in memory.
1140
1141 __ Add(tmp_ptr, base, offset);
1142
1143 if (kPoisonHeapReferences) {
1144 arm_codegen->GetAssembler()->PoisonHeapReference(expected);
1145 if (value.Is(expected)) {
1146 // Do not poison `value`, as it is the same register as
1147 // `expected`, which has just been poisoned.
1148 } else {
1149 arm_codegen->GetAssembler()->PoisonHeapReference(value);
1150 }
1151 }
1152
1153 // do {
1154 // tmp = [r_ptr] - expected;
1155 // } while (tmp == 0 && failure([r_ptr] <- r_new_value));
1156
Anton Kirilov349e61f2017-12-15 17:11:33 +00001157 vixl32::Label loop_head, comparison_failed, exit_loop;
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001158 __ Bind(&loop_head);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001159 __ Ldrex(tmp, MemOperand(tmp_ptr));
Anton Kirilov349e61f2017-12-15 17:11:33 +00001160 __ Cmp(tmp, expected);
1161 __ B(ne, &comparison_failed, /* far_target */ false);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001162 __ Strex(tmp, value, MemOperand(tmp_ptr));
Anton Kirilov349e61f2017-12-15 17:11:33 +00001163 __ CompareAndBranchIfZero(tmp, &exit_loop, /* far_target */ false);
1164 __ B(&loop_head);
1165 __ Bind(&comparison_failed);
1166 __ Clrex();
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001167 __ Bind(&exit_loop);
1168
1169 if (kPoisonHeapReferences) {
1170 arm_codegen->GetAssembler()->UnpoisonHeapReference(expected);
1171 if (value.Is(expected)) {
1172 // Do not unpoison `value`, as it is the same register as
1173 // `expected`, which has just been unpoisoned.
1174 } else {
1175 arm_codegen->GetAssembler()->UnpoisonHeapReference(value);
1176 }
1177 }
1178
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001179 __ B(GetExitLabel());
1180 }
1181
1182 private:
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001183 // The register containing the object holding the marked object reference field.
1184 const vixl32::Register obj_;
Roland Levillain54f869e2017-03-06 13:54:11 +00001185 // The offset, index and scale factor to access the reference in `obj_`.
1186 uint32_t offset_;
1187 Location index_;
1188 ScaleFactor scale_factor_;
1189 // Is a null check required?
1190 bool needs_null_check_;
1191 // A temporary register used to hold the lock word of `obj_`; and
1192 // also to hold the original reference value, when the reference is
1193 // marked.
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001194 const vixl32::Register temp1_;
Roland Levillain54f869e2017-03-06 13:54:11 +00001195 // A temporary register used in the implementation of the CAS, to
1196 // update the object's reference field.
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001197 const vixl32::Register temp2_;
1198
Roland Levillain54f869e2017-03-06 13:54:11 +00001199 DISALLOW_COPY_AND_ASSIGN(LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARMVIXL);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001200};
1201
1202// Slow path generating a read barrier for a heap reference.
1203class ReadBarrierForHeapReferenceSlowPathARMVIXL : public SlowPathCodeARMVIXL {
1204 public:
1205 ReadBarrierForHeapReferenceSlowPathARMVIXL(HInstruction* instruction,
1206 Location out,
1207 Location ref,
1208 Location obj,
1209 uint32_t offset,
1210 Location index)
1211 : SlowPathCodeARMVIXL(instruction),
1212 out_(out),
1213 ref_(ref),
1214 obj_(obj),
1215 offset_(offset),
1216 index_(index) {
1217 DCHECK(kEmitCompilerReadBarrier);
1218 // If `obj` is equal to `out` or `ref`, it means the initial object
1219 // has been overwritten by (or after) the heap object reference load
1220 // to be instrumented, e.g.:
1221 //
1222 // __ LoadFromOffset(kLoadWord, out, out, offset);
1223 // codegen_->GenerateReadBarrierSlow(instruction, out_loc, out_loc, out_loc, offset);
1224 //
1225 // In that case, we have lost the information about the original
1226 // object, and the emitted read barrier cannot work properly.
1227 DCHECK(!obj.Equals(out)) << "obj=" << obj << " out=" << out;
1228 DCHECK(!obj.Equals(ref)) << "obj=" << obj << " ref=" << ref;
1229 }
1230
1231 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
1232 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
1233 LocationSummary* locations = instruction_->GetLocations();
1234 vixl32::Register reg_out = RegisterFrom(out_);
1235 DCHECK(locations->CanCall());
1236 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out.GetCode()));
1237 DCHECK(instruction_->IsInstanceFieldGet() ||
1238 instruction_->IsStaticFieldGet() ||
1239 instruction_->IsArrayGet() ||
1240 instruction_->IsInstanceOf() ||
1241 instruction_->IsCheckCast() ||
Andreas Gamped9911ee2017-03-27 13:27:24 -07001242 (instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()))
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001243 << "Unexpected instruction in read barrier for heap reference slow path: "
1244 << instruction_->DebugName();
1245 // The read barrier instrumentation of object ArrayGet
1246 // instructions does not support the HIntermediateAddress
1247 // instruction.
1248 DCHECK(!(instruction_->IsArrayGet() &&
1249 instruction_->AsArrayGet()->GetArray()->IsIntermediateAddress()));
1250
1251 __ Bind(GetEntryLabel());
1252 SaveLiveRegisters(codegen, locations);
1253
1254 // We may have to change the index's value, but as `index_` is a
1255 // constant member (like other "inputs" of this slow path),
1256 // introduce a copy of it, `index`.
1257 Location index = index_;
1258 if (index_.IsValid()) {
1259 // Handle `index_` for HArrayGet and UnsafeGetObject/UnsafeGetObjectVolatile intrinsics.
1260 if (instruction_->IsArrayGet()) {
1261 // Compute the actual memory offset and store it in `index`.
1262 vixl32::Register index_reg = RegisterFrom(index_);
1263 DCHECK(locations->GetLiveRegisters()->ContainsCoreRegister(index_reg.GetCode()));
1264 if (codegen->IsCoreCalleeSaveRegister(index_reg.GetCode())) {
1265 // We are about to change the value of `index_reg` (see the
Roland Levillain9983e302017-07-14 14:34:22 +01001266 // calls to art::arm::ArmVIXLMacroAssembler::Lsl and
1267 // art::arm::ArmVIXLMacroAssembler::Add below), but it has
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001268 // not been saved by the previous call to
1269 // art::SlowPathCode::SaveLiveRegisters, as it is a
1270 // callee-save register --
1271 // art::SlowPathCode::SaveLiveRegisters does not consider
1272 // callee-save registers, as it has been designed with the
1273 // assumption that callee-save registers are supposed to be
1274 // handled by the called function. So, as a callee-save
1275 // register, `index_reg` _would_ eventually be saved onto
1276 // the stack, but it would be too late: we would have
1277 // changed its value earlier. Therefore, we manually save
1278 // it here into another freely available register,
1279 // `free_reg`, chosen of course among the caller-save
1280 // registers (as a callee-save `free_reg` register would
1281 // exhibit the same problem).
1282 //
1283 // Note we could have requested a temporary register from
1284 // the register allocator instead; but we prefer not to, as
1285 // this is a slow path, and we know we can find a
1286 // caller-save register that is available.
1287 vixl32::Register free_reg = FindAvailableCallerSaveRegister(codegen);
1288 __ Mov(free_reg, index_reg);
1289 index_reg = free_reg;
1290 index = LocationFrom(index_reg);
1291 } else {
1292 // The initial register stored in `index_` has already been
1293 // saved in the call to art::SlowPathCode::SaveLiveRegisters
1294 // (as it is not a callee-save register), so we can freely
1295 // use it.
1296 }
1297 // Shifting the index value contained in `index_reg` by the scale
1298 // factor (2) cannot overflow in practice, as the runtime is
1299 // unable to allocate object arrays with a size larger than
1300 // 2^26 - 1 (that is, 2^28 - 4 bytes).
1301 __ Lsl(index_reg, index_reg, TIMES_4);
1302 static_assert(
1303 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
1304 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
1305 __ Add(index_reg, index_reg, offset_);
1306 } else {
1307 // In the case of the UnsafeGetObject/UnsafeGetObjectVolatile
1308 // intrinsics, `index_` is not shifted by a scale factor of 2
1309 // (as in the case of ArrayGet), as it is actually an offset
1310 // to an object field within an object.
1311 DCHECK(instruction_->IsInvoke()) << instruction_->DebugName();
1312 DCHECK(instruction_->GetLocations()->Intrinsified());
1313 DCHECK((instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObject) ||
1314 (instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile))
1315 << instruction_->AsInvoke()->GetIntrinsic();
1316 DCHECK_EQ(offset_, 0U);
1317 DCHECK(index_.IsRegisterPair());
1318 // UnsafeGet's offset location is a register pair, the low
1319 // part contains the correct offset.
1320 index = index_.ToLow();
1321 }
1322 }
1323
1324 // We're moving two or three locations to locations that could
1325 // overlap, so we need a parallel move resolver.
1326 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Vladimir Markoca6fff82017-10-03 14:49:14 +01001327 HParallelMove parallel_move(codegen->GetGraph()->GetAllocator());
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001328 parallel_move.AddMove(ref_,
1329 LocationFrom(calling_convention.GetRegisterAt(0)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001330 DataType::Type::kReference,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001331 nullptr);
1332 parallel_move.AddMove(obj_,
1333 LocationFrom(calling_convention.GetRegisterAt(1)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001334 DataType::Type::kReference,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001335 nullptr);
1336 if (index.IsValid()) {
1337 parallel_move.AddMove(index,
1338 LocationFrom(calling_convention.GetRegisterAt(2)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001339 DataType::Type::kInt32,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00001340 nullptr);
1341 codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
1342 } else {
1343 codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
1344 __ Mov(calling_convention.GetRegisterAt(2), offset_);
1345 }
1346 arm_codegen->InvokeRuntime(kQuickReadBarrierSlow, instruction_, instruction_->GetDexPc(), this);
1347 CheckEntrypointTypes<
1348 kQuickReadBarrierSlow, mirror::Object*, mirror::Object*, mirror::Object*, uint32_t>();
1349 arm_codegen->Move32(out_, LocationFrom(r0));
1350
1351 RestoreLiveRegisters(codegen, locations);
1352 __ B(GetExitLabel());
1353 }
1354
1355 const char* GetDescription() const OVERRIDE {
1356 return "ReadBarrierForHeapReferenceSlowPathARMVIXL";
1357 }
1358
1359 private:
1360 vixl32::Register FindAvailableCallerSaveRegister(CodeGenerator* codegen) {
1361 uint32_t ref = RegisterFrom(ref_).GetCode();
1362 uint32_t obj = RegisterFrom(obj_).GetCode();
1363 for (uint32_t i = 0, e = codegen->GetNumberOfCoreRegisters(); i < e; ++i) {
1364 if (i != ref && i != obj && !codegen->IsCoreCalleeSaveRegister(i)) {
1365 return vixl32::Register(i);
1366 }
1367 }
1368 // We shall never fail to find a free caller-save register, as
1369 // there are more than two core caller-save registers on ARM
1370 // (meaning it is possible to find one which is different from
1371 // `ref` and `obj`).
1372 DCHECK_GT(codegen->GetNumberOfCoreCallerSaveRegisters(), 2u);
1373 LOG(FATAL) << "Could not find a free caller-save register";
1374 UNREACHABLE();
1375 }
1376
1377 const Location out_;
1378 const Location ref_;
1379 const Location obj_;
1380 const uint32_t offset_;
1381 // An additional location containing an index to an array.
1382 // Only used for HArrayGet and the UnsafeGetObject &
1383 // UnsafeGetObjectVolatile intrinsics.
1384 const Location index_;
1385
1386 DISALLOW_COPY_AND_ASSIGN(ReadBarrierForHeapReferenceSlowPathARMVIXL);
1387};
1388
1389// Slow path generating a read barrier for a GC root.
1390class ReadBarrierForRootSlowPathARMVIXL : public SlowPathCodeARMVIXL {
1391 public:
1392 ReadBarrierForRootSlowPathARMVIXL(HInstruction* instruction, Location out, Location root)
1393 : SlowPathCodeARMVIXL(instruction), out_(out), root_(root) {
1394 DCHECK(kEmitCompilerReadBarrier);
1395 }
1396
1397 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
1398 LocationSummary* locations = instruction_->GetLocations();
1399 vixl32::Register reg_out = RegisterFrom(out_);
1400 DCHECK(locations->CanCall());
1401 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out.GetCode()));
1402 DCHECK(instruction_->IsLoadClass() || instruction_->IsLoadString())
1403 << "Unexpected instruction in read barrier for GC root slow path: "
1404 << instruction_->DebugName();
1405
1406 __ Bind(GetEntryLabel());
1407 SaveLiveRegisters(codegen, locations);
1408
1409 InvokeRuntimeCallingConventionARMVIXL calling_convention;
1410 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
1411 arm_codegen->Move32(LocationFrom(calling_convention.GetRegisterAt(0)), root_);
1412 arm_codegen->InvokeRuntime(kQuickReadBarrierForRootSlow,
1413 instruction_,
1414 instruction_->GetDexPc(),
1415 this);
1416 CheckEntrypointTypes<kQuickReadBarrierForRootSlow, mirror::Object*, GcRoot<mirror::Object>*>();
1417 arm_codegen->Move32(out_, LocationFrom(r0));
1418
1419 RestoreLiveRegisters(codegen, locations);
1420 __ B(GetExitLabel());
1421 }
1422
1423 const char* GetDescription() const OVERRIDE { return "ReadBarrierForRootSlowPathARMVIXL"; }
1424
1425 private:
1426 const Location out_;
1427 const Location root_;
1428
1429 DISALLOW_COPY_AND_ASSIGN(ReadBarrierForRootSlowPathARMVIXL);
1430};
Scott Wakelingc34dba72016-10-03 10:14:44 +01001431
Scott Wakelingfe885462016-09-22 10:24:38 +01001432inline vixl32::Condition ARMCondition(IfCondition cond) {
1433 switch (cond) {
1434 case kCondEQ: return eq;
1435 case kCondNE: return ne;
1436 case kCondLT: return lt;
1437 case kCondLE: return le;
1438 case kCondGT: return gt;
1439 case kCondGE: return ge;
1440 case kCondB: return lo;
1441 case kCondBE: return ls;
1442 case kCondA: return hi;
1443 case kCondAE: return hs;
1444 }
1445 LOG(FATAL) << "Unreachable";
1446 UNREACHABLE();
1447}
1448
1449// Maps signed condition to unsigned condition.
1450inline vixl32::Condition ARMUnsignedCondition(IfCondition cond) {
1451 switch (cond) {
1452 case kCondEQ: return eq;
1453 case kCondNE: return ne;
1454 // Signed to unsigned.
1455 case kCondLT: return lo;
1456 case kCondLE: return ls;
1457 case kCondGT: return hi;
1458 case kCondGE: return hs;
1459 // Unsigned remain unchanged.
1460 case kCondB: return lo;
1461 case kCondBE: return ls;
1462 case kCondA: return hi;
1463 case kCondAE: return hs;
1464 }
1465 LOG(FATAL) << "Unreachable";
1466 UNREACHABLE();
1467}
1468
1469inline vixl32::Condition ARMFPCondition(IfCondition cond, bool gt_bias) {
1470 // The ARM condition codes can express all the necessary branches, see the
1471 // "Meaning (floating-point)" column in the table A8-1 of the ARMv7 reference manual.
1472 // There is no dex instruction or HIR that would need the missing conditions
1473 // "equal or unordered" or "not equal".
1474 switch (cond) {
1475 case kCondEQ: return eq;
1476 case kCondNE: return ne /* unordered */;
1477 case kCondLT: return gt_bias ? cc : lt /* unordered */;
1478 case kCondLE: return gt_bias ? ls : le /* unordered */;
1479 case kCondGT: return gt_bias ? hi /* unordered */ : gt;
1480 case kCondGE: return gt_bias ? cs /* unordered */ : ge;
1481 default:
1482 LOG(FATAL) << "UNREACHABLE";
1483 UNREACHABLE();
1484 }
1485}
1486
Anton Kirilov74234da2017-01-13 14:42:47 +00001487inline ShiftType ShiftFromOpKind(HDataProcWithShifterOp::OpKind op_kind) {
1488 switch (op_kind) {
1489 case HDataProcWithShifterOp::kASR: return ShiftType::ASR;
1490 case HDataProcWithShifterOp::kLSL: return ShiftType::LSL;
1491 case HDataProcWithShifterOp::kLSR: return ShiftType::LSR;
1492 default:
1493 LOG(FATAL) << "Unexpected op kind " << op_kind;
1494 UNREACHABLE();
1495 }
1496}
1497
Scott Wakelingfe885462016-09-22 10:24:38 +01001498void CodeGeneratorARMVIXL::DumpCoreRegister(std::ostream& stream, int reg) const {
1499 stream << vixl32::Register(reg);
1500}
1501
1502void CodeGeneratorARMVIXL::DumpFloatingPointRegister(std::ostream& stream, int reg) const {
1503 stream << vixl32::SRegister(reg);
1504}
1505
Vladimir Markoa0431112018-06-25 09:32:54 +01001506const ArmInstructionSetFeatures& CodeGeneratorARMVIXL::GetInstructionSetFeatures() const {
1507 return *GetCompilerOptions().GetInstructionSetFeatures()->AsArmInstructionSetFeatures();
1508}
1509
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001510static uint32_t ComputeSRegisterListMask(const SRegisterList& regs) {
Scott Wakelingfe885462016-09-22 10:24:38 +01001511 uint32_t mask = 0;
1512 for (uint32_t i = regs.GetFirstSRegister().GetCode();
1513 i <= regs.GetLastSRegister().GetCode();
1514 ++i) {
1515 mask |= (1 << i);
1516 }
1517 return mask;
1518}
1519
Artem Serovd4cc5b22016-11-04 11:19:09 +00001520// Saves the register in the stack. Returns the size taken on stack.
1521size_t CodeGeneratorARMVIXL::SaveCoreRegister(size_t stack_index ATTRIBUTE_UNUSED,
1522 uint32_t reg_id ATTRIBUTE_UNUSED) {
1523 TODO_VIXL32(FATAL);
1524 return 0;
1525}
1526
1527// Restores the register from the stack. Returns the size taken on stack.
1528size_t CodeGeneratorARMVIXL::RestoreCoreRegister(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::SaveFloatingPointRegister(size_t stack_index ATTRIBUTE_UNUSED,
1535 uint32_t reg_id ATTRIBUTE_UNUSED) {
1536 TODO_VIXL32(FATAL);
1537 return 0;
1538}
1539
1540size_t CodeGeneratorARMVIXL::RestoreFloatingPointRegister(size_t stack_index ATTRIBUTE_UNUSED,
1541 uint32_t reg_id ATTRIBUTE_UNUSED) {
1542 TODO_VIXL32(FATAL);
1543 return 0;
Anton Kirilove28d9ae2016-10-25 18:17:23 +01001544}
1545
Anton Kirilov74234da2017-01-13 14:42:47 +00001546static void GenerateDataProcInstruction(HInstruction::InstructionKind kind,
1547 vixl32::Register out,
1548 vixl32::Register first,
1549 const Operand& second,
1550 CodeGeneratorARMVIXL* codegen) {
1551 if (second.IsImmediate() && second.GetImmediate() == 0) {
1552 const Operand in = kind == HInstruction::kAnd
1553 ? Operand(0)
1554 : Operand(first);
1555
1556 __ Mov(out, in);
1557 } else {
1558 switch (kind) {
1559 case HInstruction::kAdd:
1560 __ Add(out, first, second);
1561 break;
1562 case HInstruction::kAnd:
1563 __ And(out, first, second);
1564 break;
1565 case HInstruction::kOr:
1566 __ Orr(out, first, second);
1567 break;
1568 case HInstruction::kSub:
1569 __ Sub(out, first, second);
1570 break;
1571 case HInstruction::kXor:
1572 __ Eor(out, first, second);
1573 break;
1574 default:
1575 LOG(FATAL) << "Unexpected instruction kind: " << kind;
1576 UNREACHABLE();
1577 }
1578 }
1579}
1580
1581static void GenerateDataProc(HInstruction::InstructionKind kind,
1582 const Location& out,
1583 const Location& first,
1584 const Operand& second_lo,
1585 const Operand& second_hi,
1586 CodeGeneratorARMVIXL* codegen) {
1587 const vixl32::Register first_hi = HighRegisterFrom(first);
1588 const vixl32::Register first_lo = LowRegisterFrom(first);
1589 const vixl32::Register out_hi = HighRegisterFrom(out);
1590 const vixl32::Register out_lo = LowRegisterFrom(out);
1591
1592 if (kind == HInstruction::kAdd) {
1593 __ Adds(out_lo, first_lo, second_lo);
1594 __ Adc(out_hi, first_hi, second_hi);
1595 } else if (kind == HInstruction::kSub) {
1596 __ Subs(out_lo, first_lo, second_lo);
1597 __ Sbc(out_hi, first_hi, second_hi);
1598 } else {
1599 GenerateDataProcInstruction(kind, out_lo, first_lo, second_lo, codegen);
1600 GenerateDataProcInstruction(kind, out_hi, first_hi, second_hi, codegen);
1601 }
1602}
1603
1604static Operand GetShifterOperand(vixl32::Register rm, ShiftType shift, uint32_t shift_imm) {
1605 return shift_imm == 0 ? Operand(rm) : Operand(rm, shift, shift_imm);
1606}
1607
1608static void GenerateLongDataProc(HDataProcWithShifterOp* instruction,
1609 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001610 DCHECK_EQ(instruction->GetType(), DataType::Type::kInt64);
Anton Kirilov74234da2017-01-13 14:42:47 +00001611 DCHECK(HDataProcWithShifterOp::IsShiftOp(instruction->GetOpKind()));
1612
1613 const LocationSummary* const locations = instruction->GetLocations();
1614 const uint32_t shift_value = instruction->GetShiftAmount();
1615 const HInstruction::InstructionKind kind = instruction->GetInstrKind();
1616 const Location first = locations->InAt(0);
1617 const Location second = locations->InAt(1);
1618 const Location out = locations->Out();
1619 const vixl32::Register first_hi = HighRegisterFrom(first);
1620 const vixl32::Register first_lo = LowRegisterFrom(first);
1621 const vixl32::Register out_hi = HighRegisterFrom(out);
1622 const vixl32::Register out_lo = LowRegisterFrom(out);
1623 const vixl32::Register second_hi = HighRegisterFrom(second);
1624 const vixl32::Register second_lo = LowRegisterFrom(second);
1625 const ShiftType shift = ShiftFromOpKind(instruction->GetOpKind());
1626
1627 if (shift_value >= 32) {
1628 if (shift == ShiftType::LSL) {
1629 GenerateDataProcInstruction(kind,
1630 out_hi,
1631 first_hi,
1632 Operand(second_lo, ShiftType::LSL, shift_value - 32),
1633 codegen);
1634 GenerateDataProcInstruction(kind, out_lo, first_lo, 0, codegen);
1635 } else if (shift == ShiftType::ASR) {
1636 GenerateDataProc(kind,
1637 out,
1638 first,
1639 GetShifterOperand(second_hi, ShiftType::ASR, shift_value - 32),
1640 Operand(second_hi, ShiftType::ASR, 31),
1641 codegen);
1642 } else {
1643 DCHECK_EQ(shift, ShiftType::LSR);
1644 GenerateDataProc(kind,
1645 out,
1646 first,
1647 GetShifterOperand(second_hi, ShiftType::LSR, shift_value - 32),
1648 0,
1649 codegen);
1650 }
1651 } else {
1652 DCHECK_GT(shift_value, 1U);
1653 DCHECK_LT(shift_value, 32U);
1654
1655 UseScratchRegisterScope temps(codegen->GetVIXLAssembler());
1656
1657 if (shift == ShiftType::LSL) {
1658 // We are not doing this for HInstruction::kAdd because the output will require
1659 // Location::kOutputOverlap; not applicable to other cases.
1660 if (kind == HInstruction::kOr || kind == HInstruction::kXor) {
1661 GenerateDataProcInstruction(kind,
1662 out_hi,
1663 first_hi,
1664 Operand(second_hi, ShiftType::LSL, shift_value),
1665 codegen);
1666 GenerateDataProcInstruction(kind,
1667 out_hi,
1668 out_hi,
1669 Operand(second_lo, ShiftType::LSR, 32 - shift_value),
1670 codegen);
1671 GenerateDataProcInstruction(kind,
1672 out_lo,
1673 first_lo,
1674 Operand(second_lo, ShiftType::LSL, shift_value),
1675 codegen);
1676 } else {
1677 const vixl32::Register temp = temps.Acquire();
1678
1679 __ Lsl(temp, second_hi, shift_value);
1680 __ Orr(temp, temp, Operand(second_lo, ShiftType::LSR, 32 - shift_value));
1681 GenerateDataProc(kind,
1682 out,
1683 first,
1684 Operand(second_lo, ShiftType::LSL, shift_value),
1685 temp,
1686 codegen);
1687 }
1688 } else {
1689 DCHECK(shift == ShiftType::ASR || shift == ShiftType::LSR);
1690
1691 // We are not doing this for HInstruction::kAdd because the output will require
1692 // Location::kOutputOverlap; not applicable to other cases.
1693 if (kind == HInstruction::kOr || kind == HInstruction::kXor) {
1694 GenerateDataProcInstruction(kind,
1695 out_lo,
1696 first_lo,
1697 Operand(second_lo, ShiftType::LSR, shift_value),
1698 codegen);
1699 GenerateDataProcInstruction(kind,
1700 out_lo,
1701 out_lo,
1702 Operand(second_hi, ShiftType::LSL, 32 - shift_value),
1703 codegen);
1704 GenerateDataProcInstruction(kind,
1705 out_hi,
1706 first_hi,
1707 Operand(second_hi, shift, shift_value),
1708 codegen);
1709 } else {
1710 const vixl32::Register temp = temps.Acquire();
1711
1712 __ Lsr(temp, second_lo, shift_value);
1713 __ Orr(temp, temp, Operand(second_hi, ShiftType::LSL, 32 - shift_value));
1714 GenerateDataProc(kind,
1715 out,
1716 first,
1717 temp,
1718 Operand(second_hi, shift, shift_value),
1719 codegen);
1720 }
1721 }
1722 }
1723}
1724
Donghui Bai426b49c2016-11-08 14:55:38 +08001725static void GenerateVcmp(HInstruction* instruction, CodeGeneratorARMVIXL* codegen) {
1726 const Location rhs_loc = instruction->GetLocations()->InAt(1);
1727 if (rhs_loc.IsConstant()) {
1728 // 0.0 is the only immediate that can be encoded directly in
1729 // a VCMP instruction.
1730 //
1731 // Both the JLS (section 15.20.1) and the JVMS (section 6.5)
1732 // specify that in a floating-point comparison, positive zero
1733 // and negative zero are considered equal, so we can use the
1734 // literal 0.0 for both cases here.
1735 //
1736 // Note however that some methods (Float.equal, Float.compare,
1737 // Float.compareTo, Double.equal, Double.compare,
1738 // Double.compareTo, Math.max, Math.min, StrictMath.max,
1739 // StrictMath.min) consider 0.0 to be (strictly) greater than
1740 // -0.0. So if we ever translate calls to these methods into a
1741 // HCompare instruction, we must handle the -0.0 case with
1742 // care here.
1743 DCHECK(rhs_loc.GetConstant()->IsArithmeticZero());
1744
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001745 const DataType::Type type = instruction->InputAt(0)->GetType();
Donghui Bai426b49c2016-11-08 14:55:38 +08001746
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001747 if (type == DataType::Type::kFloat32) {
Donghui Bai426b49c2016-11-08 14:55:38 +08001748 __ Vcmp(F32, InputSRegisterAt(instruction, 0), 0.0);
1749 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001750 DCHECK_EQ(type, DataType::Type::kFloat64);
Donghui Bai426b49c2016-11-08 14:55:38 +08001751 __ Vcmp(F64, InputDRegisterAt(instruction, 0), 0.0);
1752 }
1753 } else {
1754 __ Vcmp(InputVRegisterAt(instruction, 0), InputVRegisterAt(instruction, 1));
1755 }
1756}
1757
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001758static int64_t AdjustConstantForCondition(int64_t value,
1759 IfCondition* condition,
1760 IfCondition* opposite) {
1761 if (value == 1) {
1762 if (*condition == kCondB) {
1763 value = 0;
1764 *condition = kCondEQ;
1765 *opposite = kCondNE;
1766 } else if (*condition == kCondAE) {
1767 value = 0;
1768 *condition = kCondNE;
1769 *opposite = kCondEQ;
1770 }
1771 } else if (value == -1) {
1772 if (*condition == kCondGT) {
1773 value = 0;
1774 *condition = kCondGE;
1775 *opposite = kCondLT;
1776 } else if (*condition == kCondLE) {
1777 value = 0;
1778 *condition = kCondLT;
1779 *opposite = kCondGE;
1780 }
1781 }
1782
1783 return value;
1784}
1785
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001786static std::pair<vixl32::Condition, vixl32::Condition> GenerateLongTestConstant(
1787 HCondition* condition,
1788 bool invert,
1789 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001790 DCHECK_EQ(condition->GetLeft()->GetType(), DataType::Type::kInt64);
Donghui Bai426b49c2016-11-08 14:55:38 +08001791
1792 const LocationSummary* const locations = condition->GetLocations();
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001793 IfCondition cond = condition->GetCondition();
1794 IfCondition opposite = condition->GetOppositeCondition();
1795
1796 if (invert) {
1797 std::swap(cond, opposite);
1798 }
1799
1800 std::pair<vixl32::Condition, vixl32::Condition> ret(eq, ne);
Donghui Bai426b49c2016-11-08 14:55:38 +08001801 const Location left = locations->InAt(0);
1802 const Location right = locations->InAt(1);
1803
1804 DCHECK(right.IsConstant());
1805
1806 const vixl32::Register left_high = HighRegisterFrom(left);
1807 const vixl32::Register left_low = LowRegisterFrom(left);
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001808 int64_t value = AdjustConstantForCondition(Int64ConstantFrom(right), &cond, &opposite);
1809 UseScratchRegisterScope temps(codegen->GetVIXLAssembler());
1810
1811 // Comparisons against 0 are common enough to deserve special attention.
1812 if (value == 0) {
1813 switch (cond) {
1814 case kCondNE:
1815 // x > 0 iff x != 0 when the comparison is unsigned.
1816 case kCondA:
1817 ret = std::make_pair(ne, eq);
1818 FALLTHROUGH_INTENDED;
1819 case kCondEQ:
1820 // x <= 0 iff x == 0 when the comparison is unsigned.
1821 case kCondBE:
1822 __ Orrs(temps.Acquire(), left_low, left_high);
1823 return ret;
1824 case kCondLT:
1825 case kCondGE:
1826 __ Cmp(left_high, 0);
1827 return std::make_pair(ARMCondition(cond), ARMCondition(opposite));
1828 // Trivially true or false.
1829 case kCondB:
1830 ret = std::make_pair(ne, eq);
1831 FALLTHROUGH_INTENDED;
1832 case kCondAE:
1833 __ Cmp(left_low, left_low);
1834 return ret;
1835 default:
1836 break;
1837 }
1838 }
Donghui Bai426b49c2016-11-08 14:55:38 +08001839
1840 switch (cond) {
1841 case kCondEQ:
1842 case kCondNE:
1843 case kCondB:
1844 case kCondBE:
1845 case kCondA:
1846 case kCondAE: {
Anton Kirilov23b752b2017-07-20 14:40:44 +01001847 const uint32_t value_low = Low32Bits(value);
1848 Operand operand_low(value_low);
1849
Donghui Bai426b49c2016-11-08 14:55:38 +08001850 __ Cmp(left_high, High32Bits(value));
1851
Anton Kirilov23b752b2017-07-20 14:40:44 +01001852 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
1853 // we must ensure that the operands corresponding to the least significant
1854 // halves of the inputs fit into a 16-bit CMP encoding.
1855 if (!left_low.IsLow() || !IsUint<8>(value_low)) {
1856 operand_low = Operand(temps.Acquire());
1857 __ Mov(LeaveFlags, operand_low.GetBaseRegister(), value_low);
1858 }
1859
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001860 // We use the scope because of the IT block that follows.
Donghui Bai426b49c2016-11-08 14:55:38 +08001861 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
1862 2 * vixl32::k16BitT32InstructionSizeInBytes,
1863 CodeBufferCheckScope::kExactSize);
1864
1865 __ it(eq);
Anton Kirilov23b752b2017-07-20 14:40:44 +01001866 __ cmp(eq, left_low, operand_low);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001867 ret = std::make_pair(ARMUnsignedCondition(cond), ARMUnsignedCondition(opposite));
Donghui Bai426b49c2016-11-08 14:55:38 +08001868 break;
1869 }
1870 case kCondLE:
1871 case kCondGT:
1872 // Trivially true or false.
1873 if (value == std::numeric_limits<int64_t>::max()) {
1874 __ Cmp(left_low, left_low);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001875 ret = cond == kCondLE ? std::make_pair(eq, ne) : std::make_pair(ne, eq);
Donghui Bai426b49c2016-11-08 14:55:38 +08001876 break;
1877 }
1878
1879 if (cond == kCondLE) {
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001880 DCHECK_EQ(opposite, kCondGT);
Donghui Bai426b49c2016-11-08 14:55:38 +08001881 cond = kCondLT;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001882 opposite = kCondGE;
Donghui Bai426b49c2016-11-08 14:55:38 +08001883 } else {
1884 DCHECK_EQ(cond, kCondGT);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001885 DCHECK_EQ(opposite, kCondLE);
Donghui Bai426b49c2016-11-08 14:55:38 +08001886 cond = kCondGE;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001887 opposite = kCondLT;
Donghui Bai426b49c2016-11-08 14:55:38 +08001888 }
1889
1890 value++;
1891 FALLTHROUGH_INTENDED;
1892 case kCondGE:
1893 case kCondLT: {
Donghui Bai426b49c2016-11-08 14:55:38 +08001894 __ Cmp(left_low, Low32Bits(value));
1895 __ Sbcs(temps.Acquire(), left_high, High32Bits(value));
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001896 ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite));
Donghui Bai426b49c2016-11-08 14:55:38 +08001897 break;
1898 }
1899 default:
1900 LOG(FATAL) << "Unreachable";
1901 UNREACHABLE();
1902 }
1903
1904 return ret;
1905}
1906
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001907static std::pair<vixl32::Condition, vixl32::Condition> GenerateLongTest(
1908 HCondition* condition,
1909 bool invert,
1910 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001911 DCHECK_EQ(condition->GetLeft()->GetType(), DataType::Type::kInt64);
Donghui Bai426b49c2016-11-08 14:55:38 +08001912
1913 const LocationSummary* const locations = condition->GetLocations();
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001914 IfCondition cond = condition->GetCondition();
1915 IfCondition opposite = condition->GetOppositeCondition();
1916
1917 if (invert) {
1918 std::swap(cond, opposite);
1919 }
1920
1921 std::pair<vixl32::Condition, vixl32::Condition> ret(eq, ne);
Donghui Bai426b49c2016-11-08 14:55:38 +08001922 Location left = locations->InAt(0);
1923 Location right = locations->InAt(1);
1924
1925 DCHECK(right.IsRegisterPair());
1926
1927 switch (cond) {
1928 case kCondEQ:
1929 case kCondNE:
1930 case kCondB:
1931 case kCondBE:
1932 case kCondA:
1933 case kCondAE: {
1934 __ Cmp(HighRegisterFrom(left), HighRegisterFrom(right));
1935
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001936 // We use the scope because of the IT block that follows.
Donghui Bai426b49c2016-11-08 14:55:38 +08001937 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
1938 2 * vixl32::k16BitT32InstructionSizeInBytes,
1939 CodeBufferCheckScope::kExactSize);
1940
1941 __ it(eq);
1942 __ cmp(eq, LowRegisterFrom(left), LowRegisterFrom(right));
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001943 ret = std::make_pair(ARMUnsignedCondition(cond), ARMUnsignedCondition(opposite));
Donghui Bai426b49c2016-11-08 14:55:38 +08001944 break;
1945 }
1946 case kCondLE:
1947 case kCondGT:
1948 if (cond == kCondLE) {
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001949 DCHECK_EQ(opposite, kCondGT);
Donghui Bai426b49c2016-11-08 14:55:38 +08001950 cond = kCondGE;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001951 opposite = kCondLT;
Donghui Bai426b49c2016-11-08 14:55:38 +08001952 } else {
1953 DCHECK_EQ(cond, kCondGT);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001954 DCHECK_EQ(opposite, kCondLE);
Donghui Bai426b49c2016-11-08 14:55:38 +08001955 cond = kCondLT;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001956 opposite = kCondGE;
Donghui Bai426b49c2016-11-08 14:55:38 +08001957 }
1958
1959 std::swap(left, right);
1960 FALLTHROUGH_INTENDED;
1961 case kCondGE:
1962 case kCondLT: {
1963 UseScratchRegisterScope temps(codegen->GetVIXLAssembler());
1964
1965 __ Cmp(LowRegisterFrom(left), LowRegisterFrom(right));
1966 __ Sbcs(temps.Acquire(), HighRegisterFrom(left), HighRegisterFrom(right));
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001967 ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite));
Donghui Bai426b49c2016-11-08 14:55:38 +08001968 break;
1969 }
1970 default:
1971 LOG(FATAL) << "Unreachable";
1972 UNREACHABLE();
1973 }
1974
1975 return ret;
1976}
1977
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001978static std::pair<vixl32::Condition, vixl32::Condition> GenerateTest(HCondition* condition,
1979 bool invert,
1980 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001981 const DataType::Type type = condition->GetLeft()->GetType();
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001982 IfCondition cond = condition->GetCondition();
1983 IfCondition opposite = condition->GetOppositeCondition();
1984 std::pair<vixl32::Condition, vixl32::Condition> ret(eq, ne);
Donghui Bai426b49c2016-11-08 14:55:38 +08001985
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001986 if (invert) {
1987 std::swap(cond, opposite);
1988 }
Donghui Bai426b49c2016-11-08 14:55:38 +08001989
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001990 if (type == DataType::Type::kInt64) {
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001991 ret = condition->GetLocations()->InAt(1).IsConstant()
1992 ? GenerateLongTestConstant(condition, invert, codegen)
1993 : GenerateLongTest(condition, invert, codegen);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001994 } else if (DataType::IsFloatingPointType(type)) {
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001995 GenerateVcmp(condition, codegen);
1996 __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
1997 ret = std::make_pair(ARMFPCondition(cond, condition->IsGtBias()),
1998 ARMFPCondition(opposite, condition->IsGtBias()));
Donghui Bai426b49c2016-11-08 14:55:38 +08001999 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002000 DCHECK(DataType::IsIntegralType(type) || type == DataType::Type::kReference) << type;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00002001 __ Cmp(InputRegisterAt(condition, 0), InputOperandAt(condition, 1));
2002 ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite));
Donghui Bai426b49c2016-11-08 14:55:38 +08002003 }
2004
2005 return ret;
2006}
2007
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002008static void GenerateConditionGeneric(HCondition* cond, CodeGeneratorARMVIXL* codegen) {
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002009 const vixl32::Register out = OutputRegister(cond);
2010 const auto condition = GenerateTest(cond, false, codegen);
2011
2012 __ Mov(LeaveFlags, out, 0);
2013
2014 if (out.IsLow()) {
2015 // We use the scope because of the IT block that follows.
2016 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
2017 2 * vixl32::k16BitT32InstructionSizeInBytes,
2018 CodeBufferCheckScope::kExactSize);
2019
2020 __ it(condition.first);
2021 __ mov(condition.first, out, 1);
2022 } else {
2023 vixl32::Label done_label;
2024 vixl32::Label* const final_label = codegen->GetFinalLabel(cond, &done_label);
2025
2026 __ B(condition.second, final_label, /* far_target */ false);
2027 __ Mov(out, 1);
2028
2029 if (done_label.IsReferenced()) {
2030 __ Bind(&done_label);
2031 }
2032 }
2033}
2034
2035static void GenerateEqualLong(HCondition* cond, CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002036 DCHECK_EQ(cond->GetLeft()->GetType(), DataType::Type::kInt64);
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002037
2038 const LocationSummary* const locations = cond->GetLocations();
2039 IfCondition condition = cond->GetCondition();
2040 const vixl32::Register out = OutputRegister(cond);
2041 const Location left = locations->InAt(0);
2042 const Location right = locations->InAt(1);
2043 vixl32::Register left_high = HighRegisterFrom(left);
2044 vixl32::Register left_low = LowRegisterFrom(left);
2045 vixl32::Register temp;
2046 UseScratchRegisterScope temps(codegen->GetVIXLAssembler());
2047
2048 if (right.IsConstant()) {
2049 IfCondition opposite = cond->GetOppositeCondition();
2050 const int64_t value = AdjustConstantForCondition(Int64ConstantFrom(right),
2051 &condition,
2052 &opposite);
2053 Operand right_high = High32Bits(value);
2054 Operand right_low = Low32Bits(value);
2055
2056 // The output uses Location::kNoOutputOverlap.
2057 if (out.Is(left_high)) {
2058 std::swap(left_low, left_high);
2059 std::swap(right_low, right_high);
2060 }
2061
2062 __ Sub(out, left_low, right_low);
2063 temp = temps.Acquire();
2064 __ Sub(temp, left_high, right_high);
2065 } else {
2066 DCHECK(right.IsRegisterPair());
2067 temp = temps.Acquire();
2068 __ Sub(temp, left_high, HighRegisterFrom(right));
2069 __ Sub(out, left_low, LowRegisterFrom(right));
2070 }
2071
2072 // Need to check after calling AdjustConstantForCondition().
2073 DCHECK(condition == kCondEQ || condition == kCondNE) << condition;
2074
2075 if (condition == kCondNE && out.IsLow()) {
2076 __ Orrs(out, out, temp);
2077
2078 // We use the scope because of the IT block that follows.
2079 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
2080 2 * vixl32::k16BitT32InstructionSizeInBytes,
2081 CodeBufferCheckScope::kExactSize);
2082
2083 __ it(ne);
2084 __ mov(ne, out, 1);
2085 } else {
2086 __ Orr(out, out, temp);
2087 codegen->GenerateConditionWithZero(condition, out, out, temp);
2088 }
2089}
2090
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002091static void GenerateConditionLong(HCondition* cond, CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002092 DCHECK_EQ(cond->GetLeft()->GetType(), DataType::Type::kInt64);
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002093
2094 const LocationSummary* const locations = cond->GetLocations();
2095 IfCondition condition = cond->GetCondition();
2096 const vixl32::Register out = OutputRegister(cond);
2097 const Location left = locations->InAt(0);
2098 const Location right = locations->InAt(1);
2099
2100 if (right.IsConstant()) {
2101 IfCondition opposite = cond->GetOppositeCondition();
2102
2103 // Comparisons against 0 are common enough to deserve special attention.
2104 if (AdjustConstantForCondition(Int64ConstantFrom(right), &condition, &opposite) == 0) {
2105 switch (condition) {
2106 case kCondNE:
2107 case kCondA:
2108 if (out.IsLow()) {
2109 // We only care if both input registers are 0 or not.
2110 __ Orrs(out, LowRegisterFrom(left), HighRegisterFrom(left));
2111
2112 // We use the scope because of the IT block that follows.
2113 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
2114 2 * vixl32::k16BitT32InstructionSizeInBytes,
2115 CodeBufferCheckScope::kExactSize);
2116
2117 __ it(ne);
2118 __ mov(ne, out, 1);
2119 return;
2120 }
2121
2122 FALLTHROUGH_INTENDED;
2123 case kCondEQ:
2124 case kCondBE:
2125 // We only care if both input registers are 0 or not.
2126 __ Orr(out, LowRegisterFrom(left), HighRegisterFrom(left));
2127 codegen->GenerateConditionWithZero(condition, out, out);
2128 return;
2129 case kCondLT:
2130 case kCondGE:
2131 // We only care about the sign bit.
2132 FALLTHROUGH_INTENDED;
2133 case kCondAE:
2134 case kCondB:
2135 codegen->GenerateConditionWithZero(condition, out, HighRegisterFrom(left));
2136 return;
2137 case kCondLE:
2138 case kCondGT:
2139 default:
2140 break;
2141 }
2142 }
2143 }
2144
Anton Kirilov23b752b2017-07-20 14:40:44 +01002145 // If `out` is a low register, then the GenerateConditionGeneric()
2146 // function generates a shorter code sequence that is still branchless.
2147 if ((condition == kCondEQ || condition == kCondNE) && !out.IsLow()) {
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002148 GenerateEqualLong(cond, codegen);
2149 return;
2150 }
2151
Anton Kirilov23b752b2017-07-20 14:40:44 +01002152 GenerateConditionGeneric(cond, codegen);
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002153}
2154
Roland Levillain6d729a72017-06-30 18:34:01 +01002155static void GenerateConditionIntegralOrNonPrimitive(HCondition* cond,
2156 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002157 const DataType::Type type = cond->GetLeft()->GetType();
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002158
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002159 DCHECK(DataType::IsIntegralType(type) || type == DataType::Type::kReference) << type;
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002160
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002161 if (type == DataType::Type::kInt64) {
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002162 GenerateConditionLong(cond, codegen);
2163 return;
2164 }
2165
2166 IfCondition condition = cond->GetCondition();
2167 vixl32::Register in = InputRegisterAt(cond, 0);
2168 const vixl32::Register out = OutputRegister(cond);
2169 const Location right = cond->GetLocations()->InAt(1);
2170 int64_t value;
2171
2172 if (right.IsConstant()) {
2173 IfCondition opposite = cond->GetOppositeCondition();
2174
2175 value = AdjustConstantForCondition(Int64ConstantFrom(right), &condition, &opposite);
2176
2177 // Comparisons against 0 are common enough to deserve special attention.
2178 if (value == 0) {
2179 switch (condition) {
2180 case kCondNE:
2181 case kCondA:
2182 if (out.IsLow() && out.Is(in)) {
2183 __ Cmp(out, 0);
2184
2185 // We use the scope because of the IT block that follows.
2186 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
2187 2 * vixl32::k16BitT32InstructionSizeInBytes,
2188 CodeBufferCheckScope::kExactSize);
2189
2190 __ it(ne);
2191 __ mov(ne, out, 1);
2192 return;
2193 }
2194
2195 FALLTHROUGH_INTENDED;
2196 case kCondEQ:
2197 case kCondBE:
2198 case kCondLT:
2199 case kCondGE:
2200 case kCondAE:
2201 case kCondB:
2202 codegen->GenerateConditionWithZero(condition, out, in);
2203 return;
2204 case kCondLE:
2205 case kCondGT:
2206 default:
2207 break;
2208 }
2209 }
2210 }
2211
2212 if (condition == kCondEQ || condition == kCondNE) {
2213 Operand operand(0);
2214
2215 if (right.IsConstant()) {
2216 operand = Operand::From(value);
2217 } else if (out.Is(RegisterFrom(right))) {
2218 // Avoid 32-bit instructions if possible.
2219 operand = InputOperandAt(cond, 0);
2220 in = RegisterFrom(right);
2221 } else {
2222 operand = InputOperandAt(cond, 1);
2223 }
2224
2225 if (condition == kCondNE && out.IsLow()) {
2226 __ Subs(out, in, operand);
2227
2228 // We use the scope because of the IT block that follows.
2229 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
2230 2 * vixl32::k16BitT32InstructionSizeInBytes,
2231 CodeBufferCheckScope::kExactSize);
2232
2233 __ it(ne);
2234 __ mov(ne, out, 1);
2235 } else {
2236 __ Sub(out, in, operand);
2237 codegen->GenerateConditionWithZero(condition, out, out);
2238 }
2239
2240 return;
2241 }
2242
2243 GenerateConditionGeneric(cond, codegen);
2244}
2245
Donghui Bai426b49c2016-11-08 14:55:38 +08002246static bool CanEncodeConstantAs8BitImmediate(HConstant* constant) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002247 const DataType::Type type = constant->GetType();
Donghui Bai426b49c2016-11-08 14:55:38 +08002248 bool ret = false;
2249
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002250 DCHECK(DataType::IsIntegralType(type) || type == DataType::Type::kReference) << type;
Donghui Bai426b49c2016-11-08 14:55:38 +08002251
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002252 if (type == DataType::Type::kInt64) {
Donghui Bai426b49c2016-11-08 14:55:38 +08002253 const uint64_t value = Uint64ConstantFrom(constant);
2254
2255 ret = IsUint<8>(Low32Bits(value)) && IsUint<8>(High32Bits(value));
2256 } else {
2257 ret = IsUint<8>(Int32ConstantFrom(constant));
2258 }
2259
2260 return ret;
2261}
2262
2263static Location Arm8BitEncodableConstantOrRegister(HInstruction* constant) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002264 DCHECK(!DataType::IsFloatingPointType(constant->GetType()));
Donghui Bai426b49c2016-11-08 14:55:38 +08002265
2266 if (constant->IsConstant() && CanEncodeConstantAs8BitImmediate(constant->AsConstant())) {
2267 return Location::ConstantLocation(constant->AsConstant());
2268 }
2269
2270 return Location::RequiresRegister();
2271}
2272
2273static bool CanGenerateConditionalMove(const Location& out, const Location& src) {
2274 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
2275 // we check that we are not dealing with floating-point output (there is no
2276 // 16-bit VMOV encoding).
2277 if (!out.IsRegister() && !out.IsRegisterPair()) {
2278 return false;
2279 }
2280
2281 // For constants, we also check that the output is in one or two low registers,
2282 // and that the constants fit in an 8-bit unsigned integer, so that a 16-bit
2283 // MOV encoding can be used.
2284 if (src.IsConstant()) {
2285 if (!CanEncodeConstantAs8BitImmediate(src.GetConstant())) {
2286 return false;
2287 }
2288
2289 if (out.IsRegister()) {
2290 if (!RegisterFrom(out).IsLow()) {
2291 return false;
2292 }
2293 } else {
2294 DCHECK(out.IsRegisterPair());
2295
2296 if (!HighRegisterFrom(out).IsLow()) {
2297 return false;
2298 }
2299 }
2300 }
2301
2302 return true;
2303}
2304
Scott Wakelingfe885462016-09-22 10:24:38 +01002305#undef __
2306
Donghui Bai426b49c2016-11-08 14:55:38 +08002307vixl32::Label* CodeGeneratorARMVIXL::GetFinalLabel(HInstruction* instruction,
2308 vixl32::Label* final_label) {
2309 DCHECK(!instruction->IsControlFlow() && !instruction->IsSuspendCheck());
Anton Kirilov6f644202017-02-27 18:29:45 +00002310 DCHECK(!instruction->IsInvoke() || !instruction->GetLocations()->CanCall());
Donghui Bai426b49c2016-11-08 14:55:38 +08002311
2312 const HBasicBlock* const block = instruction->GetBlock();
2313 const HLoopInformation* const info = block->GetLoopInformation();
2314 HInstruction* const next = instruction->GetNext();
2315
2316 // Avoid a branch to a branch.
2317 if (next->IsGoto() && (info == nullptr ||
2318 !info->IsBackEdge(*block) ||
2319 !info->HasSuspendCheck())) {
2320 final_label = GetLabelOf(next->AsGoto()->GetSuccessor());
2321 }
2322
2323 return final_label;
2324}
2325
Scott Wakelingfe885462016-09-22 10:24:38 +01002326CodeGeneratorARMVIXL::CodeGeneratorARMVIXL(HGraph* graph,
Scott Wakelingfe885462016-09-22 10:24:38 +01002327 const CompilerOptions& compiler_options,
2328 OptimizingCompilerStats* stats)
2329 : CodeGenerator(graph,
2330 kNumberOfCoreRegisters,
2331 kNumberOfSRegisters,
2332 kNumberOfRegisterPairs,
2333 kCoreCalleeSaves.GetList(),
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002334 ComputeSRegisterListMask(kFpuCalleeSaves),
Scott Wakelingfe885462016-09-22 10:24:38 +01002335 compiler_options,
2336 stats),
Vladimir Markoca6fff82017-10-03 14:49:14 +01002337 block_labels_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
2338 jump_tables_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Scott Wakelingfe885462016-09-22 10:24:38 +01002339 location_builder_(graph, this),
2340 instruction_visitor_(graph, this),
Vladimir Markoca6fff82017-10-03 14:49:14 +01002341 move_resolver_(graph->GetAllocator(), this),
2342 assembler_(graph->GetAllocator()),
Artem Serovc5fcb442016-12-02 19:19:58 +00002343 uint32_literals_(std::less<uint32_t>(),
Vladimir Markoca6fff82017-10-03 14:49:14 +01002344 graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Marko59eb30f2018-02-20 11:52:34 +00002345 boot_image_method_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Markoca6fff82017-10-03 14:49:14 +01002346 method_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Marko59eb30f2018-02-20 11:52:34 +00002347 boot_image_type_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Markoca6fff82017-10-03 14:49:14 +01002348 type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Marko59eb30f2018-02-20 11:52:34 +00002349 boot_image_string_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Markoca6fff82017-10-03 14:49:14 +01002350 string_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Marko6fd16062018-06-26 11:02:04 +01002351 boot_image_intrinsic_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Markoca6fff82017-10-03 14:49:14 +01002352 baker_read_barrier_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Artem Serovc5fcb442016-12-02 19:19:58 +00002353 jit_string_patches_(StringReferenceValueComparator(),
Vladimir Markoca6fff82017-10-03 14:49:14 +01002354 graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Artem Serovc5fcb442016-12-02 19:19:58 +00002355 jit_class_patches_(TypeReferenceValueComparator(),
Vladimir Markoca6fff82017-10-03 14:49:14 +01002356 graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)) {
Scott Wakelingfe885462016-09-22 10:24:38 +01002357 // Always save the LR register to mimic Quick.
2358 AddAllocatedRegister(Location::RegisterLocation(LR));
Nicolas Geoffray13a797b2017-03-15 16:41:31 +00002359 // Give D30 and D31 as scratch register to VIXL. The register allocator only works on
2360 // S0-S31, which alias to D0-D15.
2361 GetVIXLAssembler()->GetScratchVRegisterList()->Combine(d31);
2362 GetVIXLAssembler()->GetScratchVRegisterList()->Combine(d30);
Scott Wakelingfe885462016-09-22 10:24:38 +01002363}
2364
Artem Serov551b28f2016-10-18 19:11:30 +01002365void JumpTableARMVIXL::EmitTable(CodeGeneratorARMVIXL* codegen) {
2366 uint32_t num_entries = switch_instr_->GetNumEntries();
2367 DCHECK_GE(num_entries, kPackedSwitchCompareJumpThreshold);
2368
2369 // We are about to use the assembler to place literals directly. Make sure we have enough
Scott Wakelingb77051e2016-11-21 19:46:00 +00002370 // underlying code buffer and we have generated a jump table of the right size, using
2371 // codegen->GetVIXLAssembler()->GetBuffer().Align();
Artem Serov0fb37192016-12-06 18:13:40 +00002372 ExactAssemblyScope aas(codegen->GetVIXLAssembler(),
2373 num_entries * sizeof(int32_t),
2374 CodeBufferCheckScope::kMaximumSize);
Artem Serov551b28f2016-10-18 19:11:30 +01002375 // TODO(VIXL): Check that using lower case bind is fine here.
2376 codegen->GetVIXLAssembler()->bind(&table_start_);
Artem Serov09a940d2016-11-11 16:15:11 +00002377 for (uint32_t i = 0; i < num_entries; i++) {
2378 codegen->GetVIXLAssembler()->place(bb_addresses_[i].get());
2379 }
2380}
2381
2382void JumpTableARMVIXL::FixTable(CodeGeneratorARMVIXL* codegen) {
2383 uint32_t num_entries = switch_instr_->GetNumEntries();
2384 DCHECK_GE(num_entries, kPackedSwitchCompareJumpThreshold);
2385
Artem Serov551b28f2016-10-18 19:11:30 +01002386 const ArenaVector<HBasicBlock*>& successors = switch_instr_->GetBlock()->GetSuccessors();
2387 for (uint32_t i = 0; i < num_entries; i++) {
2388 vixl32::Label* target_label = codegen->GetLabelOf(successors[i]);
2389 DCHECK(target_label->IsBound());
2390 int32_t jump_offset = target_label->GetLocation() - table_start_.GetLocation();
2391 // When doing BX to address we need to have lower bit set to 1 in T32.
2392 if (codegen->GetVIXLAssembler()->IsUsingT32()) {
2393 jump_offset++;
2394 }
2395 DCHECK_GT(jump_offset, std::numeric_limits<int32_t>::min());
2396 DCHECK_LE(jump_offset, std::numeric_limits<int32_t>::max());
Artem Serov09a940d2016-11-11 16:15:11 +00002397
Scott Wakelingb77051e2016-11-21 19:46:00 +00002398 bb_addresses_[i].get()->UpdateValue(jump_offset, codegen->GetVIXLAssembler()->GetBuffer());
Artem Serov551b28f2016-10-18 19:11:30 +01002399 }
2400}
2401
Artem Serov09a940d2016-11-11 16:15:11 +00002402void CodeGeneratorARMVIXL::FixJumpTables() {
Artem Serov551b28f2016-10-18 19:11:30 +01002403 for (auto&& jump_table : jump_tables_) {
Artem Serov09a940d2016-11-11 16:15:11 +00002404 jump_table->FixTable(this);
Artem Serov551b28f2016-10-18 19:11:30 +01002405 }
2406}
2407
Andreas Gampeca620d72016-11-08 08:09:33 -08002408#define __ reinterpret_cast<ArmVIXLAssembler*>(GetAssembler())->GetVIXLAssembler()-> // NOLINT
Scott Wakelingfe885462016-09-22 10:24:38 +01002409
2410void CodeGeneratorARMVIXL::Finalize(CodeAllocator* allocator) {
Artem Serov09a940d2016-11-11 16:15:11 +00002411 FixJumpTables();
Scott Wakelingfe885462016-09-22 10:24:38 +01002412 GetAssembler()->FinalizeCode();
2413 CodeGenerator::Finalize(allocator);
Vladimir Markoca1e0382018-04-11 09:58:41 +00002414
2415 // Verify Baker read barrier linker patches.
2416 if (kIsDebugBuild) {
2417 ArrayRef<const uint8_t> code = allocator->GetMemory();
2418 for (const BakerReadBarrierPatchInfo& info : baker_read_barrier_patches_) {
2419 DCHECK(info.label.IsBound());
2420 uint32_t literal_offset = info.label.GetLocation();
2421 DCHECK_ALIGNED(literal_offset, 2u);
2422
2423 auto GetInsn16 = [&code](uint32_t offset) {
2424 DCHECK_ALIGNED(offset, 2u);
2425 return (static_cast<uint32_t>(code[offset + 0]) << 0) +
2426 (static_cast<uint32_t>(code[offset + 1]) << 8);
2427 };
2428 auto GetInsn32 = [=](uint32_t offset) {
2429 return (GetInsn16(offset) << 16) + (GetInsn16(offset + 2u) << 0);
2430 };
2431
2432 uint32_t encoded_data = info.custom_data;
2433 BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data);
2434 // Check that the next instruction matches the expected LDR.
2435 switch (kind) {
2436 case BakerReadBarrierKind::kField: {
2437 BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data);
2438 if (width == BakerReadBarrierWidth::kWide) {
2439 DCHECK_GE(code.size() - literal_offset, 8u);
2440 uint32_t next_insn = GetInsn32(literal_offset + 4u);
2441 // LDR (immediate), encoding T3, with correct base_reg.
2442 CheckValidReg((next_insn >> 12) & 0xfu); // Check destination register.
2443 const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
2444 CHECK_EQ(next_insn & 0xffff0000u, 0xf8d00000u | (base_reg << 16));
2445 } else {
2446 DCHECK_GE(code.size() - literal_offset, 6u);
2447 uint32_t next_insn = GetInsn16(literal_offset + 4u);
2448 // LDR (immediate), encoding T1, with correct base_reg.
2449 CheckValidReg(next_insn & 0x7u); // Check destination register.
2450 const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
2451 CHECK_EQ(next_insn & 0xf838u, 0x6800u | (base_reg << 3));
2452 }
2453 break;
2454 }
2455 case BakerReadBarrierKind::kArray: {
2456 DCHECK_GE(code.size() - literal_offset, 8u);
2457 uint32_t next_insn = GetInsn32(literal_offset + 4u);
2458 // LDR (register) with correct base_reg, S=1 and option=011 (LDR Wt, [Xn, Xm, LSL #2]).
2459 CheckValidReg((next_insn >> 12) & 0xfu); // Check destination register.
2460 const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
2461 CHECK_EQ(next_insn & 0xffff0ff0u, 0xf8500020u | (base_reg << 16));
2462 CheckValidReg(next_insn & 0xf); // Check index register
2463 break;
2464 }
2465 case BakerReadBarrierKind::kGcRoot: {
2466 BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data);
2467 if (width == BakerReadBarrierWidth::kWide) {
2468 DCHECK_GE(literal_offset, 4u);
2469 uint32_t prev_insn = GetInsn32(literal_offset - 4u);
2470 // LDR (immediate), encoding T3, with correct root_reg.
2471 const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
2472 CHECK_EQ(prev_insn & 0xfff0f000u, 0xf8d00000u | (root_reg << 12));
2473 } else {
2474 DCHECK_GE(literal_offset, 2u);
2475 uint32_t prev_insn = GetInsn16(literal_offset - 2u);
2476 // LDR (immediate), encoding T1, with correct root_reg.
2477 const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
2478 CHECK_EQ(prev_insn & 0xf807u, 0x6800u | root_reg);
2479 }
2480 break;
2481 }
2482 default:
2483 LOG(FATAL) << "Unexpected kind: " << static_cast<uint32_t>(kind);
2484 UNREACHABLE();
2485 }
2486 }
2487 }
Scott Wakelingfe885462016-09-22 10:24:38 +01002488}
2489
2490void CodeGeneratorARMVIXL::SetupBlockedRegisters() const {
Scott Wakelingfe885462016-09-22 10:24:38 +01002491 // Stack register, LR and PC are always reserved.
2492 blocked_core_registers_[SP] = true;
2493 blocked_core_registers_[LR] = true;
2494 blocked_core_registers_[PC] = true;
2495
Roland Levillain6d729a72017-06-30 18:34:01 +01002496 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2497 // Reserve marking register.
2498 blocked_core_registers_[MR] = true;
2499 }
2500
Scott Wakelingfe885462016-09-22 10:24:38 +01002501 // Reserve thread register.
2502 blocked_core_registers_[TR] = true;
2503
2504 // Reserve temp register.
2505 blocked_core_registers_[IP] = true;
2506
2507 if (GetGraph()->IsDebuggable()) {
2508 // Stubs do not save callee-save floating point registers. If the graph
2509 // is debuggable, we need to deal with these registers differently. For
2510 // now, just block them.
2511 for (uint32_t i = kFpuCalleeSaves.GetFirstSRegister().GetCode();
2512 i <= kFpuCalleeSaves.GetLastSRegister().GetCode();
2513 ++i) {
2514 blocked_fpu_registers_[i] = true;
2515 }
2516 }
Scott Wakelingfe885462016-09-22 10:24:38 +01002517}
2518
Scott Wakelingfe885462016-09-22 10:24:38 +01002519InstructionCodeGeneratorARMVIXL::InstructionCodeGeneratorARMVIXL(HGraph* graph,
2520 CodeGeneratorARMVIXL* codegen)
2521 : InstructionCodeGenerator(graph, codegen),
2522 assembler_(codegen->GetAssembler()),
2523 codegen_(codegen) {}
2524
2525void CodeGeneratorARMVIXL::ComputeSpillMask() {
2526 core_spill_mask_ = allocated_registers_.GetCoreRegisters() & core_callee_save_mask_;
2527 DCHECK_NE(core_spill_mask_, 0u) << "At least the return address register must be saved";
2528 // There is no easy instruction to restore just the PC on thumb2. We spill and
2529 // restore another arbitrary register.
2530 core_spill_mask_ |= (1 << kCoreAlwaysSpillRegister.GetCode());
2531 fpu_spill_mask_ = allocated_registers_.GetFloatingPointRegisters() & fpu_callee_save_mask_;
2532 // We use vpush and vpop for saving and restoring floating point registers, which take
2533 // a SRegister and the number of registers to save/restore after that SRegister. We
2534 // therefore update the `fpu_spill_mask_` to also contain those registers not allocated,
2535 // but in the range.
2536 if (fpu_spill_mask_ != 0) {
2537 uint32_t least_significant_bit = LeastSignificantBit(fpu_spill_mask_);
2538 uint32_t most_significant_bit = MostSignificantBit(fpu_spill_mask_);
2539 for (uint32_t i = least_significant_bit + 1 ; i < most_significant_bit; ++i) {
2540 fpu_spill_mask_ |= (1 << i);
2541 }
2542 }
2543}
2544
2545void CodeGeneratorARMVIXL::GenerateFrameEntry() {
2546 bool skip_overflow_check =
2547 IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kArm);
2548 DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks());
2549 __ Bind(&frame_entry_label_);
2550
Nicolas Geoffray8d728322018-01-18 22:44:32 +00002551 if (GetCompilerOptions().CountHotnessInCompiledCode()) {
2552 UseScratchRegisterScope temps(GetVIXLAssembler());
2553 vixl32::Register temp = temps.Acquire();
2554 __ Ldrh(temp, MemOperand(kMethodRegister, ArtMethod::HotnessCountOffset().Int32Value()));
2555 __ Add(temp, temp, 1);
2556 __ Strh(temp, MemOperand(kMethodRegister, ArtMethod::HotnessCountOffset().Int32Value()));
2557 }
2558
Scott Wakelingfe885462016-09-22 10:24:38 +01002559 if (HasEmptyFrame()) {
2560 return;
2561 }
2562
Scott Wakelingfe885462016-09-22 10:24:38 +01002563 if (!skip_overflow_check) {
xueliang.zhong10049552018-01-31 17:10:36 +00002564 // Using r4 instead of IP saves 2 bytes.
Nicolas Geoffray1a4f3ca2018-01-25 14:07:15 +00002565 UseScratchRegisterScope temps(GetVIXLAssembler());
xueliang.zhong10049552018-01-31 17:10:36 +00002566 vixl32::Register temp;
2567 // TODO: Remove this check when R4 is made a callee-save register
2568 // in ART compiled code (b/72801708). Currently we need to make
2569 // sure r4 is not blocked, e.g. in special purpose
2570 // TestCodeGeneratorARMVIXL; also asserting that r4 is available
2571 // here.
2572 if (!blocked_core_registers_[R4]) {
2573 for (vixl32::Register reg : kParameterCoreRegistersVIXL) {
2574 DCHECK(!reg.Is(r4));
2575 }
2576 DCHECK(!kCoreCalleeSaves.Includes(r4));
2577 temp = r4;
2578 } else {
2579 temp = temps.Acquire();
2580 }
Vladimir Marko33bff252017-11-01 14:35:42 +00002581 __ Sub(temp, sp, Operand::From(GetStackOverflowReservedBytes(InstructionSet::kArm)));
Scott Wakelingfe885462016-09-22 10:24:38 +01002582 // The load must immediately precede RecordPcInfo.
Artem Serov0fb37192016-12-06 18:13:40 +00002583 ExactAssemblyScope aas(GetVIXLAssembler(),
2584 vixl32::kMaxInstructionSizeInBytes,
2585 CodeBufferCheckScope::kMaximumSize);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002586 __ ldr(temp, MemOperand(temp));
2587 RecordPcInfo(nullptr, 0);
Scott Wakelingfe885462016-09-22 10:24:38 +01002588 }
2589
2590 __ Push(RegisterList(core_spill_mask_));
2591 GetAssembler()->cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(core_spill_mask_));
2592 GetAssembler()->cfi().RelOffsetForMany(DWARFReg(kMethodRegister),
2593 0,
2594 core_spill_mask_,
2595 kArmWordSize);
2596 if (fpu_spill_mask_ != 0) {
2597 uint32_t first = LeastSignificantBit(fpu_spill_mask_);
2598
2599 // Check that list is contiguous.
2600 DCHECK_EQ(fpu_spill_mask_ >> CTZ(fpu_spill_mask_), ~0u >> (32 - POPCOUNT(fpu_spill_mask_)));
2601
2602 __ Vpush(SRegisterList(vixl32::SRegister(first), POPCOUNT(fpu_spill_mask_)));
2603 GetAssembler()->cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(fpu_spill_mask_));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002604 GetAssembler()->cfi().RelOffsetForMany(DWARFReg(s0), 0, fpu_spill_mask_, kArmWordSize);
Scott Wakelingfe885462016-09-22 10:24:38 +01002605 }
Scott Wakelingbffdc702016-12-07 17:46:03 +00002606
Scott Wakelingfe885462016-09-22 10:24:38 +01002607 int adjust = GetFrameSize() - FrameEntrySpillSize();
2608 __ Sub(sp, sp, adjust);
2609 GetAssembler()->cfi().AdjustCFAOffset(adjust);
Scott Wakelingbffdc702016-12-07 17:46:03 +00002610
2611 // Save the current method if we need it. Note that we do not
2612 // do this in HCurrentMethod, as the instruction might have been removed
2613 // in the SSA graph.
2614 if (RequiresCurrentMethod()) {
2615 GetAssembler()->StoreToOffset(kStoreWord, kMethodRegister, sp, 0);
2616 }
Nicolas Geoffrayf7893532017-06-15 12:34:36 +01002617
2618 if (GetGraph()->HasShouldDeoptimizeFlag()) {
2619 UseScratchRegisterScope temps(GetVIXLAssembler());
2620 vixl32::Register temp = temps.Acquire();
2621 // Initialize should_deoptimize flag to 0.
2622 __ Mov(temp, 0);
2623 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, GetStackOffsetOfShouldDeoptimizeFlag());
2624 }
Roland Levillain5daa4952017-07-03 17:23:56 +01002625
2626 MaybeGenerateMarkingRegisterCheck(/* code */ 1);
Scott Wakelingfe885462016-09-22 10:24:38 +01002627}
2628
2629void CodeGeneratorARMVIXL::GenerateFrameExit() {
2630 if (HasEmptyFrame()) {
2631 __ Bx(lr);
2632 return;
2633 }
2634 GetAssembler()->cfi().RememberState();
2635 int adjust = GetFrameSize() - FrameEntrySpillSize();
2636 __ Add(sp, sp, adjust);
2637 GetAssembler()->cfi().AdjustCFAOffset(-adjust);
2638 if (fpu_spill_mask_ != 0) {
2639 uint32_t first = LeastSignificantBit(fpu_spill_mask_);
2640
2641 // Check that list is contiguous.
2642 DCHECK_EQ(fpu_spill_mask_ >> CTZ(fpu_spill_mask_), ~0u >> (32 - POPCOUNT(fpu_spill_mask_)));
2643
2644 __ Vpop(SRegisterList(vixl32::SRegister(first), POPCOUNT(fpu_spill_mask_)));
2645 GetAssembler()->cfi().AdjustCFAOffset(
2646 -static_cast<int>(kArmWordSize) * POPCOUNT(fpu_spill_mask_));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002647 GetAssembler()->cfi().RestoreMany(DWARFReg(vixl32::SRegister(0)), fpu_spill_mask_);
Scott Wakelingfe885462016-09-22 10:24:38 +01002648 }
2649 // Pop LR into PC to return.
2650 DCHECK_NE(core_spill_mask_ & (1 << kLrCode), 0U);
2651 uint32_t pop_mask = (core_spill_mask_ & (~(1 << kLrCode))) | 1 << kPcCode;
2652 __ Pop(RegisterList(pop_mask));
2653 GetAssembler()->cfi().RestoreState();
2654 GetAssembler()->cfi().DefCFAOffset(GetFrameSize());
2655}
2656
2657void CodeGeneratorARMVIXL::Bind(HBasicBlock* block) {
2658 __ Bind(GetLabelOf(block));
2659}
2660
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002661Location InvokeDexCallingConventionVisitorARMVIXL::GetNextLocation(DataType::Type type) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002662 switch (type) {
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01002663 case DataType::Type::kReference:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002664 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01002665 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002666 case DataType::Type::kInt8:
2667 case DataType::Type::kUint16:
2668 case DataType::Type::kInt16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01002669 case DataType::Type::kInt32: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002670 uint32_t index = gp_index_++;
2671 uint32_t stack_index = stack_index_++;
2672 if (index < calling_convention.GetNumberOfRegisters()) {
2673 return LocationFrom(calling_convention.GetRegisterAt(index));
2674 } else {
2675 return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index));
2676 }
2677 }
2678
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002679 case DataType::Type::kInt64: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002680 uint32_t index = gp_index_;
2681 uint32_t stack_index = stack_index_;
2682 gp_index_ += 2;
2683 stack_index_ += 2;
2684 if (index + 1 < calling_convention.GetNumberOfRegisters()) {
2685 if (calling_convention.GetRegisterAt(index).Is(r1)) {
2686 // Skip R1, and use R2_R3 instead.
2687 gp_index_++;
2688 index++;
2689 }
2690 }
2691 if (index + 1 < calling_convention.GetNumberOfRegisters()) {
2692 DCHECK_EQ(calling_convention.GetRegisterAt(index).GetCode() + 1,
2693 calling_convention.GetRegisterAt(index + 1).GetCode());
2694
2695 return LocationFrom(calling_convention.GetRegisterAt(index),
2696 calling_convention.GetRegisterAt(index + 1));
2697 } else {
2698 return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index));
2699 }
2700 }
2701
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002702 case DataType::Type::kFloat32: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002703 uint32_t stack_index = stack_index_++;
2704 if (float_index_ % 2 == 0) {
2705 float_index_ = std::max(double_index_, float_index_);
2706 }
2707 if (float_index_ < calling_convention.GetNumberOfFpuRegisters()) {
2708 return LocationFrom(calling_convention.GetFpuRegisterAt(float_index_++));
2709 } else {
2710 return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index));
2711 }
2712 }
2713
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002714 case DataType::Type::kFloat64: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002715 double_index_ = std::max(double_index_, RoundUp(float_index_, 2));
2716 uint32_t stack_index = stack_index_;
2717 stack_index_ += 2;
2718 if (double_index_ + 1 < calling_convention.GetNumberOfFpuRegisters()) {
2719 uint32_t index = double_index_;
2720 double_index_ += 2;
2721 Location result = LocationFrom(
2722 calling_convention.GetFpuRegisterAt(index),
2723 calling_convention.GetFpuRegisterAt(index + 1));
2724 DCHECK(ExpectedPairLayout(result));
2725 return result;
2726 } else {
2727 return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index));
2728 }
2729 }
2730
Aart Bik66c158e2018-01-31 12:55:04 -08002731 case DataType::Type::kUint32:
2732 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002733 case DataType::Type::kVoid:
Artem Serovd4cc5b22016-11-04 11:19:09 +00002734 LOG(FATAL) << "Unexpected parameter type " << type;
2735 break;
2736 }
2737 return Location::NoLocation();
2738}
2739
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002740Location InvokeDexCallingConventionVisitorARMVIXL::GetReturnLocation(DataType::Type type) const {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002741 switch (type) {
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01002742 case DataType::Type::kReference:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002743 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01002744 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002745 case DataType::Type::kInt8:
2746 case DataType::Type::kUint16:
2747 case DataType::Type::kInt16:
Aart Bik66c158e2018-01-31 12:55:04 -08002748 case DataType::Type::kUint32:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01002749 case DataType::Type::kInt32: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002750 return LocationFrom(r0);
2751 }
2752
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002753 case DataType::Type::kFloat32: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002754 return LocationFrom(s0);
2755 }
2756
Aart Bik66c158e2018-01-31 12:55:04 -08002757 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002758 case DataType::Type::kInt64: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002759 return LocationFrom(r0, r1);
2760 }
2761
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002762 case DataType::Type::kFloat64: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002763 return LocationFrom(s0, s1);
2764 }
2765
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002766 case DataType::Type::kVoid:
Artem Serovd4cc5b22016-11-04 11:19:09 +00002767 return Location::NoLocation();
2768 }
2769
2770 UNREACHABLE();
2771}
2772
2773Location InvokeDexCallingConventionVisitorARMVIXL::GetMethodLocation() const {
2774 return LocationFrom(kMethodRegister);
2775}
2776
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002777void CodeGeneratorARMVIXL::Move32(Location destination, Location source) {
2778 if (source.Equals(destination)) {
2779 return;
2780 }
2781 if (destination.IsRegister()) {
2782 if (source.IsRegister()) {
2783 __ Mov(RegisterFrom(destination), RegisterFrom(source));
2784 } else if (source.IsFpuRegister()) {
2785 __ Vmov(RegisterFrom(destination), SRegisterFrom(source));
2786 } else {
2787 GetAssembler()->LoadFromOffset(kLoadWord,
2788 RegisterFrom(destination),
2789 sp,
2790 source.GetStackIndex());
2791 }
2792 } else if (destination.IsFpuRegister()) {
2793 if (source.IsRegister()) {
2794 __ Vmov(SRegisterFrom(destination), RegisterFrom(source));
2795 } else if (source.IsFpuRegister()) {
2796 __ Vmov(SRegisterFrom(destination), SRegisterFrom(source));
2797 } else {
2798 GetAssembler()->LoadSFromOffset(SRegisterFrom(destination), sp, source.GetStackIndex());
2799 }
2800 } else {
2801 DCHECK(destination.IsStackSlot()) << destination;
2802 if (source.IsRegister()) {
2803 GetAssembler()->StoreToOffset(kStoreWord,
2804 RegisterFrom(source),
2805 sp,
2806 destination.GetStackIndex());
2807 } else if (source.IsFpuRegister()) {
2808 GetAssembler()->StoreSToOffset(SRegisterFrom(source), sp, destination.GetStackIndex());
2809 } else {
2810 DCHECK(source.IsStackSlot()) << source;
2811 UseScratchRegisterScope temps(GetVIXLAssembler());
2812 vixl32::Register temp = temps.Acquire();
2813 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, source.GetStackIndex());
2814 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
2815 }
2816 }
2817}
2818
Artem Serovcfbe9132016-10-14 15:58:56 +01002819void CodeGeneratorARMVIXL::MoveConstant(Location location, int32_t value) {
2820 DCHECK(location.IsRegister());
2821 __ Mov(RegisterFrom(location), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01002822}
2823
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002824void CodeGeneratorARMVIXL::MoveLocation(Location dst, Location src, DataType::Type dst_type) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002825 // TODO(VIXL): Maybe refactor to have the 'move' implementation here and use it in
2826 // `ParallelMoveResolverARMVIXL::EmitMove`, as is done in the `arm64` backend.
Vladimir Markoca6fff82017-10-03 14:49:14 +01002827 HParallelMove move(GetGraph()->GetAllocator());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002828 move.AddMove(src, dst, dst_type, nullptr);
2829 GetMoveResolver()->EmitNativeCode(&move);
Scott Wakelingfe885462016-09-22 10:24:38 +01002830}
2831
Artem Serovcfbe9132016-10-14 15:58:56 +01002832void CodeGeneratorARMVIXL::AddLocationAsTemp(Location location, LocationSummary* locations) {
2833 if (location.IsRegister()) {
2834 locations->AddTemp(location);
2835 } else if (location.IsRegisterPair()) {
2836 locations->AddTemp(LocationFrom(LowRegisterFrom(location)));
2837 locations->AddTemp(LocationFrom(HighRegisterFrom(location)));
2838 } else {
2839 UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location;
2840 }
Scott Wakelingfe885462016-09-22 10:24:38 +01002841}
2842
2843void CodeGeneratorARMVIXL::InvokeRuntime(QuickEntrypointEnum entrypoint,
2844 HInstruction* instruction,
2845 uint32_t dex_pc,
2846 SlowPathCode* slow_path) {
2847 ValidateInvokeRuntime(entrypoint, instruction, slow_path);
Alexandre Rames374ddf32016-11-04 10:40:49 +00002848 __ Ldr(lr, MemOperand(tr, GetThreadOffset<kArmPointerSize>(entrypoint).Int32Value()));
2849 // Ensure the pc position is recorded immediately after the `blx` instruction.
2850 // 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 +00002851 ExactAssemblyScope aas(GetVIXLAssembler(),
2852 vixl32::k16BitT32InstructionSizeInBytes,
2853 CodeBufferCheckScope::kExactSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00002854 __ blx(lr);
Scott Wakelingfe885462016-09-22 10:24:38 +01002855 if (EntrypointRequiresStackMap(entrypoint)) {
2856 RecordPcInfo(instruction, dex_pc, slow_path);
2857 }
2858}
2859
2860void CodeGeneratorARMVIXL::InvokeRuntimeWithoutRecordingPcInfo(int32_t entry_point_offset,
2861 HInstruction* instruction,
2862 SlowPathCode* slow_path) {
2863 ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction, slow_path);
Alexandre Rames374ddf32016-11-04 10:40:49 +00002864 __ Ldr(lr, MemOperand(tr, entry_point_offset));
Scott Wakelingfe885462016-09-22 10:24:38 +01002865 __ Blx(lr);
2866}
2867
Scott Wakelingfe885462016-09-22 10:24:38 +01002868void InstructionCodeGeneratorARMVIXL::HandleGoto(HInstruction* got, HBasicBlock* successor) {
Aart Bika8b8e9b2018-01-09 11:01:02 -08002869 if (successor->IsExitBlock()) {
2870 DCHECK(got->GetPrevious()->AlwaysThrows());
2871 return; // no code needed
2872 }
2873
Scott Wakelingfe885462016-09-22 10:24:38 +01002874 HBasicBlock* block = got->GetBlock();
2875 HInstruction* previous = got->GetPrevious();
2876 HLoopInformation* info = block->GetLoopInformation();
2877
2878 if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) {
Nicolas Geoffray8d728322018-01-18 22:44:32 +00002879 if (codegen_->GetCompilerOptions().CountHotnessInCompiledCode()) {
2880 UseScratchRegisterScope temps(GetVIXLAssembler());
2881 vixl32::Register temp = temps.Acquire();
2882 __ Push(vixl32::Register(kMethodRegister));
2883 GetAssembler()->LoadFromOffset(kLoadWord, kMethodRegister, sp, kArmWordSize);
2884 __ Ldrh(temp, MemOperand(kMethodRegister, ArtMethod::HotnessCountOffset().Int32Value()));
2885 __ Add(temp, temp, 1);
2886 __ Strh(temp, MemOperand(kMethodRegister, ArtMethod::HotnessCountOffset().Int32Value()));
2887 __ Pop(vixl32::Register(kMethodRegister));
2888 }
Scott Wakelingfe885462016-09-22 10:24:38 +01002889 GenerateSuspendCheck(info->GetSuspendCheck(), successor);
2890 return;
2891 }
2892 if (block->IsEntryBlock() && (previous != nullptr) && previous->IsSuspendCheck()) {
2893 GenerateSuspendCheck(previous->AsSuspendCheck(), nullptr);
Roland Levillain5daa4952017-07-03 17:23:56 +01002894 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 2);
Scott Wakelingfe885462016-09-22 10:24:38 +01002895 }
2896 if (!codegen_->GoesToNextBlock(block, successor)) {
2897 __ B(codegen_->GetLabelOf(successor));
2898 }
2899}
2900
2901void LocationsBuilderARMVIXL::VisitGoto(HGoto* got) {
2902 got->SetLocations(nullptr);
2903}
2904
2905void InstructionCodeGeneratorARMVIXL::VisitGoto(HGoto* got) {
2906 HandleGoto(got, got->GetSuccessor());
2907}
2908
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002909void LocationsBuilderARMVIXL::VisitTryBoundary(HTryBoundary* try_boundary) {
2910 try_boundary->SetLocations(nullptr);
2911}
2912
2913void InstructionCodeGeneratorARMVIXL::VisitTryBoundary(HTryBoundary* try_boundary) {
2914 HBasicBlock* successor = try_boundary->GetNormalFlowSuccessor();
2915 if (!successor->IsExitBlock()) {
2916 HandleGoto(try_boundary, successor);
2917 }
2918}
2919
Scott Wakelingfe885462016-09-22 10:24:38 +01002920void LocationsBuilderARMVIXL::VisitExit(HExit* exit) {
2921 exit->SetLocations(nullptr);
2922}
2923
2924void InstructionCodeGeneratorARMVIXL::VisitExit(HExit* exit ATTRIBUTE_UNUSED) {
2925}
2926
Scott Wakelingfe885462016-09-22 10:24:38 +01002927void InstructionCodeGeneratorARMVIXL::GenerateCompareTestAndBranch(HCondition* condition,
Anton Kirilov23b752b2017-07-20 14:40:44 +01002928 vixl32::Label* true_target,
2929 vixl32::Label* false_target,
Anton Kirilovfd522532017-05-10 12:46:57 +01002930 bool is_far_target) {
Anton Kirilov23b752b2017-07-20 14:40:44 +01002931 if (true_target == false_target) {
2932 DCHECK(true_target != nullptr);
2933 __ B(true_target);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00002934 return;
2935 }
2936
Anton Kirilov23b752b2017-07-20 14:40:44 +01002937 vixl32::Label* non_fallthrough_target;
2938 bool invert;
2939 bool emit_both_branches;
Scott Wakelingfe885462016-09-22 10:24:38 +01002940
Anton Kirilov23b752b2017-07-20 14:40:44 +01002941 if (true_target == nullptr) {
2942 // The true target is fallthrough.
2943 DCHECK(false_target != nullptr);
2944 non_fallthrough_target = false_target;
2945 invert = true;
2946 emit_both_branches = false;
2947 } else {
2948 non_fallthrough_target = true_target;
2949 invert = false;
2950 // Either the false target is fallthrough, or there is no fallthrough
2951 // and both branches must be emitted.
2952 emit_both_branches = (false_target != nullptr);
Scott Wakelingfe885462016-09-22 10:24:38 +01002953 }
2954
Anton Kirilov23b752b2017-07-20 14:40:44 +01002955 const auto cond = GenerateTest(condition, invert, codegen_);
2956
2957 __ B(cond.first, non_fallthrough_target, is_far_target);
2958
2959 if (emit_both_branches) {
2960 // No target falls through, we need to branch.
2961 __ B(false_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01002962 }
2963}
2964
2965void InstructionCodeGeneratorARMVIXL::GenerateTestAndBranch(HInstruction* instruction,
2966 size_t condition_input_index,
2967 vixl32::Label* true_target,
xueliang.zhongf51bc622016-11-04 09:23:32 +00002968 vixl32::Label* false_target,
2969 bool far_target) {
Scott Wakelingfe885462016-09-22 10:24:38 +01002970 HInstruction* cond = instruction->InputAt(condition_input_index);
2971
2972 if (true_target == nullptr && false_target == nullptr) {
2973 // Nothing to do. The code always falls through.
2974 return;
2975 } else if (cond->IsIntConstant()) {
2976 // Constant condition, statically compared against "true" (integer value 1).
2977 if (cond->AsIntConstant()->IsTrue()) {
2978 if (true_target != nullptr) {
2979 __ B(true_target);
2980 }
2981 } else {
Anton Kirilov644032c2016-12-06 17:51:43 +00002982 DCHECK(cond->AsIntConstant()->IsFalse()) << Int32ConstantFrom(cond);
Scott Wakelingfe885462016-09-22 10:24:38 +01002983 if (false_target != nullptr) {
2984 __ B(false_target);
2985 }
2986 }
2987 return;
2988 }
2989
2990 // The following code generates these patterns:
2991 // (1) true_target == nullptr && false_target != nullptr
2992 // - opposite condition true => branch to false_target
2993 // (2) true_target != nullptr && false_target == nullptr
2994 // - condition true => branch to true_target
2995 // (3) true_target != nullptr && false_target != nullptr
2996 // - condition true => branch to true_target
2997 // - branch to false_target
2998 if (IsBooleanValueOrMaterializedCondition(cond)) {
2999 // Condition has been materialized, compare the output to 0.
3000 if (kIsDebugBuild) {
3001 Location cond_val = instruction->GetLocations()->InAt(condition_input_index);
3002 DCHECK(cond_val.IsRegister());
3003 }
3004 if (true_target == nullptr) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00003005 __ CompareAndBranchIfZero(InputRegisterAt(instruction, condition_input_index),
3006 false_target,
3007 far_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01003008 } else {
xueliang.zhongf51bc622016-11-04 09:23:32 +00003009 __ CompareAndBranchIfNonZero(InputRegisterAt(instruction, condition_input_index),
3010 true_target,
3011 far_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01003012 }
3013 } else {
3014 // Condition has not been materialized. Use its inputs as the comparison and
3015 // its condition as the branch condition.
3016 HCondition* condition = cond->AsCondition();
3017
3018 // If this is a long or FP comparison that has been folded into
3019 // the HCondition, generate the comparison directly.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003020 DataType::Type type = condition->InputAt(0)->GetType();
3021 if (type == DataType::Type::kInt64 || DataType::IsFloatingPointType(type)) {
Anton Kirilovfd522532017-05-10 12:46:57 +01003022 GenerateCompareTestAndBranch(condition, true_target, false_target, far_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01003023 return;
3024 }
3025
Donghui Bai426b49c2016-11-08 14:55:38 +08003026 vixl32::Label* non_fallthrough_target;
3027 vixl32::Condition arm_cond = vixl32::Condition::None();
3028 const vixl32::Register left = InputRegisterAt(cond, 0);
3029 const Operand right = InputOperandAt(cond, 1);
3030
Scott Wakelingfe885462016-09-22 10:24:38 +01003031 if (true_target == nullptr) {
Donghui Bai426b49c2016-11-08 14:55:38 +08003032 arm_cond = ARMCondition(condition->GetOppositeCondition());
3033 non_fallthrough_target = false_target;
Scott Wakelingfe885462016-09-22 10:24:38 +01003034 } else {
Donghui Bai426b49c2016-11-08 14:55:38 +08003035 arm_cond = ARMCondition(condition->GetCondition());
3036 non_fallthrough_target = true_target;
3037 }
3038
3039 if (right.IsImmediate() && right.GetImmediate() == 0 && (arm_cond.Is(ne) || arm_cond.Is(eq))) {
3040 if (arm_cond.Is(eq)) {
Anton Kirilovfd522532017-05-10 12:46:57 +01003041 __ CompareAndBranchIfZero(left, non_fallthrough_target, far_target);
Donghui Bai426b49c2016-11-08 14:55:38 +08003042 } else {
3043 DCHECK(arm_cond.Is(ne));
Anton Kirilovfd522532017-05-10 12:46:57 +01003044 __ CompareAndBranchIfNonZero(left, non_fallthrough_target, far_target);
Donghui Bai426b49c2016-11-08 14:55:38 +08003045 }
3046 } else {
3047 __ Cmp(left, right);
Anton Kirilovfd522532017-05-10 12:46:57 +01003048 __ B(arm_cond, non_fallthrough_target, far_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01003049 }
3050 }
3051
3052 // If neither branch falls through (case 3), the conditional branch to `true_target`
3053 // was already emitted (case 2) and we need to emit a jump to `false_target`.
3054 if (true_target != nullptr && false_target != nullptr) {
3055 __ B(false_target);
3056 }
3057}
3058
3059void LocationsBuilderARMVIXL::VisitIf(HIf* if_instr) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01003060 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(if_instr);
Scott Wakelingfe885462016-09-22 10:24:38 +01003061 if (IsBooleanValueOrMaterializedCondition(if_instr->InputAt(0))) {
3062 locations->SetInAt(0, Location::RequiresRegister());
3063 }
3064}
3065
3066void InstructionCodeGeneratorARMVIXL::VisitIf(HIf* if_instr) {
3067 HBasicBlock* true_successor = if_instr->IfTrueSuccessor();
3068 HBasicBlock* false_successor = if_instr->IfFalseSuccessor();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003069 vixl32::Label* true_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), true_successor) ?
3070 nullptr : codegen_->GetLabelOf(true_successor);
3071 vixl32::Label* false_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), false_successor) ?
3072 nullptr : codegen_->GetLabelOf(false_successor);
Scott Wakelingfe885462016-09-22 10:24:38 +01003073 GenerateTestAndBranch(if_instr, /* condition_input_index */ 0, true_target, false_target);
3074}
3075
Scott Wakelingc34dba72016-10-03 10:14:44 +01003076void LocationsBuilderARMVIXL::VisitDeoptimize(HDeoptimize* deoptimize) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01003077 LocationSummary* locations = new (GetGraph()->GetAllocator())
Scott Wakelingc34dba72016-10-03 10:14:44 +01003078 LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath);
Nicolas Geoffray4e92c3c2017-05-08 09:34:26 +01003079 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3080 RegisterSet caller_saves = RegisterSet::Empty();
3081 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0)));
3082 locations->SetCustomSlowPathCallerSaves(caller_saves);
Scott Wakelingc34dba72016-10-03 10:14:44 +01003083 if (IsBooleanValueOrMaterializedCondition(deoptimize->InputAt(0))) {
3084 locations->SetInAt(0, Location::RequiresRegister());
3085 }
3086}
3087
3088void InstructionCodeGeneratorARMVIXL::VisitDeoptimize(HDeoptimize* deoptimize) {
3089 SlowPathCodeARMVIXL* slow_path =
3090 deopt_slow_paths_.NewSlowPath<DeoptimizationSlowPathARMVIXL>(deoptimize);
3091 GenerateTestAndBranch(deoptimize,
3092 /* condition_input_index */ 0,
3093 slow_path->GetEntryLabel(),
3094 /* false_target */ nullptr);
3095}
3096
Artem Serovd4cc5b22016-11-04 11:19:09 +00003097void LocationsBuilderARMVIXL::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01003098 LocationSummary* locations = new (GetGraph()->GetAllocator())
Artem Serovd4cc5b22016-11-04 11:19:09 +00003099 LocationSummary(flag, LocationSummary::kNoCall);
3100 locations->SetOut(Location::RequiresRegister());
3101}
3102
3103void InstructionCodeGeneratorARMVIXL::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
3104 GetAssembler()->LoadFromOffset(kLoadWord,
3105 OutputRegister(flag),
3106 sp,
3107 codegen_->GetStackOffsetOfShouldDeoptimizeFlag());
3108}
3109
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003110void LocationsBuilderARMVIXL::VisitSelect(HSelect* select) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01003111 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(select);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003112 const bool is_floating_point = DataType::IsFloatingPointType(select->GetType());
Donghui Bai426b49c2016-11-08 14:55:38 +08003113
3114 if (is_floating_point) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003115 locations->SetInAt(0, Location::RequiresFpuRegister());
Donghui Bai426b49c2016-11-08 14:55:38 +08003116 locations->SetInAt(1, Location::FpuRegisterOrConstant(select->GetTrueValue()));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003117 } else {
3118 locations->SetInAt(0, Location::RequiresRegister());
Donghui Bai426b49c2016-11-08 14:55:38 +08003119 locations->SetInAt(1, Arm8BitEncodableConstantOrRegister(select->GetTrueValue()));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003120 }
Donghui Bai426b49c2016-11-08 14:55:38 +08003121
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003122 if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) {
Donghui Bai426b49c2016-11-08 14:55:38 +08003123 locations->SetInAt(2, Location::RegisterOrConstant(select->GetCondition()));
3124 // The code generator handles overlap with the values, but not with the condition.
3125 locations->SetOut(Location::SameAsFirstInput());
3126 } else if (is_floating_point) {
3127 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3128 } else {
3129 if (!locations->InAt(1).IsConstant()) {
3130 locations->SetInAt(0, Arm8BitEncodableConstantOrRegister(select->GetFalseValue()));
3131 }
3132
3133 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003134 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003135}
3136
3137void InstructionCodeGeneratorARMVIXL::VisitSelect(HSelect* select) {
Donghui Bai426b49c2016-11-08 14:55:38 +08003138 HInstruction* const condition = select->GetCondition();
3139 const LocationSummary* const locations = select->GetLocations();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003140 const DataType::Type type = select->GetType();
Donghui Bai426b49c2016-11-08 14:55:38 +08003141 const Location first = locations->InAt(0);
3142 const Location out = locations->Out();
3143 const Location second = locations->InAt(1);
3144 Location src;
3145
3146 if (condition->IsIntConstant()) {
3147 if (condition->AsIntConstant()->IsFalse()) {
3148 src = first;
3149 } else {
3150 src = second;
3151 }
3152
3153 codegen_->MoveLocation(out, src, type);
3154 return;
3155 }
3156
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003157 if (!DataType::IsFloatingPointType(type)) {
Donghui Bai426b49c2016-11-08 14:55:38 +08003158 bool invert = false;
3159
3160 if (out.Equals(second)) {
3161 src = first;
3162 invert = true;
3163 } else if (out.Equals(first)) {
3164 src = second;
3165 } else if (second.IsConstant()) {
3166 DCHECK(CanEncodeConstantAs8BitImmediate(second.GetConstant()));
3167 src = second;
3168 } else if (first.IsConstant()) {
3169 DCHECK(CanEncodeConstantAs8BitImmediate(first.GetConstant()));
3170 src = first;
3171 invert = true;
3172 } else {
3173 src = second;
3174 }
3175
3176 if (CanGenerateConditionalMove(out, src)) {
3177 if (!out.Equals(first) && !out.Equals(second)) {
3178 codegen_->MoveLocation(out, src.Equals(first) ? second : first, type);
3179 }
3180
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003181 std::pair<vixl32::Condition, vixl32::Condition> cond(eq, ne);
3182
3183 if (IsBooleanValueOrMaterializedCondition(condition)) {
3184 __ Cmp(InputRegisterAt(select, 2), 0);
3185 cond = invert ? std::make_pair(eq, ne) : std::make_pair(ne, eq);
3186 } else {
3187 cond = GenerateTest(condition->AsCondition(), invert, codegen_);
3188 }
3189
Donghui Bai426b49c2016-11-08 14:55:38 +08003190 const size_t instr_count = out.IsRegisterPair() ? 4 : 2;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003191 // We use the scope because of the IT block that follows.
Donghui Bai426b49c2016-11-08 14:55:38 +08003192 ExactAssemblyScope guard(GetVIXLAssembler(),
3193 instr_count * vixl32::k16BitT32InstructionSizeInBytes,
3194 CodeBufferCheckScope::kExactSize);
3195
3196 if (out.IsRegister()) {
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003197 __ it(cond.first);
3198 __ mov(cond.first, RegisterFrom(out), OperandFrom(src, type));
Donghui Bai426b49c2016-11-08 14:55:38 +08003199 } else {
3200 DCHECK(out.IsRegisterPair());
3201
3202 Operand operand_high(0);
3203 Operand operand_low(0);
3204
3205 if (src.IsConstant()) {
3206 const int64_t value = Int64ConstantFrom(src);
3207
3208 operand_high = High32Bits(value);
3209 operand_low = Low32Bits(value);
3210 } else {
3211 DCHECK(src.IsRegisterPair());
3212 operand_high = HighRegisterFrom(src);
3213 operand_low = LowRegisterFrom(src);
3214 }
3215
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003216 __ it(cond.first);
3217 __ mov(cond.first, LowRegisterFrom(out), operand_low);
3218 __ it(cond.first);
3219 __ mov(cond.first, HighRegisterFrom(out), operand_high);
Donghui Bai426b49c2016-11-08 14:55:38 +08003220 }
3221
3222 return;
3223 }
3224 }
3225
3226 vixl32::Label* false_target = nullptr;
3227 vixl32::Label* true_target = nullptr;
3228 vixl32::Label select_end;
3229 vixl32::Label* const target = codegen_->GetFinalLabel(select, &select_end);
3230
3231 if (out.Equals(second)) {
3232 true_target = target;
3233 src = first;
3234 } else {
3235 false_target = target;
3236 src = second;
3237
3238 if (!out.Equals(first)) {
3239 codegen_->MoveLocation(out, first, type);
3240 }
3241 }
3242
3243 GenerateTestAndBranch(select, 2, true_target, false_target, /* far_target */ false);
3244 codegen_->MoveLocation(out, src, type);
3245
3246 if (select_end.IsReferenced()) {
3247 __ Bind(&select_end);
3248 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003249}
3250
Artem Serov551b28f2016-10-18 19:11:30 +01003251void LocationsBuilderARMVIXL::VisitNativeDebugInfo(HNativeDebugInfo* info) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01003252 new (GetGraph()->GetAllocator()) LocationSummary(info);
Artem Serov551b28f2016-10-18 19:11:30 +01003253}
3254
3255void InstructionCodeGeneratorARMVIXL::VisitNativeDebugInfo(HNativeDebugInfo*) {
3256 // MaybeRecordNativeDebugInfo is already called implicitly in CodeGenerator::Compile.
3257}
3258
Scott Wakelingfe885462016-09-22 10:24:38 +01003259void CodeGeneratorARMVIXL::GenerateNop() {
3260 __ Nop();
3261}
3262
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003263// `temp` is an extra temporary register that is used for some conditions;
3264// callers may not specify it, in which case the method will use a scratch
3265// register instead.
3266void CodeGeneratorARMVIXL::GenerateConditionWithZero(IfCondition condition,
3267 vixl32::Register out,
3268 vixl32::Register in,
3269 vixl32::Register temp) {
3270 switch (condition) {
3271 case kCondEQ:
3272 // x <= 0 iff x == 0 when the comparison is unsigned.
3273 case kCondBE:
3274 if (!temp.IsValid() || (out.IsLow() && !out.Is(in))) {
3275 temp = out;
3276 }
3277
3278 // Avoid 32-bit instructions if possible; note that `in` and `temp` must be
3279 // different as well.
3280 if (in.IsLow() && temp.IsLow() && !in.Is(temp)) {
3281 // temp = - in; only 0 sets the carry flag.
3282 __ Rsbs(temp, in, 0);
3283
3284 if (out.Is(in)) {
3285 std::swap(in, temp);
3286 }
3287
3288 // out = - in + in + carry = carry
3289 __ Adc(out, temp, in);
3290 } else {
3291 // If `in` is 0, then it has 32 leading zeros, and less than that otherwise.
3292 __ Clz(out, in);
3293 // Any number less than 32 logically shifted right by 5 bits results in 0;
3294 // the same operation on 32 yields 1.
3295 __ Lsr(out, out, 5);
3296 }
3297
3298 break;
3299 case kCondNE:
3300 // x > 0 iff x != 0 when the comparison is unsigned.
3301 case kCondA: {
3302 UseScratchRegisterScope temps(GetVIXLAssembler());
3303
3304 if (out.Is(in)) {
3305 if (!temp.IsValid() || in.Is(temp)) {
3306 temp = temps.Acquire();
3307 }
3308 } else if (!temp.IsValid() || !temp.IsLow()) {
3309 temp = out;
3310 }
3311
3312 // temp = in - 1; only 0 does not set the carry flag.
3313 __ Subs(temp, in, 1);
3314 // out = in + ~temp + carry = in + (-(in - 1) - 1) + carry = in - in + 1 - 1 + carry = carry
3315 __ Sbc(out, in, temp);
3316 break;
3317 }
3318 case kCondGE:
3319 __ Mvn(out, in);
3320 in = out;
3321 FALLTHROUGH_INTENDED;
3322 case kCondLT:
3323 // We only care about the sign bit.
3324 __ Lsr(out, in, 31);
3325 break;
3326 case kCondAE:
3327 // Trivially true.
3328 __ Mov(out, 1);
3329 break;
3330 case kCondB:
3331 // Trivially false.
3332 __ Mov(out, 0);
3333 break;
3334 default:
3335 LOG(FATAL) << "Unexpected condition " << condition;
3336 UNREACHABLE();
3337 }
3338}
3339
Scott Wakelingfe885462016-09-22 10:24:38 +01003340void LocationsBuilderARMVIXL::HandleCondition(HCondition* cond) {
3341 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003342 new (GetGraph()->GetAllocator()) LocationSummary(cond, LocationSummary::kNoCall);
Scott Wakelingfe885462016-09-22 10:24:38 +01003343 // Handle the long/FP comparisons made in instruction simplification.
3344 switch (cond->InputAt(0)->GetType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003345 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003346 locations->SetInAt(0, Location::RequiresRegister());
3347 locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
3348 if (!cond->IsEmittedAtUseSite()) {
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003349 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Scott Wakelingfe885462016-09-22 10:24:38 +01003350 }
3351 break;
3352
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003353 case DataType::Type::kFloat32:
3354 case DataType::Type::kFloat64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003355 locations->SetInAt(0, Location::RequiresFpuRegister());
Artem Serov657022c2016-11-23 14:19:38 +00003356 locations->SetInAt(1, ArithmeticZeroOrFpuRegister(cond->InputAt(1)));
Scott Wakelingfe885462016-09-22 10:24:38 +01003357 if (!cond->IsEmittedAtUseSite()) {
3358 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3359 }
3360 break;
3361
3362 default:
3363 locations->SetInAt(0, Location::RequiresRegister());
3364 locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
3365 if (!cond->IsEmittedAtUseSite()) {
3366 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3367 }
3368 }
3369}
3370
3371void InstructionCodeGeneratorARMVIXL::HandleCondition(HCondition* cond) {
3372 if (cond->IsEmittedAtUseSite()) {
3373 return;
3374 }
3375
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003376 const DataType::Type type = cond->GetLeft()->GetType();
Scott Wakelingfe885462016-09-22 10:24:38 +01003377
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003378 if (DataType::IsFloatingPointType(type)) {
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003379 GenerateConditionGeneric(cond, codegen_);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003380 return;
Scott Wakelingfe885462016-09-22 10:24:38 +01003381 }
3382
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003383 DCHECK(DataType::IsIntegralType(type) || type == DataType::Type::kReference) << type;
Scott Wakelingfe885462016-09-22 10:24:38 +01003384
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003385 const IfCondition condition = cond->GetCondition();
Scott Wakelingfe885462016-09-22 10:24:38 +01003386
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003387 // A condition with only one boolean input, or two boolean inputs without being equality or
3388 // inequality results from transformations done by the instruction simplifier, and is handled
3389 // as a regular condition with integral inputs.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003390 if (type == DataType::Type::kBool &&
3391 cond->GetRight()->GetType() == DataType::Type::kBool &&
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003392 (condition == kCondEQ || condition == kCondNE)) {
3393 vixl32::Register left = InputRegisterAt(cond, 0);
3394 const vixl32::Register out = OutputRegister(cond);
3395 const Location right_loc = cond->GetLocations()->InAt(1);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003396
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003397 // The constant case is handled by the instruction simplifier.
3398 DCHECK(!right_loc.IsConstant());
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003399
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003400 vixl32::Register right = RegisterFrom(right_loc);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003401
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003402 // Avoid 32-bit instructions if possible.
3403 if (out.Is(right)) {
3404 std::swap(left, right);
3405 }
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003406
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003407 __ Eor(out, left, right);
3408
3409 if (condition == kCondEQ) {
3410 __ Eor(out, out, 1);
3411 }
3412
3413 return;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003414 }
Anton Kirilov6f644202017-02-27 18:29:45 +00003415
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003416 GenerateConditionIntegralOrNonPrimitive(cond, codegen_);
Scott Wakelingfe885462016-09-22 10:24:38 +01003417}
3418
3419void LocationsBuilderARMVIXL::VisitEqual(HEqual* comp) {
3420 HandleCondition(comp);
3421}
3422
3423void InstructionCodeGeneratorARMVIXL::VisitEqual(HEqual* comp) {
3424 HandleCondition(comp);
3425}
3426
3427void LocationsBuilderARMVIXL::VisitNotEqual(HNotEqual* comp) {
3428 HandleCondition(comp);
3429}
3430
3431void InstructionCodeGeneratorARMVIXL::VisitNotEqual(HNotEqual* comp) {
3432 HandleCondition(comp);
3433}
3434
3435void LocationsBuilderARMVIXL::VisitLessThan(HLessThan* comp) {
3436 HandleCondition(comp);
3437}
3438
3439void InstructionCodeGeneratorARMVIXL::VisitLessThan(HLessThan* comp) {
3440 HandleCondition(comp);
3441}
3442
3443void LocationsBuilderARMVIXL::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
3444 HandleCondition(comp);
3445}
3446
3447void InstructionCodeGeneratorARMVIXL::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
3448 HandleCondition(comp);
3449}
3450
3451void LocationsBuilderARMVIXL::VisitGreaterThan(HGreaterThan* comp) {
3452 HandleCondition(comp);
3453}
3454
3455void InstructionCodeGeneratorARMVIXL::VisitGreaterThan(HGreaterThan* comp) {
3456 HandleCondition(comp);
3457}
3458
3459void LocationsBuilderARMVIXL::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
3460 HandleCondition(comp);
3461}
3462
3463void InstructionCodeGeneratorARMVIXL::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
3464 HandleCondition(comp);
3465}
3466
3467void LocationsBuilderARMVIXL::VisitBelow(HBelow* comp) {
3468 HandleCondition(comp);
3469}
3470
3471void InstructionCodeGeneratorARMVIXL::VisitBelow(HBelow* comp) {
3472 HandleCondition(comp);
3473}
3474
3475void LocationsBuilderARMVIXL::VisitBelowOrEqual(HBelowOrEqual* comp) {
3476 HandleCondition(comp);
3477}
3478
3479void InstructionCodeGeneratorARMVIXL::VisitBelowOrEqual(HBelowOrEqual* comp) {
3480 HandleCondition(comp);
3481}
3482
3483void LocationsBuilderARMVIXL::VisitAbove(HAbove* comp) {
3484 HandleCondition(comp);
3485}
3486
3487void InstructionCodeGeneratorARMVIXL::VisitAbove(HAbove* comp) {
3488 HandleCondition(comp);
3489}
3490
3491void LocationsBuilderARMVIXL::VisitAboveOrEqual(HAboveOrEqual* comp) {
3492 HandleCondition(comp);
3493}
3494
3495void InstructionCodeGeneratorARMVIXL::VisitAboveOrEqual(HAboveOrEqual* comp) {
3496 HandleCondition(comp);
3497}
3498
3499void LocationsBuilderARMVIXL::VisitIntConstant(HIntConstant* constant) {
3500 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003501 new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
Scott Wakelingfe885462016-09-22 10:24:38 +01003502 locations->SetOut(Location::ConstantLocation(constant));
3503}
3504
3505void InstructionCodeGeneratorARMVIXL::VisitIntConstant(HIntConstant* constant ATTRIBUTE_UNUSED) {
3506 // Will be generated at use site.
3507}
3508
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003509void LocationsBuilderARMVIXL::VisitNullConstant(HNullConstant* constant) {
3510 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003511 new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003512 locations->SetOut(Location::ConstantLocation(constant));
3513}
3514
3515void InstructionCodeGeneratorARMVIXL::VisitNullConstant(HNullConstant* constant ATTRIBUTE_UNUSED) {
3516 // Will be generated at use site.
3517}
3518
Scott Wakelingfe885462016-09-22 10:24:38 +01003519void LocationsBuilderARMVIXL::VisitLongConstant(HLongConstant* constant) {
3520 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003521 new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
Scott Wakelingfe885462016-09-22 10:24:38 +01003522 locations->SetOut(Location::ConstantLocation(constant));
3523}
3524
3525void InstructionCodeGeneratorARMVIXL::VisitLongConstant(HLongConstant* constant ATTRIBUTE_UNUSED) {
3526 // Will be generated at use site.
3527}
3528
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01003529void LocationsBuilderARMVIXL::VisitFloatConstant(HFloatConstant* constant) {
3530 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003531 new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01003532 locations->SetOut(Location::ConstantLocation(constant));
3533}
3534
Scott Wakelingc34dba72016-10-03 10:14:44 +01003535void InstructionCodeGeneratorARMVIXL::VisitFloatConstant(
3536 HFloatConstant* constant ATTRIBUTE_UNUSED) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01003537 // Will be generated at use site.
3538}
3539
3540void LocationsBuilderARMVIXL::VisitDoubleConstant(HDoubleConstant* constant) {
3541 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003542 new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01003543 locations->SetOut(Location::ConstantLocation(constant));
3544}
3545
Scott Wakelingc34dba72016-10-03 10:14:44 +01003546void InstructionCodeGeneratorARMVIXL::VisitDoubleConstant(
3547 HDoubleConstant* constant ATTRIBUTE_UNUSED) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01003548 // Will be generated at use site.
3549}
3550
Igor Murashkind01745e2017-04-05 16:40:31 -07003551void LocationsBuilderARMVIXL::VisitConstructorFence(HConstructorFence* constructor_fence) {
3552 constructor_fence->SetLocations(nullptr);
3553}
3554
3555void InstructionCodeGeneratorARMVIXL::VisitConstructorFence(
3556 HConstructorFence* constructor_fence ATTRIBUTE_UNUSED) {
3557 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
3558}
3559
Scott Wakelingfe885462016-09-22 10:24:38 +01003560void LocationsBuilderARMVIXL::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
3561 memory_barrier->SetLocations(nullptr);
3562}
3563
3564void InstructionCodeGeneratorARMVIXL::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
3565 codegen_->GenerateMemoryBarrier(memory_barrier->GetBarrierKind());
3566}
3567
3568void LocationsBuilderARMVIXL::VisitReturnVoid(HReturnVoid* ret) {
3569 ret->SetLocations(nullptr);
3570}
3571
3572void InstructionCodeGeneratorARMVIXL::VisitReturnVoid(HReturnVoid* ret ATTRIBUTE_UNUSED) {
3573 codegen_->GenerateFrameExit();
3574}
3575
3576void LocationsBuilderARMVIXL::VisitReturn(HReturn* ret) {
3577 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003578 new (GetGraph()->GetAllocator()) LocationSummary(ret, LocationSummary::kNoCall);
Scott Wakelingfe885462016-09-22 10:24:38 +01003579 locations->SetInAt(0, parameter_visitor_.GetReturnLocation(ret->InputAt(0)->GetType()));
3580}
3581
3582void InstructionCodeGeneratorARMVIXL::VisitReturn(HReturn* ret ATTRIBUTE_UNUSED) {
3583 codegen_->GenerateFrameExit();
3584}
3585
Artem Serovcfbe9132016-10-14 15:58:56 +01003586void LocationsBuilderARMVIXL::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
3587 // The trampoline uses the same calling convention as dex calling conventions,
3588 // except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain
3589 // the method_idx.
3590 HandleInvoke(invoke);
3591}
3592
3593void InstructionCodeGeneratorARMVIXL::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
3594 codegen_->GenerateInvokeUnresolvedRuntimeCall(invoke);
Roland Levillain5daa4952017-07-03 17:23:56 +01003595 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 3);
Artem Serovcfbe9132016-10-14 15:58:56 +01003596}
3597
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003598void LocationsBuilderARMVIXL::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
3599 // Explicit clinit checks triggered by static invokes must have been pruned by
3600 // art::PrepareForRegisterAllocation.
3601 DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
3602
Anton Kirilov5ec62182016-10-13 20:16:02 +01003603 IntrinsicLocationsBuilderARMVIXL intrinsic(codegen_);
3604 if (intrinsic.TryDispatch(invoke)) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01003605 return;
3606 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003607
3608 HandleInvoke(invoke);
3609}
3610
Anton Kirilov5ec62182016-10-13 20:16:02 +01003611static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorARMVIXL* codegen) {
3612 if (invoke->GetLocations()->Intrinsified()) {
3613 IntrinsicCodeGeneratorARMVIXL intrinsic(codegen);
3614 intrinsic.Dispatch(invoke);
3615 return true;
3616 }
3617 return false;
3618}
3619
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003620void InstructionCodeGeneratorARMVIXL::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
3621 // Explicit clinit checks triggered by static invokes must have been pruned by
3622 // art::PrepareForRegisterAllocation.
3623 DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
3624
Anton Kirilov5ec62182016-10-13 20:16:02 +01003625 if (TryGenerateIntrinsicCode(invoke, codegen_)) {
Roland Levillain5daa4952017-07-03 17:23:56 +01003626 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 4);
Anton Kirilov5ec62182016-10-13 20:16:02 +01003627 return;
3628 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003629
3630 LocationSummary* locations = invoke->GetLocations();
Artem Serovd4cc5b22016-11-04 11:19:09 +00003631 codegen_->GenerateStaticOrDirectCall(
3632 invoke, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation());
Roland Levillain5daa4952017-07-03 17:23:56 +01003633
3634 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 5);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003635}
3636
3637void LocationsBuilderARMVIXL::HandleInvoke(HInvoke* invoke) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00003638 InvokeDexCallingConventionVisitorARMVIXL calling_convention_visitor;
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003639 CodeGenerator::CreateCommonInvokeLocationSummary(invoke, &calling_convention_visitor);
3640}
3641
3642void LocationsBuilderARMVIXL::VisitInvokeVirtual(HInvokeVirtual* invoke) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01003643 IntrinsicLocationsBuilderARMVIXL intrinsic(codegen_);
3644 if (intrinsic.TryDispatch(invoke)) {
3645 return;
3646 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003647
3648 HandleInvoke(invoke);
3649}
3650
3651void InstructionCodeGeneratorARMVIXL::VisitInvokeVirtual(HInvokeVirtual* invoke) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01003652 if (TryGenerateIntrinsicCode(invoke, codegen_)) {
Roland Levillain5daa4952017-07-03 17:23:56 +01003653 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 6);
Anton Kirilov5ec62182016-10-13 20:16:02 +01003654 return;
3655 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003656
3657 codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0));
Alexandre Rames374ddf32016-11-04 10:40:49 +00003658 DCHECK(!codegen_->IsLeafMethod());
Roland Levillain5daa4952017-07-03 17:23:56 +01003659
3660 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 7);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003661}
3662
Artem Serovcfbe9132016-10-14 15:58:56 +01003663void LocationsBuilderARMVIXL::VisitInvokeInterface(HInvokeInterface* invoke) {
3664 HandleInvoke(invoke);
3665 // Add the hidden argument.
3666 invoke->GetLocations()->AddTemp(LocationFrom(r12));
3667}
3668
3669void InstructionCodeGeneratorARMVIXL::VisitInvokeInterface(HInvokeInterface* invoke) {
3670 // TODO: b/18116999, our IMTs can miss an IncompatibleClassChangeError.
3671 LocationSummary* locations = invoke->GetLocations();
3672 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
3673 vixl32::Register hidden_reg = RegisterFrom(locations->GetTemp(1));
3674 Location receiver = locations->InAt(0);
3675 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
3676
3677 DCHECK(!receiver.IsStackSlot());
3678
Alexandre Rames374ddf32016-11-04 10:40:49 +00003679 // Ensure the pc position is recorded immediately after the `ldr` instruction.
3680 {
Artem Serov0fb37192016-12-06 18:13:40 +00003681 ExactAssemblyScope aas(GetVIXLAssembler(),
3682 vixl32::kMaxInstructionSizeInBytes,
3683 CodeBufferCheckScope::kMaximumSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00003684 // /* HeapReference<Class> */ temp = receiver->klass_
3685 __ ldr(temp, MemOperand(RegisterFrom(receiver), class_offset));
3686 codegen_->MaybeRecordImplicitNullCheck(invoke);
3687 }
Artem Serovcfbe9132016-10-14 15:58:56 +01003688 // Instead of simply (possibly) unpoisoning `temp` here, we should
3689 // emit a read barrier for the previous class reference load.
3690 // However this is not required in practice, as this is an
3691 // intermediate/temporary reference and because the current
3692 // concurrent copying collector keeps the from-space memory
3693 // intact/accessible until the end of the marking phase (the
3694 // concurrent copying collector may not in the future).
3695 GetAssembler()->MaybeUnpoisonHeapReference(temp);
3696 GetAssembler()->LoadFromOffset(kLoadWord,
3697 temp,
3698 temp,
3699 mirror::Class::ImtPtrOffset(kArmPointerSize).Uint32Value());
3700 uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement(
3701 invoke->GetImtIndex(), kArmPointerSize));
3702 // temp = temp->GetImtEntryAt(method_offset);
3703 GetAssembler()->LoadFromOffset(kLoadWord, temp, temp, method_offset);
3704 uint32_t entry_point =
3705 ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value();
3706 // LR = temp->GetEntryPoint();
3707 GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, entry_point);
3708
3709 // Set the hidden (in r12) argument. It is done here, right before a BLX to prevent other
3710 // instruction from clobbering it as they might use r12 as a scratch register.
3711 DCHECK(hidden_reg.Is(r12));
Scott Wakelingb77051e2016-11-21 19:46:00 +00003712
3713 {
3714 // The VIXL macro assembler may clobber any of the scratch registers that are available to it,
3715 // so it checks if the application is using them (by passing them to the macro assembler
3716 // methods). The following application of UseScratchRegisterScope corrects VIXL's notion of
3717 // what is available, and is the opposite of the standard usage: Instead of requesting a
3718 // temporary location, it imposes an external constraint (i.e. a specific register is reserved
3719 // for the hidden argument). Note that this works even if VIXL needs a scratch register itself
3720 // (to materialize the constant), since the destination register becomes available for such use
3721 // internally for the duration of the macro instruction.
3722 UseScratchRegisterScope temps(GetVIXLAssembler());
3723 temps.Exclude(hidden_reg);
3724 __ Mov(hidden_reg, invoke->GetDexMethodIndex());
3725 }
Artem Serovcfbe9132016-10-14 15:58:56 +01003726 {
Alexandre Rames374ddf32016-11-04 10:40:49 +00003727 // Ensure the pc position is recorded immediately after the `blx` instruction.
3728 // 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 +00003729 ExactAssemblyScope aas(GetVIXLAssembler(),
Alexandre Rames374ddf32016-11-04 10:40:49 +00003730 vixl32::k16BitT32InstructionSizeInBytes,
3731 CodeBufferCheckScope::kExactSize);
Artem Serovcfbe9132016-10-14 15:58:56 +01003732 // LR();
3733 __ blx(lr);
Artem Serovcfbe9132016-10-14 15:58:56 +01003734 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
Alexandre Rames374ddf32016-11-04 10:40:49 +00003735 DCHECK(!codegen_->IsLeafMethod());
Artem Serovcfbe9132016-10-14 15:58:56 +01003736 }
Roland Levillain5daa4952017-07-03 17:23:56 +01003737
3738 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 8);
Artem Serovcfbe9132016-10-14 15:58:56 +01003739}
3740
Orion Hodsonac141392017-01-13 11:53:47 +00003741void LocationsBuilderARMVIXL::VisitInvokePolymorphic(HInvokePolymorphic* invoke) {
3742 HandleInvoke(invoke);
3743}
3744
3745void InstructionCodeGeneratorARMVIXL::VisitInvokePolymorphic(HInvokePolymorphic* invoke) {
3746 codegen_->GenerateInvokePolymorphicCall(invoke);
Roland Levillain5daa4952017-07-03 17:23:56 +01003747 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 9);
Orion Hodsonac141392017-01-13 11:53:47 +00003748}
3749
Orion Hodson4c8e12e2018-05-18 08:33:20 +01003750void LocationsBuilderARMVIXL::VisitInvokeCustom(HInvokeCustom* invoke) {
3751 HandleInvoke(invoke);
3752}
3753
3754void InstructionCodeGeneratorARMVIXL::VisitInvokeCustom(HInvokeCustom* invoke) {
3755 codegen_->GenerateInvokeCustomCall(invoke);
3756 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 10);
3757}
3758
Artem Serov02109dd2016-09-23 17:17:54 +01003759void LocationsBuilderARMVIXL::VisitNeg(HNeg* neg) {
3760 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003761 new (GetGraph()->GetAllocator()) LocationSummary(neg, LocationSummary::kNoCall);
Artem Serov02109dd2016-09-23 17:17:54 +01003762 switch (neg->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003763 case DataType::Type::kInt32: {
Artem Serov02109dd2016-09-23 17:17:54 +01003764 locations->SetInAt(0, Location::RequiresRegister());
3765 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3766 break;
3767 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003768 case DataType::Type::kInt64: {
Artem Serov02109dd2016-09-23 17:17:54 +01003769 locations->SetInAt(0, Location::RequiresRegister());
3770 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
3771 break;
3772 }
3773
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003774 case DataType::Type::kFloat32:
3775 case DataType::Type::kFloat64:
Artem Serov02109dd2016-09-23 17:17:54 +01003776 locations->SetInAt(0, Location::RequiresFpuRegister());
3777 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3778 break;
3779
3780 default:
3781 LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
3782 }
3783}
3784
3785void InstructionCodeGeneratorARMVIXL::VisitNeg(HNeg* neg) {
3786 LocationSummary* locations = neg->GetLocations();
3787 Location out = locations->Out();
3788 Location in = locations->InAt(0);
3789 switch (neg->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003790 case DataType::Type::kInt32:
Artem Serov02109dd2016-09-23 17:17:54 +01003791 __ Rsb(OutputRegister(neg), InputRegisterAt(neg, 0), 0);
3792 break;
3793
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003794 case DataType::Type::kInt64:
Artem Serov02109dd2016-09-23 17:17:54 +01003795 // out.lo = 0 - in.lo (and update the carry/borrow (C) flag)
3796 __ Rsbs(LowRegisterFrom(out), LowRegisterFrom(in), 0);
3797 // We cannot emit an RSC (Reverse Subtract with Carry)
3798 // instruction here, as it does not exist in the Thumb-2
3799 // instruction set. We use the following approach
3800 // using SBC and SUB instead.
3801 //
3802 // out.hi = -C
3803 __ Sbc(HighRegisterFrom(out), HighRegisterFrom(out), HighRegisterFrom(out));
3804 // out.hi = out.hi - in.hi
3805 __ Sub(HighRegisterFrom(out), HighRegisterFrom(out), HighRegisterFrom(in));
3806 break;
3807
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003808 case DataType::Type::kFloat32:
3809 case DataType::Type::kFloat64:
Anton Kirilov644032c2016-12-06 17:51:43 +00003810 __ Vneg(OutputVRegister(neg), InputVRegister(neg));
Artem Serov02109dd2016-09-23 17:17:54 +01003811 break;
3812
3813 default:
3814 LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
3815 }
3816}
3817
Scott Wakelingfe885462016-09-22 10:24:38 +01003818void LocationsBuilderARMVIXL::VisitTypeConversion(HTypeConversion* conversion) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003819 DataType::Type result_type = conversion->GetResultType();
3820 DataType::Type input_type = conversion->GetInputType();
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003821 DCHECK(!DataType::IsTypeConversionImplicit(input_type, result_type))
3822 << input_type << " -> " << result_type;
Scott Wakelingfe885462016-09-22 10:24:38 +01003823
3824 // The float-to-long, double-to-long and long-to-float type conversions
3825 // rely on a call to the runtime.
3826 LocationSummary::CallKind call_kind =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003827 (((input_type == DataType::Type::kFloat32 || input_type == DataType::Type::kFloat64)
3828 && result_type == DataType::Type::kInt64)
3829 || (input_type == DataType::Type::kInt64 && result_type == DataType::Type::kFloat32))
Scott Wakelingfe885462016-09-22 10:24:38 +01003830 ? LocationSummary::kCallOnMainOnly
3831 : LocationSummary::kNoCall;
3832 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003833 new (GetGraph()->GetAllocator()) LocationSummary(conversion, call_kind);
Scott Wakelingfe885462016-09-22 10:24:38 +01003834
Scott Wakelingfe885462016-09-22 10:24:38 +01003835 switch (result_type) {
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003836 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003837 case DataType::Type::kInt8:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003838 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003839 case DataType::Type::kInt16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003840 DCHECK(DataType::IsIntegralType(input_type)) << input_type;
3841 locations->SetInAt(0, Location::RequiresRegister());
3842 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Scott Wakelingfe885462016-09-22 10:24:38 +01003843 break;
3844
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003845 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003846 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003847 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003848 locations->SetInAt(0, Location::Any());
3849 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3850 break;
3851
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003852 case DataType::Type::kFloat32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003853 locations->SetInAt(0, Location::RequiresFpuRegister());
3854 locations->SetOut(Location::RequiresRegister());
3855 locations->AddTemp(Location::RequiresFpuRegister());
3856 break;
3857
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003858 case DataType::Type::kFloat64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003859 locations->SetInAt(0, Location::RequiresFpuRegister());
3860 locations->SetOut(Location::RequiresRegister());
3861 locations->AddTemp(Location::RequiresFpuRegister());
3862 break;
3863
3864 default:
3865 LOG(FATAL) << "Unexpected type conversion from " << input_type
3866 << " to " << result_type;
3867 }
3868 break;
3869
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003870 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003871 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003872 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003873 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003874 case DataType::Type::kInt8:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003875 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003876 case DataType::Type::kInt16:
3877 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003878 locations->SetInAt(0, Location::RequiresRegister());
3879 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3880 break;
3881
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003882 case DataType::Type::kFloat32: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003883 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3884 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
3885 locations->SetOut(LocationFrom(r0, r1));
Scott Wakelingfe885462016-09-22 10:24:38 +01003886 break;
3887 }
3888
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003889 case DataType::Type::kFloat64: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003890 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3891 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0),
3892 calling_convention.GetFpuRegisterAt(1)));
3893 locations->SetOut(LocationFrom(r0, r1));
Scott Wakelingfe885462016-09-22 10:24:38 +01003894 break;
3895 }
3896
3897 default:
3898 LOG(FATAL) << "Unexpected type conversion from " << input_type
3899 << " to " << result_type;
3900 }
3901 break;
3902
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003903 case DataType::Type::kFloat32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003904 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003905 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003906 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003907 case DataType::Type::kInt8:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003908 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003909 case DataType::Type::kInt16:
3910 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003911 locations->SetInAt(0, Location::RequiresRegister());
3912 locations->SetOut(Location::RequiresFpuRegister());
3913 break;
3914
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003915 case DataType::Type::kInt64: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003916 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3917 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0),
3918 calling_convention.GetRegisterAt(1)));
3919 locations->SetOut(LocationFrom(calling_convention.GetFpuRegisterAt(0)));
Scott Wakelingfe885462016-09-22 10:24:38 +01003920 break;
3921 }
3922
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003923 case DataType::Type::kFloat64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003924 locations->SetInAt(0, Location::RequiresFpuRegister());
3925 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3926 break;
3927
3928 default:
3929 LOG(FATAL) << "Unexpected type conversion from " << input_type
3930 << " to " << result_type;
Igor Murashkin2ffb7032017-11-08 13:35:21 -08003931 }
Scott Wakelingfe885462016-09-22 10:24:38 +01003932 break;
3933
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003934 case DataType::Type::kFloat64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003935 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003936 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003937 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003938 case DataType::Type::kInt8:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003939 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003940 case DataType::Type::kInt16:
3941 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003942 locations->SetInAt(0, Location::RequiresRegister());
3943 locations->SetOut(Location::RequiresFpuRegister());
3944 break;
3945
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003946 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003947 locations->SetInAt(0, Location::RequiresRegister());
3948 locations->SetOut(Location::RequiresFpuRegister());
3949 locations->AddTemp(Location::RequiresFpuRegister());
3950 locations->AddTemp(Location::RequiresFpuRegister());
3951 break;
3952
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003953 case DataType::Type::kFloat32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003954 locations->SetInAt(0, Location::RequiresFpuRegister());
3955 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3956 break;
3957
3958 default:
3959 LOG(FATAL) << "Unexpected type conversion from " << input_type
3960 << " to " << result_type;
Igor Murashkin2ffb7032017-11-08 13:35:21 -08003961 }
Scott Wakelingfe885462016-09-22 10:24:38 +01003962 break;
3963
3964 default:
3965 LOG(FATAL) << "Unexpected type conversion from " << input_type
3966 << " to " << result_type;
3967 }
3968}
3969
3970void InstructionCodeGeneratorARMVIXL::VisitTypeConversion(HTypeConversion* conversion) {
3971 LocationSummary* locations = conversion->GetLocations();
3972 Location out = locations->Out();
3973 Location in = locations->InAt(0);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003974 DataType::Type result_type = conversion->GetResultType();
3975 DataType::Type input_type = conversion->GetInputType();
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003976 DCHECK(!DataType::IsTypeConversionImplicit(input_type, result_type))
3977 << input_type << " -> " << result_type;
Scott Wakelingfe885462016-09-22 10:24:38 +01003978 switch (result_type) {
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003979 case DataType::Type::kUint8:
Scott Wakelingfe885462016-09-22 10:24:38 +01003980 switch (input_type) {
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003981 case DataType::Type::kInt8:
3982 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003983 case DataType::Type::kInt16:
3984 case DataType::Type::kInt32:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003985 __ Ubfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 8);
3986 break;
3987 case DataType::Type::kInt64:
3988 __ Ubfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 8);
3989 break;
3990
3991 default:
3992 LOG(FATAL) << "Unexpected type conversion from " << input_type
3993 << " to " << result_type;
3994 }
3995 break;
3996
3997 case DataType::Type::kInt8:
3998 switch (input_type) {
3999 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004000 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01004001 case DataType::Type::kInt16:
4002 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01004003 __ Sbfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 8);
4004 break;
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01004005 case DataType::Type::kInt64:
4006 __ Sbfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 8);
4007 break;
4008
4009 default:
4010 LOG(FATAL) << "Unexpected type conversion from " << input_type
4011 << " to " << result_type;
4012 }
4013 break;
4014
4015 case DataType::Type::kUint16:
4016 switch (input_type) {
4017 case DataType::Type::kInt8:
4018 case DataType::Type::kInt16:
4019 case DataType::Type::kInt32:
4020 __ Ubfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 16);
4021 break;
4022 case DataType::Type::kInt64:
4023 __ Ubfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 16);
4024 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01004025
4026 default:
4027 LOG(FATAL) << "Unexpected type conversion from " << input_type
4028 << " to " << result_type;
4029 }
4030 break;
4031
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004032 case DataType::Type::kInt16:
Scott Wakelingfe885462016-09-22 10:24:38 +01004033 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004034 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01004035 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01004036 __ Sbfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 16);
4037 break;
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01004038 case DataType::Type::kInt64:
4039 __ Sbfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 16);
4040 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01004041
4042 default:
4043 LOG(FATAL) << "Unexpected type conversion from " << input_type
4044 << " to " << result_type;
4045 }
4046 break;
4047
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004048 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01004049 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004050 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01004051 DCHECK(out.IsRegister());
4052 if (in.IsRegisterPair()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004053 __ Mov(OutputRegister(conversion), LowRegisterFrom(in));
Scott Wakelingfe885462016-09-22 10:24:38 +01004054 } else if (in.IsDoubleStackSlot()) {
4055 GetAssembler()->LoadFromOffset(kLoadWord,
4056 OutputRegister(conversion),
4057 sp,
4058 in.GetStackIndex());
4059 } else {
4060 DCHECK(in.IsConstant());
4061 DCHECK(in.GetConstant()->IsLongConstant());
Vladimir Markoba1a48e2017-04-13 11:50:14 +01004062 int64_t value = in.GetConstant()->AsLongConstant()->GetValue();
4063 __ Mov(OutputRegister(conversion), static_cast<int32_t>(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01004064 }
4065 break;
4066
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004067 case DataType::Type::kFloat32: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004068 vixl32::SRegister temp = LowSRegisterFrom(locations->GetTemp(0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01004069 __ Vcvt(S32, F32, temp, InputSRegisterAt(conversion, 0));
Scott Wakelingfe885462016-09-22 10:24:38 +01004070 __ Vmov(OutputRegister(conversion), temp);
4071 break;
4072 }
4073
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004074 case DataType::Type::kFloat64: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004075 vixl32::SRegister temp_s = LowSRegisterFrom(locations->GetTemp(0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01004076 __ Vcvt(S32, F64, temp_s, DRegisterFrom(in));
Scott Wakelingfe885462016-09-22 10:24:38 +01004077 __ Vmov(OutputRegister(conversion), temp_s);
4078 break;
4079 }
4080
4081 default:
4082 LOG(FATAL) << "Unexpected type conversion from " << input_type
4083 << " to " << result_type;
4084 }
4085 break;
4086
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004087 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01004088 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004089 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01004090 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004091 case DataType::Type::kInt8:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01004092 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004093 case DataType::Type::kInt16:
4094 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01004095 DCHECK(out.IsRegisterPair());
4096 DCHECK(in.IsRegister());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004097 __ Mov(LowRegisterFrom(out), InputRegisterAt(conversion, 0));
Scott Wakelingfe885462016-09-22 10:24:38 +01004098 // Sign extension.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004099 __ Asr(HighRegisterFrom(out), LowRegisterFrom(out), 31);
Scott Wakelingfe885462016-09-22 10:24:38 +01004100 break;
4101
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004102 case DataType::Type::kFloat32:
Scott Wakelingfe885462016-09-22 10:24:38 +01004103 codegen_->InvokeRuntime(kQuickF2l, conversion, conversion->GetDexPc());
4104 CheckEntrypointTypes<kQuickF2l, int64_t, float>();
4105 break;
4106
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004107 case DataType::Type::kFloat64:
Scott Wakelingfe885462016-09-22 10:24:38 +01004108 codegen_->InvokeRuntime(kQuickD2l, conversion, conversion->GetDexPc());
4109 CheckEntrypointTypes<kQuickD2l, int64_t, double>();
4110 break;
4111
4112 default:
4113 LOG(FATAL) << "Unexpected type conversion from " << input_type
4114 << " to " << result_type;
4115 }
4116 break;
4117
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004118 case DataType::Type::kFloat32:
Scott Wakelingfe885462016-09-22 10:24:38 +01004119 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004120 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01004121 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004122 case DataType::Type::kInt8:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01004123 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004124 case DataType::Type::kInt16:
4125 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01004126 __ Vmov(OutputSRegister(conversion), InputRegisterAt(conversion, 0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01004127 __ Vcvt(F32, S32, OutputSRegister(conversion), OutputSRegister(conversion));
Scott Wakelingfe885462016-09-22 10:24:38 +01004128 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01004129
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004130 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01004131 codegen_->InvokeRuntime(kQuickL2f, conversion, conversion->GetDexPc());
4132 CheckEntrypointTypes<kQuickL2f, float, int64_t>();
4133 break;
4134
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004135 case DataType::Type::kFloat64:
Scott Wakelingc34dba72016-10-03 10:14:44 +01004136 __ Vcvt(F32, F64, OutputSRegister(conversion), DRegisterFrom(in));
Scott Wakelingfe885462016-09-22 10:24:38 +01004137 break;
4138
4139 default:
4140 LOG(FATAL) << "Unexpected type conversion from " << input_type
4141 << " to " << result_type;
Igor Murashkin2ffb7032017-11-08 13:35:21 -08004142 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004143 break;
4144
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004145 case DataType::Type::kFloat64:
Scott Wakelingfe885462016-09-22 10:24:38 +01004146 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004147 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01004148 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004149 case DataType::Type::kInt8:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01004150 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004151 case DataType::Type::kInt16:
4152 case DataType::Type::kInt32:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004153 __ Vmov(LowSRegisterFrom(out), InputRegisterAt(conversion, 0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01004154 __ Vcvt(F64, S32, DRegisterFrom(out), LowSRegisterFrom(out));
Scott Wakelingfe885462016-09-22 10:24:38 +01004155 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01004156
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004157 case DataType::Type::kInt64: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004158 vixl32::Register low = LowRegisterFrom(in);
4159 vixl32::Register high = HighRegisterFrom(in);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004160 vixl32::SRegister out_s = LowSRegisterFrom(out);
Scott Wakelingc34dba72016-10-03 10:14:44 +01004161 vixl32::DRegister out_d = DRegisterFrom(out);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004162 vixl32::SRegister temp_s = LowSRegisterFrom(locations->GetTemp(0));
Scott Wakelingc34dba72016-10-03 10:14:44 +01004163 vixl32::DRegister temp_d = DRegisterFrom(locations->GetTemp(0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01004164 vixl32::DRegister constant_d = DRegisterFrom(locations->GetTemp(1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004165
4166 // temp_d = int-to-double(high)
4167 __ Vmov(temp_s, high);
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01004168 __ Vcvt(F64, S32, temp_d, temp_s);
Scott Wakelingfe885462016-09-22 10:24:38 +01004169 // constant_d = k2Pow32EncodingForDouble
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004170 __ Vmov(constant_d, bit_cast<double, int64_t>(k2Pow32EncodingForDouble));
Scott Wakelingfe885462016-09-22 10:24:38 +01004171 // out_d = unsigned-to-double(low)
4172 __ Vmov(out_s, low);
4173 __ Vcvt(F64, U32, out_d, out_s);
4174 // out_d += temp_d * constant_d
4175 __ Vmla(F64, out_d, temp_d, constant_d);
4176 break;
4177 }
4178
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004179 case DataType::Type::kFloat32:
Scott Wakelingc34dba72016-10-03 10:14:44 +01004180 __ Vcvt(F64, F32, DRegisterFrom(out), InputSRegisterAt(conversion, 0));
Scott Wakelingfe885462016-09-22 10:24:38 +01004181 break;
4182
4183 default:
4184 LOG(FATAL) << "Unexpected type conversion from " << input_type
4185 << " to " << result_type;
Igor Murashkin2ffb7032017-11-08 13:35:21 -08004186 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004187 break;
4188
4189 default:
4190 LOG(FATAL) << "Unexpected type conversion from " << input_type
4191 << " to " << result_type;
4192 }
4193}
4194
4195void LocationsBuilderARMVIXL::VisitAdd(HAdd* add) {
4196 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01004197 new (GetGraph()->GetAllocator()) LocationSummary(add, LocationSummary::kNoCall);
Scott Wakelingfe885462016-09-22 10:24:38 +01004198 switch (add->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004199 case DataType::Type::kInt32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004200 locations->SetInAt(0, Location::RequiresRegister());
4201 locations->SetInAt(1, Location::RegisterOrConstant(add->InputAt(1)));
4202 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4203 break;
4204 }
4205
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004206 case DataType::Type::kInt64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004207 locations->SetInAt(0, Location::RequiresRegister());
Anton Kirilovdda43962016-11-21 19:55:20 +00004208 locations->SetInAt(1, ArmEncodableConstantOrRegister(add->InputAt(1), ADD));
Scott Wakelingfe885462016-09-22 10:24:38 +01004209 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4210 break;
4211 }
4212
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004213 case DataType::Type::kFloat32:
4214 case DataType::Type::kFloat64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004215 locations->SetInAt(0, Location::RequiresFpuRegister());
4216 locations->SetInAt(1, Location::RequiresFpuRegister());
4217 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4218 break;
4219 }
4220
4221 default:
4222 LOG(FATAL) << "Unexpected add type " << add->GetResultType();
4223 }
4224}
4225
4226void InstructionCodeGeneratorARMVIXL::VisitAdd(HAdd* add) {
4227 LocationSummary* locations = add->GetLocations();
4228 Location out = locations->Out();
4229 Location first = locations->InAt(0);
4230 Location second = locations->InAt(1);
4231
4232 switch (add->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004233 case DataType::Type::kInt32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004234 __ Add(OutputRegister(add), InputRegisterAt(add, 0), InputOperandAt(add, 1));
4235 }
4236 break;
4237
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004238 case DataType::Type::kInt64: {
Anton Kirilovdda43962016-11-21 19:55:20 +00004239 if (second.IsConstant()) {
4240 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
4241 GenerateAddLongConst(out, first, value);
4242 } else {
4243 DCHECK(second.IsRegisterPair());
4244 __ Adds(LowRegisterFrom(out), LowRegisterFrom(first), LowRegisterFrom(second));
4245 __ Adc(HighRegisterFrom(out), HighRegisterFrom(first), HighRegisterFrom(second));
4246 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004247 break;
4248 }
4249
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004250 case DataType::Type::kFloat32:
4251 case DataType::Type::kFloat64:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004252 __ Vadd(OutputVRegister(add), InputVRegisterAt(add, 0), InputVRegisterAt(add, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004253 break;
4254
4255 default:
4256 LOG(FATAL) << "Unexpected add type " << add->GetResultType();
4257 }
4258}
4259
4260void LocationsBuilderARMVIXL::VisitSub(HSub* sub) {
4261 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01004262 new (GetGraph()->GetAllocator()) LocationSummary(sub, LocationSummary::kNoCall);
Scott Wakelingfe885462016-09-22 10:24:38 +01004263 switch (sub->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004264 case DataType::Type::kInt32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004265 locations->SetInAt(0, Location::RequiresRegister());
4266 locations->SetInAt(1, Location::RegisterOrConstant(sub->InputAt(1)));
4267 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4268 break;
4269 }
4270
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004271 case DataType::Type::kInt64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004272 locations->SetInAt(0, Location::RequiresRegister());
Anton Kirilovdda43962016-11-21 19:55:20 +00004273 locations->SetInAt(1, ArmEncodableConstantOrRegister(sub->InputAt(1), SUB));
Scott Wakelingfe885462016-09-22 10:24:38 +01004274 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4275 break;
4276 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004277 case DataType::Type::kFloat32:
4278 case DataType::Type::kFloat64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004279 locations->SetInAt(0, Location::RequiresFpuRegister());
4280 locations->SetInAt(1, Location::RequiresFpuRegister());
4281 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4282 break;
4283 }
4284 default:
4285 LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
4286 }
4287}
4288
4289void InstructionCodeGeneratorARMVIXL::VisitSub(HSub* sub) {
4290 LocationSummary* locations = sub->GetLocations();
4291 Location out = locations->Out();
4292 Location first = locations->InAt(0);
4293 Location second = locations->InAt(1);
4294 switch (sub->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004295 case DataType::Type::kInt32: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004296 __ Sub(OutputRegister(sub), InputRegisterAt(sub, 0), InputOperandAt(sub, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004297 break;
4298 }
4299
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004300 case DataType::Type::kInt64: {
Anton Kirilovdda43962016-11-21 19:55:20 +00004301 if (second.IsConstant()) {
4302 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
4303 GenerateAddLongConst(out, first, -value);
4304 } else {
4305 DCHECK(second.IsRegisterPair());
4306 __ Subs(LowRegisterFrom(out), LowRegisterFrom(first), LowRegisterFrom(second));
4307 __ Sbc(HighRegisterFrom(out), HighRegisterFrom(first), HighRegisterFrom(second));
4308 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004309 break;
4310 }
4311
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004312 case DataType::Type::kFloat32:
4313 case DataType::Type::kFloat64:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004314 __ Vsub(OutputVRegister(sub), InputVRegisterAt(sub, 0), InputVRegisterAt(sub, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004315 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01004316
4317 default:
4318 LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
4319 }
4320}
4321
4322void LocationsBuilderARMVIXL::VisitMul(HMul* mul) {
4323 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01004324 new (GetGraph()->GetAllocator()) LocationSummary(mul, LocationSummary::kNoCall);
Scott Wakelingfe885462016-09-22 10:24:38 +01004325 switch (mul->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004326 case DataType::Type::kInt32:
4327 case DataType::Type::kInt64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004328 locations->SetInAt(0, Location::RequiresRegister());
4329 locations->SetInAt(1, Location::RequiresRegister());
4330 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4331 break;
4332 }
4333
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004334 case DataType::Type::kFloat32:
4335 case DataType::Type::kFloat64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004336 locations->SetInAt(0, Location::RequiresFpuRegister());
4337 locations->SetInAt(1, Location::RequiresFpuRegister());
4338 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4339 break;
4340 }
4341
4342 default:
4343 LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
4344 }
4345}
4346
4347void InstructionCodeGeneratorARMVIXL::VisitMul(HMul* mul) {
4348 LocationSummary* locations = mul->GetLocations();
4349 Location out = locations->Out();
4350 Location first = locations->InAt(0);
4351 Location second = locations->InAt(1);
4352 switch (mul->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004353 case DataType::Type::kInt32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004354 __ Mul(OutputRegister(mul), InputRegisterAt(mul, 0), InputRegisterAt(mul, 1));
4355 break;
4356 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004357 case DataType::Type::kInt64: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004358 vixl32::Register out_hi = HighRegisterFrom(out);
4359 vixl32::Register out_lo = LowRegisterFrom(out);
4360 vixl32::Register in1_hi = HighRegisterFrom(first);
4361 vixl32::Register in1_lo = LowRegisterFrom(first);
4362 vixl32::Register in2_hi = HighRegisterFrom(second);
4363 vixl32::Register in2_lo = LowRegisterFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01004364
4365 // Extra checks to protect caused by the existence of R1_R2.
4366 // The algorithm is wrong if out.hi is either in1.lo or in2.lo:
4367 // (e.g. in1=r0_r1, in2=r2_r3 and out=r1_r2);
Anton Kirilov644032c2016-12-06 17:51:43 +00004368 DCHECK(!out_hi.Is(in1_lo));
4369 DCHECK(!out_hi.Is(in2_lo));
Scott Wakelingfe885462016-09-22 10:24:38 +01004370
4371 // input: in1 - 64 bits, in2 - 64 bits
4372 // output: out
4373 // formula: out.hi : out.lo = (in1.lo * in2.hi + in1.hi * in2.lo)* 2^32 + in1.lo * in2.lo
4374 // parts: out.hi = in1.lo * in2.hi + in1.hi * in2.lo + (in1.lo * in2.lo)[63:32]
4375 // parts: out.lo = (in1.lo * in2.lo)[31:0]
4376
4377 UseScratchRegisterScope temps(GetVIXLAssembler());
4378 vixl32::Register temp = temps.Acquire();
4379 // temp <- in1.lo * in2.hi
4380 __ Mul(temp, in1_lo, in2_hi);
4381 // out.hi <- in1.lo * in2.hi + in1.hi * in2.lo
4382 __ Mla(out_hi, in1_hi, in2_lo, temp);
4383 // out.lo <- (in1.lo * in2.lo)[31:0];
4384 __ Umull(out_lo, temp, in1_lo, in2_lo);
4385 // out.hi <- in2.hi * in1.lo + in2.lo * in1.hi + (in1.lo * in2.lo)[63:32]
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004386 __ Add(out_hi, out_hi, temp);
Scott Wakelingfe885462016-09-22 10:24:38 +01004387 break;
4388 }
4389
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004390 case DataType::Type::kFloat32:
4391 case DataType::Type::kFloat64:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004392 __ Vmul(OutputVRegister(mul), InputVRegisterAt(mul, 0), InputVRegisterAt(mul, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004393 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01004394
4395 default:
4396 LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
4397 }
4398}
4399
Scott Wakelingfe885462016-09-22 10:24:38 +01004400void InstructionCodeGeneratorARMVIXL::DivRemOneOrMinusOne(HBinaryOperation* instruction) {
4401 DCHECK(instruction->IsDiv() || instruction->IsRem());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004402 DCHECK(instruction->GetResultType() == DataType::Type::kInt32);
Scott Wakelingfe885462016-09-22 10:24:38 +01004403
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004404 Location second = instruction->GetLocations()->InAt(1);
Scott Wakelingfe885462016-09-22 10:24:38 +01004405 DCHECK(second.IsConstant());
4406
4407 vixl32::Register out = OutputRegister(instruction);
4408 vixl32::Register dividend = InputRegisterAt(instruction, 0);
Anton Kirilov644032c2016-12-06 17:51:43 +00004409 int32_t imm = Int32ConstantFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01004410 DCHECK(imm == 1 || imm == -1);
4411
4412 if (instruction->IsRem()) {
4413 __ Mov(out, 0);
4414 } else {
4415 if (imm == 1) {
4416 __ Mov(out, dividend);
4417 } else {
4418 __ Rsb(out, dividend, 0);
4419 }
4420 }
4421}
4422
4423void InstructionCodeGeneratorARMVIXL::DivRemByPowerOfTwo(HBinaryOperation* instruction) {
4424 DCHECK(instruction->IsDiv() || instruction->IsRem());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004425 DCHECK(instruction->GetResultType() == DataType::Type::kInt32);
Scott Wakelingfe885462016-09-22 10:24:38 +01004426
4427 LocationSummary* locations = instruction->GetLocations();
4428 Location second = locations->InAt(1);
4429 DCHECK(second.IsConstant());
4430
4431 vixl32::Register out = OutputRegister(instruction);
4432 vixl32::Register dividend = InputRegisterAt(instruction, 0);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004433 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
Anton Kirilov644032c2016-12-06 17:51:43 +00004434 int32_t imm = Int32ConstantFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01004435 uint32_t abs_imm = static_cast<uint32_t>(AbsOrMin(imm));
4436 int ctz_imm = CTZ(abs_imm);
4437
4438 if (ctz_imm == 1) {
4439 __ Lsr(temp, dividend, 32 - ctz_imm);
4440 } else {
4441 __ Asr(temp, dividend, 31);
4442 __ Lsr(temp, temp, 32 - ctz_imm);
4443 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004444 __ Add(out, temp, dividend);
Scott Wakelingfe885462016-09-22 10:24:38 +01004445
4446 if (instruction->IsDiv()) {
4447 __ Asr(out, out, ctz_imm);
4448 if (imm < 0) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004449 __ Rsb(out, out, 0);
Scott Wakelingfe885462016-09-22 10:24:38 +01004450 }
4451 } else {
4452 __ Ubfx(out, out, 0, ctz_imm);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004453 __ Sub(out, out, temp);
Scott Wakelingfe885462016-09-22 10:24:38 +01004454 }
4455}
4456
4457void InstructionCodeGeneratorARMVIXL::GenerateDivRemWithAnyConstant(HBinaryOperation* instruction) {
4458 DCHECK(instruction->IsDiv() || instruction->IsRem());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004459 DCHECK(instruction->GetResultType() == DataType::Type::kInt32);
Scott Wakelingfe885462016-09-22 10:24:38 +01004460
4461 LocationSummary* locations = instruction->GetLocations();
4462 Location second = locations->InAt(1);
4463 DCHECK(second.IsConstant());
4464
4465 vixl32::Register out = OutputRegister(instruction);
4466 vixl32::Register dividend = InputRegisterAt(instruction, 0);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004467 vixl32::Register temp1 = RegisterFrom(locations->GetTemp(0));
4468 vixl32::Register temp2 = RegisterFrom(locations->GetTemp(1));
Scott Wakelingb77051e2016-11-21 19:46:00 +00004469 int32_t imm = Int32ConstantFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01004470
4471 int64_t magic;
4472 int shift;
4473 CalculateMagicAndShiftForDivRem(imm, false /* is_long */, &magic, &shift);
4474
Anton Kirilovdda43962016-11-21 19:55:20 +00004475 // TODO(VIXL): Change the static cast to Operand::From() after VIXL is fixed.
4476 __ Mov(temp1, static_cast<int32_t>(magic));
Scott Wakelingfe885462016-09-22 10:24:38 +01004477 __ Smull(temp2, temp1, dividend, temp1);
4478
4479 if (imm > 0 && magic < 0) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004480 __ Add(temp1, temp1, dividend);
Scott Wakelingfe885462016-09-22 10:24:38 +01004481 } else if (imm < 0 && magic > 0) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004482 __ Sub(temp1, temp1, dividend);
Scott Wakelingfe885462016-09-22 10:24:38 +01004483 }
4484
4485 if (shift != 0) {
4486 __ Asr(temp1, temp1, shift);
4487 }
4488
4489 if (instruction->IsDiv()) {
4490 __ Sub(out, temp1, Operand(temp1, vixl32::Shift(ASR), 31));
4491 } else {
4492 __ Sub(temp1, temp1, Operand(temp1, vixl32::Shift(ASR), 31));
4493 // TODO: Strength reduction for mls.
4494 __ Mov(temp2, imm);
4495 __ Mls(out, temp1, temp2, dividend);
4496 }
4497}
4498
4499void InstructionCodeGeneratorARMVIXL::GenerateDivRemConstantIntegral(
4500 HBinaryOperation* instruction) {
4501 DCHECK(instruction->IsDiv() || instruction->IsRem());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004502 DCHECK(instruction->GetResultType() == DataType::Type::kInt32);
Scott Wakelingfe885462016-09-22 10:24:38 +01004503
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004504 Location second = instruction->GetLocations()->InAt(1);
Scott Wakelingfe885462016-09-22 10:24:38 +01004505 DCHECK(second.IsConstant());
4506
Anton Kirilov644032c2016-12-06 17:51:43 +00004507 int32_t imm = Int32ConstantFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01004508 if (imm == 0) {
4509 // Do not generate anything. DivZeroCheck would prevent any code to be executed.
4510 } else if (imm == 1 || imm == -1) {
4511 DivRemOneOrMinusOne(instruction);
4512 } else if (IsPowerOfTwo(AbsOrMin(imm))) {
4513 DivRemByPowerOfTwo(instruction);
4514 } else {
4515 DCHECK(imm <= -2 || imm >= 2);
4516 GenerateDivRemWithAnyConstant(instruction);
4517 }
4518}
4519
4520void LocationsBuilderARMVIXL::VisitDiv(HDiv* div) {
4521 LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004522 if (div->GetResultType() == DataType::Type::kInt64) {
Scott Wakelingfe885462016-09-22 10:24:38 +01004523 // pLdiv runtime call.
4524 call_kind = LocationSummary::kCallOnMainOnly;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004525 } else if (div->GetResultType() == DataType::Type::kInt32 && div->InputAt(1)->IsConstant()) {
Scott Wakelingfe885462016-09-22 10:24:38 +01004526 // sdiv will be replaced by other instruction sequence.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004527 } else if (div->GetResultType() == DataType::Type::kInt32 &&
Scott Wakelingfe885462016-09-22 10:24:38 +01004528 !codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4529 // pIdivmod runtime call.
4530 call_kind = LocationSummary::kCallOnMainOnly;
4531 }
4532
Vladimir Markoca6fff82017-10-03 14:49:14 +01004533 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(div, call_kind);
Scott Wakelingfe885462016-09-22 10:24:38 +01004534
4535 switch (div->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004536 case DataType::Type::kInt32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004537 if (div->InputAt(1)->IsConstant()) {
4538 locations->SetInAt(0, Location::RequiresRegister());
4539 locations->SetInAt(1, Location::ConstantLocation(div->InputAt(1)->AsConstant()));
4540 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Anton Kirilov644032c2016-12-06 17:51:43 +00004541 int32_t value = Int32ConstantFrom(div->InputAt(1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004542 if (value == 1 || value == 0 || value == -1) {
4543 // No temp register required.
4544 } else {
4545 locations->AddTemp(Location::RequiresRegister());
4546 if (!IsPowerOfTwo(AbsOrMin(value))) {
4547 locations->AddTemp(Location::RequiresRegister());
4548 }
4549 }
4550 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4551 locations->SetInAt(0, Location::RequiresRegister());
4552 locations->SetInAt(1, Location::RequiresRegister());
4553 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4554 } else {
Artem Serov551b28f2016-10-18 19:11:30 +01004555 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4556 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
4557 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
Roland Levillain5e8d5f02016-10-18 18:03:43 +01004558 // Note: divmod will compute both the quotient and the remainder as the pair R0 and R1, but
Artem Serov551b28f2016-10-18 19:11:30 +01004559 // we only need the former.
4560 locations->SetOut(LocationFrom(r0));
Scott Wakelingfe885462016-09-22 10:24:38 +01004561 }
4562 break;
4563 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004564 case DataType::Type::kInt64: {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004565 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4566 locations->SetInAt(0, LocationFrom(
4567 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
4568 locations->SetInAt(1, LocationFrom(
4569 calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3)));
4570 locations->SetOut(LocationFrom(r0, r1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004571 break;
4572 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004573 case DataType::Type::kFloat32:
4574 case DataType::Type::kFloat64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004575 locations->SetInAt(0, Location::RequiresFpuRegister());
4576 locations->SetInAt(1, Location::RequiresFpuRegister());
4577 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4578 break;
4579 }
4580
4581 default:
4582 LOG(FATAL) << "Unexpected div type " << div->GetResultType();
4583 }
4584}
4585
4586void InstructionCodeGeneratorARMVIXL::VisitDiv(HDiv* div) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004587 Location lhs = div->GetLocations()->InAt(0);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004588 Location rhs = div->GetLocations()->InAt(1);
Scott Wakelingfe885462016-09-22 10:24:38 +01004589
4590 switch (div->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004591 case DataType::Type::kInt32: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004592 if (rhs.IsConstant()) {
Scott Wakelingfe885462016-09-22 10:24:38 +01004593 GenerateDivRemConstantIntegral(div);
4594 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4595 __ Sdiv(OutputRegister(div), InputRegisterAt(div, 0), InputRegisterAt(div, 1));
4596 } else {
Artem Serov551b28f2016-10-18 19:11:30 +01004597 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4598 DCHECK(calling_convention.GetRegisterAt(0).Is(RegisterFrom(lhs)));
4599 DCHECK(calling_convention.GetRegisterAt(1).Is(RegisterFrom(rhs)));
4600 DCHECK(r0.Is(OutputRegister(div)));
4601
4602 codegen_->InvokeRuntime(kQuickIdivmod, div, div->GetDexPc());
4603 CheckEntrypointTypes<kQuickIdivmod, int32_t, int32_t, int32_t>();
Scott Wakelingfe885462016-09-22 10:24:38 +01004604 }
4605 break;
4606 }
4607
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004608 case DataType::Type::kInt64: {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004609 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4610 DCHECK(calling_convention.GetRegisterAt(0).Is(LowRegisterFrom(lhs)));
4611 DCHECK(calling_convention.GetRegisterAt(1).Is(HighRegisterFrom(lhs)));
4612 DCHECK(calling_convention.GetRegisterAt(2).Is(LowRegisterFrom(rhs)));
4613 DCHECK(calling_convention.GetRegisterAt(3).Is(HighRegisterFrom(rhs)));
4614 DCHECK(LowRegisterFrom(div->GetLocations()->Out()).Is(r0));
4615 DCHECK(HighRegisterFrom(div->GetLocations()->Out()).Is(r1));
4616
4617 codegen_->InvokeRuntime(kQuickLdiv, div, div->GetDexPc());
4618 CheckEntrypointTypes<kQuickLdiv, int64_t, int64_t, int64_t>();
Scott Wakelingfe885462016-09-22 10:24:38 +01004619 break;
4620 }
4621
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004622 case DataType::Type::kFloat32:
4623 case DataType::Type::kFloat64:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004624 __ Vdiv(OutputVRegister(div), InputVRegisterAt(div, 0), InputVRegisterAt(div, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004625 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01004626
4627 default:
4628 LOG(FATAL) << "Unexpected div type " << div->GetResultType();
4629 }
4630}
4631
Artem Serov551b28f2016-10-18 19:11:30 +01004632void LocationsBuilderARMVIXL::VisitRem(HRem* rem) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004633 DataType::Type type = rem->GetResultType();
Artem Serov551b28f2016-10-18 19:11:30 +01004634
4635 // Most remainders are implemented in the runtime.
4636 LocationSummary::CallKind call_kind = LocationSummary::kCallOnMainOnly;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004637 if (rem->GetResultType() == DataType::Type::kInt32 && rem->InputAt(1)->IsConstant()) {
Artem Serov551b28f2016-10-18 19:11:30 +01004638 // sdiv will be replaced by other instruction sequence.
4639 call_kind = LocationSummary::kNoCall;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004640 } else if ((rem->GetResultType() == DataType::Type::kInt32)
Artem Serov551b28f2016-10-18 19:11:30 +01004641 && codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4642 // Have hardware divide instruction for int, do it with three instructions.
4643 call_kind = LocationSummary::kNoCall;
4644 }
4645
Vladimir Markoca6fff82017-10-03 14:49:14 +01004646 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(rem, call_kind);
Artem Serov551b28f2016-10-18 19:11:30 +01004647
4648 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004649 case DataType::Type::kInt32: {
Artem Serov551b28f2016-10-18 19:11:30 +01004650 if (rem->InputAt(1)->IsConstant()) {
4651 locations->SetInAt(0, Location::RequiresRegister());
4652 locations->SetInAt(1, Location::ConstantLocation(rem->InputAt(1)->AsConstant()));
4653 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Anton Kirilov644032c2016-12-06 17:51:43 +00004654 int32_t value = Int32ConstantFrom(rem->InputAt(1));
Artem Serov551b28f2016-10-18 19:11:30 +01004655 if (value == 1 || value == 0 || value == -1) {
4656 // No temp register required.
4657 } else {
4658 locations->AddTemp(Location::RequiresRegister());
4659 if (!IsPowerOfTwo(AbsOrMin(value))) {
4660 locations->AddTemp(Location::RequiresRegister());
4661 }
4662 }
4663 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4664 locations->SetInAt(0, Location::RequiresRegister());
4665 locations->SetInAt(1, Location::RequiresRegister());
4666 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4667 locations->AddTemp(Location::RequiresRegister());
4668 } else {
4669 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4670 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
4671 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
Roland Levillain5e8d5f02016-10-18 18:03:43 +01004672 // Note: divmod will compute both the quotient and the remainder as the pair R0 and R1, but
Artem Serov551b28f2016-10-18 19:11:30 +01004673 // we only need the latter.
4674 locations->SetOut(LocationFrom(r1));
4675 }
4676 break;
4677 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004678 case DataType::Type::kInt64: {
Artem Serov551b28f2016-10-18 19:11:30 +01004679 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4680 locations->SetInAt(0, LocationFrom(
4681 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
4682 locations->SetInAt(1, LocationFrom(
4683 calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3)));
4684 // The runtime helper puts the output in R2,R3.
4685 locations->SetOut(LocationFrom(r2, r3));
4686 break;
4687 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004688 case DataType::Type::kFloat32: {
Artem Serov551b28f2016-10-18 19:11:30 +01004689 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4690 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
4691 locations->SetInAt(1, LocationFrom(calling_convention.GetFpuRegisterAt(1)));
4692 locations->SetOut(LocationFrom(s0));
4693 break;
4694 }
4695
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004696 case DataType::Type::kFloat64: {
Artem Serov551b28f2016-10-18 19:11:30 +01004697 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4698 locations->SetInAt(0, LocationFrom(
4699 calling_convention.GetFpuRegisterAt(0), calling_convention.GetFpuRegisterAt(1)));
4700 locations->SetInAt(1, LocationFrom(
4701 calling_convention.GetFpuRegisterAt(2), calling_convention.GetFpuRegisterAt(3)));
4702 locations->SetOut(LocationFrom(s0, s1));
4703 break;
4704 }
4705
4706 default:
4707 LOG(FATAL) << "Unexpected rem type " << type;
4708 }
4709}
4710
4711void InstructionCodeGeneratorARMVIXL::VisitRem(HRem* rem) {
4712 LocationSummary* locations = rem->GetLocations();
4713 Location second = locations->InAt(1);
4714
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004715 DataType::Type type = rem->GetResultType();
Artem Serov551b28f2016-10-18 19:11:30 +01004716 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004717 case DataType::Type::kInt32: {
Artem Serov551b28f2016-10-18 19:11:30 +01004718 vixl32::Register reg1 = InputRegisterAt(rem, 0);
4719 vixl32::Register out_reg = OutputRegister(rem);
4720 if (second.IsConstant()) {
4721 GenerateDivRemConstantIntegral(rem);
4722 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4723 vixl32::Register reg2 = RegisterFrom(second);
4724 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
4725
4726 // temp = reg1 / reg2 (integer division)
4727 // dest = reg1 - temp * reg2
4728 __ Sdiv(temp, reg1, reg2);
4729 __ Mls(out_reg, temp, reg2, reg1);
4730 } else {
4731 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4732 DCHECK(reg1.Is(calling_convention.GetRegisterAt(0)));
4733 DCHECK(RegisterFrom(second).Is(calling_convention.GetRegisterAt(1)));
4734 DCHECK(out_reg.Is(r1));
4735
4736 codegen_->InvokeRuntime(kQuickIdivmod, rem, rem->GetDexPc());
4737 CheckEntrypointTypes<kQuickIdivmod, int32_t, int32_t, int32_t>();
4738 }
4739 break;
4740 }
4741
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004742 case DataType::Type::kInt64: {
Artem Serov551b28f2016-10-18 19:11:30 +01004743 codegen_->InvokeRuntime(kQuickLmod, rem, rem->GetDexPc());
4744 CheckEntrypointTypes<kQuickLmod, int64_t, int64_t, int64_t>();
4745 break;
4746 }
4747
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004748 case DataType::Type::kFloat32: {
Artem Serov551b28f2016-10-18 19:11:30 +01004749 codegen_->InvokeRuntime(kQuickFmodf, rem, rem->GetDexPc());
4750 CheckEntrypointTypes<kQuickFmodf, float, float, float>();
4751 break;
4752 }
4753
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004754 case DataType::Type::kFloat64: {
Artem Serov551b28f2016-10-18 19:11:30 +01004755 codegen_->InvokeRuntime(kQuickFmod, rem, rem->GetDexPc());
4756 CheckEntrypointTypes<kQuickFmod, double, double, double>();
4757 break;
4758 }
4759
4760 default:
4761 LOG(FATAL) << "Unexpected rem type " << type;
4762 }
4763}
4764
Aart Bik1f8d51b2018-02-15 10:42:37 -08004765static void CreateMinMaxLocations(ArenaAllocator* allocator, HBinaryOperation* minmax) {
4766 LocationSummary* locations = new (allocator) LocationSummary(minmax);
4767 switch (minmax->GetResultType()) {
4768 case DataType::Type::kInt32:
4769 locations->SetInAt(0, Location::RequiresRegister());
4770 locations->SetInAt(1, Location::RequiresRegister());
4771 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4772 break;
4773 case DataType::Type::kInt64:
4774 locations->SetInAt(0, Location::RequiresRegister());
4775 locations->SetInAt(1, Location::RequiresRegister());
4776 locations->SetOut(Location::SameAsFirstInput());
4777 break;
4778 case DataType::Type::kFloat32:
4779 locations->SetInAt(0, Location::RequiresFpuRegister());
4780 locations->SetInAt(1, Location::RequiresFpuRegister());
4781 locations->SetOut(Location::SameAsFirstInput());
4782 locations->AddTemp(Location::RequiresRegister());
4783 break;
4784 case DataType::Type::kFloat64:
4785 locations->SetInAt(0, Location::RequiresFpuRegister());
4786 locations->SetInAt(1, Location::RequiresFpuRegister());
4787 locations->SetOut(Location::SameAsFirstInput());
4788 break;
4789 default:
4790 LOG(FATAL) << "Unexpected type for HMinMax " << minmax->GetResultType();
4791 }
4792}
4793
Aart Bik351df3e2018-03-07 11:54:57 -08004794void InstructionCodeGeneratorARMVIXL::GenerateMinMaxInt(LocationSummary* locations, bool is_min) {
Aart Bik1f8d51b2018-02-15 10:42:37 -08004795 Location op1_loc = locations->InAt(0);
4796 Location op2_loc = locations->InAt(1);
4797 Location out_loc = locations->Out();
4798
4799 vixl32::Register op1 = RegisterFrom(op1_loc);
4800 vixl32::Register op2 = RegisterFrom(op2_loc);
4801 vixl32::Register out = RegisterFrom(out_loc);
4802
4803 __ Cmp(op1, op2);
4804
4805 {
4806 ExactAssemblyScope aas(GetVIXLAssembler(),
4807 3 * kMaxInstructionSizeInBytes,
4808 CodeBufferCheckScope::kMaximumSize);
4809
4810 __ ite(is_min ? lt : gt);
4811 __ mov(is_min ? lt : gt, out, op1);
4812 __ mov(is_min ? ge : le, out, op2);
4813 }
4814}
4815
4816void InstructionCodeGeneratorARMVIXL::GenerateMinMaxLong(LocationSummary* locations, bool is_min) {
4817 Location op1_loc = locations->InAt(0);
4818 Location op2_loc = locations->InAt(1);
4819 Location out_loc = locations->Out();
4820
4821 // Optimization: don't generate any code if inputs are the same.
4822 if (op1_loc.Equals(op2_loc)) {
4823 DCHECK(out_loc.Equals(op1_loc)); // out_loc is set as SameAsFirstInput() in location builder.
4824 return;
4825 }
4826
4827 vixl32::Register op1_lo = LowRegisterFrom(op1_loc);
4828 vixl32::Register op1_hi = HighRegisterFrom(op1_loc);
4829 vixl32::Register op2_lo = LowRegisterFrom(op2_loc);
4830 vixl32::Register op2_hi = HighRegisterFrom(op2_loc);
4831 vixl32::Register out_lo = LowRegisterFrom(out_loc);
4832 vixl32::Register out_hi = HighRegisterFrom(out_loc);
4833 UseScratchRegisterScope temps(GetVIXLAssembler());
4834 const vixl32::Register temp = temps.Acquire();
4835
4836 DCHECK(op1_lo.Is(out_lo));
4837 DCHECK(op1_hi.Is(out_hi));
4838
4839 // Compare op1 >= op2, or op1 < op2.
4840 __ Cmp(out_lo, op2_lo);
4841 __ Sbcs(temp, out_hi, op2_hi);
4842
4843 // Now GE/LT condition code is correct for the long comparison.
4844 {
4845 vixl32::ConditionType cond = is_min ? ge : lt;
4846 ExactAssemblyScope it_scope(GetVIXLAssembler(),
4847 3 * kMaxInstructionSizeInBytes,
4848 CodeBufferCheckScope::kMaximumSize);
4849 __ itt(cond);
4850 __ mov(cond, out_lo, op2_lo);
4851 __ mov(cond, out_hi, op2_hi);
4852 }
4853}
4854
Aart Bik351df3e2018-03-07 11:54:57 -08004855void InstructionCodeGeneratorARMVIXL::GenerateMinMaxFloat(HInstruction* minmax, bool is_min) {
4856 LocationSummary* locations = minmax->GetLocations();
Aart Bik1f8d51b2018-02-15 10:42:37 -08004857 Location op1_loc = locations->InAt(0);
4858 Location op2_loc = locations->InAt(1);
4859 Location out_loc = locations->Out();
4860
4861 // Optimization: don't generate any code if inputs are the same.
4862 if (op1_loc.Equals(op2_loc)) {
4863 DCHECK(out_loc.Equals(op1_loc)); // out_loc is set as SameAsFirstInput() in location builder.
4864 return;
4865 }
4866
4867 vixl32::SRegister op1 = SRegisterFrom(op1_loc);
4868 vixl32::SRegister op2 = SRegisterFrom(op2_loc);
4869 vixl32::SRegister out = SRegisterFrom(out_loc);
4870
4871 UseScratchRegisterScope temps(GetVIXLAssembler());
4872 const vixl32::Register temp1 = temps.Acquire();
4873 vixl32::Register temp2 = RegisterFrom(locations->GetTemp(0));
4874 vixl32::Label nan, done;
Aart Bik351df3e2018-03-07 11:54:57 -08004875 vixl32::Label* final_label = codegen_->GetFinalLabel(minmax, &done);
Aart Bik1f8d51b2018-02-15 10:42:37 -08004876
4877 DCHECK(op1.Is(out));
4878
4879 __ Vcmp(op1, op2);
4880 __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
4881 __ B(vs, &nan, /* far_target */ false); // if un-ordered, go to NaN handling.
4882
4883 // op1 <> op2
4884 vixl32::ConditionType cond = is_min ? gt : lt;
4885 {
4886 ExactAssemblyScope it_scope(GetVIXLAssembler(),
4887 2 * kMaxInstructionSizeInBytes,
4888 CodeBufferCheckScope::kMaximumSize);
4889 __ it(cond);
4890 __ vmov(cond, F32, out, op2);
4891 }
4892 // for <>(not equal), we've done min/max calculation.
4893 __ B(ne, final_label, /* far_target */ false);
4894
4895 // handle op1 == op2, max(+0.0,-0.0), min(+0.0,-0.0).
4896 __ Vmov(temp1, op1);
4897 __ Vmov(temp2, op2);
4898 if (is_min) {
4899 __ Orr(temp1, temp1, temp2);
4900 } else {
4901 __ And(temp1, temp1, temp2);
4902 }
4903 __ Vmov(out, temp1);
4904 __ B(final_label);
4905
4906 // handle NaN input.
4907 __ Bind(&nan);
4908 __ Movt(temp1, High16Bits(kNanFloat)); // 0x7FC0xxxx is a NaN.
4909 __ Vmov(out, temp1);
4910
4911 if (done.IsReferenced()) {
4912 __ Bind(&done);
4913 }
4914}
4915
Aart Bik351df3e2018-03-07 11:54:57 -08004916void InstructionCodeGeneratorARMVIXL::GenerateMinMaxDouble(HInstruction* minmax, bool is_min) {
4917 LocationSummary* locations = minmax->GetLocations();
Aart Bik1f8d51b2018-02-15 10:42:37 -08004918 Location op1_loc = locations->InAt(0);
4919 Location op2_loc = locations->InAt(1);
4920 Location out_loc = locations->Out();
4921
4922 // Optimization: don't generate any code if inputs are the same.
4923 if (op1_loc.Equals(op2_loc)) {
4924 DCHECK(out_loc.Equals(op1_loc)); // out_loc is set as SameAsFirstInput() in.
4925 return;
4926 }
4927
4928 vixl32::DRegister op1 = DRegisterFrom(op1_loc);
4929 vixl32::DRegister op2 = DRegisterFrom(op2_loc);
4930 vixl32::DRegister out = DRegisterFrom(out_loc);
4931 vixl32::Label handle_nan_eq, done;
Aart Bik351df3e2018-03-07 11:54:57 -08004932 vixl32::Label* final_label = codegen_->GetFinalLabel(minmax, &done);
Aart Bik1f8d51b2018-02-15 10:42:37 -08004933
4934 DCHECK(op1.Is(out));
4935
4936 __ Vcmp(op1, op2);
4937 __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
4938 __ B(vs, &handle_nan_eq, /* far_target */ false); // if un-ordered, go to NaN handling.
4939
4940 // op1 <> op2
4941 vixl32::ConditionType cond = is_min ? gt : lt;
4942 {
4943 ExactAssemblyScope it_scope(GetVIXLAssembler(),
4944 2 * kMaxInstructionSizeInBytes,
4945 CodeBufferCheckScope::kMaximumSize);
4946 __ it(cond);
4947 __ vmov(cond, F64, out, op2);
4948 }
4949 // for <>(not equal), we've done min/max calculation.
4950 __ B(ne, final_label, /* far_target */ false);
4951
4952 // handle op1 == op2, max(+0.0,-0.0).
4953 if (!is_min) {
4954 __ Vand(F64, out, op1, op2);
4955 __ B(final_label);
4956 }
4957
4958 // handle op1 == op2, min(+0.0,-0.0), NaN input.
4959 __ Bind(&handle_nan_eq);
4960 __ Vorr(F64, out, op1, op2); // assemble op1/-0.0/NaN.
4961
4962 if (done.IsReferenced()) {
4963 __ Bind(&done);
4964 }
4965}
4966
Aart Bik351df3e2018-03-07 11:54:57 -08004967void InstructionCodeGeneratorARMVIXL::GenerateMinMax(HBinaryOperation* minmax, bool is_min) {
4968 DataType::Type type = minmax->GetResultType();
4969 switch (type) {
4970 case DataType::Type::kInt32:
4971 GenerateMinMaxInt(minmax->GetLocations(), is_min);
4972 break;
4973 case DataType::Type::kInt64:
4974 GenerateMinMaxLong(minmax->GetLocations(), is_min);
4975 break;
4976 case DataType::Type::kFloat32:
4977 GenerateMinMaxFloat(minmax, is_min);
4978 break;
4979 case DataType::Type::kFloat64:
4980 GenerateMinMaxDouble(minmax, is_min);
4981 break;
4982 default:
4983 LOG(FATAL) << "Unexpected type for HMinMax " << type;
4984 }
4985}
4986
Aart Bik1f8d51b2018-02-15 10:42:37 -08004987void LocationsBuilderARMVIXL::VisitMin(HMin* min) {
4988 CreateMinMaxLocations(GetGraph()->GetAllocator(), min);
4989}
4990
4991void InstructionCodeGeneratorARMVIXL::VisitMin(HMin* min) {
Aart Bik351df3e2018-03-07 11:54:57 -08004992 GenerateMinMax(min, /*is_min*/ true);
Aart Bik1f8d51b2018-02-15 10:42:37 -08004993}
4994
4995void LocationsBuilderARMVIXL::VisitMax(HMax* max) {
4996 CreateMinMaxLocations(GetGraph()->GetAllocator(), max);
4997}
4998
4999void InstructionCodeGeneratorARMVIXL::VisitMax(HMax* max) {
Aart Bik351df3e2018-03-07 11:54:57 -08005000 GenerateMinMax(max, /*is_min*/ false);
Aart Bik1f8d51b2018-02-15 10:42:37 -08005001}
5002
Aart Bik3dad3412018-02-28 12:01:46 -08005003void LocationsBuilderARMVIXL::VisitAbs(HAbs* abs) {
5004 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(abs);
5005 switch (abs->GetResultType()) {
5006 case DataType::Type::kInt32:
5007 case DataType::Type::kInt64:
5008 locations->SetInAt(0, Location::RequiresRegister());
5009 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5010 locations->AddTemp(Location::RequiresRegister());
5011 break;
5012 case DataType::Type::kFloat32:
5013 case DataType::Type::kFloat64:
5014 locations->SetInAt(0, Location::RequiresFpuRegister());
5015 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
5016 break;
5017 default:
5018 LOG(FATAL) << "Unexpected type for abs operation " << abs->GetResultType();
5019 }
5020}
5021
5022void InstructionCodeGeneratorARMVIXL::VisitAbs(HAbs* abs) {
5023 LocationSummary* locations = abs->GetLocations();
5024 switch (abs->GetResultType()) {
5025 case DataType::Type::kInt32: {
5026 vixl32::Register in_reg = RegisterFrom(locations->InAt(0));
5027 vixl32::Register out_reg = RegisterFrom(locations->Out());
5028 vixl32::Register mask = RegisterFrom(locations->GetTemp(0));
5029 __ Asr(mask, in_reg, 31);
5030 __ Add(out_reg, in_reg, mask);
5031 __ Eor(out_reg, out_reg, mask);
5032 break;
5033 }
5034 case DataType::Type::kInt64: {
5035 Location in = locations->InAt(0);
5036 vixl32::Register in_reg_lo = LowRegisterFrom(in);
5037 vixl32::Register in_reg_hi = HighRegisterFrom(in);
5038 Location output = locations->Out();
5039 vixl32::Register out_reg_lo = LowRegisterFrom(output);
5040 vixl32::Register out_reg_hi = HighRegisterFrom(output);
5041 DCHECK(!out_reg_lo.Is(in_reg_hi)) << "Diagonal overlap unexpected.";
5042 vixl32::Register mask = RegisterFrom(locations->GetTemp(0));
5043 __ Asr(mask, in_reg_hi, 31);
5044 __ Adds(out_reg_lo, in_reg_lo, mask);
5045 __ Adc(out_reg_hi, in_reg_hi, mask);
5046 __ Eor(out_reg_lo, out_reg_lo, mask);
5047 __ Eor(out_reg_hi, out_reg_hi, mask);
5048 break;
5049 }
5050 case DataType::Type::kFloat32:
5051 case DataType::Type::kFloat64:
5052 __ Vabs(OutputVRegister(abs), InputVRegisterAt(abs, 0));
5053 break;
5054 default:
5055 LOG(FATAL) << "Unexpected type for abs operation " << abs->GetResultType();
5056 }
5057}
Artem Serov551b28f2016-10-18 19:11:30 +01005058
Scott Wakelingfe885462016-09-22 10:24:38 +01005059void LocationsBuilderARMVIXL::VisitDivZeroCheck(HDivZeroCheck* instruction) {
Artem Serov657022c2016-11-23 14:19:38 +00005060 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
Scott Wakelingfe885462016-09-22 10:24:38 +01005061 locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0)));
Scott Wakelingfe885462016-09-22 10:24:38 +01005062}
5063
5064void InstructionCodeGeneratorARMVIXL::VisitDivZeroCheck(HDivZeroCheck* instruction) {
5065 DivZeroCheckSlowPathARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01005066 new (codegen_->GetScopedAllocator()) DivZeroCheckSlowPathARMVIXL(instruction);
Scott Wakelingfe885462016-09-22 10:24:38 +01005067 codegen_->AddSlowPath(slow_path);
5068
5069 LocationSummary* locations = instruction->GetLocations();
5070 Location value = locations->InAt(0);
5071
5072 switch (instruction->GetType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005073 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005074 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005075 case DataType::Type::kInt8:
5076 case DataType::Type::kUint16:
5077 case DataType::Type::kInt16:
5078 case DataType::Type::kInt32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01005079 if (value.IsRegister()) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00005080 __ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
Scott Wakelingfe885462016-09-22 10:24:38 +01005081 } else {
5082 DCHECK(value.IsConstant()) << value;
Anton Kirilov644032c2016-12-06 17:51:43 +00005083 if (Int32ConstantFrom(value) == 0) {
Scott Wakelingfe885462016-09-22 10:24:38 +01005084 __ B(slow_path->GetEntryLabel());
5085 }
5086 }
5087 break;
5088 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005089 case DataType::Type::kInt64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01005090 if (value.IsRegisterPair()) {
5091 UseScratchRegisterScope temps(GetVIXLAssembler());
5092 vixl32::Register temp = temps.Acquire();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005093 __ Orrs(temp, LowRegisterFrom(value), HighRegisterFrom(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01005094 __ B(eq, slow_path->GetEntryLabel());
5095 } else {
5096 DCHECK(value.IsConstant()) << value;
Anton Kirilov644032c2016-12-06 17:51:43 +00005097 if (Int64ConstantFrom(value) == 0) {
Scott Wakelingfe885462016-09-22 10:24:38 +01005098 __ B(slow_path->GetEntryLabel());
5099 }
5100 }
5101 break;
5102 }
5103 default:
5104 LOG(FATAL) << "Unexpected type for HDivZeroCheck " << instruction->GetType();
5105 }
5106}
5107
Artem Serov02109dd2016-09-23 17:17:54 +01005108void InstructionCodeGeneratorARMVIXL::HandleIntegerRotate(HRor* ror) {
5109 LocationSummary* locations = ror->GetLocations();
5110 vixl32::Register in = InputRegisterAt(ror, 0);
5111 Location rhs = locations->InAt(1);
5112 vixl32::Register out = OutputRegister(ror);
5113
5114 if (rhs.IsConstant()) {
5115 // Arm32 and Thumb2 assemblers require a rotation on the interval [1,31],
5116 // so map all rotations to a +ve. equivalent in that range.
5117 // (e.g. left *or* right by -2 bits == 30 bits in the same direction.)
5118 uint32_t rot = CodeGenerator::GetInt32ValueOf(rhs.GetConstant()) & 0x1F;
5119 if (rot) {
5120 // Rotate, mapping left rotations to right equivalents if necessary.
5121 // (e.g. left by 2 bits == right by 30.)
5122 __ Ror(out, in, rot);
5123 } else if (!out.Is(in)) {
5124 __ Mov(out, in);
5125 }
5126 } else {
5127 __ Ror(out, in, RegisterFrom(rhs));
5128 }
5129}
5130
5131// Gain some speed by mapping all Long rotates onto equivalent pairs of Integer
5132// rotates by swapping input regs (effectively rotating by the first 32-bits of
5133// a larger rotation) or flipping direction (thus treating larger right/left
5134// rotations as sub-word sized rotations in the other direction) as appropriate.
5135void InstructionCodeGeneratorARMVIXL::HandleLongRotate(HRor* ror) {
5136 LocationSummary* locations = ror->GetLocations();
5137 vixl32::Register in_reg_lo = LowRegisterFrom(locations->InAt(0));
5138 vixl32::Register in_reg_hi = HighRegisterFrom(locations->InAt(0));
5139 Location rhs = locations->InAt(1);
5140 vixl32::Register out_reg_lo = LowRegisterFrom(locations->Out());
5141 vixl32::Register out_reg_hi = HighRegisterFrom(locations->Out());
5142
5143 if (rhs.IsConstant()) {
5144 uint64_t rot = CodeGenerator::GetInt64ValueOf(rhs.GetConstant());
5145 // Map all rotations to +ve. equivalents on the interval [0,63].
5146 rot &= kMaxLongShiftDistance;
5147 // For rotates over a word in size, 'pre-rotate' by 32-bits to keep rotate
5148 // logic below to a simple pair of binary orr.
5149 // (e.g. 34 bits == in_reg swap + 2 bits right.)
5150 if (rot >= kArmBitsPerWord) {
5151 rot -= kArmBitsPerWord;
5152 std::swap(in_reg_hi, in_reg_lo);
5153 }
5154 // Rotate, or mov to out for zero or word size rotations.
5155 if (rot != 0u) {
Scott Wakelingb77051e2016-11-21 19:46:00 +00005156 __ Lsr(out_reg_hi, in_reg_hi, Operand::From(rot));
Artem Serov02109dd2016-09-23 17:17:54 +01005157 __ Orr(out_reg_hi, out_reg_hi, Operand(in_reg_lo, ShiftType::LSL, kArmBitsPerWord - rot));
Scott Wakelingb77051e2016-11-21 19:46:00 +00005158 __ Lsr(out_reg_lo, in_reg_lo, Operand::From(rot));
Artem Serov02109dd2016-09-23 17:17:54 +01005159 __ Orr(out_reg_lo, out_reg_lo, Operand(in_reg_hi, ShiftType::LSL, kArmBitsPerWord - rot));
5160 } else {
5161 __ Mov(out_reg_lo, in_reg_lo);
5162 __ Mov(out_reg_hi, in_reg_hi);
5163 }
5164 } else {
5165 vixl32::Register shift_right = RegisterFrom(locations->GetTemp(0));
5166 vixl32::Register shift_left = RegisterFrom(locations->GetTemp(1));
5167 vixl32::Label end;
5168 vixl32::Label shift_by_32_plus_shift_right;
Anton Kirilov6f644202017-02-27 18:29:45 +00005169 vixl32::Label* final_label = codegen_->GetFinalLabel(ror, &end);
Artem Serov02109dd2016-09-23 17:17:54 +01005170
5171 __ And(shift_right, RegisterFrom(rhs), 0x1F);
5172 __ Lsrs(shift_left, RegisterFrom(rhs), 6);
Scott Wakelingbffdc702016-12-07 17:46:03 +00005173 __ Rsb(LeaveFlags, shift_left, shift_right, Operand::From(kArmBitsPerWord));
Artem Serov517d9f62016-12-12 15:51:15 +00005174 __ B(cc, &shift_by_32_plus_shift_right, /* far_target */ false);
Artem Serov02109dd2016-09-23 17:17:54 +01005175
5176 // out_reg_hi = (reg_hi << shift_left) | (reg_lo >> shift_right).
5177 // out_reg_lo = (reg_lo << shift_left) | (reg_hi >> shift_right).
5178 __ Lsl(out_reg_hi, in_reg_hi, shift_left);
5179 __ Lsr(out_reg_lo, in_reg_lo, shift_right);
5180 __ Add(out_reg_hi, out_reg_hi, out_reg_lo);
5181 __ Lsl(out_reg_lo, in_reg_lo, shift_left);
5182 __ Lsr(shift_left, in_reg_hi, shift_right);
5183 __ Add(out_reg_lo, out_reg_lo, shift_left);
Anton Kirilov6f644202017-02-27 18:29:45 +00005184 __ B(final_label);
Artem Serov02109dd2016-09-23 17:17:54 +01005185
5186 __ Bind(&shift_by_32_plus_shift_right); // Shift by 32+shift_right.
5187 // out_reg_hi = (reg_hi >> shift_right) | (reg_lo << shift_left).
5188 // out_reg_lo = (reg_lo >> shift_right) | (reg_hi << shift_left).
5189 __ Lsr(out_reg_hi, in_reg_hi, shift_right);
5190 __ Lsl(out_reg_lo, in_reg_lo, shift_left);
5191 __ Add(out_reg_hi, out_reg_hi, out_reg_lo);
5192 __ Lsr(out_reg_lo, in_reg_lo, shift_right);
5193 __ Lsl(shift_right, in_reg_hi, shift_left);
5194 __ Add(out_reg_lo, out_reg_lo, shift_right);
5195
Anton Kirilov6f644202017-02-27 18:29:45 +00005196 if (end.IsReferenced()) {
5197 __ Bind(&end);
5198 }
Artem Serov02109dd2016-09-23 17:17:54 +01005199 }
5200}
5201
5202void LocationsBuilderARMVIXL::VisitRor(HRor* ror) {
5203 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005204 new (GetGraph()->GetAllocator()) LocationSummary(ror, LocationSummary::kNoCall);
Artem Serov02109dd2016-09-23 17:17:54 +01005205 switch (ror->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005206 case DataType::Type::kInt32: {
Artem Serov02109dd2016-09-23 17:17:54 +01005207 locations->SetInAt(0, Location::RequiresRegister());
5208 locations->SetInAt(1, Location::RegisterOrConstant(ror->InputAt(1)));
5209 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5210 break;
5211 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005212 case DataType::Type::kInt64: {
Artem Serov02109dd2016-09-23 17:17:54 +01005213 locations->SetInAt(0, Location::RequiresRegister());
5214 if (ror->InputAt(1)->IsConstant()) {
5215 locations->SetInAt(1, Location::ConstantLocation(ror->InputAt(1)->AsConstant()));
5216 } else {
5217 locations->SetInAt(1, Location::RequiresRegister());
5218 locations->AddTemp(Location::RequiresRegister());
5219 locations->AddTemp(Location::RequiresRegister());
5220 }
5221 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
5222 break;
5223 }
5224 default:
5225 LOG(FATAL) << "Unexpected operation type " << ror->GetResultType();
5226 }
5227}
5228
5229void InstructionCodeGeneratorARMVIXL::VisitRor(HRor* ror) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005230 DataType::Type type = ror->GetResultType();
Artem Serov02109dd2016-09-23 17:17:54 +01005231 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005232 case DataType::Type::kInt32: {
Artem Serov02109dd2016-09-23 17:17:54 +01005233 HandleIntegerRotate(ror);
5234 break;
5235 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005236 case DataType::Type::kInt64: {
Artem Serov02109dd2016-09-23 17:17:54 +01005237 HandleLongRotate(ror);
5238 break;
5239 }
5240 default:
5241 LOG(FATAL) << "Unexpected operation type " << type;
5242 UNREACHABLE();
5243 }
5244}
5245
Artem Serov02d37832016-10-25 15:25:33 +01005246void LocationsBuilderARMVIXL::HandleShift(HBinaryOperation* op) {
5247 DCHECK(op->IsShl() || op->IsShr() || op->IsUShr());
5248
5249 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005250 new (GetGraph()->GetAllocator()) LocationSummary(op, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01005251
5252 switch (op->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005253 case DataType::Type::kInt32: {
Artem Serov02d37832016-10-25 15:25:33 +01005254 locations->SetInAt(0, Location::RequiresRegister());
5255 if (op->InputAt(1)->IsConstant()) {
5256 locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant()));
5257 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5258 } else {
5259 locations->SetInAt(1, Location::RequiresRegister());
5260 // Make the output overlap, as it will be used to hold the masked
5261 // second input.
5262 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
5263 }
5264 break;
5265 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005266 case DataType::Type::kInt64: {
Artem Serov02d37832016-10-25 15:25:33 +01005267 locations->SetInAt(0, Location::RequiresRegister());
5268 if (op->InputAt(1)->IsConstant()) {
5269 locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant()));
5270 // For simplicity, use kOutputOverlap even though we only require that low registers
5271 // don't clash with high registers which the register allocator currently guarantees.
5272 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
5273 } else {
5274 locations->SetInAt(1, Location::RequiresRegister());
5275 locations->AddTemp(Location::RequiresRegister());
5276 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
5277 }
5278 break;
5279 }
5280 default:
5281 LOG(FATAL) << "Unexpected operation type " << op->GetResultType();
5282 }
5283}
5284
5285void InstructionCodeGeneratorARMVIXL::HandleShift(HBinaryOperation* op) {
5286 DCHECK(op->IsShl() || op->IsShr() || op->IsUShr());
5287
5288 LocationSummary* locations = op->GetLocations();
5289 Location out = locations->Out();
5290 Location first = locations->InAt(0);
5291 Location second = locations->InAt(1);
5292
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005293 DataType::Type type = op->GetResultType();
Artem Serov02d37832016-10-25 15:25:33 +01005294 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005295 case DataType::Type::kInt32: {
Artem Serov02d37832016-10-25 15:25:33 +01005296 vixl32::Register out_reg = OutputRegister(op);
5297 vixl32::Register first_reg = InputRegisterAt(op, 0);
5298 if (second.IsRegister()) {
5299 vixl32::Register second_reg = RegisterFrom(second);
5300 // ARM doesn't mask the shift count so we need to do it ourselves.
5301 __ And(out_reg, second_reg, kMaxIntShiftDistance);
5302 if (op->IsShl()) {
5303 __ Lsl(out_reg, first_reg, out_reg);
5304 } else if (op->IsShr()) {
5305 __ Asr(out_reg, first_reg, out_reg);
5306 } else {
5307 __ Lsr(out_reg, first_reg, out_reg);
5308 }
5309 } else {
Anton Kirilov644032c2016-12-06 17:51:43 +00005310 int32_t cst = Int32ConstantFrom(second);
Artem Serov02d37832016-10-25 15:25:33 +01005311 uint32_t shift_value = cst & kMaxIntShiftDistance;
5312 if (shift_value == 0) { // ARM does not support shifting with 0 immediate.
5313 __ Mov(out_reg, first_reg);
5314 } else if (op->IsShl()) {
5315 __ Lsl(out_reg, first_reg, shift_value);
5316 } else if (op->IsShr()) {
5317 __ Asr(out_reg, first_reg, shift_value);
5318 } else {
5319 __ Lsr(out_reg, first_reg, shift_value);
5320 }
5321 }
5322 break;
5323 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005324 case DataType::Type::kInt64: {
Artem Serov02d37832016-10-25 15:25:33 +01005325 vixl32::Register o_h = HighRegisterFrom(out);
5326 vixl32::Register o_l = LowRegisterFrom(out);
5327
5328 vixl32::Register high = HighRegisterFrom(first);
5329 vixl32::Register low = LowRegisterFrom(first);
5330
5331 if (second.IsRegister()) {
5332 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
5333
5334 vixl32::Register second_reg = RegisterFrom(second);
5335
5336 if (op->IsShl()) {
5337 __ And(o_l, second_reg, kMaxLongShiftDistance);
5338 // Shift the high part
5339 __ Lsl(o_h, high, o_l);
5340 // Shift the low part and `or` what overflew on the high part
Scott Wakelingb77051e2016-11-21 19:46:00 +00005341 __ Rsb(temp, o_l, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01005342 __ Lsr(temp, low, temp);
5343 __ Orr(o_h, o_h, temp);
5344 // If the shift is > 32 bits, override the high part
Scott Wakelingb77051e2016-11-21 19:46:00 +00005345 __ Subs(temp, o_l, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01005346 {
Artem Serov0fb37192016-12-06 18:13:40 +00005347 ExactAssemblyScope guard(GetVIXLAssembler(),
5348 2 * vixl32::kMaxInstructionSizeInBytes,
5349 CodeBufferCheckScope::kMaximumSize);
Artem Serov02d37832016-10-25 15:25:33 +01005350 __ it(pl);
5351 __ lsl(pl, o_h, low, temp);
5352 }
5353 // Shift the low part
5354 __ Lsl(o_l, low, o_l);
5355 } else if (op->IsShr()) {
5356 __ And(o_h, second_reg, kMaxLongShiftDistance);
5357 // Shift the low part
5358 __ Lsr(o_l, low, o_h);
5359 // Shift the high part and `or` what underflew on the low part
Scott Wakelingb77051e2016-11-21 19:46:00 +00005360 __ Rsb(temp, o_h, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01005361 __ Lsl(temp, high, temp);
5362 __ Orr(o_l, o_l, temp);
5363 // If the shift is > 32 bits, override the low part
Scott Wakelingb77051e2016-11-21 19:46:00 +00005364 __ Subs(temp, o_h, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01005365 {
Artem Serov0fb37192016-12-06 18:13:40 +00005366 ExactAssemblyScope guard(GetVIXLAssembler(),
5367 2 * vixl32::kMaxInstructionSizeInBytes,
5368 CodeBufferCheckScope::kMaximumSize);
Artem Serov02d37832016-10-25 15:25:33 +01005369 __ it(pl);
5370 __ asr(pl, o_l, high, temp);
5371 }
5372 // Shift the high part
5373 __ Asr(o_h, high, o_h);
5374 } else {
5375 __ And(o_h, second_reg, kMaxLongShiftDistance);
5376 // same as Shr except we use `Lsr`s and not `Asr`s
5377 __ Lsr(o_l, low, o_h);
Scott Wakelingb77051e2016-11-21 19:46:00 +00005378 __ Rsb(temp, o_h, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01005379 __ Lsl(temp, high, temp);
5380 __ Orr(o_l, o_l, temp);
Scott Wakelingb77051e2016-11-21 19:46:00 +00005381 __ Subs(temp, o_h, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01005382 {
Artem Serov0fb37192016-12-06 18:13:40 +00005383 ExactAssemblyScope guard(GetVIXLAssembler(),
5384 2 * vixl32::kMaxInstructionSizeInBytes,
5385 CodeBufferCheckScope::kMaximumSize);
Artem Serov02d37832016-10-25 15:25:33 +01005386 __ it(pl);
5387 __ lsr(pl, o_l, high, temp);
5388 }
5389 __ Lsr(o_h, high, o_h);
5390 }
5391 } else {
5392 // Register allocator doesn't create partial overlap.
5393 DCHECK(!o_l.Is(high));
5394 DCHECK(!o_h.Is(low));
Anton Kirilov644032c2016-12-06 17:51:43 +00005395 int32_t cst = Int32ConstantFrom(second);
Artem Serov02d37832016-10-25 15:25:33 +01005396 uint32_t shift_value = cst & kMaxLongShiftDistance;
5397 if (shift_value > 32) {
5398 if (op->IsShl()) {
5399 __ Lsl(o_h, low, shift_value - 32);
5400 __ Mov(o_l, 0);
5401 } else if (op->IsShr()) {
5402 __ Asr(o_l, high, shift_value - 32);
5403 __ Asr(o_h, high, 31);
5404 } else {
5405 __ Lsr(o_l, high, shift_value - 32);
5406 __ Mov(o_h, 0);
5407 }
5408 } else if (shift_value == 32) {
5409 if (op->IsShl()) {
5410 __ Mov(o_h, low);
5411 __ Mov(o_l, 0);
5412 } else if (op->IsShr()) {
5413 __ Mov(o_l, high);
5414 __ Asr(o_h, high, 31);
5415 } else {
5416 __ Mov(o_l, high);
5417 __ Mov(o_h, 0);
5418 }
5419 } else if (shift_value == 1) {
5420 if (op->IsShl()) {
5421 __ Lsls(o_l, low, 1);
5422 __ Adc(o_h, high, high);
5423 } else if (op->IsShr()) {
5424 __ Asrs(o_h, high, 1);
5425 __ Rrx(o_l, low);
5426 } else {
5427 __ Lsrs(o_h, high, 1);
5428 __ Rrx(o_l, low);
5429 }
5430 } else {
5431 DCHECK(2 <= shift_value && shift_value < 32) << shift_value;
5432 if (op->IsShl()) {
5433 __ Lsl(o_h, high, shift_value);
5434 __ Orr(o_h, o_h, Operand(low, ShiftType::LSR, 32 - shift_value));
5435 __ Lsl(o_l, low, shift_value);
5436 } else if (op->IsShr()) {
5437 __ Lsr(o_l, low, shift_value);
5438 __ Orr(o_l, o_l, Operand(high, ShiftType::LSL, 32 - shift_value));
5439 __ Asr(o_h, high, shift_value);
5440 } else {
5441 __ Lsr(o_l, low, shift_value);
5442 __ Orr(o_l, o_l, Operand(high, ShiftType::LSL, 32 - shift_value));
5443 __ Lsr(o_h, high, shift_value);
5444 }
5445 }
5446 }
5447 break;
5448 }
5449 default:
5450 LOG(FATAL) << "Unexpected operation type " << type;
5451 UNREACHABLE();
5452 }
5453}
5454
5455void LocationsBuilderARMVIXL::VisitShl(HShl* shl) {
5456 HandleShift(shl);
5457}
5458
5459void InstructionCodeGeneratorARMVIXL::VisitShl(HShl* shl) {
5460 HandleShift(shl);
5461}
5462
5463void LocationsBuilderARMVIXL::VisitShr(HShr* shr) {
5464 HandleShift(shr);
5465}
5466
5467void InstructionCodeGeneratorARMVIXL::VisitShr(HShr* shr) {
5468 HandleShift(shr);
5469}
5470
5471void LocationsBuilderARMVIXL::VisitUShr(HUShr* ushr) {
5472 HandleShift(ushr);
5473}
5474
5475void InstructionCodeGeneratorARMVIXL::VisitUShr(HUShr* ushr) {
5476 HandleShift(ushr);
5477}
5478
5479void LocationsBuilderARMVIXL::VisitNewInstance(HNewInstance* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01005480 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
5481 instruction, LocationSummary::kCallOnMainOnly);
Artem Serov02d37832016-10-25 15:25:33 +01005482 if (instruction->IsStringAlloc()) {
5483 locations->AddTemp(LocationFrom(kMethodRegister));
5484 } else {
5485 InvokeRuntimeCallingConventionARMVIXL calling_convention;
5486 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
Artem Serov02d37832016-10-25 15:25:33 +01005487 }
5488 locations->SetOut(LocationFrom(r0));
5489}
5490
5491void InstructionCodeGeneratorARMVIXL::VisitNewInstance(HNewInstance* instruction) {
5492 // Note: if heap poisoning is enabled, the entry point takes cares
5493 // of poisoning the reference.
5494 if (instruction->IsStringAlloc()) {
5495 // String is allocated through StringFactory. Call NewEmptyString entry point.
5496 vixl32::Register temp = RegisterFrom(instruction->GetLocations()->GetTemp(0));
5497 MemberOffset code_offset = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize);
5498 GetAssembler()->LoadFromOffset(kLoadWord, temp, tr, QUICK_ENTRY_POINT(pNewEmptyString));
5499 GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, code_offset.Int32Value());
Alexandre Rames374ddf32016-11-04 10:40:49 +00005500 // 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 +00005501 ExactAssemblyScope aas(GetVIXLAssembler(),
5502 vixl32::k16BitT32InstructionSizeInBytes,
5503 CodeBufferCheckScope::kExactSize);
Artem Serov02d37832016-10-25 15:25:33 +01005504 __ blx(lr);
5505 codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
5506 } else {
5507 codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
Nicolas Geoffray0d3998b2017-01-12 15:35:12 +00005508 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
Artem Serov02d37832016-10-25 15:25:33 +01005509 }
Orion Hodson4c8e12e2018-05-18 08:33:20 +01005510 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 11);
Artem Serov02d37832016-10-25 15:25:33 +01005511}
5512
5513void LocationsBuilderARMVIXL::VisitNewArray(HNewArray* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01005514 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
5515 instruction, LocationSummary::kCallOnMainOnly);
Artem Serov02d37832016-10-25 15:25:33 +01005516 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Artem Serov02d37832016-10-25 15:25:33 +01005517 locations->SetOut(LocationFrom(r0));
Nicolas Geoffray8c7c4f12017-01-26 10:13:11 +00005518 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
5519 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
Artem Serov02d37832016-10-25 15:25:33 +01005520}
5521
5522void InstructionCodeGeneratorARMVIXL::VisitNewArray(HNewArray* instruction) {
Artem Serov02d37832016-10-25 15:25:33 +01005523 // Note: if heap poisoning is enabled, the entry point takes cares
5524 // of poisoning the reference.
Artem Serov7b3672e2017-02-03 17:30:34 +00005525 QuickEntrypointEnum entrypoint =
5526 CodeGenerator::GetArrayAllocationEntrypoint(instruction->GetLoadClass()->GetClass());
5527 codegen_->InvokeRuntime(entrypoint, instruction, instruction->GetDexPc());
Nicolas Geoffraye761bcc2017-01-19 08:59:37 +00005528 CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>();
Artem Serov7b3672e2017-02-03 17:30:34 +00005529 DCHECK(!codegen_->IsLeafMethod());
Orion Hodson4c8e12e2018-05-18 08:33:20 +01005530 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 12);
Artem Serov02d37832016-10-25 15:25:33 +01005531}
5532
5533void LocationsBuilderARMVIXL::VisitParameterValue(HParameterValue* instruction) {
5534 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005535 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01005536 Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
5537 if (location.IsStackSlot()) {
5538 location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
5539 } else if (location.IsDoubleStackSlot()) {
5540 location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
5541 }
5542 locations->SetOut(location);
5543}
5544
5545void InstructionCodeGeneratorARMVIXL::VisitParameterValue(
5546 HParameterValue* instruction ATTRIBUTE_UNUSED) {
5547 // Nothing to do, the parameter is already at its location.
5548}
5549
5550void LocationsBuilderARMVIXL::VisitCurrentMethod(HCurrentMethod* instruction) {
5551 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005552 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01005553 locations->SetOut(LocationFrom(kMethodRegister));
5554}
5555
5556void InstructionCodeGeneratorARMVIXL::VisitCurrentMethod(
5557 HCurrentMethod* instruction ATTRIBUTE_UNUSED) {
5558 // Nothing to do, the method is already at its location.
5559}
5560
5561void LocationsBuilderARMVIXL::VisitNot(HNot* not_) {
5562 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005563 new (GetGraph()->GetAllocator()) LocationSummary(not_, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01005564 locations->SetInAt(0, Location::RequiresRegister());
5565 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5566}
5567
5568void InstructionCodeGeneratorARMVIXL::VisitNot(HNot* not_) {
5569 LocationSummary* locations = not_->GetLocations();
5570 Location out = locations->Out();
5571 Location in = locations->InAt(0);
5572 switch (not_->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005573 case DataType::Type::kInt32:
Artem Serov02d37832016-10-25 15:25:33 +01005574 __ Mvn(OutputRegister(not_), InputRegisterAt(not_, 0));
5575 break;
5576
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005577 case DataType::Type::kInt64:
Artem Serov02d37832016-10-25 15:25:33 +01005578 __ Mvn(LowRegisterFrom(out), LowRegisterFrom(in));
5579 __ Mvn(HighRegisterFrom(out), HighRegisterFrom(in));
5580 break;
5581
5582 default:
5583 LOG(FATAL) << "Unimplemented type for not operation " << not_->GetResultType();
5584 }
5585}
5586
Scott Wakelingc34dba72016-10-03 10:14:44 +01005587void LocationsBuilderARMVIXL::VisitBooleanNot(HBooleanNot* bool_not) {
5588 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005589 new (GetGraph()->GetAllocator()) LocationSummary(bool_not, LocationSummary::kNoCall);
Scott Wakelingc34dba72016-10-03 10:14:44 +01005590 locations->SetInAt(0, Location::RequiresRegister());
5591 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5592}
5593
5594void InstructionCodeGeneratorARMVIXL::VisitBooleanNot(HBooleanNot* bool_not) {
5595 __ Eor(OutputRegister(bool_not), InputRegister(bool_not), 1);
5596}
5597
Artem Serov02d37832016-10-25 15:25:33 +01005598void LocationsBuilderARMVIXL::VisitCompare(HCompare* compare) {
5599 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005600 new (GetGraph()->GetAllocator()) LocationSummary(compare, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01005601 switch (compare->InputAt(0)->GetType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005602 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005603 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005604 case DataType::Type::kInt8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005605 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005606 case DataType::Type::kInt16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005607 case DataType::Type::kInt32:
5608 case DataType::Type::kInt64: {
Artem Serov02d37832016-10-25 15:25:33 +01005609 locations->SetInAt(0, Location::RequiresRegister());
5610 locations->SetInAt(1, Location::RequiresRegister());
5611 // Output overlaps because it is written before doing the low comparison.
5612 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
5613 break;
5614 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005615 case DataType::Type::kFloat32:
5616 case DataType::Type::kFloat64: {
Artem Serov02d37832016-10-25 15:25:33 +01005617 locations->SetInAt(0, Location::RequiresFpuRegister());
5618 locations->SetInAt(1, ArithmeticZeroOrFpuRegister(compare->InputAt(1)));
5619 locations->SetOut(Location::RequiresRegister());
5620 break;
5621 }
5622 default:
5623 LOG(FATAL) << "Unexpected type for compare operation " << compare->InputAt(0)->GetType();
5624 }
5625}
5626
5627void InstructionCodeGeneratorARMVIXL::VisitCompare(HCompare* compare) {
5628 LocationSummary* locations = compare->GetLocations();
5629 vixl32::Register out = OutputRegister(compare);
5630 Location left = locations->InAt(0);
5631 Location right = locations->InAt(1);
5632
5633 vixl32::Label less, greater, done;
Anton Kirilov6f644202017-02-27 18:29:45 +00005634 vixl32::Label* final_label = codegen_->GetFinalLabel(compare, &done);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005635 DataType::Type type = compare->InputAt(0)->GetType();
Vladimir Marko33bff252017-11-01 14:35:42 +00005636 vixl32::Condition less_cond = vixl32::Condition::None();
Artem Serov02d37832016-10-25 15:25:33 +01005637 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005638 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005639 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005640 case DataType::Type::kInt8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005641 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005642 case DataType::Type::kInt16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005643 case DataType::Type::kInt32: {
Artem Serov02d37832016-10-25 15:25:33 +01005644 // Emit move to `out` before the `Cmp`, as `Mov` might affect the status flags.
5645 __ Mov(out, 0);
5646 __ Cmp(RegisterFrom(left), RegisterFrom(right)); // Signed compare.
5647 less_cond = lt;
5648 break;
5649 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005650 case DataType::Type::kInt64: {
Artem Serov02d37832016-10-25 15:25:33 +01005651 __ Cmp(HighRegisterFrom(left), HighRegisterFrom(right)); // Signed compare.
Artem Serov517d9f62016-12-12 15:51:15 +00005652 __ B(lt, &less, /* far_target */ false);
5653 __ B(gt, &greater, /* far_target */ false);
Artem Serov02d37832016-10-25 15:25:33 +01005654 // Emit move to `out` before the last `Cmp`, as `Mov` might affect the status flags.
5655 __ Mov(out, 0);
5656 __ Cmp(LowRegisterFrom(left), LowRegisterFrom(right)); // Unsigned compare.
5657 less_cond = lo;
5658 break;
5659 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005660 case DataType::Type::kFloat32:
5661 case DataType::Type::kFloat64: {
Artem Serov02d37832016-10-25 15:25:33 +01005662 __ Mov(out, 0);
Donghui Bai426b49c2016-11-08 14:55:38 +08005663 GenerateVcmp(compare, codegen_);
Artem Serov02d37832016-10-25 15:25:33 +01005664 // To branch on the FP compare result we transfer FPSCR to APSR (encoded as PC in VMRS).
5665 __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
5666 less_cond = ARMFPCondition(kCondLT, compare->IsGtBias());
5667 break;
5668 }
5669 default:
5670 LOG(FATAL) << "Unexpected compare type " << type;
5671 UNREACHABLE();
5672 }
5673
Anton Kirilov6f644202017-02-27 18:29:45 +00005674 __ B(eq, final_label, /* far_target */ false);
Artem Serov517d9f62016-12-12 15:51:15 +00005675 __ B(less_cond, &less, /* far_target */ false);
Artem Serov02d37832016-10-25 15:25:33 +01005676
5677 __ Bind(&greater);
5678 __ Mov(out, 1);
Anton Kirilov6f644202017-02-27 18:29:45 +00005679 __ B(final_label);
Artem Serov02d37832016-10-25 15:25:33 +01005680
5681 __ Bind(&less);
5682 __ Mov(out, -1);
5683
Anton Kirilov6f644202017-02-27 18:29:45 +00005684 if (done.IsReferenced()) {
5685 __ Bind(&done);
5686 }
Artem Serov02d37832016-10-25 15:25:33 +01005687}
5688
5689void LocationsBuilderARMVIXL::VisitPhi(HPhi* instruction) {
5690 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005691 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01005692 for (size_t i = 0, e = locations->GetInputCount(); i < e; ++i) {
5693 locations->SetInAt(i, Location::Any());
5694 }
5695 locations->SetOut(Location::Any());
5696}
5697
5698void InstructionCodeGeneratorARMVIXL::VisitPhi(HPhi* instruction ATTRIBUTE_UNUSED) {
5699 LOG(FATAL) << "Unreachable";
5700}
5701
5702void CodeGeneratorARMVIXL::GenerateMemoryBarrier(MemBarrierKind kind) {
5703 // TODO (ported from quick): revisit ARM barrier kinds.
5704 DmbOptions flavor = DmbOptions::ISH; // Quiet C++ warnings.
5705 switch (kind) {
5706 case MemBarrierKind::kAnyStore:
5707 case MemBarrierKind::kLoadAny:
5708 case MemBarrierKind::kAnyAny: {
5709 flavor = DmbOptions::ISH;
5710 break;
5711 }
5712 case MemBarrierKind::kStoreStore: {
5713 flavor = DmbOptions::ISHST;
5714 break;
5715 }
5716 default:
5717 LOG(FATAL) << "Unexpected memory barrier " << kind;
5718 }
5719 __ Dmb(flavor);
5720}
5721
5722void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicLoad(vixl32::Register addr,
5723 uint32_t offset,
5724 vixl32::Register out_lo,
5725 vixl32::Register out_hi) {
5726 UseScratchRegisterScope temps(GetVIXLAssembler());
5727 if (offset != 0) {
5728 vixl32::Register temp = temps.Acquire();
5729 __ Add(temp, addr, offset);
5730 addr = temp;
5731 }
Scott Wakelingb77051e2016-11-21 19:46:00 +00005732 __ Ldrexd(out_lo, out_hi, MemOperand(addr));
Artem Serov02d37832016-10-25 15:25:33 +01005733}
5734
5735void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicStore(vixl32::Register addr,
5736 uint32_t offset,
5737 vixl32::Register value_lo,
5738 vixl32::Register value_hi,
5739 vixl32::Register temp1,
5740 vixl32::Register temp2,
5741 HInstruction* instruction) {
5742 UseScratchRegisterScope temps(GetVIXLAssembler());
5743 vixl32::Label fail;
5744 if (offset != 0) {
5745 vixl32::Register temp = temps.Acquire();
5746 __ Add(temp, addr, offset);
5747 addr = temp;
5748 }
5749 __ Bind(&fail);
Alexandre Rames374ddf32016-11-04 10:40:49 +00005750 {
5751 // Ensure the pc position is recorded immediately after the `ldrexd` instruction.
Artem Serov0fb37192016-12-06 18:13:40 +00005752 ExactAssemblyScope aas(GetVIXLAssembler(),
5753 vixl32::kMaxInstructionSizeInBytes,
5754 CodeBufferCheckScope::kMaximumSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00005755 // We need a load followed by store. (The address used in a STREX instruction must
5756 // be the same as the address in the most recently executed LDREX instruction.)
5757 __ ldrexd(temp1, temp2, MemOperand(addr));
5758 codegen_->MaybeRecordImplicitNullCheck(instruction);
5759 }
Scott Wakelingb77051e2016-11-21 19:46:00 +00005760 __ Strexd(temp1, value_lo, value_hi, MemOperand(addr));
xueliang.zhongf51bc622016-11-04 09:23:32 +00005761 __ CompareAndBranchIfNonZero(temp1, &fail);
Artem Serov02d37832016-10-25 15:25:33 +01005762}
Artem Serov02109dd2016-09-23 17:17:54 +01005763
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005764void LocationsBuilderARMVIXL::HandleFieldSet(
5765 HInstruction* instruction, const FieldInfo& field_info) {
5766 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
5767
5768 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005769 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005770 locations->SetInAt(0, Location::RequiresRegister());
5771
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005772 DataType::Type field_type = field_info.GetFieldType();
5773 if (DataType::IsFloatingPointType(field_type)) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005774 locations->SetInAt(1, Location::RequiresFpuRegister());
5775 } else {
5776 locations->SetInAt(1, Location::RequiresRegister());
5777 }
5778
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005779 bool is_wide = field_type == DataType::Type::kInt64 || field_type == DataType::Type::kFloat64;
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005780 bool generate_volatile = field_info.IsVolatile()
5781 && is_wide
5782 && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
5783 bool needs_write_barrier =
5784 CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1));
5785 // Temporary registers for the write barrier.
5786 // TODO: consider renaming StoreNeedsWriteBarrier to StoreNeedsGCMark.
5787 if (needs_write_barrier) {
5788 locations->AddTemp(Location::RequiresRegister()); // Possibly used for reference poisoning too.
5789 locations->AddTemp(Location::RequiresRegister());
5790 } else if (generate_volatile) {
5791 // ARM encoding have some additional constraints for ldrexd/strexd:
5792 // - registers need to be consecutive
5793 // - the first register should be even but not R14.
5794 // We don't test for ARM yet, and the assertion makes sure that we
5795 // revisit this if we ever enable ARM encoding.
5796 DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet());
5797
5798 locations->AddTemp(Location::RequiresRegister());
5799 locations->AddTemp(Location::RequiresRegister());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005800 if (field_type == DataType::Type::kFloat64) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005801 // For doubles we need two more registers to copy the value.
5802 locations->AddTemp(LocationFrom(r2));
5803 locations->AddTemp(LocationFrom(r3));
5804 }
5805 }
5806}
5807
5808void InstructionCodeGeneratorARMVIXL::HandleFieldSet(HInstruction* instruction,
5809 const FieldInfo& field_info,
5810 bool value_can_be_null) {
5811 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
5812
5813 LocationSummary* locations = instruction->GetLocations();
5814 vixl32::Register base = InputRegisterAt(instruction, 0);
5815 Location value = locations->InAt(1);
5816
5817 bool is_volatile = field_info.IsVolatile();
5818 bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005819 DataType::Type field_type = field_info.GetFieldType();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005820 uint32_t offset = field_info.GetFieldOffset().Uint32Value();
5821 bool needs_write_barrier =
5822 CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1));
5823
5824 if (is_volatile) {
5825 codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
5826 }
5827
5828 switch (field_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005829 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005830 case DataType::Type::kUint8:
5831 case DataType::Type::kInt8:
5832 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005833 case DataType::Type::kInt16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005834 case DataType::Type::kInt32: {
5835 StoreOperandType operand_type = GetStoreOperandType(field_type);
5836 GetAssembler()->StoreToOffset(operand_type, RegisterFrom(value), base, offset);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005837 break;
5838 }
5839
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005840 case DataType::Type::kReference: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005841 if (kPoisonHeapReferences && needs_write_barrier) {
5842 // Note that in the case where `value` is a null reference,
5843 // we do not enter this block, as a null reference does not
5844 // need poisoning.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005845 DCHECK_EQ(field_type, DataType::Type::kReference);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005846 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
5847 __ Mov(temp, RegisterFrom(value));
5848 GetAssembler()->PoisonHeapReference(temp);
5849 GetAssembler()->StoreToOffset(kStoreWord, temp, base, offset);
5850 } else {
5851 GetAssembler()->StoreToOffset(kStoreWord, RegisterFrom(value), base, offset);
5852 }
5853 break;
5854 }
5855
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005856 case DataType::Type::kInt64: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005857 if (is_volatile && !atomic_ldrd_strd) {
5858 GenerateWideAtomicStore(base,
5859 offset,
5860 LowRegisterFrom(value),
5861 HighRegisterFrom(value),
5862 RegisterFrom(locations->GetTemp(0)),
5863 RegisterFrom(locations->GetTemp(1)),
5864 instruction);
5865 } else {
5866 GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), base, offset);
5867 codegen_->MaybeRecordImplicitNullCheck(instruction);
5868 }
5869 break;
5870 }
5871
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005872 case DataType::Type::kFloat32: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005873 GetAssembler()->StoreSToOffset(SRegisterFrom(value), base, offset);
5874 break;
5875 }
5876
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005877 case DataType::Type::kFloat64: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01005878 vixl32::DRegister value_reg = DRegisterFrom(value);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005879 if (is_volatile && !atomic_ldrd_strd) {
5880 vixl32::Register value_reg_lo = RegisterFrom(locations->GetTemp(0));
5881 vixl32::Register value_reg_hi = RegisterFrom(locations->GetTemp(1));
5882
5883 __ Vmov(value_reg_lo, value_reg_hi, value_reg);
5884
5885 GenerateWideAtomicStore(base,
5886 offset,
5887 value_reg_lo,
5888 value_reg_hi,
5889 RegisterFrom(locations->GetTemp(2)),
5890 RegisterFrom(locations->GetTemp(3)),
5891 instruction);
5892 } else {
5893 GetAssembler()->StoreDToOffset(value_reg, base, offset);
5894 codegen_->MaybeRecordImplicitNullCheck(instruction);
5895 }
5896 break;
5897 }
5898
Aart Bik66c158e2018-01-31 12:55:04 -08005899 case DataType::Type::kUint32:
5900 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005901 case DataType::Type::kVoid:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005902 LOG(FATAL) << "Unreachable type " << field_type;
5903 UNREACHABLE();
5904 }
5905
5906 // Longs and doubles are handled in the switch.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005907 if (field_type != DataType::Type::kInt64 && field_type != DataType::Type::kFloat64) {
Alexandre Rames374ddf32016-11-04 10:40:49 +00005908 // TODO(VIXL): Here and for other calls to `MaybeRecordImplicitNullCheck` in this method, we
5909 // should use a scope and the assembler to emit the store instruction to guarantee that we
5910 // record the pc at the correct position. But the `Assembler` does not automatically handle
5911 // unencodable offsets. Practically, everything is fine because the helper and VIXL, at the time
5912 // of writing, do generate the store instruction last.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005913 codegen_->MaybeRecordImplicitNullCheck(instruction);
5914 }
5915
5916 if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
5917 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
5918 vixl32::Register card = RegisterFrom(locations->GetTemp(1));
5919 codegen_->MarkGCCard(temp, card, base, RegisterFrom(value), value_can_be_null);
5920 }
5921
5922 if (is_volatile) {
5923 codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
5924 }
5925}
5926
Artem Serov02d37832016-10-25 15:25:33 +01005927void LocationsBuilderARMVIXL::HandleFieldGet(HInstruction* instruction,
5928 const FieldInfo& field_info) {
5929 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
5930
5931 bool object_field_get_with_read_barrier =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005932 kEmitCompilerReadBarrier && (field_info.GetFieldType() == DataType::Type::kReference);
Artem Serov02d37832016-10-25 15:25:33 +01005933 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005934 new (GetGraph()->GetAllocator()) LocationSummary(instruction,
5935 object_field_get_with_read_barrier
5936 ? LocationSummary::kCallOnSlowPath
5937 : LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01005938 if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
5939 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
5940 }
5941 locations->SetInAt(0, Location::RequiresRegister());
5942
5943 bool volatile_for_double = field_info.IsVolatile()
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005944 && (field_info.GetFieldType() == DataType::Type::kFloat64)
Artem Serov02d37832016-10-25 15:25:33 +01005945 && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
5946 // The output overlaps in case of volatile long: we don't want the
5947 // code generated by GenerateWideAtomicLoad to overwrite the
5948 // object's location. Likewise, in the case of an object field get
5949 // with read barriers enabled, we do not want the load to overwrite
5950 // the object's location, as we need it to emit the read barrier.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005951 bool overlap =
5952 (field_info.IsVolatile() && (field_info.GetFieldType() == DataType::Type::kInt64)) ||
Artem Serov02d37832016-10-25 15:25:33 +01005953 object_field_get_with_read_barrier;
5954
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005955 if (DataType::IsFloatingPointType(instruction->GetType())) {
Artem Serov02d37832016-10-25 15:25:33 +01005956 locations->SetOut(Location::RequiresFpuRegister());
5957 } else {
5958 locations->SetOut(Location::RequiresRegister(),
5959 (overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap));
5960 }
5961 if (volatile_for_double) {
5962 // ARM encoding have some additional constraints for ldrexd/strexd:
5963 // - registers need to be consecutive
5964 // - the first register should be even but not R14.
5965 // We don't test for ARM yet, and the assertion makes sure that we
5966 // revisit this if we ever enable ARM encoding.
5967 DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet());
5968 locations->AddTemp(Location::RequiresRegister());
5969 locations->AddTemp(Location::RequiresRegister());
5970 } else if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
5971 // We need a temporary register for the read barrier marking slow
Artem Serovc5fcb442016-12-02 19:19:58 +00005972 // path in CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01005973 if (kBakerReadBarrierLinkTimeThunksEnableForFields &&
5974 !Runtime::Current()->UseJitCompilation()) {
5975 // If link-time thunks for the Baker read barrier are enabled, for AOT
5976 // loads we need a temporary only if the offset is too big.
5977 if (field_info.GetFieldOffset().Uint32Value() >= kReferenceLoadMinFarOffset) {
5978 locations->AddTemp(Location::RequiresRegister());
5979 }
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01005980 } else {
5981 locations->AddTemp(Location::RequiresRegister());
5982 }
Artem Serov02d37832016-10-25 15:25:33 +01005983 }
5984}
5985
5986Location LocationsBuilderARMVIXL::ArithmeticZeroOrFpuRegister(HInstruction* input) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005987 DCHECK(DataType::IsFloatingPointType(input->GetType())) << input->GetType();
Artem Serov02d37832016-10-25 15:25:33 +01005988 if ((input->IsFloatConstant() && (input->AsFloatConstant()->IsArithmeticZero())) ||
5989 (input->IsDoubleConstant() && (input->AsDoubleConstant()->IsArithmeticZero()))) {
5990 return Location::ConstantLocation(input->AsConstant());
5991 } else {
5992 return Location::RequiresFpuRegister();
5993 }
5994}
5995
Artem Serov02109dd2016-09-23 17:17:54 +01005996Location LocationsBuilderARMVIXL::ArmEncodableConstantOrRegister(HInstruction* constant,
5997 Opcode opcode) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005998 DCHECK(!DataType::IsFloatingPointType(constant->GetType()));
Artem Serov02109dd2016-09-23 17:17:54 +01005999 if (constant->IsConstant() &&
6000 CanEncodeConstantAsImmediate(constant->AsConstant(), opcode)) {
6001 return Location::ConstantLocation(constant->AsConstant());
6002 }
6003 return Location::RequiresRegister();
6004}
6005
Vladimir Markof0a6a1d2018-01-08 14:23:56 +00006006static bool CanEncode32BitConstantAsImmediate(
6007 CodeGeneratorARMVIXL* codegen,
6008 uint32_t value,
6009 Opcode opcode,
6010 vixl32::FlagsUpdate flags_update = vixl32::FlagsUpdate::DontCare) {
6011 ArmVIXLAssembler* assembler = codegen->GetAssembler();
6012 if (assembler->ShifterOperandCanHold(opcode, value, flags_update)) {
Artem Serov02109dd2016-09-23 17:17:54 +01006013 return true;
6014 }
6015 Opcode neg_opcode = kNoOperand;
Anton Kiriloveffd5bf2017-02-28 16:59:15 +00006016 uint32_t neg_value = 0;
Artem Serov02109dd2016-09-23 17:17:54 +01006017 switch (opcode) {
Anton Kiriloveffd5bf2017-02-28 16:59:15 +00006018 case AND: neg_opcode = BIC; neg_value = ~value; break;
6019 case ORR: neg_opcode = ORN; neg_value = ~value; break;
6020 case ADD: neg_opcode = SUB; neg_value = -value; break;
6021 case ADC: neg_opcode = SBC; neg_value = ~value; break;
6022 case SUB: neg_opcode = ADD; neg_value = -value; break;
6023 case SBC: neg_opcode = ADC; neg_value = ~value; break;
6024 case MOV: neg_opcode = MVN; neg_value = ~value; break;
Artem Serov02109dd2016-09-23 17:17:54 +01006025 default:
6026 return false;
6027 }
Anton Kiriloveffd5bf2017-02-28 16:59:15 +00006028
Vladimir Markof0a6a1d2018-01-08 14:23:56 +00006029 if (assembler->ShifterOperandCanHold(neg_opcode, neg_value, flags_update)) {
Anton Kiriloveffd5bf2017-02-28 16:59:15 +00006030 return true;
6031 }
6032
6033 return opcode == AND && IsPowerOfTwo(value + 1);
Artem Serov02109dd2016-09-23 17:17:54 +01006034}
6035
Vladimir Markof0a6a1d2018-01-08 14:23:56 +00006036bool LocationsBuilderARMVIXL::CanEncodeConstantAsImmediate(HConstant* input_cst, Opcode opcode) {
6037 uint64_t value = static_cast<uint64_t>(Int64FromConstant(input_cst));
6038 if (DataType::Is64BitType(input_cst->GetType())) {
6039 Opcode high_opcode = opcode;
6040 vixl32::FlagsUpdate low_flags_update = vixl32::FlagsUpdate::DontCare;
6041 switch (opcode) {
6042 case SUB:
6043 // Flip the operation to an ADD.
6044 value = -value;
6045 opcode = ADD;
6046 FALLTHROUGH_INTENDED;
6047 case ADD:
6048 if (Low32Bits(value) == 0u) {
6049 return CanEncode32BitConstantAsImmediate(codegen_, High32Bits(value), opcode);
6050 }
6051 high_opcode = ADC;
6052 low_flags_update = vixl32::FlagsUpdate::SetFlags;
6053 break;
6054 default:
6055 break;
6056 }
6057 return CanEncode32BitConstantAsImmediate(codegen_, High32Bits(value), high_opcode) &&
6058 CanEncode32BitConstantAsImmediate(codegen_, Low32Bits(value), opcode, low_flags_update);
6059 } else {
6060 return CanEncode32BitConstantAsImmediate(codegen_, Low32Bits(value), opcode);
6061 }
6062}
6063
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006064void InstructionCodeGeneratorARMVIXL::HandleFieldGet(HInstruction* instruction,
6065 const FieldInfo& field_info) {
6066 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
6067
6068 LocationSummary* locations = instruction->GetLocations();
6069 vixl32::Register base = InputRegisterAt(instruction, 0);
6070 Location out = locations->Out();
6071 bool is_volatile = field_info.IsVolatile();
6072 bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
Vladimir Marko61b92282017-10-11 13:23:17 +01006073 DCHECK_EQ(DataType::Size(field_info.GetFieldType()), DataType::Size(instruction->GetType()));
6074 DataType::Type load_type = instruction->GetType();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006075 uint32_t offset = field_info.GetFieldOffset().Uint32Value();
6076
Vladimir Marko61b92282017-10-11 13:23:17 +01006077 switch (load_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006078 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006079 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006080 case DataType::Type::kInt8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006081 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006082 case DataType::Type::kInt16:
6083 case DataType::Type::kInt32: {
Vladimir Marko61b92282017-10-11 13:23:17 +01006084 LoadOperandType operand_type = GetLoadOperandType(load_type);
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006085 GetAssembler()->LoadFromOffset(operand_type, RegisterFrom(out), base, offset);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006086 break;
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006087 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006088
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006089 case DataType::Type::kReference: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006090 // /* HeapReference<Object> */ out = *(base + offset)
6091 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
Vladimir Markodcd117e2018-04-19 11:54:00 +01006092 Location maybe_temp = (locations->GetTempCount() != 0) ? locations->GetTemp(0) : Location();
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006093 // Note that a potential implicit null check is handled in this
6094 // CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier call.
6095 codegen_->GenerateFieldLoadWithBakerReadBarrier(
Vladimir Markodcd117e2018-04-19 11:54:00 +01006096 instruction, out, base, offset, maybe_temp, /* needs_null_check */ true);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006097 if (is_volatile) {
6098 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
6099 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006100 } else {
6101 GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(out), base, offset);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006102 codegen_->MaybeRecordImplicitNullCheck(instruction);
6103 if (is_volatile) {
6104 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
6105 }
6106 // If read barriers are enabled, emit read barriers other than
6107 // Baker's using a slow path (and also unpoison the loaded
6108 // reference, if heap poisoning is enabled).
6109 codegen_->MaybeGenerateReadBarrierSlow(instruction, out, out, locations->InAt(0), offset);
6110 }
6111 break;
6112 }
6113
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006114 case DataType::Type::kInt64:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006115 if (is_volatile && !atomic_ldrd_strd) {
6116 GenerateWideAtomicLoad(base, offset, LowRegisterFrom(out), HighRegisterFrom(out));
6117 } else {
6118 GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out), base, offset);
6119 }
6120 break;
6121
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006122 case DataType::Type::kFloat32:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006123 GetAssembler()->LoadSFromOffset(SRegisterFrom(out), base, offset);
6124 break;
6125
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006126 case DataType::Type::kFloat64: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006127 vixl32::DRegister out_dreg = DRegisterFrom(out);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006128 if (is_volatile && !atomic_ldrd_strd) {
6129 vixl32::Register lo = RegisterFrom(locations->GetTemp(0));
6130 vixl32::Register hi = RegisterFrom(locations->GetTemp(1));
6131 GenerateWideAtomicLoad(base, offset, lo, hi);
6132 // TODO(VIXL): Do we need to be immediately after the ldrexd instruction? If so we need a
6133 // scope.
6134 codegen_->MaybeRecordImplicitNullCheck(instruction);
6135 __ Vmov(out_dreg, lo, hi);
6136 } else {
6137 GetAssembler()->LoadDFromOffset(out_dreg, base, offset);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006138 codegen_->MaybeRecordImplicitNullCheck(instruction);
6139 }
6140 break;
6141 }
6142
Aart Bik66c158e2018-01-31 12:55:04 -08006143 case DataType::Type::kUint32:
6144 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006145 case DataType::Type::kVoid:
Vladimir Marko61b92282017-10-11 13:23:17 +01006146 LOG(FATAL) << "Unreachable type " << load_type;
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006147 UNREACHABLE();
6148 }
6149
Vladimir Marko61b92282017-10-11 13:23:17 +01006150 if (load_type == DataType::Type::kReference || load_type == DataType::Type::kFloat64) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006151 // Potential implicit null checks, in the case of reference or
6152 // double fields, are handled in the previous switch statement.
6153 } else {
6154 // Address cases other than reference and double that may require an implicit null check.
Alexandre Rames374ddf32016-11-04 10:40:49 +00006155 // TODO(VIXL): Here and for other calls to `MaybeRecordImplicitNullCheck` in this method, we
6156 // should use a scope and the assembler to emit the load instruction to guarantee that we
6157 // record the pc at the correct position. But the `Assembler` does not automatically handle
6158 // unencodable offsets. Practically, everything is fine because the helper and VIXL, at the time
6159 // of writing, do generate the store instruction last.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006160 codegen_->MaybeRecordImplicitNullCheck(instruction);
6161 }
6162
6163 if (is_volatile) {
Vladimir Marko61b92282017-10-11 13:23:17 +01006164 if (load_type == DataType::Type::kReference) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006165 // Memory barriers, in the case of references, are also handled
6166 // in the previous switch statement.
6167 } else {
6168 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
6169 }
6170 }
6171}
6172
6173void LocationsBuilderARMVIXL::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
6174 HandleFieldSet(instruction, instruction->GetFieldInfo());
6175}
6176
6177void InstructionCodeGeneratorARMVIXL::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
6178 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
6179}
6180
6181void LocationsBuilderARMVIXL::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
6182 HandleFieldGet(instruction, instruction->GetFieldInfo());
6183}
6184
6185void InstructionCodeGeneratorARMVIXL::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
6186 HandleFieldGet(instruction, instruction->GetFieldInfo());
6187}
6188
6189void LocationsBuilderARMVIXL::VisitStaticFieldGet(HStaticFieldGet* instruction) {
6190 HandleFieldGet(instruction, instruction->GetFieldInfo());
6191}
6192
6193void InstructionCodeGeneratorARMVIXL::VisitStaticFieldGet(HStaticFieldGet* instruction) {
6194 HandleFieldGet(instruction, instruction->GetFieldInfo());
6195}
6196
Scott Wakelingc34dba72016-10-03 10:14:44 +01006197void LocationsBuilderARMVIXL::VisitStaticFieldSet(HStaticFieldSet* instruction) {
6198 HandleFieldSet(instruction, instruction->GetFieldInfo());
6199}
6200
6201void InstructionCodeGeneratorARMVIXL::VisitStaticFieldSet(HStaticFieldSet* instruction) {
6202 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
6203}
6204
Artem Serovcfbe9132016-10-14 15:58:56 +01006205void LocationsBuilderARMVIXL::VisitUnresolvedInstanceFieldGet(
6206 HUnresolvedInstanceFieldGet* instruction) {
6207 FieldAccessCallingConventionARMVIXL calling_convention;
6208 codegen_->CreateUnresolvedFieldLocationSummary(
6209 instruction, instruction->GetFieldType(), calling_convention);
6210}
6211
6212void InstructionCodeGeneratorARMVIXL::VisitUnresolvedInstanceFieldGet(
6213 HUnresolvedInstanceFieldGet* instruction) {
6214 FieldAccessCallingConventionARMVIXL calling_convention;
6215 codegen_->GenerateUnresolvedFieldAccess(instruction,
6216 instruction->GetFieldType(),
6217 instruction->GetFieldIndex(),
6218 instruction->GetDexPc(),
6219 calling_convention);
6220}
6221
6222void LocationsBuilderARMVIXL::VisitUnresolvedInstanceFieldSet(
6223 HUnresolvedInstanceFieldSet* instruction) {
6224 FieldAccessCallingConventionARMVIXL calling_convention;
6225 codegen_->CreateUnresolvedFieldLocationSummary(
6226 instruction, instruction->GetFieldType(), calling_convention);
6227}
6228
6229void InstructionCodeGeneratorARMVIXL::VisitUnresolvedInstanceFieldSet(
6230 HUnresolvedInstanceFieldSet* instruction) {
6231 FieldAccessCallingConventionARMVIXL calling_convention;
6232 codegen_->GenerateUnresolvedFieldAccess(instruction,
6233 instruction->GetFieldType(),
6234 instruction->GetFieldIndex(),
6235 instruction->GetDexPc(),
6236 calling_convention);
6237}
6238
6239void LocationsBuilderARMVIXL::VisitUnresolvedStaticFieldGet(
6240 HUnresolvedStaticFieldGet* instruction) {
6241 FieldAccessCallingConventionARMVIXL calling_convention;
6242 codegen_->CreateUnresolvedFieldLocationSummary(
6243 instruction, instruction->GetFieldType(), calling_convention);
6244}
6245
6246void InstructionCodeGeneratorARMVIXL::VisitUnresolvedStaticFieldGet(
6247 HUnresolvedStaticFieldGet* instruction) {
6248 FieldAccessCallingConventionARMVIXL calling_convention;
6249 codegen_->GenerateUnresolvedFieldAccess(instruction,
6250 instruction->GetFieldType(),
6251 instruction->GetFieldIndex(),
6252 instruction->GetDexPc(),
6253 calling_convention);
6254}
6255
6256void LocationsBuilderARMVIXL::VisitUnresolvedStaticFieldSet(
6257 HUnresolvedStaticFieldSet* instruction) {
6258 FieldAccessCallingConventionARMVIXL calling_convention;
6259 codegen_->CreateUnresolvedFieldLocationSummary(
6260 instruction, instruction->GetFieldType(), calling_convention);
6261}
6262
6263void InstructionCodeGeneratorARMVIXL::VisitUnresolvedStaticFieldSet(
6264 HUnresolvedStaticFieldSet* instruction) {
6265 FieldAccessCallingConventionARMVIXL calling_convention;
6266 codegen_->GenerateUnresolvedFieldAccess(instruction,
6267 instruction->GetFieldType(),
6268 instruction->GetFieldIndex(),
6269 instruction->GetDexPc(),
6270 calling_convention);
6271}
6272
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006273void LocationsBuilderARMVIXL::VisitNullCheck(HNullCheck* instruction) {
Artem Serov657022c2016-11-23 14:19:38 +00006274 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006275 locations->SetInAt(0, Location::RequiresRegister());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006276}
6277
6278void CodeGeneratorARMVIXL::GenerateImplicitNullCheck(HNullCheck* instruction) {
6279 if (CanMoveNullCheckToUser(instruction)) {
6280 return;
6281 }
6282
6283 UseScratchRegisterScope temps(GetVIXLAssembler());
Alexandre Rames374ddf32016-11-04 10:40:49 +00006284 // Ensure the pc position is recorded immediately after the `ldr` instruction.
Artem Serov0fb37192016-12-06 18:13:40 +00006285 ExactAssemblyScope aas(GetVIXLAssembler(),
6286 vixl32::kMaxInstructionSizeInBytes,
6287 CodeBufferCheckScope::kMaximumSize);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006288 __ ldr(temps.Acquire(), MemOperand(InputRegisterAt(instruction, 0)));
6289 RecordPcInfo(instruction, instruction->GetDexPc());
6290}
6291
6292void CodeGeneratorARMVIXL::GenerateExplicitNullCheck(HNullCheck* instruction) {
6293 NullCheckSlowPathARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01006294 new (GetScopedAllocator()) NullCheckSlowPathARMVIXL(instruction);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006295 AddSlowPath(slow_path);
xueliang.zhongf51bc622016-11-04 09:23:32 +00006296 __ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006297}
6298
6299void InstructionCodeGeneratorARMVIXL::VisitNullCheck(HNullCheck* instruction) {
6300 codegen_->GenerateNullCheck(instruction);
6301}
6302
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006303void CodeGeneratorARMVIXL::LoadFromShiftedRegOffset(DataType::Type type,
Scott Wakelingc34dba72016-10-03 10:14:44 +01006304 Location out_loc,
6305 vixl32::Register base,
6306 vixl32::Register reg_index,
6307 vixl32::Condition cond) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006308 uint32_t shift_count = DataType::SizeShift(type);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006309 MemOperand mem_address(base, reg_index, vixl32::LSL, shift_count);
6310
6311 switch (type) {
Vladimir Marko61b92282017-10-11 13:23:17 +01006312 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006313 case DataType::Type::kUint8:
Vladimir Marko61b92282017-10-11 13:23:17 +01006314 __ Ldrb(cond, RegisterFrom(out_loc), mem_address);
6315 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006316 case DataType::Type::kInt8:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006317 __ Ldrsb(cond, RegisterFrom(out_loc), mem_address);
6318 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006319 case DataType::Type::kUint16:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006320 __ Ldrh(cond, RegisterFrom(out_loc), mem_address);
6321 break;
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006322 case DataType::Type::kInt16:
6323 __ Ldrsh(cond, RegisterFrom(out_loc), mem_address);
6324 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006325 case DataType::Type::kReference:
6326 case DataType::Type::kInt32:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006327 __ Ldr(cond, RegisterFrom(out_loc), mem_address);
6328 break;
6329 // T32 doesn't support LoadFromShiftedRegOffset mem address mode for these types.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006330 case DataType::Type::kInt64:
6331 case DataType::Type::kFloat32:
6332 case DataType::Type::kFloat64:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006333 default:
6334 LOG(FATAL) << "Unreachable type " << type;
6335 UNREACHABLE();
6336 }
6337}
6338
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006339void CodeGeneratorARMVIXL::StoreToShiftedRegOffset(DataType::Type type,
Scott Wakelingc34dba72016-10-03 10:14:44 +01006340 Location loc,
6341 vixl32::Register base,
6342 vixl32::Register reg_index,
6343 vixl32::Condition cond) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006344 uint32_t shift_count = DataType::SizeShift(type);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006345 MemOperand mem_address(base, reg_index, vixl32::LSL, shift_count);
6346
6347 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006348 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006349 case DataType::Type::kUint8:
6350 case DataType::Type::kInt8:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006351 __ Strb(cond, RegisterFrom(loc), mem_address);
6352 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006353 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006354 case DataType::Type::kInt16:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006355 __ Strh(cond, RegisterFrom(loc), mem_address);
6356 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006357 case DataType::Type::kReference:
6358 case DataType::Type::kInt32:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006359 __ Str(cond, RegisterFrom(loc), mem_address);
6360 break;
6361 // T32 doesn't support StoreToShiftedRegOffset mem address mode for these types.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006362 case DataType::Type::kInt64:
6363 case DataType::Type::kFloat32:
6364 case DataType::Type::kFloat64:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006365 default:
6366 LOG(FATAL) << "Unreachable type " << type;
6367 UNREACHABLE();
6368 }
6369}
6370
6371void LocationsBuilderARMVIXL::VisitArrayGet(HArrayGet* instruction) {
6372 bool object_array_get_with_read_barrier =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006373 kEmitCompilerReadBarrier && (instruction->GetType() == DataType::Type::kReference);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006374 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01006375 new (GetGraph()->GetAllocator()) LocationSummary(instruction,
6376 object_array_get_with_read_barrier
6377 ? LocationSummary::kCallOnSlowPath
6378 : LocationSummary::kNoCall);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006379 if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006380 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Scott Wakelingc34dba72016-10-03 10:14:44 +01006381 }
6382 locations->SetInAt(0, Location::RequiresRegister());
6383 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006384 if (DataType::IsFloatingPointType(instruction->GetType())) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006385 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
6386 } else {
6387 // The output overlaps in the case of an object array get with
6388 // read barriers enabled: we do not want the move to overwrite the
6389 // array's location, as we need it to emit the read barrier.
6390 locations->SetOut(
6391 Location::RequiresRegister(),
6392 object_array_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap);
6393 }
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006394 if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006395 if (kBakerReadBarrierLinkTimeThunksEnableForFields &&
6396 !Runtime::Current()->UseJitCompilation() &&
6397 instruction->GetIndex()->IsConstant()) {
6398 // Array loads with constant index are treated as field loads.
6399 // If link-time thunks for the Baker read barrier are enabled, for AOT
6400 // constant index loads we need a temporary only if the offset is too big.
6401 uint32_t offset = CodeGenerator::GetArrayDataOffset(instruction);
6402 uint32_t index = instruction->GetIndex()->AsIntConstant()->GetValue();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006403 offset += index << DataType::SizeShift(DataType::Type::kReference);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006404 if (offset >= kReferenceLoadMinFarOffset) {
6405 locations->AddTemp(Location::RequiresRegister());
6406 }
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006407 } else {
Vladimir Markodcd117e2018-04-19 11:54:00 +01006408 // If using introspection, we need a non-scratch temporary for the array data pointer.
6409 // Otherwise, we need a temporary register for the read barrier marking slow
6410 // path in CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006411 locations->AddTemp(Location::RequiresRegister());
6412 }
6413 } else if (mirror::kUseStringCompression && instruction->IsStringCharAt()) {
6414 // Also need a temporary for String compression feature.
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006415 locations->AddTemp(Location::RequiresRegister());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006416 }
6417}
6418
6419void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006420 LocationSummary* locations = instruction->GetLocations();
6421 Location obj_loc = locations->InAt(0);
6422 vixl32::Register obj = InputRegisterAt(instruction, 0);
6423 Location index = locations->InAt(1);
6424 Location out_loc = locations->Out();
6425 uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006426 DataType::Type type = instruction->GetType();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006427 const bool maybe_compressed_char_at = mirror::kUseStringCompression &&
6428 instruction->IsStringCharAt();
6429 HInstruction* array_instr = instruction->GetArray();
6430 bool has_intermediate_address = array_instr->IsIntermediateAddress();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006431
6432 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006433 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006434 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006435 case DataType::Type::kInt8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006436 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006437 case DataType::Type::kInt16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006438 case DataType::Type::kInt32: {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01006439 vixl32::Register length;
6440 if (maybe_compressed_char_at) {
6441 length = RegisterFrom(locations->GetTemp(0));
6442 uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
6443 GetAssembler()->LoadFromOffset(kLoadWord, length, obj, count_offset);
6444 codegen_->MaybeRecordImplicitNullCheck(instruction);
6445 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006446 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006447 int32_t const_index = Int32ConstantFrom(index);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006448 if (maybe_compressed_char_at) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006449 vixl32::Label uncompressed_load, done;
Anton Kirilov6f644202017-02-27 18:29:45 +00006450 vixl32::Label* final_label = codegen_->GetFinalLabel(instruction, &done);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01006451 __ Lsrs(length, length, 1u); // LSRS has a 16-bit encoding, TST (immediate) does not.
6452 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
6453 "Expecting 0=compressed, 1=uncompressed");
Artem Serov517d9f62016-12-12 15:51:15 +00006454 __ B(cs, &uncompressed_load, /* far_target */ false);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006455 GetAssembler()->LoadFromOffset(kLoadUnsignedByte,
6456 RegisterFrom(out_loc),
6457 obj,
6458 data_offset + const_index);
Anton Kirilov6f644202017-02-27 18:29:45 +00006459 __ B(final_label);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006460 __ Bind(&uncompressed_load);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006461 GetAssembler()->LoadFromOffset(GetLoadOperandType(DataType::Type::kUint16),
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006462 RegisterFrom(out_loc),
6463 obj,
6464 data_offset + (const_index << 1));
Anton Kirilov6f644202017-02-27 18:29:45 +00006465 if (done.IsReferenced()) {
6466 __ Bind(&done);
6467 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006468 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006469 uint32_t full_offset = data_offset + (const_index << DataType::SizeShift(type));
Scott Wakelingc34dba72016-10-03 10:14:44 +01006470
6471 LoadOperandType load_type = GetLoadOperandType(type);
6472 GetAssembler()->LoadFromOffset(load_type, RegisterFrom(out_loc), obj, full_offset);
6473 }
6474 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006475 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006476 vixl32::Register temp = temps.Acquire();
6477
6478 if (has_intermediate_address) {
Artem Serov2bbc9532016-10-21 11:51:50 +01006479 // We do not need to compute the intermediate address from the array: the
6480 // input instruction has done it already. See the comment in
6481 // `TryExtractArrayAccessAddress()`.
6482 if (kIsDebugBuild) {
6483 HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
Anton Kirilov644032c2016-12-06 17:51:43 +00006484 DCHECK_EQ(Uint64ConstantFrom(tmp->GetOffset()), data_offset);
Artem Serov2bbc9532016-10-21 11:51:50 +01006485 }
6486 temp = obj;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006487 } else {
6488 __ Add(temp, obj, data_offset);
6489 }
6490 if (maybe_compressed_char_at) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006491 vixl32::Label uncompressed_load, done;
Anton Kirilov6f644202017-02-27 18:29:45 +00006492 vixl32::Label* final_label = codegen_->GetFinalLabel(instruction, &done);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01006493 __ Lsrs(length, length, 1u); // LSRS has a 16-bit encoding, TST (immediate) does not.
6494 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
6495 "Expecting 0=compressed, 1=uncompressed");
Artem Serov517d9f62016-12-12 15:51:15 +00006496 __ B(cs, &uncompressed_load, /* far_target */ false);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006497 __ Ldrb(RegisterFrom(out_loc), MemOperand(temp, RegisterFrom(index), vixl32::LSL, 0));
Anton Kirilov6f644202017-02-27 18:29:45 +00006498 __ B(final_label);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006499 __ Bind(&uncompressed_load);
6500 __ Ldrh(RegisterFrom(out_loc), MemOperand(temp, RegisterFrom(index), vixl32::LSL, 1));
Anton Kirilov6f644202017-02-27 18:29:45 +00006501 if (done.IsReferenced()) {
6502 __ Bind(&done);
6503 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006504 } else {
6505 codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index));
6506 }
6507 }
6508 break;
6509 }
6510
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006511 case DataType::Type::kReference: {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006512 // The read barrier instrumentation of object ArrayGet
6513 // instructions does not support the HIntermediateAddress
6514 // instruction.
6515 DCHECK(!(has_intermediate_address && kEmitCompilerReadBarrier));
6516
Scott Wakelingc34dba72016-10-03 10:14:44 +01006517 static_assert(
6518 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
6519 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
6520 // /* HeapReference<Object> */ out =
6521 // *(obj + data_offset + index * sizeof(HeapReference<Object>))
6522 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006523 // Note that a potential implicit null check is handled in this
6524 // CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier call.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006525 DCHECK(!instruction->CanDoImplicitNullCheckOn(instruction->InputAt(0)));
6526 if (index.IsConstant()) {
6527 // Array load with a constant index can be treated as a field load.
Vladimir Markodcd117e2018-04-19 11:54:00 +01006528 Location maybe_temp =
6529 (locations->GetTempCount() != 0) ? locations->GetTemp(0) : Location();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006530 data_offset += Int32ConstantFrom(index) << DataType::SizeShift(type);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006531 codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction,
6532 out_loc,
6533 obj,
6534 data_offset,
Vladimir Markodcd117e2018-04-19 11:54:00 +01006535 maybe_temp,
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006536 /* needs_null_check */ false);
6537 } else {
Vladimir Markodcd117e2018-04-19 11:54:00 +01006538 Location temp = locations->GetTemp(0);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006539 codegen_->GenerateArrayLoadWithBakerReadBarrier(
6540 instruction, out_loc, obj, data_offset, index, temp, /* needs_null_check */ false);
6541 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006542 } else {
6543 vixl32::Register out = OutputRegister(instruction);
6544 if (index.IsConstant()) {
6545 size_t offset =
Anton Kirilov644032c2016-12-06 17:51:43 +00006546 (Int32ConstantFrom(index) << TIMES_4) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006547 GetAssembler()->LoadFromOffset(kLoadWord, out, obj, offset);
Alexandre Rames374ddf32016-11-04 10:40:49 +00006548 // TODO(VIXL): Here and for other calls to `MaybeRecordImplicitNullCheck` in this method,
6549 // we should use a scope and the assembler to emit the load instruction to guarantee that
6550 // we record the pc at the correct position. But the `Assembler` does not automatically
6551 // handle unencodable offsets. Practically, everything is fine because the helper and
6552 // VIXL, at the time of writing, do generate the store instruction last.
Scott Wakelingc34dba72016-10-03 10:14:44 +01006553 codegen_->MaybeRecordImplicitNullCheck(instruction);
6554 // If read barriers are enabled, emit read barriers other than
6555 // Baker's using a slow path (and also unpoison the loaded
6556 // reference, if heap poisoning is enabled).
6557 codegen_->MaybeGenerateReadBarrierSlow(instruction, out_loc, out_loc, obj_loc, offset);
6558 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006559 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006560 vixl32::Register temp = temps.Acquire();
6561
6562 if (has_intermediate_address) {
Artem Serov2bbc9532016-10-21 11:51:50 +01006563 // We do not need to compute the intermediate address from the array: the
6564 // input instruction has done it already. See the comment in
6565 // `TryExtractArrayAccessAddress()`.
6566 if (kIsDebugBuild) {
6567 HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
Anton Kirilov644032c2016-12-06 17:51:43 +00006568 DCHECK_EQ(Uint64ConstantFrom(tmp->GetOffset()), data_offset);
Artem Serov2bbc9532016-10-21 11:51:50 +01006569 }
6570 temp = obj;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006571 } else {
6572 __ Add(temp, obj, data_offset);
6573 }
6574 codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index));
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006575 temps.Close();
Alexandre Rames374ddf32016-11-04 10:40:49 +00006576 // TODO(VIXL): Use a scope to ensure that we record the pc position immediately after the
6577 // load instruction. Practically, everything is fine because the helper and VIXL, at the
6578 // time of writing, do generate the store instruction last.
Scott Wakelingc34dba72016-10-03 10:14:44 +01006579 codegen_->MaybeRecordImplicitNullCheck(instruction);
6580 // If read barriers are enabled, emit read barriers other than
6581 // Baker's using a slow path (and also unpoison the loaded
6582 // reference, if heap poisoning is enabled).
6583 codegen_->MaybeGenerateReadBarrierSlow(
6584 instruction, out_loc, out_loc, obj_loc, data_offset, index);
6585 }
6586 }
6587 break;
6588 }
6589
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006590 case DataType::Type::kInt64: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006591 if (index.IsConstant()) {
6592 size_t offset =
Anton Kirilov644032c2016-12-06 17:51:43 +00006593 (Int32ConstantFrom(index) << TIMES_8) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006594 GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out_loc), 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_8));
6599 GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out_loc), temp, data_offset);
6600 }
6601 break;
6602 }
6603
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006604 case DataType::Type::kFloat32: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006605 vixl32::SRegister out = SRegisterFrom(out_loc);
6606 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006607 size_t offset = (Int32ConstantFrom(index) << TIMES_4) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006608 GetAssembler()->LoadSFromOffset(out, obj, offset);
6609 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006610 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006611 vixl32::Register temp = temps.Acquire();
6612 __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_4));
6613 GetAssembler()->LoadSFromOffset(out, temp, data_offset);
6614 }
6615 break;
6616 }
6617
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006618 case DataType::Type::kFloat64: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006619 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006620 size_t offset = (Int32ConstantFrom(index) << TIMES_8) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006621 GetAssembler()->LoadDFromOffset(DRegisterFrom(out_loc), obj, offset);
6622 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006623 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006624 vixl32::Register temp = temps.Acquire();
6625 __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
6626 GetAssembler()->LoadDFromOffset(DRegisterFrom(out_loc), temp, data_offset);
6627 }
6628 break;
6629 }
6630
Aart Bik66c158e2018-01-31 12:55:04 -08006631 case DataType::Type::kUint32:
6632 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006633 case DataType::Type::kVoid:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006634 LOG(FATAL) << "Unreachable type " << type;
6635 UNREACHABLE();
6636 }
6637
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006638 if (type == DataType::Type::kReference) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006639 // Potential implicit null checks, in the case of reference
6640 // arrays, are handled in the previous switch statement.
6641 } else if (!maybe_compressed_char_at) {
Alexandre Rames374ddf32016-11-04 10:40:49 +00006642 // TODO(VIXL): Use a scope to ensure we record the pc info immediately after
6643 // the preceding load instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01006644 codegen_->MaybeRecordImplicitNullCheck(instruction);
6645 }
6646}
6647
6648void LocationsBuilderARMVIXL::VisitArraySet(HArraySet* instruction) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006649 DataType::Type value_type = instruction->GetComponentType();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006650
6651 bool needs_write_barrier =
6652 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
6653 bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck();
6654
Vladimir Markoca6fff82017-10-03 14:49:14 +01006655 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
Scott Wakelingc34dba72016-10-03 10:14:44 +01006656 instruction,
6657 may_need_runtime_call_for_type_check ?
6658 LocationSummary::kCallOnSlowPath :
6659 LocationSummary::kNoCall);
6660
6661 locations->SetInAt(0, Location::RequiresRegister());
6662 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006663 if (DataType::IsFloatingPointType(value_type)) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006664 locations->SetInAt(2, Location::RequiresFpuRegister());
6665 } else {
6666 locations->SetInAt(2, Location::RequiresRegister());
6667 }
6668 if (needs_write_barrier) {
6669 // Temporary registers for the write barrier.
6670 locations->AddTemp(Location::RequiresRegister()); // Possibly used for ref. poisoning too.
6671 locations->AddTemp(Location::RequiresRegister());
6672 }
6673}
6674
6675void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006676 LocationSummary* locations = instruction->GetLocations();
6677 vixl32::Register array = InputRegisterAt(instruction, 0);
6678 Location index = locations->InAt(1);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006679 DataType::Type value_type = instruction->GetComponentType();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006680 bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck();
6681 bool needs_write_barrier =
6682 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
6683 uint32_t data_offset =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006684 mirror::Array::DataOffset(DataType::Size(value_type)).Uint32Value();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006685 Location value_loc = locations->InAt(2);
6686 HInstruction* array_instr = instruction->GetArray();
6687 bool has_intermediate_address = array_instr->IsIntermediateAddress();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006688
6689 switch (value_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006690 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006691 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006692 case DataType::Type::kInt8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006693 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006694 case DataType::Type::kInt16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006695 case DataType::Type::kInt32: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006696 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006697 int32_t const_index = Int32ConstantFrom(index);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006698 uint32_t full_offset =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006699 data_offset + (const_index << DataType::SizeShift(value_type));
Scott Wakelingc34dba72016-10-03 10:14:44 +01006700 StoreOperandType store_type = GetStoreOperandType(value_type);
6701 GetAssembler()->StoreToOffset(store_type, RegisterFrom(value_loc), array, full_offset);
6702 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006703 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006704 vixl32::Register temp = temps.Acquire();
6705
6706 if (has_intermediate_address) {
Artem Serov2bbc9532016-10-21 11:51:50 +01006707 // We do not need to compute the intermediate address from the array: the
6708 // input instruction has done it already. See the comment in
6709 // `TryExtractArrayAccessAddress()`.
6710 if (kIsDebugBuild) {
6711 HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
Anton Kirilov644032c2016-12-06 17:51:43 +00006712 DCHECK_EQ(Uint64ConstantFrom(tmp->GetOffset()), data_offset);
Artem Serov2bbc9532016-10-21 11:51:50 +01006713 }
6714 temp = array;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006715 } else {
6716 __ Add(temp, array, data_offset);
6717 }
6718 codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
6719 }
6720 break;
6721 }
6722
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006723 case DataType::Type::kReference: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006724 vixl32::Register value = RegisterFrom(value_loc);
6725 // TryExtractArrayAccessAddress optimization is never applied for non-primitive ArraySet.
6726 // See the comment in instruction_simplifier_shared.cc.
6727 DCHECK(!has_intermediate_address);
6728
6729 if (instruction->InputAt(2)->IsNullConstant()) {
6730 // Just setting null.
6731 if (index.IsConstant()) {
6732 size_t offset =
Anton Kirilov644032c2016-12-06 17:51:43 +00006733 (Int32ConstantFrom(index) << TIMES_4) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006734 GetAssembler()->StoreToOffset(kStoreWord, value, array, offset);
6735 } else {
6736 DCHECK(index.IsRegister()) << index;
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006737 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006738 vixl32::Register temp = temps.Acquire();
6739 __ Add(temp, array, data_offset);
6740 codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
6741 }
Alexandre Rames374ddf32016-11-04 10:40:49 +00006742 // TODO(VIXL): Use a scope to ensure we record the pc info immediately after the preceding
6743 // store instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01006744 codegen_->MaybeRecordImplicitNullCheck(instruction);
6745 DCHECK(!needs_write_barrier);
6746 DCHECK(!may_need_runtime_call_for_type_check);
6747 break;
6748 }
6749
6750 DCHECK(needs_write_barrier);
6751 Location temp1_loc = locations->GetTemp(0);
6752 vixl32::Register temp1 = RegisterFrom(temp1_loc);
6753 Location temp2_loc = locations->GetTemp(1);
6754 vixl32::Register temp2 = RegisterFrom(temp2_loc);
6755 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
6756 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
6757 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
6758 vixl32::Label done;
Anton Kirilov6f644202017-02-27 18:29:45 +00006759 vixl32::Label* final_label = codegen_->GetFinalLabel(instruction, &done);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006760 SlowPathCodeARMVIXL* slow_path = nullptr;
6761
6762 if (may_need_runtime_call_for_type_check) {
Vladimir Marko174b2e22017-10-12 13:34:49 +01006763 slow_path = new (codegen_->GetScopedAllocator()) ArraySetSlowPathARMVIXL(instruction);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006764 codegen_->AddSlowPath(slow_path);
6765 if (instruction->GetValueCanBeNull()) {
6766 vixl32::Label non_zero;
xueliang.zhongf51bc622016-11-04 09:23:32 +00006767 __ CompareAndBranchIfNonZero(value, &non_zero);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006768 if (index.IsConstant()) {
6769 size_t offset =
Anton Kirilov644032c2016-12-06 17:51:43 +00006770 (Int32ConstantFrom(index) << TIMES_4) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006771 GetAssembler()->StoreToOffset(kStoreWord, value, array, offset);
6772 } else {
6773 DCHECK(index.IsRegister()) << index;
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006774 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006775 vixl32::Register temp = temps.Acquire();
6776 __ Add(temp, array, data_offset);
6777 codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
6778 }
Alexandre Rames374ddf32016-11-04 10:40:49 +00006779 // TODO(VIXL): Use a scope to ensure we record the pc info immediately after the preceding
6780 // store instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01006781 codegen_->MaybeRecordImplicitNullCheck(instruction);
Anton Kirilov6f644202017-02-27 18:29:45 +00006782 __ B(final_label);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006783 __ Bind(&non_zero);
6784 }
6785
6786 // Note that when read barriers are enabled, the type checks
6787 // are performed without read barriers. This is fine, even in
6788 // the case where a class object is in the from-space after
6789 // the flip, as a comparison involving such a type would not
6790 // produce a false positive; it may of course produce a false
6791 // negative, in which case we would take the ArraySet slow
6792 // path.
6793
Alexandre Rames374ddf32016-11-04 10:40:49 +00006794 {
6795 // Ensure we record the pc position immediately after the `ldr` instruction.
Artem Serov0fb37192016-12-06 18:13:40 +00006796 ExactAssemblyScope aas(GetVIXLAssembler(),
6797 vixl32::kMaxInstructionSizeInBytes,
6798 CodeBufferCheckScope::kMaximumSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00006799 // /* HeapReference<Class> */ temp1 = array->klass_
6800 __ ldr(temp1, MemOperand(array, class_offset));
6801 codegen_->MaybeRecordImplicitNullCheck(instruction);
6802 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006803 GetAssembler()->MaybeUnpoisonHeapReference(temp1);
6804
6805 // /* HeapReference<Class> */ temp1 = temp1->component_type_
6806 GetAssembler()->LoadFromOffset(kLoadWord, temp1, temp1, component_offset);
6807 // /* HeapReference<Class> */ temp2 = value->klass_
6808 GetAssembler()->LoadFromOffset(kLoadWord, temp2, value, class_offset);
6809 // If heap poisoning is enabled, no need to unpoison `temp1`
6810 // nor `temp2`, as we are comparing two poisoned references.
6811 __ Cmp(temp1, temp2);
6812
6813 if (instruction->StaticTypeOfArrayIsObjectArray()) {
6814 vixl32::Label do_put;
Artem Serov517d9f62016-12-12 15:51:15 +00006815 __ B(eq, &do_put, /* far_target */ false);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006816 // If heap poisoning is enabled, the `temp1` reference has
6817 // not been unpoisoned yet; unpoison it now.
6818 GetAssembler()->MaybeUnpoisonHeapReference(temp1);
6819
6820 // /* HeapReference<Class> */ temp1 = temp1->super_class_
6821 GetAssembler()->LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
6822 // If heap poisoning is enabled, no need to unpoison
6823 // `temp1`, as we are comparing against null below.
xueliang.zhongf51bc622016-11-04 09:23:32 +00006824 __ CompareAndBranchIfNonZero(temp1, slow_path->GetEntryLabel());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006825 __ Bind(&do_put);
6826 } else {
6827 __ B(ne, slow_path->GetEntryLabel());
6828 }
6829 }
6830
6831 vixl32::Register source = value;
6832 if (kPoisonHeapReferences) {
6833 // Note that in the case where `value` is a null reference,
6834 // we do not enter this block, as a null reference does not
6835 // need poisoning.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006836 DCHECK_EQ(value_type, DataType::Type::kReference);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006837 __ Mov(temp1, value);
6838 GetAssembler()->PoisonHeapReference(temp1);
6839 source = temp1;
6840 }
6841
6842 if (index.IsConstant()) {
6843 size_t offset =
Anton Kirilov644032c2016-12-06 17:51:43 +00006844 (Int32ConstantFrom(index) << TIMES_4) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006845 GetAssembler()->StoreToOffset(kStoreWord, source, array, offset);
6846 } else {
6847 DCHECK(index.IsRegister()) << index;
6848
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006849 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006850 vixl32::Register temp = temps.Acquire();
6851 __ Add(temp, array, data_offset);
6852 codegen_->StoreToShiftedRegOffset(value_type,
6853 LocationFrom(source),
6854 temp,
6855 RegisterFrom(index));
6856 }
6857
6858 if (!may_need_runtime_call_for_type_check) {
Alexandre Rames374ddf32016-11-04 10:40:49 +00006859 // TODO(VIXL): Ensure we record the pc position immediately after the preceding store
6860 // instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01006861 codegen_->MaybeRecordImplicitNullCheck(instruction);
6862 }
6863
6864 codegen_->MarkGCCard(temp1, temp2, array, value, instruction->GetValueCanBeNull());
6865
6866 if (done.IsReferenced()) {
6867 __ Bind(&done);
6868 }
6869
6870 if (slow_path != nullptr) {
6871 __ Bind(slow_path->GetExitLabel());
6872 }
6873
6874 break;
6875 }
6876
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006877 case DataType::Type::kInt64: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006878 Location value = locations->InAt(2);
6879 if (index.IsConstant()) {
6880 size_t offset =
Anton Kirilov644032c2016-12-06 17:51:43 +00006881 (Int32ConstantFrom(index) << TIMES_8) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006882 GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), array, offset);
6883 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006884 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006885 vixl32::Register temp = temps.Acquire();
6886 __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
6887 GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), temp, data_offset);
6888 }
6889 break;
6890 }
6891
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006892 case DataType::Type::kFloat32: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006893 Location value = locations->InAt(2);
6894 DCHECK(value.IsFpuRegister());
6895 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006896 size_t offset = (Int32ConstantFrom(index) << TIMES_4) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006897 GetAssembler()->StoreSToOffset(SRegisterFrom(value), array, offset);
6898 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006899 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006900 vixl32::Register temp = temps.Acquire();
6901 __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_4));
6902 GetAssembler()->StoreSToOffset(SRegisterFrom(value), temp, data_offset);
6903 }
6904 break;
6905 }
6906
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006907 case DataType::Type::kFloat64: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006908 Location value = locations->InAt(2);
6909 DCHECK(value.IsFpuRegisterPair());
6910 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006911 size_t offset = (Int32ConstantFrom(index) << TIMES_8) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006912 GetAssembler()->StoreDToOffset(DRegisterFrom(value), array, offset);
6913 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006914 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006915 vixl32::Register temp = temps.Acquire();
6916 __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
6917 GetAssembler()->StoreDToOffset(DRegisterFrom(value), temp, data_offset);
6918 }
6919 break;
6920 }
6921
Aart Bik66c158e2018-01-31 12:55:04 -08006922 case DataType::Type::kUint32:
6923 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006924 case DataType::Type::kVoid:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006925 LOG(FATAL) << "Unreachable type " << value_type;
6926 UNREACHABLE();
6927 }
6928
6929 // Objects are handled in the switch.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006930 if (value_type != DataType::Type::kReference) {
Alexandre Rames374ddf32016-11-04 10:40:49 +00006931 // TODO(VIXL): Ensure we record the pc position immediately after the preceding store
6932 // instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01006933 codegen_->MaybeRecordImplicitNullCheck(instruction);
6934 }
6935}
6936
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006937void LocationsBuilderARMVIXL::VisitArrayLength(HArrayLength* instruction) {
6938 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01006939 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006940 locations->SetInAt(0, Location::RequiresRegister());
6941 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
6942}
6943
6944void InstructionCodeGeneratorARMVIXL::VisitArrayLength(HArrayLength* instruction) {
6945 uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction);
6946 vixl32::Register obj = InputRegisterAt(instruction, 0);
6947 vixl32::Register out = OutputRegister(instruction);
Alexandre Rames374ddf32016-11-04 10:40:49 +00006948 {
Artem Serov0fb37192016-12-06 18:13:40 +00006949 ExactAssemblyScope aas(GetVIXLAssembler(),
6950 vixl32::kMaxInstructionSizeInBytes,
6951 CodeBufferCheckScope::kMaximumSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00006952 __ ldr(out, MemOperand(obj, offset));
6953 codegen_->MaybeRecordImplicitNullCheck(instruction);
6954 }
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006955 // Mask out compression flag from String's array length.
6956 if (mirror::kUseStringCompression && instruction->IsStringLength()) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01006957 __ Lsr(out, out, 1u);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006958 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006959}
6960
Artem Serov2bbc9532016-10-21 11:51:50 +01006961void LocationsBuilderARMVIXL::VisitIntermediateAddress(HIntermediateAddress* instruction) {
Artem Serov2bbc9532016-10-21 11:51:50 +01006962 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01006963 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Artem Serov2bbc9532016-10-21 11:51:50 +01006964
6965 locations->SetInAt(0, Location::RequiresRegister());
6966 locations->SetInAt(1, Location::RegisterOrConstant(instruction->GetOffset()));
6967 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
6968}
6969
6970void InstructionCodeGeneratorARMVIXL::VisitIntermediateAddress(HIntermediateAddress* instruction) {
6971 vixl32::Register out = OutputRegister(instruction);
6972 vixl32::Register first = InputRegisterAt(instruction, 0);
6973 Location second = instruction->GetLocations()->InAt(1);
6974
Artem Serov2bbc9532016-10-21 11:51:50 +01006975 if (second.IsRegister()) {
6976 __ Add(out, first, RegisterFrom(second));
6977 } else {
Anton Kirilov644032c2016-12-06 17:51:43 +00006978 __ Add(out, first, Int32ConstantFrom(second));
Artem Serov2bbc9532016-10-21 11:51:50 +01006979 }
6980}
6981
Artem Serove1811ed2017-04-27 16:50:47 +01006982void LocationsBuilderARMVIXL::VisitIntermediateAddressIndex(
6983 HIntermediateAddressIndex* instruction) {
6984 LOG(FATAL) << "Unreachable " << instruction->GetId();
6985}
6986
6987void InstructionCodeGeneratorARMVIXL::VisitIntermediateAddressIndex(
6988 HIntermediateAddressIndex* instruction) {
6989 LOG(FATAL) << "Unreachable " << instruction->GetId();
6990}
6991
Scott Wakelingc34dba72016-10-03 10:14:44 +01006992void LocationsBuilderARMVIXL::VisitBoundsCheck(HBoundsCheck* instruction) {
6993 RegisterSet caller_saves = RegisterSet::Empty();
6994 InvokeRuntimeCallingConventionARMVIXL calling_convention;
6995 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0)));
6996 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(1)));
6997 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction, caller_saves);
Artem Serov2dd053d2017-03-08 14:54:06 +00006998
6999 HInstruction* index = instruction->InputAt(0);
7000 HInstruction* length = instruction->InputAt(1);
7001 // If both index and length are constants we can statically check the bounds. But if at least one
7002 // of them is not encodable ArmEncodableConstantOrRegister will create
7003 // Location::RequiresRegister() which is not desired to happen. Instead we create constant
7004 // locations.
7005 bool both_const = index->IsConstant() && length->IsConstant();
7006 locations->SetInAt(0, both_const
7007 ? Location::ConstantLocation(index->AsConstant())
7008 : ArmEncodableConstantOrRegister(index, CMP));
7009 locations->SetInAt(1, both_const
7010 ? Location::ConstantLocation(length->AsConstant())
7011 : ArmEncodableConstantOrRegister(length, CMP));
Scott Wakelingc34dba72016-10-03 10:14:44 +01007012}
7013
7014void InstructionCodeGeneratorARMVIXL::VisitBoundsCheck(HBoundsCheck* instruction) {
Artem Serov2dd053d2017-03-08 14:54:06 +00007015 LocationSummary* locations = instruction->GetLocations();
7016 Location index_loc = locations->InAt(0);
7017 Location length_loc = locations->InAt(1);
Scott Wakelingc34dba72016-10-03 10:14:44 +01007018
Artem Serov2dd053d2017-03-08 14:54:06 +00007019 if (length_loc.IsConstant()) {
7020 int32_t length = Int32ConstantFrom(length_loc);
7021 if (index_loc.IsConstant()) {
7022 // BCE will remove the bounds check if we are guaranteed to pass.
7023 int32_t index = Int32ConstantFrom(index_loc);
7024 if (index < 0 || index >= length) {
7025 SlowPathCodeARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01007026 new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathARMVIXL(instruction);
Artem Serov2dd053d2017-03-08 14:54:06 +00007027 codegen_->AddSlowPath(slow_path);
7028 __ B(slow_path->GetEntryLabel());
7029 } else {
7030 // Some optimization after BCE may have generated this, and we should not
7031 // generate a bounds check if it is a valid range.
7032 }
7033 return;
7034 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01007035
Artem Serov2dd053d2017-03-08 14:54:06 +00007036 SlowPathCodeARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01007037 new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathARMVIXL(instruction);
Artem Serov2dd053d2017-03-08 14:54:06 +00007038 __ Cmp(RegisterFrom(index_loc), length);
7039 codegen_->AddSlowPath(slow_path);
7040 __ B(hs, slow_path->GetEntryLabel());
7041 } else {
7042 SlowPathCodeARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01007043 new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathARMVIXL(instruction);
Artem Serov2dd053d2017-03-08 14:54:06 +00007044 __ Cmp(RegisterFrom(length_loc), InputOperandAt(instruction, 0));
7045 codegen_->AddSlowPath(slow_path);
7046 __ B(ls, slow_path->GetEntryLabel());
7047 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01007048}
7049
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007050void CodeGeneratorARMVIXL::MarkGCCard(vixl32::Register temp,
7051 vixl32::Register card,
7052 vixl32::Register object,
7053 vixl32::Register value,
7054 bool can_be_null) {
7055 vixl32::Label is_null;
7056 if (can_be_null) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00007057 __ CompareAndBranchIfZero(value, &is_null);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007058 }
7059 GetAssembler()->LoadFromOffset(
7060 kLoadWord, card, tr, Thread::CardTableOffset<kArmPointerSize>().Int32Value());
Scott Wakelingb77051e2016-11-21 19:46:00 +00007061 __ Lsr(temp, object, Operand::From(gc::accounting::CardTable::kCardShift));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007062 __ Strb(card, MemOperand(card, temp));
7063 if (can_be_null) {
7064 __ Bind(&is_null);
7065 }
7066}
7067
Scott Wakelingfe885462016-09-22 10:24:38 +01007068void LocationsBuilderARMVIXL::VisitParallelMove(HParallelMove* instruction ATTRIBUTE_UNUSED) {
7069 LOG(FATAL) << "Unreachable";
7070}
7071
7072void InstructionCodeGeneratorARMVIXL::VisitParallelMove(HParallelMove* instruction) {
Vladimir Markobea75ff2017-10-11 20:39:54 +01007073 if (instruction->GetNext()->IsSuspendCheck() &&
7074 instruction->GetBlock()->GetLoopInformation() != nullptr) {
7075 HSuspendCheck* suspend_check = instruction->GetNext()->AsSuspendCheck();
7076 // The back edge will generate the suspend check.
7077 codegen_->ClearSpillSlotsFromLoopPhisInStackMap(suspend_check, instruction);
7078 }
7079
Scott Wakelingfe885462016-09-22 10:24:38 +01007080 codegen_->GetMoveResolver()->EmitNativeCode(instruction);
7081}
7082
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007083void LocationsBuilderARMVIXL::VisitSuspendCheck(HSuspendCheck* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01007084 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
7085 instruction, LocationSummary::kCallOnSlowPath);
Artem Serov657022c2016-11-23 14:19:38 +00007086 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007087}
7088
7089void InstructionCodeGeneratorARMVIXL::VisitSuspendCheck(HSuspendCheck* instruction) {
7090 HBasicBlock* block = instruction->GetBlock();
7091 if (block->GetLoopInformation() != nullptr) {
7092 DCHECK(block->GetLoopInformation()->GetSuspendCheck() == instruction);
7093 // The back edge will generate the suspend check.
7094 return;
7095 }
7096 if (block->IsEntryBlock() && instruction->GetNext()->IsGoto()) {
7097 // The goto will generate the suspend check.
7098 return;
7099 }
7100 GenerateSuspendCheck(instruction, nullptr);
Orion Hodson4c8e12e2018-05-18 08:33:20 +01007101 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 13);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007102}
7103
7104void InstructionCodeGeneratorARMVIXL::GenerateSuspendCheck(HSuspendCheck* instruction,
7105 HBasicBlock* successor) {
7106 SuspendCheckSlowPathARMVIXL* slow_path =
7107 down_cast<SuspendCheckSlowPathARMVIXL*>(instruction->GetSlowPath());
7108 if (slow_path == nullptr) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01007109 slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01007110 new (codegen_->GetScopedAllocator()) SuspendCheckSlowPathARMVIXL(instruction, successor);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007111 instruction->SetSlowPath(slow_path);
7112 codegen_->AddSlowPath(slow_path);
7113 if (successor != nullptr) {
7114 DCHECK(successor->IsLoopHeader());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007115 }
7116 } else {
7117 DCHECK_EQ(slow_path->GetSuccessor(), successor);
7118 }
7119
Anton Kirilovedb2ac32016-11-30 15:14:10 +00007120 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007121 vixl32::Register temp = temps.Acquire();
7122 GetAssembler()->LoadFromOffset(
7123 kLoadUnsignedHalfword, temp, tr, Thread::ThreadFlagsOffset<kArmPointerSize>().Int32Value());
7124 if (successor == nullptr) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00007125 __ CompareAndBranchIfNonZero(temp, slow_path->GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007126 __ Bind(slow_path->GetReturnLabel());
7127 } else {
xueliang.zhongf51bc622016-11-04 09:23:32 +00007128 __ CompareAndBranchIfZero(temp, codegen_->GetLabelOf(successor));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007129 __ B(slow_path->GetEntryLabel());
7130 }
7131}
7132
Scott Wakelingfe885462016-09-22 10:24:38 +01007133ArmVIXLAssembler* ParallelMoveResolverARMVIXL::GetAssembler() const {
7134 return codegen_->GetAssembler();
7135}
7136
7137void ParallelMoveResolverARMVIXL::EmitMove(size_t index) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007138 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
Scott Wakelingfe885462016-09-22 10:24:38 +01007139 MoveOperands* move = moves_[index];
7140 Location source = move->GetSource();
7141 Location destination = move->GetDestination();
7142
7143 if (source.IsRegister()) {
7144 if (destination.IsRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007145 __ Mov(RegisterFrom(destination), RegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01007146 } else if (destination.IsFpuRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007147 __ Vmov(SRegisterFrom(destination), RegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01007148 } else {
7149 DCHECK(destination.IsStackSlot());
7150 GetAssembler()->StoreToOffset(kStoreWord,
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007151 RegisterFrom(source),
Scott Wakelingfe885462016-09-22 10:24:38 +01007152 sp,
7153 destination.GetStackIndex());
7154 }
7155 } else if (source.IsStackSlot()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007156 if (destination.IsRegister()) {
7157 GetAssembler()->LoadFromOffset(kLoadWord,
7158 RegisterFrom(destination),
7159 sp,
7160 source.GetStackIndex());
7161 } else if (destination.IsFpuRegister()) {
7162 GetAssembler()->LoadSFromOffset(SRegisterFrom(destination), sp, source.GetStackIndex());
7163 } else {
7164 DCHECK(destination.IsStackSlot());
7165 vixl32::Register temp = temps.Acquire();
7166 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, source.GetStackIndex());
7167 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
7168 }
Scott Wakelingfe885462016-09-22 10:24:38 +01007169 } else if (source.IsFpuRegister()) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01007170 if (destination.IsRegister()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01007171 __ Vmov(RegisterFrom(destination), SRegisterFrom(source));
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01007172 } else if (destination.IsFpuRegister()) {
7173 __ Vmov(SRegisterFrom(destination), SRegisterFrom(source));
7174 } else {
7175 DCHECK(destination.IsStackSlot());
7176 GetAssembler()->StoreSToOffset(SRegisterFrom(source), sp, destination.GetStackIndex());
7177 }
Scott Wakelingfe885462016-09-22 10:24:38 +01007178 } else if (source.IsDoubleStackSlot()) {
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007179 if (destination.IsDoubleStackSlot()) {
7180 vixl32::DRegister temp = temps.AcquireD();
7181 GetAssembler()->LoadDFromOffset(temp, sp, source.GetStackIndex());
7182 GetAssembler()->StoreDToOffset(temp, sp, destination.GetStackIndex());
7183 } else if (destination.IsRegisterPair()) {
7184 DCHECK(ExpectedPairLayout(destination));
7185 GetAssembler()->LoadFromOffset(
7186 kLoadWordPair, LowRegisterFrom(destination), sp, source.GetStackIndex());
7187 } else {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01007188 DCHECK(destination.IsFpuRegisterPair()) << destination;
7189 GetAssembler()->LoadDFromOffset(DRegisterFrom(destination), sp, source.GetStackIndex());
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007190 }
Scott Wakelingfe885462016-09-22 10:24:38 +01007191 } else if (source.IsRegisterPair()) {
7192 if (destination.IsRegisterPair()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007193 __ Mov(LowRegisterFrom(destination), LowRegisterFrom(source));
7194 __ Mov(HighRegisterFrom(destination), HighRegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01007195 } else if (destination.IsFpuRegisterPair()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01007196 __ Vmov(DRegisterFrom(destination), LowRegisterFrom(source), HighRegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01007197 } else {
7198 DCHECK(destination.IsDoubleStackSlot()) << destination;
7199 DCHECK(ExpectedPairLayout(source));
7200 GetAssembler()->StoreToOffset(kStoreWordPair,
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007201 LowRegisterFrom(source),
Scott Wakelingfe885462016-09-22 10:24:38 +01007202 sp,
7203 destination.GetStackIndex());
7204 }
7205 } else if (source.IsFpuRegisterPair()) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01007206 if (destination.IsRegisterPair()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01007207 __ Vmov(LowRegisterFrom(destination), HighRegisterFrom(destination), DRegisterFrom(source));
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01007208 } else if (destination.IsFpuRegisterPair()) {
7209 __ Vmov(DRegisterFrom(destination), DRegisterFrom(source));
7210 } else {
7211 DCHECK(destination.IsDoubleStackSlot()) << destination;
7212 GetAssembler()->StoreDToOffset(DRegisterFrom(source), sp, destination.GetStackIndex());
7213 }
Scott Wakelingfe885462016-09-22 10:24:38 +01007214 } else {
7215 DCHECK(source.IsConstant()) << source;
7216 HConstant* constant = source.GetConstant();
7217 if (constant->IsIntConstant() || constant->IsNullConstant()) {
7218 int32_t value = CodeGenerator::GetInt32ValueOf(constant);
7219 if (destination.IsRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007220 __ Mov(RegisterFrom(destination), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01007221 } else {
7222 DCHECK(destination.IsStackSlot());
Scott Wakelingfe885462016-09-22 10:24:38 +01007223 vixl32::Register temp = temps.Acquire();
7224 __ Mov(temp, value);
7225 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
7226 }
7227 } else if (constant->IsLongConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00007228 int64_t value = Int64ConstantFrom(source);
Scott Wakelingfe885462016-09-22 10:24:38 +01007229 if (destination.IsRegisterPair()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007230 __ Mov(LowRegisterFrom(destination), Low32Bits(value));
7231 __ Mov(HighRegisterFrom(destination), High32Bits(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01007232 } else {
7233 DCHECK(destination.IsDoubleStackSlot()) << destination;
Scott Wakelingfe885462016-09-22 10:24:38 +01007234 vixl32::Register temp = temps.Acquire();
7235 __ Mov(temp, Low32Bits(value));
7236 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
7237 __ Mov(temp, High32Bits(value));
7238 GetAssembler()->StoreToOffset(kStoreWord,
7239 temp,
7240 sp,
7241 destination.GetHighStackIndex(kArmWordSize));
7242 }
7243 } else if (constant->IsDoubleConstant()) {
7244 double value = constant->AsDoubleConstant()->GetValue();
7245 if (destination.IsFpuRegisterPair()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01007246 __ Vmov(DRegisterFrom(destination), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01007247 } else {
7248 DCHECK(destination.IsDoubleStackSlot()) << destination;
7249 uint64_t int_value = bit_cast<uint64_t, double>(value);
Scott Wakelingfe885462016-09-22 10:24:38 +01007250 vixl32::Register temp = temps.Acquire();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007251 __ Mov(temp, Low32Bits(int_value));
Scott Wakelingfe885462016-09-22 10:24:38 +01007252 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007253 __ Mov(temp, High32Bits(int_value));
Scott Wakelingfe885462016-09-22 10:24:38 +01007254 GetAssembler()->StoreToOffset(kStoreWord,
7255 temp,
7256 sp,
7257 destination.GetHighStackIndex(kArmWordSize));
7258 }
7259 } else {
7260 DCHECK(constant->IsFloatConstant()) << constant->DebugName();
7261 float value = constant->AsFloatConstant()->GetValue();
7262 if (destination.IsFpuRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007263 __ Vmov(SRegisterFrom(destination), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01007264 } else {
7265 DCHECK(destination.IsStackSlot());
Scott Wakelingfe885462016-09-22 10:24:38 +01007266 vixl32::Register temp = temps.Acquire();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007267 __ Mov(temp, bit_cast<int32_t, float>(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01007268 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
7269 }
7270 }
7271 }
7272}
7273
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007274void ParallelMoveResolverARMVIXL::Exchange(vixl32::Register reg, int mem) {
7275 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
7276 vixl32::Register temp = temps.Acquire();
7277 __ Mov(temp, reg);
7278 GetAssembler()->LoadFromOffset(kLoadWord, reg, sp, mem);
7279 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, mem);
Scott Wakelingfe885462016-09-22 10:24:38 +01007280}
7281
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007282void ParallelMoveResolverARMVIXL::Exchange(int mem1, int mem2) {
7283 // TODO(VIXL32): Double check the performance of this implementation.
7284 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
Nicolas Geoffray13a797b2017-03-15 16:41:31 +00007285 vixl32::Register temp1 = temps.Acquire();
7286 ScratchRegisterScope ensure_scratch(
7287 this, temp1.GetCode(), r0.GetCode(), codegen_->GetNumberOfCoreRegisters());
7288 vixl32::Register temp2(ensure_scratch.GetRegister());
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007289
Nicolas Geoffray13a797b2017-03-15 16:41:31 +00007290 int stack_offset = ensure_scratch.IsSpilled() ? kArmWordSize : 0;
7291 GetAssembler()->LoadFromOffset(kLoadWord, temp1, sp, mem1 + stack_offset);
7292 GetAssembler()->LoadFromOffset(kLoadWord, temp2, sp, mem2 + stack_offset);
7293 GetAssembler()->StoreToOffset(kStoreWord, temp1, sp, mem2 + stack_offset);
7294 GetAssembler()->StoreToOffset(kStoreWord, temp2, sp, mem1 + stack_offset);
Scott Wakelingfe885462016-09-22 10:24:38 +01007295}
7296
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007297void ParallelMoveResolverARMVIXL::EmitSwap(size_t index) {
7298 MoveOperands* move = moves_[index];
7299 Location source = move->GetSource();
7300 Location destination = move->GetDestination();
7301 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
7302
7303 if (source.IsRegister() && destination.IsRegister()) {
7304 vixl32::Register temp = temps.Acquire();
7305 DCHECK(!RegisterFrom(source).Is(temp));
7306 DCHECK(!RegisterFrom(destination).Is(temp));
7307 __ Mov(temp, RegisterFrom(destination));
7308 __ Mov(RegisterFrom(destination), RegisterFrom(source));
7309 __ Mov(RegisterFrom(source), temp);
7310 } else if (source.IsRegister() && destination.IsStackSlot()) {
7311 Exchange(RegisterFrom(source), destination.GetStackIndex());
7312 } else if (source.IsStackSlot() && destination.IsRegister()) {
7313 Exchange(RegisterFrom(destination), source.GetStackIndex());
7314 } else if (source.IsStackSlot() && destination.IsStackSlot()) {
Anton Kirilovdda43962016-11-21 19:55:20 +00007315 Exchange(source.GetStackIndex(), destination.GetStackIndex());
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007316 } else if (source.IsFpuRegister() && destination.IsFpuRegister()) {
Nicolas Geoffray13a797b2017-03-15 16:41:31 +00007317 vixl32::Register temp = temps.Acquire();
Anton Kirilovdda43962016-11-21 19:55:20 +00007318 __ Vmov(temp, SRegisterFrom(source));
7319 __ Vmov(SRegisterFrom(source), SRegisterFrom(destination));
7320 __ Vmov(SRegisterFrom(destination), temp);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007321 } else if (source.IsRegisterPair() && destination.IsRegisterPair()) {
7322 vixl32::DRegister temp = temps.AcquireD();
7323 __ Vmov(temp, LowRegisterFrom(source), HighRegisterFrom(source));
7324 __ Mov(LowRegisterFrom(source), LowRegisterFrom(destination));
7325 __ Mov(HighRegisterFrom(source), HighRegisterFrom(destination));
7326 __ Vmov(LowRegisterFrom(destination), HighRegisterFrom(destination), temp);
7327 } else if (source.IsRegisterPair() || destination.IsRegisterPair()) {
7328 vixl32::Register low_reg = LowRegisterFrom(source.IsRegisterPair() ? source : destination);
7329 int mem = source.IsRegisterPair() ? destination.GetStackIndex() : source.GetStackIndex();
7330 DCHECK(ExpectedPairLayout(source.IsRegisterPair() ? source : destination));
7331 vixl32::DRegister temp = temps.AcquireD();
7332 __ Vmov(temp, low_reg, vixl32::Register(low_reg.GetCode() + 1));
7333 GetAssembler()->LoadFromOffset(kLoadWordPair, low_reg, sp, mem);
7334 GetAssembler()->StoreDToOffset(temp, sp, mem);
7335 } else if (source.IsFpuRegisterPair() && destination.IsFpuRegisterPair()) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007336 vixl32::DRegister first = DRegisterFrom(source);
7337 vixl32::DRegister second = DRegisterFrom(destination);
7338 vixl32::DRegister temp = temps.AcquireD();
7339 __ Vmov(temp, first);
7340 __ Vmov(first, second);
7341 __ Vmov(second, temp);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007342 } else if (source.IsFpuRegisterPair() || destination.IsFpuRegisterPair()) {
Anton Kirilovdda43962016-11-21 19:55:20 +00007343 vixl32::DRegister reg = source.IsFpuRegisterPair()
7344 ? DRegisterFrom(source)
7345 : DRegisterFrom(destination);
7346 int mem = source.IsFpuRegisterPair()
7347 ? destination.GetStackIndex()
7348 : source.GetStackIndex();
7349 vixl32::DRegister temp = temps.AcquireD();
7350 __ Vmov(temp, reg);
7351 GetAssembler()->LoadDFromOffset(reg, sp, mem);
7352 GetAssembler()->StoreDToOffset(temp, sp, mem);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007353 } else if (source.IsFpuRegister() || destination.IsFpuRegister()) {
Anton Kirilovdda43962016-11-21 19:55:20 +00007354 vixl32::SRegister reg = source.IsFpuRegister()
7355 ? SRegisterFrom(source)
7356 : SRegisterFrom(destination);
7357 int mem = source.IsFpuRegister()
7358 ? destination.GetStackIndex()
7359 : source.GetStackIndex();
7360 vixl32::Register temp = temps.Acquire();
7361 __ Vmov(temp, reg);
7362 GetAssembler()->LoadSFromOffset(reg, sp, mem);
7363 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, mem);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007364 } else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) {
7365 vixl32::DRegister temp1 = temps.AcquireD();
7366 vixl32::DRegister temp2 = temps.AcquireD();
7367 __ Vldr(temp1, MemOperand(sp, source.GetStackIndex()));
7368 __ Vldr(temp2, MemOperand(sp, destination.GetStackIndex()));
7369 __ Vstr(temp1, MemOperand(sp, destination.GetStackIndex()));
7370 __ Vstr(temp2, MemOperand(sp, source.GetStackIndex()));
7371 } else {
7372 LOG(FATAL) << "Unimplemented" << source << " <-> " << destination;
7373 }
Scott Wakelingfe885462016-09-22 10:24:38 +01007374}
7375
Nicolas Geoffray13a797b2017-03-15 16:41:31 +00007376void ParallelMoveResolverARMVIXL::SpillScratch(int reg) {
7377 __ Push(vixl32::Register(reg));
Scott Wakelingfe885462016-09-22 10:24:38 +01007378}
7379
Nicolas Geoffray13a797b2017-03-15 16:41:31 +00007380void ParallelMoveResolverARMVIXL::RestoreScratch(int reg) {
7381 __ Pop(vixl32::Register(reg));
Scott Wakelingfe885462016-09-22 10:24:38 +01007382}
7383
Artem Serov02d37832016-10-25 15:25:33 +01007384HLoadClass::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadClassKind(
Artem Serovd4cc5b22016-11-04 11:19:09 +00007385 HLoadClass::LoadKind desired_class_load_kind) {
7386 switch (desired_class_load_kind) {
Nicolas Geoffray83c8e272017-01-31 14:36:37 +00007387 case HLoadClass::LoadKind::kInvalid:
7388 LOG(FATAL) << "UNREACHABLE";
7389 UNREACHABLE();
Artem Serovd4cc5b22016-11-04 11:19:09 +00007390 case HLoadClass::LoadKind::kReferrersClass:
7391 break;
Artem Serovd4cc5b22016-11-04 11:19:09 +00007392 case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
Vladimir Markoe47f60c2018-02-21 13:43:28 +00007393 case HLoadClass::LoadKind::kBootImageRelRo:
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007394 case HLoadClass::LoadKind::kBssEntry:
7395 DCHECK(!Runtime::Current()->UseJitCompilation());
7396 break;
Nicolas Geoffray22384ae2016-12-12 22:33:36 +00007397 case HLoadClass::LoadKind::kJitTableAddress:
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007398 DCHECK(Runtime::Current()->UseJitCompilation());
Artem Serovc5fcb442016-12-02 19:19:58 +00007399 break;
Vladimir Marko764d4542017-05-16 10:31:41 +01007400 case HLoadClass::LoadKind::kBootImageAddress:
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007401 case HLoadClass::LoadKind::kRuntimeCall:
Artem Serovd4cc5b22016-11-04 11:19:09 +00007402 break;
7403 }
7404 return desired_class_load_kind;
Artem Serov02d37832016-10-25 15:25:33 +01007405}
7406
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007407void LocationsBuilderARMVIXL::VisitLoadClass(HLoadClass* cls) {
Vladimir Marko41559982017-01-06 14:04:23 +00007408 HLoadClass::LoadKind load_kind = cls->GetLoadKind();
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007409 if (load_kind == HLoadClass::LoadKind::kRuntimeCall) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007410 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Vladimir Marko41559982017-01-06 14:04:23 +00007411 CodeGenerator::CreateLoadClassRuntimeCallLocationSummary(
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007412 cls,
7413 LocationFrom(calling_convention.GetRegisterAt(0)),
Vladimir Marko41559982017-01-06 14:04:23 +00007414 LocationFrom(r0));
Vladimir Markoea4c1262017-02-06 19:59:33 +00007415 DCHECK(calling_convention.GetRegisterAt(0).Is(r0));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007416 return;
7417 }
Vladimir Marko41559982017-01-06 14:04:23 +00007418 DCHECK(!cls->NeedsAccessCheck());
Scott Wakelingfe885462016-09-22 10:24:38 +01007419
Artem Serovd4cc5b22016-11-04 11:19:09 +00007420 const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
7421 LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier)
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007422 ? LocationSummary::kCallOnSlowPath
7423 : LocationSummary::kNoCall;
Vladimir Markoca6fff82017-10-03 14:49:14 +01007424 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(cls, call_kind);
Artem Serovd4cc5b22016-11-04 11:19:09 +00007425 if (kUseBakerReadBarrier && requires_read_barrier && !cls->NeedsEnvironment()) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00007426 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Artem Serovd4cc5b22016-11-04 11:19:09 +00007427 }
7428
Vladimir Marko41559982017-01-06 14:04:23 +00007429 if (load_kind == HLoadClass::LoadKind::kReferrersClass) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007430 locations->SetInAt(0, Location::RequiresRegister());
7431 }
7432 locations->SetOut(Location::RequiresRegister());
Vladimir Markoea4c1262017-02-06 19:59:33 +00007433 if (load_kind == HLoadClass::LoadKind::kBssEntry) {
7434 if (!kUseReadBarrier || kUseBakerReadBarrier) {
7435 // Rely on the type resolution or initialization and marking to save everything we need.
Vladimir Markoea4c1262017-02-06 19:59:33 +00007436 RegisterSet caller_saves = RegisterSet::Empty();
7437 InvokeRuntimeCallingConventionARMVIXL calling_convention;
7438 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0)));
7439 // TODO: Add GetReturnLocation() to the calling convention so that we can DCHECK()
7440 // that the the kPrimNot result register is the same as the first argument register.
7441 locations->SetCustomSlowPathCallerSaves(caller_saves);
7442 } else {
7443 // For non-Baker read barrier we have a temp-clobbering call.
7444 }
7445 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007446}
7447
Nicolas Geoffray5247c082017-01-13 14:17:29 +00007448// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
7449// move.
7450void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFETY_ANALYSIS {
Vladimir Marko41559982017-01-06 14:04:23 +00007451 HLoadClass::LoadKind load_kind = cls->GetLoadKind();
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007452 if (load_kind == HLoadClass::LoadKind::kRuntimeCall) {
Vladimir Marko41559982017-01-06 14:04:23 +00007453 codegen_->GenerateLoadClassRuntimeCall(cls);
Orion Hodson4c8e12e2018-05-18 08:33:20 +01007454 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 14);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007455 return;
7456 }
Vladimir Marko41559982017-01-06 14:04:23 +00007457 DCHECK(!cls->NeedsAccessCheck());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007458
Vladimir Marko41559982017-01-06 14:04:23 +00007459 LocationSummary* locations = cls->GetLocations();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007460 Location out_loc = locations->Out();
7461 vixl32::Register out = OutputRegister(cls);
7462
Artem Serovd4cc5b22016-11-04 11:19:09 +00007463 const ReadBarrierOption read_barrier_option = cls->IsInBootImage()
7464 ? kWithoutReadBarrier
7465 : kCompilerReadBarrierOption;
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007466 bool generate_null_check = false;
Vladimir Marko41559982017-01-06 14:04:23 +00007467 switch (load_kind) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007468 case HLoadClass::LoadKind::kReferrersClass: {
7469 DCHECK(!cls->CanCallRuntime());
7470 DCHECK(!cls->MustGenerateClinitCheck());
7471 // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
7472 vixl32::Register current_method = InputRegisterAt(cls, 0);
Vladimir Markoca1e0382018-04-11 09:58:41 +00007473 codegen_->GenerateGcRootFieldLoad(cls,
7474 out_loc,
7475 current_method,
7476 ArtMethod::DeclaringClassOffset().Int32Value(),
7477 read_barrier_option);
Artem Serovd4cc5b22016-11-04 11:19:09 +00007478 break;
7479 }
Artem Serovd4cc5b22016-11-04 11:19:09 +00007480 case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: {
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007481 DCHECK(codegen_->GetCompilerOptions().IsBootImage());
Artem Serovd4cc5b22016-11-04 11:19:09 +00007482 DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
7483 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
Vladimir Marko59eb30f2018-02-20 11:52:34 +00007484 codegen_->NewBootImageTypePatch(cls->GetDexFile(), cls->GetTypeIndex());
Artem Serovd4cc5b22016-11-04 11:19:09 +00007485 codegen_->EmitMovwMovtPlaceholder(labels, out);
7486 break;
7487 }
7488 case HLoadClass::LoadKind::kBootImageAddress: {
Artem Serovc5fcb442016-12-02 19:19:58 +00007489 DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
Nicolas Geoffray5247c082017-01-13 14:17:29 +00007490 uint32_t address = dchecked_integral_cast<uint32_t>(
7491 reinterpret_cast<uintptr_t>(cls->GetClass().Get()));
7492 DCHECK_NE(address, 0u);
Artem Serovc5fcb442016-12-02 19:19:58 +00007493 __ Ldr(out, codegen_->DeduplicateBootImageAddressLiteral(address));
Artem Serovd4cc5b22016-11-04 11:19:09 +00007494 break;
7495 }
Vladimir Markoe47f60c2018-02-21 13:43:28 +00007496 case HLoadClass::LoadKind::kBootImageRelRo: {
Vladimir Marko94ec2db2017-09-06 17:21:03 +01007497 DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
7498 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
Vladimir Markoe47f60c2018-02-21 13:43:28 +00007499 codegen_->NewBootImageRelRoPatch(codegen_->GetBootImageOffset(cls));
Vladimir Marko94ec2db2017-09-06 17:21:03 +01007500 codegen_->EmitMovwMovtPlaceholder(labels, out);
7501 __ Ldr(out, MemOperand(out, /* offset */ 0));
Vladimir Marko94ec2db2017-09-06 17:21:03 +01007502 break;
7503 }
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007504 case HLoadClass::LoadKind::kBssEntry: {
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007505 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
Vladimir Marko1998cd02017-01-13 13:02:58 +00007506 codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex());
Vladimir Markof3c52b42017-11-17 17:32:12 +00007507 codegen_->EmitMovwMovtPlaceholder(labels, out);
Vladimir Markoca1e0382018-04-11 09:58:41 +00007508 codegen_->GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, read_barrier_option);
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007509 generate_null_check = true;
7510 break;
7511 }
Nicolas Geoffray22384ae2016-12-12 22:33:36 +00007512 case HLoadClass::LoadKind::kJitTableAddress: {
Artem Serovc5fcb442016-12-02 19:19:58 +00007513 __ Ldr(out, codegen_->DeduplicateJitClassLiteral(cls->GetDexFile(),
7514 cls->GetTypeIndex(),
Nicolas Geoffray5247c082017-01-13 14:17:29 +00007515 cls->GetClass()));
Artem Serovc5fcb442016-12-02 19:19:58 +00007516 // /* GcRoot<mirror::Class> */ out = *out
Vladimir Markoca1e0382018-04-11 09:58:41 +00007517 codegen_->GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, read_barrier_option);
Artem Serovd4cc5b22016-11-04 11:19:09 +00007518 break;
7519 }
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007520 case HLoadClass::LoadKind::kRuntimeCall:
Nicolas Geoffray83c8e272017-01-31 14:36:37 +00007521 case HLoadClass::LoadKind::kInvalid:
Vladimir Marko41559982017-01-06 14:04:23 +00007522 LOG(FATAL) << "UNREACHABLE";
7523 UNREACHABLE();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007524 }
7525
7526 if (generate_null_check || cls->MustGenerateClinitCheck()) {
7527 DCHECK(cls->CanCallRuntime());
Vladimir Marko174b2e22017-10-12 13:34:49 +01007528 LoadClassSlowPathARMVIXL* slow_path =
7529 new (codegen_->GetScopedAllocator()) LoadClassSlowPathARMVIXL(
7530 cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007531 codegen_->AddSlowPath(slow_path);
7532 if (generate_null_check) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00007533 __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007534 }
7535 if (cls->MustGenerateClinitCheck()) {
7536 GenerateClassInitializationCheck(slow_path, out);
7537 } else {
7538 __ Bind(slow_path->GetExitLabel());
7539 }
Orion Hodson4c8e12e2018-05-18 08:33:20 +01007540 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 15);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007541 }
7542}
7543
Orion Hodsondbaa5c72018-05-10 08:22:46 +01007544void LocationsBuilderARMVIXL::VisitLoadMethodHandle(HLoadMethodHandle* load) {
7545 InvokeRuntimeCallingConventionARMVIXL calling_convention;
7546 Location location = LocationFrom(calling_convention.GetRegisterAt(0));
7547 CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(load, location, location);
7548}
7549
7550void InstructionCodeGeneratorARMVIXL::VisitLoadMethodHandle(HLoadMethodHandle* load) {
7551 codegen_->GenerateLoadMethodHandleRuntimeCall(load);
7552}
7553
Orion Hodson18259d72018-04-12 11:18:23 +01007554void LocationsBuilderARMVIXL::VisitLoadMethodType(HLoadMethodType* load) {
7555 InvokeRuntimeCallingConventionARMVIXL calling_convention;
7556 Location location = LocationFrom(calling_convention.GetRegisterAt(0));
7557 CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, location, location);
7558}
7559
7560void InstructionCodeGeneratorARMVIXL::VisitLoadMethodType(HLoadMethodType* load) {
7561 codegen_->GenerateLoadMethodTypeRuntimeCall(load);
7562}
7563
Artem Serov02d37832016-10-25 15:25:33 +01007564void LocationsBuilderARMVIXL::VisitClinitCheck(HClinitCheck* check) {
7565 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01007566 new (GetGraph()->GetAllocator()) LocationSummary(check, LocationSummary::kCallOnSlowPath);
Artem Serov02d37832016-10-25 15:25:33 +01007567 locations->SetInAt(0, Location::RequiresRegister());
7568 if (check->HasUses()) {
7569 locations->SetOut(Location::SameAsFirstInput());
7570 }
7571}
7572
7573void InstructionCodeGeneratorARMVIXL::VisitClinitCheck(HClinitCheck* check) {
7574 // We assume the class is not null.
7575 LoadClassSlowPathARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01007576 new (codegen_->GetScopedAllocator()) LoadClassSlowPathARMVIXL(check->GetLoadClass(),
7577 check,
7578 check->GetDexPc(),
7579 /* do_clinit */ true);
Artem Serov02d37832016-10-25 15:25:33 +01007580 codegen_->AddSlowPath(slow_path);
7581 GenerateClassInitializationCheck(slow_path, InputRegisterAt(check, 0));
7582}
7583
7584void InstructionCodeGeneratorARMVIXL::GenerateClassInitializationCheck(
7585 LoadClassSlowPathARMVIXL* slow_path, vixl32::Register class_reg) {
7586 UseScratchRegisterScope temps(GetVIXLAssembler());
7587 vixl32::Register temp = temps.Acquire();
Vladimir Markodc682aa2018-01-04 18:42:57 +00007588 constexpr size_t status_lsb_position = SubtypeCheckBits::BitStructSizeOf();
7589 const size_t status_byte_offset =
7590 mirror::Class::StatusOffset().SizeValue() + (status_lsb_position / kBitsPerByte);
7591 constexpr uint32_t shifted_initialized_value =
7592 enum_cast<uint32_t>(ClassStatus::kInitialized) << (status_lsb_position % kBitsPerByte);
7593
7594 GetAssembler()->LoadFromOffset(kLoadUnsignedByte, temp, class_reg, status_byte_offset);
7595 __ Cmp(temp, shifted_initialized_value);
Vladimir Marko2c64a832018-01-04 11:31:56 +00007596 __ B(lo, slow_path->GetEntryLabel());
Artem Serov02d37832016-10-25 15:25:33 +01007597 // Even if the initialized flag is set, we may be in a situation where caches are not synced
7598 // properly. Therefore, we do a memory fence.
7599 __ Dmb(ISH);
7600 __ Bind(slow_path->GetExitLabel());
7601}
7602
Vladimir Marko175e7862018-03-27 09:03:13 +00007603void InstructionCodeGeneratorARMVIXL::GenerateBitstringTypeCheckCompare(
7604 HTypeCheckInstruction* check,
7605 vixl32::Register temp,
7606 vixl32::FlagsUpdate flags_update) {
7607 uint32_t path_to_root = check->GetBitstringPathToRoot();
7608 uint32_t mask = check->GetBitstringMask();
7609 DCHECK(IsPowerOfTwo(mask + 1));
7610 size_t mask_bits = WhichPowerOf2(mask + 1);
7611
7612 // Note that HInstanceOf shall check for zero value in `temp` but HCheckCast needs
7613 // the Z flag for BNE. This is indicated by the `flags_update` parameter.
7614 if (mask_bits == 16u) {
7615 // Load only the bitstring part of the status word.
7616 __ Ldrh(temp, MemOperand(temp, mirror::Class::StatusOffset().Int32Value()));
7617 // Check if the bitstring bits are equal to `path_to_root`.
7618 if (flags_update == SetFlags) {
7619 __ Cmp(temp, path_to_root);
7620 } else {
7621 __ Sub(temp, temp, path_to_root);
7622 }
7623 } else {
7624 // /* uint32_t */ temp = temp->status_
7625 __ Ldr(temp, MemOperand(temp, mirror::Class::StatusOffset().Int32Value()));
7626 if (GetAssembler()->ShifterOperandCanHold(SUB, path_to_root)) {
7627 // Compare the bitstring bits using SUB.
7628 __ Sub(temp, temp, path_to_root);
7629 // Shift out bits that do not contribute to the comparison.
7630 __ Lsl(flags_update, temp, temp, dchecked_integral_cast<uint32_t>(32u - mask_bits));
7631 } else if (IsUint<16>(path_to_root)) {
7632 if (temp.IsLow()) {
7633 // Note: Optimized for size but contains one more dependent instruction than necessary.
7634 // MOVW+SUB(register) would be 8 bytes unless we find a low-reg temporary but the
7635 // macro assembler would use the high reg IP for the constant by default.
7636 // Compare the bitstring bits using SUB.
7637 __ Sub(temp, temp, path_to_root & 0x00ffu); // 16-bit SUB (immediate) T2
7638 __ Sub(temp, temp, path_to_root & 0xff00u); // 32-bit SUB (immediate) T3
7639 // Shift out bits that do not contribute to the comparison.
7640 __ Lsl(flags_update, temp, temp, dchecked_integral_cast<uint32_t>(32u - mask_bits));
7641 } else {
7642 // Extract the bitstring bits.
7643 __ Ubfx(temp, temp, 0, mask_bits);
7644 // Check if the bitstring bits are equal to `path_to_root`.
7645 if (flags_update == SetFlags) {
7646 __ Cmp(temp, path_to_root);
7647 } else {
7648 __ Sub(temp, temp, path_to_root);
7649 }
7650 }
7651 } else {
7652 // Shift out bits that do not contribute to the comparison.
7653 __ Lsl(temp, temp, dchecked_integral_cast<uint32_t>(32u - mask_bits));
7654 // Check if the shifted bitstring bits are equal to `path_to_root << (32u - mask_bits)`.
7655 if (flags_update == SetFlags) {
7656 __ Cmp(temp, path_to_root << (32u - mask_bits));
7657 } else {
7658 __ Sub(temp, temp, path_to_root << (32u - mask_bits));
7659 }
7660 }
7661 }
7662}
7663
Artem Serov02d37832016-10-25 15:25:33 +01007664HLoadString::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadStringKind(
Artem Serovd4cc5b22016-11-04 11:19:09 +00007665 HLoadString::LoadKind desired_string_load_kind) {
7666 switch (desired_string_load_kind) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00007667 case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
Vladimir Markoe47f60c2018-02-21 13:43:28 +00007668 case HLoadString::LoadKind::kBootImageRelRo:
Artem Serovd4cc5b22016-11-04 11:19:09 +00007669 case HLoadString::LoadKind::kBssEntry:
7670 DCHECK(!Runtime::Current()->UseJitCompilation());
7671 break;
7672 case HLoadString::LoadKind::kJitTableAddress:
7673 DCHECK(Runtime::Current()->UseJitCompilation());
Artem Serovc5fcb442016-12-02 19:19:58 +00007674 break;
Vladimir Marko764d4542017-05-16 10:31:41 +01007675 case HLoadString::LoadKind::kBootImageAddress:
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007676 case HLoadString::LoadKind::kRuntimeCall:
Artem Serovd4cc5b22016-11-04 11:19:09 +00007677 break;
7678 }
7679 return desired_string_load_kind;
Artem Serov02d37832016-10-25 15:25:33 +01007680}
7681
7682void LocationsBuilderARMVIXL::VisitLoadString(HLoadString* load) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00007683 LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load);
Vladimir Markoca6fff82017-10-03 14:49:14 +01007684 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(load, call_kind);
Artem Serov02d37832016-10-25 15:25:33 +01007685 HLoadString::LoadKind load_kind = load->GetLoadKind();
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007686 if (load_kind == HLoadString::LoadKind::kRuntimeCall) {
Artem Serov02d37832016-10-25 15:25:33 +01007687 locations->SetOut(LocationFrom(r0));
7688 } else {
7689 locations->SetOut(Location::RequiresRegister());
Artem Serovd4cc5b22016-11-04 11:19:09 +00007690 if (load_kind == HLoadString::LoadKind::kBssEntry) {
7691 if (!kUseReadBarrier || kUseBakerReadBarrier) {
Vladimir Markoea4c1262017-02-06 19:59:33 +00007692 // Rely on the pResolveString and marking to save everything we need, including temps.
Artem Serovd4cc5b22016-11-04 11:19:09 +00007693 RegisterSet caller_saves = RegisterSet::Empty();
7694 InvokeRuntimeCallingConventionARMVIXL calling_convention;
7695 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0)));
7696 // TODO: Add GetReturnLocation() to the calling convention so that we can DCHECK()
7697 // that the the kPrimNot result register is the same as the first argument register.
7698 locations->SetCustomSlowPathCallerSaves(caller_saves);
7699 } else {
7700 // For non-Baker read barrier we have a temp-clobbering call.
7701 }
7702 }
Artem Serov02d37832016-10-25 15:25:33 +01007703 }
7704}
7705
Nicolas Geoffrayf0acfe72017-01-09 20:54:52 +00007706// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
7707// move.
7708void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) NO_THREAD_SAFETY_ANALYSIS {
Artem Serovd4cc5b22016-11-04 11:19:09 +00007709 LocationSummary* locations = load->GetLocations();
7710 Location out_loc = locations->Out();
7711 vixl32::Register out = OutputRegister(load);
7712 HLoadString::LoadKind load_kind = load->GetLoadKind();
7713
7714 switch (load_kind) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00007715 case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
7716 DCHECK(codegen_->GetCompilerOptions().IsBootImage());
7717 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
Vladimir Marko59eb30f2018-02-20 11:52:34 +00007718 codegen_->NewBootImageStringPatch(load->GetDexFile(), load->GetStringIndex());
Artem Serovd4cc5b22016-11-04 11:19:09 +00007719 codegen_->EmitMovwMovtPlaceholder(labels, out);
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01007720 return;
Artem Serovd4cc5b22016-11-04 11:19:09 +00007721 }
7722 case HLoadString::LoadKind::kBootImageAddress: {
Nicolas Geoffrayf0acfe72017-01-09 20:54:52 +00007723 uint32_t address = dchecked_integral_cast<uint32_t>(
7724 reinterpret_cast<uintptr_t>(load->GetString().Get()));
7725 DCHECK_NE(address, 0u);
Artem Serovc5fcb442016-12-02 19:19:58 +00007726 __ Ldr(out, codegen_->DeduplicateBootImageAddressLiteral(address));
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01007727 return;
7728 }
Vladimir Markoe47f60c2018-02-21 13:43:28 +00007729 case HLoadString::LoadKind::kBootImageRelRo: {
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01007730 DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
7731 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
Vladimir Markoe47f60c2018-02-21 13:43:28 +00007732 codegen_->NewBootImageRelRoPatch(codegen_->GetBootImageOffset(load));
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01007733 codegen_->EmitMovwMovtPlaceholder(labels, out);
7734 __ Ldr(out, MemOperand(out, /* offset */ 0));
7735 return;
Artem Serovd4cc5b22016-11-04 11:19:09 +00007736 }
7737 case HLoadString::LoadKind::kBssEntry: {
7738 DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
Artem Serovd4cc5b22016-11-04 11:19:09 +00007739 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01007740 codegen_->NewStringBssEntryPatch(load->GetDexFile(), load->GetStringIndex());
Vladimir Markof3c52b42017-11-17 17:32:12 +00007741 codegen_->EmitMovwMovtPlaceholder(labels, out);
Vladimir Markoca1e0382018-04-11 09:58:41 +00007742 codegen_->GenerateGcRootFieldLoad(
7743 load, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption);
Artem Serovd4cc5b22016-11-04 11:19:09 +00007744 LoadStringSlowPathARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01007745 new (codegen_->GetScopedAllocator()) LoadStringSlowPathARMVIXL(load);
Artem Serovd4cc5b22016-11-04 11:19:09 +00007746 codegen_->AddSlowPath(slow_path);
7747 __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
7748 __ Bind(slow_path->GetExitLabel());
Orion Hodson4c8e12e2018-05-18 08:33:20 +01007749 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 16);
Artem Serovd4cc5b22016-11-04 11:19:09 +00007750 return;
7751 }
7752 case HLoadString::LoadKind::kJitTableAddress: {
Artem Serovc5fcb442016-12-02 19:19:58 +00007753 __ Ldr(out, codegen_->DeduplicateJitStringLiteral(load->GetDexFile(),
Nicolas Geoffrayf0acfe72017-01-09 20:54:52 +00007754 load->GetStringIndex(),
7755 load->GetString()));
Artem Serovc5fcb442016-12-02 19:19:58 +00007756 // /* GcRoot<mirror::String> */ out = *out
Vladimir Markoca1e0382018-04-11 09:58:41 +00007757 codegen_->GenerateGcRootFieldLoad(
7758 load, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption);
Artem Serovc5fcb442016-12-02 19:19:58 +00007759 return;
Artem Serovd4cc5b22016-11-04 11:19:09 +00007760 }
7761 default:
7762 break;
7763 }
Artem Serov02d37832016-10-25 15:25:33 +01007764
7765 // TODO: Re-add the compiler code to do string dex cache lookup again.
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007766 DCHECK_EQ(load->GetLoadKind(), HLoadString::LoadKind::kRuntimeCall);
Artem Serov02d37832016-10-25 15:25:33 +01007767 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Andreas Gampe8a0128a2016-11-28 07:38:35 -08007768 __ Mov(calling_convention.GetRegisterAt(0), load->GetStringIndex().index_);
Artem Serov02d37832016-10-25 15:25:33 +01007769 codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc());
7770 CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
Orion Hodson4c8e12e2018-05-18 08:33:20 +01007771 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 17);
Artem Serov02d37832016-10-25 15:25:33 +01007772}
7773
7774static int32_t GetExceptionTlsOffset() {
7775 return Thread::ExceptionOffset<kArmPointerSize>().Int32Value();
7776}
7777
7778void LocationsBuilderARMVIXL::VisitLoadException(HLoadException* load) {
7779 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01007780 new (GetGraph()->GetAllocator()) LocationSummary(load, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01007781 locations->SetOut(Location::RequiresRegister());
7782}
7783
7784void InstructionCodeGeneratorARMVIXL::VisitLoadException(HLoadException* load) {
7785 vixl32::Register out = OutputRegister(load);
7786 GetAssembler()->LoadFromOffset(kLoadWord, out, tr, GetExceptionTlsOffset());
7787}
7788
7789
7790void LocationsBuilderARMVIXL::VisitClearException(HClearException* clear) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01007791 new (GetGraph()->GetAllocator()) LocationSummary(clear, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01007792}
7793
7794void InstructionCodeGeneratorARMVIXL::VisitClearException(HClearException* clear ATTRIBUTE_UNUSED) {
7795 UseScratchRegisterScope temps(GetVIXLAssembler());
7796 vixl32::Register temp = temps.Acquire();
7797 __ Mov(temp, 0);
7798 GetAssembler()->StoreToOffset(kStoreWord, temp, tr, GetExceptionTlsOffset());
7799}
7800
7801void LocationsBuilderARMVIXL::VisitThrow(HThrow* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01007802 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
7803 instruction, LocationSummary::kCallOnMainOnly);
Artem Serov02d37832016-10-25 15:25:33 +01007804 InvokeRuntimeCallingConventionARMVIXL calling_convention;
7805 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
7806}
7807
7808void InstructionCodeGeneratorARMVIXL::VisitThrow(HThrow* instruction) {
7809 codegen_->InvokeRuntime(kQuickDeliverException, instruction, instruction->GetDexPc());
7810 CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
7811}
7812
Artem Serov657022c2016-11-23 14:19:38 +00007813// Temp is used for read barrier.
7814static size_t NumberOfInstanceOfTemps(TypeCheckKind type_check_kind) {
7815 if (kEmitCompilerReadBarrier &&
7816 (kUseBakerReadBarrier ||
7817 type_check_kind == TypeCheckKind::kAbstractClassCheck ||
7818 type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
7819 type_check_kind == TypeCheckKind::kArrayObjectCheck)) {
7820 return 1;
7821 }
7822 return 0;
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007823}
7824
Artem Serov657022c2016-11-23 14:19:38 +00007825// Interface case has 3 temps, one for holding the number of interfaces, one for the current
7826// interface pointer, one for loading the current interface.
7827// The other checks have one temp for loading the object's class.
7828static size_t NumberOfCheckCastTemps(TypeCheckKind type_check_kind) {
7829 if (type_check_kind == TypeCheckKind::kInterfaceCheck) {
7830 return 3;
7831 }
7832 return 1 + NumberOfInstanceOfTemps(type_check_kind);
7833}
Artem Serovcfbe9132016-10-14 15:58:56 +01007834
7835void LocationsBuilderARMVIXL::VisitInstanceOf(HInstanceOf* instruction) {
7836 LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
7837 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
7838 bool baker_read_barrier_slow_path = false;
7839 switch (type_check_kind) {
7840 case TypeCheckKind::kExactCheck:
7841 case TypeCheckKind::kAbstractClassCheck:
7842 case TypeCheckKind::kClassHierarchyCheck:
Vladimir Marko87584542017-12-12 17:47:52 +00007843 case TypeCheckKind::kArrayObjectCheck: {
7844 bool needs_read_barrier = CodeGenerator::InstanceOfNeedsReadBarrier(instruction);
7845 call_kind = needs_read_barrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall;
7846 baker_read_barrier_slow_path = kUseBakerReadBarrier && needs_read_barrier;
Artem Serovcfbe9132016-10-14 15:58:56 +01007847 break;
Vladimir Marko87584542017-12-12 17:47:52 +00007848 }
Artem Serovcfbe9132016-10-14 15:58:56 +01007849 case TypeCheckKind::kArrayCheck:
7850 case TypeCheckKind::kUnresolvedCheck:
7851 case TypeCheckKind::kInterfaceCheck:
7852 call_kind = LocationSummary::kCallOnSlowPath;
7853 break;
Vladimir Marko175e7862018-03-27 09:03:13 +00007854 case TypeCheckKind::kBitstringCheck:
7855 break;
Artem Serovcfbe9132016-10-14 15:58:56 +01007856 }
7857
Vladimir Markoca6fff82017-10-03 14:49:14 +01007858 LocationSummary* locations =
7859 new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
Artem Serovcfbe9132016-10-14 15:58:56 +01007860 if (baker_read_barrier_slow_path) {
7861 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
7862 }
7863 locations->SetInAt(0, Location::RequiresRegister());
Vladimir Marko175e7862018-03-27 09:03:13 +00007864 if (type_check_kind == TypeCheckKind::kBitstringCheck) {
7865 locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
7866 locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
7867 locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
7868 } else {
7869 locations->SetInAt(1, Location::RequiresRegister());
7870 }
Artem Serovcfbe9132016-10-14 15:58:56 +01007871 // The "out" register is used as a temporary, so it overlaps with the inputs.
7872 // Note that TypeCheckSlowPathARM uses this register too.
7873 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
Artem Serov657022c2016-11-23 14:19:38 +00007874 locations->AddRegisterTemps(NumberOfInstanceOfTemps(type_check_kind));
Artem Serovcfbe9132016-10-14 15:58:56 +01007875}
7876
7877void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) {
7878 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
7879 LocationSummary* locations = instruction->GetLocations();
7880 Location obj_loc = locations->InAt(0);
7881 vixl32::Register obj = InputRegisterAt(instruction, 0);
Vladimir Marko175e7862018-03-27 09:03:13 +00007882 vixl32::Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck)
7883 ? vixl32::Register()
7884 : InputRegisterAt(instruction, 1);
Artem Serovcfbe9132016-10-14 15:58:56 +01007885 Location out_loc = locations->Out();
7886 vixl32::Register out = OutputRegister(instruction);
Artem Serov657022c2016-11-23 14:19:38 +00007887 const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind);
7888 DCHECK_LE(num_temps, 1u);
7889 Location maybe_temp_loc = (num_temps >= 1) ? locations->GetTemp(0) : Location::NoLocation();
Artem Serovcfbe9132016-10-14 15:58:56 +01007890 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
7891 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
7892 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
7893 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007894 vixl32::Label done;
7895 vixl32::Label* const final_label = codegen_->GetFinalLabel(instruction, &done);
Artem Serovcfbe9132016-10-14 15:58:56 +01007896 SlowPathCodeARMVIXL* slow_path = nullptr;
7897
7898 // Return 0 if `obj` is null.
7899 // avoid null check if we know obj is not null.
7900 if (instruction->MustDoNullCheck()) {
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007901 DCHECK(!out.Is(obj));
7902 __ Mov(out, 0);
7903 __ CompareAndBranchIfZero(obj, final_label, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007904 }
7905
Artem Serovcfbe9132016-10-14 15:58:56 +01007906 switch (type_check_kind) {
7907 case TypeCheckKind::kExactCheck: {
Vladimir Marko87584542017-12-12 17:47:52 +00007908 ReadBarrierOption read_barrier_option =
7909 CodeGenerator::ReadBarrierOptionForInstanceOf(instruction);
Mathieu Chartier6beced42016-11-15 15:51:31 -08007910 // /* HeapReference<Class> */ out = obj->klass_
7911 GenerateReferenceLoadTwoRegisters(instruction,
7912 out_loc,
7913 obj_loc,
7914 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00007915 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00007916 read_barrier_option);
Artem Serovcfbe9132016-10-14 15:58:56 +01007917 // Classes must be equal for the instanceof to succeed.
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007918 __ Cmp(out, cls);
7919 // We speculatively set the result to false without changing the condition
7920 // flags, which allows us to avoid some branching later.
7921 __ Mov(LeaveFlags, out, 0);
7922
7923 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
7924 // we check that the output is in a low register, so that a 16-bit MOV
7925 // encoding can be used.
7926 if (out.IsLow()) {
7927 // We use the scope because of the IT block that follows.
7928 ExactAssemblyScope guard(GetVIXLAssembler(),
7929 2 * vixl32::k16BitT32InstructionSizeInBytes,
7930 CodeBufferCheckScope::kExactSize);
7931
7932 __ it(eq);
7933 __ mov(eq, out, 1);
7934 } else {
7935 __ B(ne, final_label, /* far_target */ false);
7936 __ Mov(out, 1);
7937 }
7938
Artem Serovcfbe9132016-10-14 15:58:56 +01007939 break;
7940 }
7941
7942 case TypeCheckKind::kAbstractClassCheck: {
Vladimir Marko87584542017-12-12 17:47:52 +00007943 ReadBarrierOption read_barrier_option =
7944 CodeGenerator::ReadBarrierOptionForInstanceOf(instruction);
Mathieu Chartier6beced42016-11-15 15:51:31 -08007945 // /* HeapReference<Class> */ out = obj->klass_
7946 GenerateReferenceLoadTwoRegisters(instruction,
7947 out_loc,
7948 obj_loc,
7949 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00007950 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00007951 read_barrier_option);
Artem Serovcfbe9132016-10-14 15:58:56 +01007952 // If the class is abstract, we eagerly fetch the super class of the
7953 // object to avoid doing a comparison we know will fail.
7954 vixl32::Label loop;
7955 __ Bind(&loop);
7956 // /* HeapReference<Class> */ out = out->super_class_
Artem Serov657022c2016-11-23 14:19:38 +00007957 GenerateReferenceLoadOneRegister(instruction,
7958 out_loc,
7959 super_offset,
7960 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00007961 read_barrier_option);
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007962 // If `out` is null, we use it for the result, and jump to the final label.
Anton Kirilov6f644202017-02-27 18:29:45 +00007963 __ CompareAndBranchIfZero(out, final_label, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007964 __ Cmp(out, cls);
Artem Serov517d9f62016-12-12 15:51:15 +00007965 __ B(ne, &loop, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007966 __ Mov(out, 1);
Artem Serovcfbe9132016-10-14 15:58:56 +01007967 break;
7968 }
7969
7970 case TypeCheckKind::kClassHierarchyCheck: {
Vladimir Marko87584542017-12-12 17:47:52 +00007971 ReadBarrierOption read_barrier_option =
7972 CodeGenerator::ReadBarrierOptionForInstanceOf(instruction);
Mathieu Chartier6beced42016-11-15 15:51:31 -08007973 // /* HeapReference<Class> */ out = obj->klass_
7974 GenerateReferenceLoadTwoRegisters(instruction,
7975 out_loc,
7976 obj_loc,
7977 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00007978 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00007979 read_barrier_option);
Artem Serovcfbe9132016-10-14 15:58:56 +01007980 // Walk over the class hierarchy to find a match.
7981 vixl32::Label loop, success;
7982 __ Bind(&loop);
7983 __ Cmp(out, cls);
Artem Serov517d9f62016-12-12 15:51:15 +00007984 __ B(eq, &success, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007985 // /* HeapReference<Class> */ out = out->super_class_
Artem Serov657022c2016-11-23 14:19:38 +00007986 GenerateReferenceLoadOneRegister(instruction,
7987 out_loc,
7988 super_offset,
7989 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00007990 read_barrier_option);
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007991 // This is essentially a null check, but it sets the condition flags to the
7992 // proper value for the code that follows the loop, i.e. not `eq`.
7993 __ Cmp(out, 1);
7994 __ B(hs, &loop, /* far_target */ false);
7995
7996 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
7997 // we check that the output is in a low register, so that a 16-bit MOV
7998 // encoding can be used.
7999 if (out.IsLow()) {
8000 // If `out` is null, we use it for the result, and the condition flags
8001 // have already been set to `ne`, so the IT block that comes afterwards
8002 // (and which handles the successful case) turns into a NOP (instead of
8003 // overwriting `out`).
8004 __ Bind(&success);
8005
8006 // We use the scope because of the IT block that follows.
8007 ExactAssemblyScope guard(GetVIXLAssembler(),
8008 2 * vixl32::k16BitT32InstructionSizeInBytes,
8009 CodeBufferCheckScope::kExactSize);
8010
8011 // There is only one branch to the `success` label (which is bound to this
8012 // IT block), and it has the same condition, `eq`, so in that case the MOV
8013 // is executed.
8014 __ it(eq);
8015 __ mov(eq, out, 1);
8016 } else {
8017 // If `out` is null, we use it for the result, and jump to the final label.
Anton Kirilov6f644202017-02-27 18:29:45 +00008018 __ B(final_label);
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00008019 __ Bind(&success);
8020 __ Mov(out, 1);
Artem Serovcfbe9132016-10-14 15:58:56 +01008021 }
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00008022
Artem Serovcfbe9132016-10-14 15:58:56 +01008023 break;
8024 }
8025
8026 case TypeCheckKind::kArrayObjectCheck: {
Vladimir Marko87584542017-12-12 17:47:52 +00008027 ReadBarrierOption read_barrier_option =
8028 CodeGenerator::ReadBarrierOptionForInstanceOf(instruction);
Mathieu Chartier6beced42016-11-15 15:51:31 -08008029 // /* HeapReference<Class> */ out = obj->klass_
8030 GenerateReferenceLoadTwoRegisters(instruction,
8031 out_loc,
8032 obj_loc,
8033 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00008034 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00008035 read_barrier_option);
Artem Serovcfbe9132016-10-14 15:58:56 +01008036 // Do an exact check.
8037 vixl32::Label exact_check;
8038 __ Cmp(out, cls);
Artem Serov517d9f62016-12-12 15:51:15 +00008039 __ B(eq, &exact_check, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01008040 // Otherwise, we need to check that the object's class is a non-primitive array.
8041 // /* HeapReference<Class> */ out = out->component_type_
Artem Serov657022c2016-11-23 14:19:38 +00008042 GenerateReferenceLoadOneRegister(instruction,
8043 out_loc,
8044 component_offset,
8045 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00008046 read_barrier_option);
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00008047 // If `out` is null, we use it for the result, and jump to the final label.
Anton Kirilov6f644202017-02-27 18:29:45 +00008048 __ CompareAndBranchIfZero(out, final_label, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01008049 GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset);
8050 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00008051 __ Cmp(out, 0);
8052 // We speculatively set the result to false without changing the condition
8053 // flags, which allows us to avoid some branching later.
8054 __ Mov(LeaveFlags, out, 0);
8055
8056 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
8057 // we check that the output is in a low register, so that a 16-bit MOV
8058 // encoding can be used.
8059 if (out.IsLow()) {
8060 __ Bind(&exact_check);
8061
8062 // We use the scope because of the IT block that follows.
8063 ExactAssemblyScope guard(GetVIXLAssembler(),
8064 2 * vixl32::k16BitT32InstructionSizeInBytes,
8065 CodeBufferCheckScope::kExactSize);
8066
8067 __ it(eq);
8068 __ mov(eq, out, 1);
8069 } else {
8070 __ B(ne, final_label, /* far_target */ false);
8071 __ Bind(&exact_check);
8072 __ Mov(out, 1);
8073 }
8074
Artem Serovcfbe9132016-10-14 15:58:56 +01008075 break;
8076 }
8077
8078 case TypeCheckKind::kArrayCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00008079 // No read barrier since the slow path will retry upon failure.
Mathieu Chartier6beced42016-11-15 15:51:31 -08008080 // /* HeapReference<Class> */ out = obj->klass_
8081 GenerateReferenceLoadTwoRegisters(instruction,
8082 out_loc,
8083 obj_loc,
8084 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00008085 maybe_temp_loc,
8086 kWithoutReadBarrier);
Artem Serovcfbe9132016-10-14 15:58:56 +01008087 __ Cmp(out, cls);
8088 DCHECK(locations->OnlyCallsOnSlowPath());
Vladimir Marko174b2e22017-10-12 13:34:49 +01008089 slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathARMVIXL(
8090 instruction, /* is_fatal */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01008091 codegen_->AddSlowPath(slow_path);
8092 __ B(ne, slow_path->GetEntryLabel());
8093 __ Mov(out, 1);
Artem Serovcfbe9132016-10-14 15:58:56 +01008094 break;
8095 }
8096
8097 case TypeCheckKind::kUnresolvedCheck:
8098 case TypeCheckKind::kInterfaceCheck: {
8099 // Note that we indeed only call on slow path, but we always go
8100 // into the slow path for the unresolved and interface check
8101 // cases.
8102 //
8103 // We cannot directly call the InstanceofNonTrivial runtime
8104 // entry point without resorting to a type checking slow path
8105 // here (i.e. by calling InvokeRuntime directly), as it would
8106 // require to assign fixed registers for the inputs of this
8107 // HInstanceOf instruction (following the runtime calling
8108 // convention), which might be cluttered by the potential first
8109 // read barrier emission at the beginning of this method.
8110 //
8111 // TODO: Introduce a new runtime entry point taking the object
8112 // to test (instead of its class) as argument, and let it deal
8113 // with the read barrier issues. This will let us refactor this
8114 // case of the `switch` code as it was previously (with a direct
8115 // call to the runtime not using a type checking slow path).
8116 // This should also be beneficial for the other cases above.
8117 DCHECK(locations->OnlyCallsOnSlowPath());
Vladimir Marko174b2e22017-10-12 13:34:49 +01008118 slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathARMVIXL(
8119 instruction, /* is_fatal */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01008120 codegen_->AddSlowPath(slow_path);
8121 __ B(slow_path->GetEntryLabel());
Artem Serovcfbe9132016-10-14 15:58:56 +01008122 break;
8123 }
Vladimir Marko175e7862018-03-27 09:03:13 +00008124
8125 case TypeCheckKind::kBitstringCheck: {
8126 // /* HeapReference<Class> */ temp = obj->klass_
8127 GenerateReferenceLoadTwoRegisters(instruction,
8128 out_loc,
8129 obj_loc,
8130 class_offset,
8131 maybe_temp_loc,
8132 kWithoutReadBarrier);
8133
8134 GenerateBitstringTypeCheckCompare(instruction, out, DontCare);
8135 // If `out` is a low reg and we would have another low reg temp, we could
8136 // optimize this as RSBS+ADC, see GenerateConditionWithZero().
8137 //
8138 // Also, in some cases when `out` is a low reg and we're loading a constant to IP
8139 // it would make sense to use CMP+MOV+IT+MOV instead of SUB+CLZ+LSR as the code size
8140 // would be the same and we would have fewer direct data dependencies.
8141 codegen_->GenerateConditionWithZero(kCondEQ, out, out); // CLZ+LSR
8142 break;
8143 }
Artem Serovcfbe9132016-10-14 15:58:56 +01008144 }
8145
Artem Serovcfbe9132016-10-14 15:58:56 +01008146 if (done.IsReferenced()) {
8147 __ Bind(&done);
8148 }
8149
8150 if (slow_path != nullptr) {
8151 __ Bind(slow_path->GetExitLabel());
8152 }
8153}
8154
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008155void LocationsBuilderARMVIXL::VisitCheckCast(HCheckCast* instruction) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008156 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
Vladimir Marko87584542017-12-12 17:47:52 +00008157 LocationSummary::CallKind call_kind = CodeGenerator::GetCheckCastCallKind(instruction);
Vladimir Markoca6fff82017-10-03 14:49:14 +01008158 LocationSummary* locations =
8159 new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008160 locations->SetInAt(0, Location::RequiresRegister());
Vladimir Marko175e7862018-03-27 09:03:13 +00008161 if (type_check_kind == TypeCheckKind::kBitstringCheck) {
8162 locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
8163 locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
8164 locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
8165 } else {
8166 locations->SetInAt(1, Location::RequiresRegister());
8167 }
Artem Serov657022c2016-11-23 14:19:38 +00008168 locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind));
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008169}
8170
8171void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) {
8172 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
8173 LocationSummary* locations = instruction->GetLocations();
8174 Location obj_loc = locations->InAt(0);
8175 vixl32::Register obj = InputRegisterAt(instruction, 0);
Vladimir Marko175e7862018-03-27 09:03:13 +00008176 vixl32::Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck)
8177 ? vixl32::Register()
8178 : InputRegisterAt(instruction, 1);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008179 Location temp_loc = locations->GetTemp(0);
8180 vixl32::Register temp = RegisterFrom(temp_loc);
Artem Serov657022c2016-11-23 14:19:38 +00008181 const size_t num_temps = NumberOfCheckCastTemps(type_check_kind);
8182 DCHECK_LE(num_temps, 3u);
8183 Location maybe_temp2_loc = (num_temps >= 2) ? locations->GetTemp(1) : Location::NoLocation();
8184 Location maybe_temp3_loc = (num_temps >= 3) ? locations->GetTemp(2) : Location::NoLocation();
8185 const uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
8186 const uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
8187 const uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
8188 const uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
8189 const uint32_t iftable_offset = mirror::Class::IfTableOffset().Uint32Value();
8190 const uint32_t array_length_offset = mirror::Array::LengthOffset().Uint32Value();
8191 const uint32_t object_array_data_offset =
8192 mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value();
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008193
Vladimir Marko87584542017-12-12 17:47:52 +00008194 bool is_type_check_slow_path_fatal = CodeGenerator::IsTypeCheckSlowPathFatal(instruction);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008195 SlowPathCodeARMVIXL* type_check_slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01008196 new (codegen_->GetScopedAllocator()) TypeCheckSlowPathARMVIXL(
8197 instruction, is_type_check_slow_path_fatal);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008198 codegen_->AddSlowPath(type_check_slow_path);
8199
8200 vixl32::Label done;
Anton Kirilov6f644202017-02-27 18:29:45 +00008201 vixl32::Label* final_label = codegen_->GetFinalLabel(instruction, &done);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008202 // Avoid null check if we know obj is not null.
8203 if (instruction->MustDoNullCheck()) {
Anton Kirilov6f644202017-02-27 18:29:45 +00008204 __ CompareAndBranchIfZero(obj, final_label, /* far_target */ false);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008205 }
8206
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008207 switch (type_check_kind) {
8208 case TypeCheckKind::kExactCheck:
8209 case TypeCheckKind::kArrayCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00008210 // /* HeapReference<Class> */ temp = obj->klass_
8211 GenerateReferenceLoadTwoRegisters(instruction,
8212 temp_loc,
8213 obj_loc,
8214 class_offset,
8215 maybe_temp2_loc,
8216 kWithoutReadBarrier);
8217
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008218 __ Cmp(temp, cls);
8219 // Jump to slow path for throwing the exception or doing a
8220 // more involved array check.
8221 __ B(ne, type_check_slow_path->GetEntryLabel());
8222 break;
8223 }
8224
8225 case TypeCheckKind::kAbstractClassCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00008226 // /* HeapReference<Class> */ temp = obj->klass_
8227 GenerateReferenceLoadTwoRegisters(instruction,
8228 temp_loc,
8229 obj_loc,
8230 class_offset,
8231 maybe_temp2_loc,
8232 kWithoutReadBarrier);
8233
Artem Serovcfbe9132016-10-14 15:58:56 +01008234 // If the class is abstract, we eagerly fetch the super class of the
8235 // object to avoid doing a comparison we know will fail.
8236 vixl32::Label loop;
8237 __ Bind(&loop);
8238 // /* HeapReference<Class> */ temp = temp->super_class_
Artem Serov657022c2016-11-23 14:19:38 +00008239 GenerateReferenceLoadOneRegister(instruction,
8240 temp_loc,
8241 super_offset,
8242 maybe_temp2_loc,
8243 kWithoutReadBarrier);
Artem Serovcfbe9132016-10-14 15:58:56 +01008244
8245 // If the class reference currently in `temp` is null, jump to the slow path to throw the
8246 // exception.
xueliang.zhongf51bc622016-11-04 09:23:32 +00008247 __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
Artem Serovcfbe9132016-10-14 15:58:56 +01008248
8249 // Otherwise, compare the classes.
8250 __ Cmp(temp, cls);
Artem Serov517d9f62016-12-12 15:51:15 +00008251 __ B(ne, &loop, /* far_target */ false);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008252 break;
8253 }
8254
8255 case TypeCheckKind::kClassHierarchyCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00008256 // /* HeapReference<Class> */ temp = obj->klass_
8257 GenerateReferenceLoadTwoRegisters(instruction,
8258 temp_loc,
8259 obj_loc,
8260 class_offset,
8261 maybe_temp2_loc,
8262 kWithoutReadBarrier);
8263
Artem Serovcfbe9132016-10-14 15:58:56 +01008264 // Walk over the class hierarchy to find a match.
8265 vixl32::Label loop;
8266 __ Bind(&loop);
8267 __ Cmp(temp, cls);
Anton Kirilov6f644202017-02-27 18:29:45 +00008268 __ B(eq, final_label, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01008269
8270 // /* HeapReference<Class> */ temp = temp->super_class_
Artem Serov657022c2016-11-23 14:19:38 +00008271 GenerateReferenceLoadOneRegister(instruction,
8272 temp_loc,
8273 super_offset,
8274 maybe_temp2_loc,
8275 kWithoutReadBarrier);
Artem Serovcfbe9132016-10-14 15:58:56 +01008276
8277 // If the class reference currently in `temp` is null, jump to the slow path to throw the
8278 // exception.
xueliang.zhongf51bc622016-11-04 09:23:32 +00008279 __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
Artem Serovcfbe9132016-10-14 15:58:56 +01008280 // Otherwise, jump to the beginning of the loop.
8281 __ B(&loop);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008282 break;
8283 }
8284
Artem Serovcfbe9132016-10-14 15:58:56 +01008285 case TypeCheckKind::kArrayObjectCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00008286 // /* HeapReference<Class> */ temp = obj->klass_
8287 GenerateReferenceLoadTwoRegisters(instruction,
8288 temp_loc,
8289 obj_loc,
8290 class_offset,
8291 maybe_temp2_loc,
8292 kWithoutReadBarrier);
8293
Artem Serovcfbe9132016-10-14 15:58:56 +01008294 // Do an exact check.
8295 __ Cmp(temp, cls);
Anton Kirilov6f644202017-02-27 18:29:45 +00008296 __ B(eq, final_label, /* far_target */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01008297
8298 // Otherwise, we need to check that the object's class is a non-primitive array.
8299 // /* HeapReference<Class> */ temp = temp->component_type_
Artem Serov657022c2016-11-23 14:19:38 +00008300 GenerateReferenceLoadOneRegister(instruction,
8301 temp_loc,
8302 component_offset,
8303 maybe_temp2_loc,
8304 kWithoutReadBarrier);
Artem Serovcfbe9132016-10-14 15:58:56 +01008305 // If the component type is null, jump to the slow path to throw the exception.
xueliang.zhongf51bc622016-11-04 09:23:32 +00008306 __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
Artem Serovcfbe9132016-10-14 15:58:56 +01008307 // Otherwise,the object is indeed an array, jump to label `check_non_primitive_component_type`
8308 // to further check that this component type is not a primitive type.
8309 GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, temp, temp, primitive_offset);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008310 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
xueliang.zhongf51bc622016-11-04 09:23:32 +00008311 __ CompareAndBranchIfNonZero(temp, type_check_slow_path->GetEntryLabel());
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008312 break;
8313 }
8314
8315 case TypeCheckKind::kUnresolvedCheck:
Artem Serov657022c2016-11-23 14:19:38 +00008316 // We always go into the type check slow path for the unresolved check case.
Artem Serovcfbe9132016-10-14 15:58:56 +01008317 // We cannot directly call the CheckCast runtime entry point
8318 // without resorting to a type checking slow path here (i.e. by
8319 // calling InvokeRuntime directly), as it would require to
8320 // assign fixed registers for the inputs of this HInstanceOf
8321 // instruction (following the runtime calling convention), which
8322 // might be cluttered by the potential first read barrier
8323 // emission at the beginning of this method.
Artem Serov657022c2016-11-23 14:19:38 +00008324
Artem Serovcfbe9132016-10-14 15:58:56 +01008325 __ B(type_check_slow_path->GetEntryLabel());
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008326 break;
Artem Serov657022c2016-11-23 14:19:38 +00008327
8328 case TypeCheckKind::kInterfaceCheck: {
8329 // Avoid read barriers to improve performance of the fast path. We can not get false
8330 // positives by doing this.
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 // /* HeapReference<Class> */ temp = temp->iftable_
8340 GenerateReferenceLoadTwoRegisters(instruction,
8341 temp_loc,
8342 temp_loc,
8343 iftable_offset,
8344 maybe_temp2_loc,
8345 kWithoutReadBarrier);
8346 // Iftable is never null.
8347 __ Ldr(RegisterFrom(maybe_temp2_loc), MemOperand(temp, array_length_offset));
8348 // Loop through the iftable and check if any class matches.
8349 vixl32::Label start_loop;
8350 __ Bind(&start_loop);
8351 __ CompareAndBranchIfZero(RegisterFrom(maybe_temp2_loc),
8352 type_check_slow_path->GetEntryLabel());
8353 __ Ldr(RegisterFrom(maybe_temp3_loc), MemOperand(temp, object_array_data_offset));
8354 GetAssembler()->MaybeUnpoisonHeapReference(RegisterFrom(maybe_temp3_loc));
8355 // Go to next interface.
8356 __ Add(temp, temp, Operand::From(2 * kHeapReferenceSize));
8357 __ Sub(RegisterFrom(maybe_temp2_loc), RegisterFrom(maybe_temp2_loc), 2);
8358 // Compare the classes and continue the loop if they do not match.
8359 __ Cmp(cls, RegisterFrom(maybe_temp3_loc));
Artem Serov517d9f62016-12-12 15:51:15 +00008360 __ B(ne, &start_loop, /* far_target */ false);
Artem Serov657022c2016-11-23 14:19:38 +00008361 break;
8362 }
Vladimir Marko175e7862018-03-27 09:03:13 +00008363
8364 case TypeCheckKind::kBitstringCheck: {
8365 // /* HeapReference<Class> */ temp = obj->klass_
8366 GenerateReferenceLoadTwoRegisters(instruction,
8367 temp_loc,
8368 obj_loc,
8369 class_offset,
8370 maybe_temp2_loc,
8371 kWithoutReadBarrier);
8372
8373 GenerateBitstringTypeCheckCompare(instruction, temp, SetFlags);
8374 __ B(ne, type_check_slow_path->GetEntryLabel());
8375 break;
8376 }
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008377 }
Anton Kirilov6f644202017-02-27 18:29:45 +00008378 if (done.IsReferenced()) {
8379 __ Bind(&done);
8380 }
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008381
8382 __ Bind(type_check_slow_path->GetExitLabel());
8383}
8384
Artem Serov551b28f2016-10-18 19:11:30 +01008385void LocationsBuilderARMVIXL::VisitMonitorOperation(HMonitorOperation* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01008386 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
8387 instruction, LocationSummary::kCallOnMainOnly);
Artem Serov551b28f2016-10-18 19:11:30 +01008388 InvokeRuntimeCallingConventionARMVIXL calling_convention;
8389 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
8390}
8391
8392void InstructionCodeGeneratorARMVIXL::VisitMonitorOperation(HMonitorOperation* instruction) {
8393 codegen_->InvokeRuntime(instruction->IsEnter() ? kQuickLockObject : kQuickUnlockObject,
8394 instruction,
8395 instruction->GetDexPc());
8396 if (instruction->IsEnter()) {
8397 CheckEntrypointTypes<kQuickLockObject, void, mirror::Object*>();
8398 } else {
8399 CheckEntrypointTypes<kQuickUnlockObject, void, mirror::Object*>();
8400 }
Orion Hodson4c8e12e2018-05-18 08:33:20 +01008401 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ 18);
Artem Serov551b28f2016-10-18 19:11:30 +01008402}
8403
Artem Serov02109dd2016-09-23 17:17:54 +01008404void LocationsBuilderARMVIXL::VisitAnd(HAnd* instruction) {
8405 HandleBitwiseOperation(instruction, AND);
8406}
8407
8408void LocationsBuilderARMVIXL::VisitOr(HOr* instruction) {
8409 HandleBitwiseOperation(instruction, ORR);
8410}
8411
8412void LocationsBuilderARMVIXL::VisitXor(HXor* instruction) {
8413 HandleBitwiseOperation(instruction, EOR);
8414}
8415
8416void LocationsBuilderARMVIXL::HandleBitwiseOperation(HBinaryOperation* instruction, Opcode opcode) {
8417 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01008418 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008419 DCHECK(instruction->GetResultType() == DataType::Type::kInt32
8420 || instruction->GetResultType() == DataType::Type::kInt64);
Artem Serov02109dd2016-09-23 17:17:54 +01008421 // Note: GVN reorders commutative operations to have the constant on the right hand side.
8422 locations->SetInAt(0, Location::RequiresRegister());
8423 locations->SetInAt(1, ArmEncodableConstantOrRegister(instruction->InputAt(1), opcode));
8424 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
8425}
8426
8427void InstructionCodeGeneratorARMVIXL::VisitAnd(HAnd* instruction) {
8428 HandleBitwiseOperation(instruction);
8429}
8430
8431void InstructionCodeGeneratorARMVIXL::VisitOr(HOr* instruction) {
8432 HandleBitwiseOperation(instruction);
8433}
8434
8435void InstructionCodeGeneratorARMVIXL::VisitXor(HXor* instruction) {
8436 HandleBitwiseOperation(instruction);
8437}
8438
Artem Serov2bbc9532016-10-21 11:51:50 +01008439void LocationsBuilderARMVIXL::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) {
8440 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01008441 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008442 DCHECK(instruction->GetResultType() == DataType::Type::kInt32
8443 || instruction->GetResultType() == DataType::Type::kInt64);
Artem Serov2bbc9532016-10-21 11:51:50 +01008444
8445 locations->SetInAt(0, Location::RequiresRegister());
8446 locations->SetInAt(1, Location::RequiresRegister());
8447 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
8448}
8449
8450void InstructionCodeGeneratorARMVIXL::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) {
8451 LocationSummary* locations = instruction->GetLocations();
8452 Location first = locations->InAt(0);
8453 Location second = locations->InAt(1);
8454 Location out = locations->Out();
8455
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008456 if (instruction->GetResultType() == DataType::Type::kInt32) {
Artem Serov2bbc9532016-10-21 11:51:50 +01008457 vixl32::Register first_reg = RegisterFrom(first);
8458 vixl32::Register second_reg = RegisterFrom(second);
8459 vixl32::Register out_reg = RegisterFrom(out);
8460
8461 switch (instruction->GetOpKind()) {
8462 case HInstruction::kAnd:
8463 __ Bic(out_reg, first_reg, second_reg);
8464 break;
8465 case HInstruction::kOr:
8466 __ Orn(out_reg, first_reg, second_reg);
8467 break;
8468 // There is no EON on arm.
8469 case HInstruction::kXor:
8470 default:
8471 LOG(FATAL) << "Unexpected instruction " << instruction->DebugName();
8472 UNREACHABLE();
8473 }
8474 return;
8475
8476 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008477 DCHECK_EQ(instruction->GetResultType(), DataType::Type::kInt64);
Artem Serov2bbc9532016-10-21 11:51:50 +01008478 vixl32::Register first_low = LowRegisterFrom(first);
8479 vixl32::Register first_high = HighRegisterFrom(first);
8480 vixl32::Register second_low = LowRegisterFrom(second);
8481 vixl32::Register second_high = HighRegisterFrom(second);
8482 vixl32::Register out_low = LowRegisterFrom(out);
8483 vixl32::Register out_high = HighRegisterFrom(out);
8484
8485 switch (instruction->GetOpKind()) {
8486 case HInstruction::kAnd:
8487 __ Bic(out_low, first_low, second_low);
8488 __ Bic(out_high, first_high, second_high);
8489 break;
8490 case HInstruction::kOr:
8491 __ Orn(out_low, first_low, second_low);
8492 __ Orn(out_high, first_high, second_high);
8493 break;
8494 // There is no EON on arm.
8495 case HInstruction::kXor:
8496 default:
8497 LOG(FATAL) << "Unexpected instruction " << instruction->DebugName();
8498 UNREACHABLE();
8499 }
8500 }
8501}
8502
Anton Kirilov74234da2017-01-13 14:42:47 +00008503void LocationsBuilderARMVIXL::VisitDataProcWithShifterOp(
8504 HDataProcWithShifterOp* instruction) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008505 DCHECK(instruction->GetType() == DataType::Type::kInt32 ||
8506 instruction->GetType() == DataType::Type::kInt64);
Anton Kirilov74234da2017-01-13 14:42:47 +00008507 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01008508 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008509 const bool overlap = instruction->GetType() == DataType::Type::kInt64 &&
Anton Kirilov74234da2017-01-13 14:42:47 +00008510 HDataProcWithShifterOp::IsExtensionOp(instruction->GetOpKind());
8511
8512 locations->SetInAt(0, Location::RequiresRegister());
8513 locations->SetInAt(1, Location::RequiresRegister());
8514 locations->SetOut(Location::RequiresRegister(),
8515 overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap);
8516}
8517
8518void InstructionCodeGeneratorARMVIXL::VisitDataProcWithShifterOp(
8519 HDataProcWithShifterOp* instruction) {
8520 const LocationSummary* const locations = instruction->GetLocations();
8521 const HInstruction::InstructionKind kind = instruction->GetInstrKind();
8522 const HDataProcWithShifterOp::OpKind op_kind = instruction->GetOpKind();
8523
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008524 if (instruction->GetType() == DataType::Type::kInt32) {
Anton Kirilov420ee302017-02-21 18:10:26 +00008525 const vixl32::Register first = InputRegisterAt(instruction, 0);
8526 const vixl32::Register output = OutputRegister(instruction);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008527 const vixl32::Register second = instruction->InputAt(1)->GetType() == DataType::Type::kInt64
Anton Kirilov74234da2017-01-13 14:42:47 +00008528 ? LowRegisterFrom(locations->InAt(1))
8529 : InputRegisterAt(instruction, 1);
8530
Anton Kirilov420ee302017-02-21 18:10:26 +00008531 if (HDataProcWithShifterOp::IsExtensionOp(op_kind)) {
8532 DCHECK_EQ(kind, HInstruction::kAdd);
8533
8534 switch (op_kind) {
8535 case HDataProcWithShifterOp::kUXTB:
8536 __ Uxtab(output, first, second);
8537 break;
8538 case HDataProcWithShifterOp::kUXTH:
8539 __ Uxtah(output, first, second);
8540 break;
8541 case HDataProcWithShifterOp::kSXTB:
8542 __ Sxtab(output, first, second);
8543 break;
8544 case HDataProcWithShifterOp::kSXTH:
8545 __ Sxtah(output, first, second);
8546 break;
8547 default:
8548 LOG(FATAL) << "Unexpected operation kind: " << op_kind;
8549 UNREACHABLE();
8550 }
8551 } else {
8552 GenerateDataProcInstruction(kind,
8553 output,
8554 first,
8555 Operand(second,
8556 ShiftFromOpKind(op_kind),
8557 instruction->GetShiftAmount()),
8558 codegen_);
8559 }
Anton Kirilov74234da2017-01-13 14:42:47 +00008560 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008561 DCHECK_EQ(instruction->GetType(), DataType::Type::kInt64);
Anton Kirilov74234da2017-01-13 14:42:47 +00008562
8563 if (HDataProcWithShifterOp::IsExtensionOp(op_kind)) {
8564 const vixl32::Register second = InputRegisterAt(instruction, 1);
8565
8566 DCHECK(!LowRegisterFrom(locations->Out()).Is(second));
8567 GenerateDataProc(kind,
8568 locations->Out(),
8569 locations->InAt(0),
8570 second,
8571 Operand(second, ShiftType::ASR, 31),
8572 codegen_);
8573 } else {
8574 GenerateLongDataProc(instruction, codegen_);
8575 }
8576 }
8577}
8578
Artem Serov02109dd2016-09-23 17:17:54 +01008579// TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
8580void InstructionCodeGeneratorARMVIXL::GenerateAndConst(vixl32::Register out,
8581 vixl32::Register first,
8582 uint32_t value) {
8583 // Optimize special cases for individual halfs of `and-long` (`and` is simplified earlier).
8584 if (value == 0xffffffffu) {
8585 if (!out.Is(first)) {
8586 __ Mov(out, first);
8587 }
8588 return;
8589 }
8590 if (value == 0u) {
8591 __ Mov(out, 0);
8592 return;
8593 }
8594 if (GetAssembler()->ShifterOperandCanHold(AND, value)) {
Anton Kiriloveffd5bf2017-02-28 16:59:15 +00008595 __ And(out, first, value);
8596 } else if (GetAssembler()->ShifterOperandCanHold(BIC, ~value)) {
8597 __ Bic(out, first, ~value);
Artem Serov02109dd2016-09-23 17:17:54 +01008598 } else {
Anton Kiriloveffd5bf2017-02-28 16:59:15 +00008599 DCHECK(IsPowerOfTwo(value + 1));
8600 __ Ubfx(out, first, 0, WhichPowerOf2(value + 1));
Artem Serov02109dd2016-09-23 17:17:54 +01008601 }
8602}
8603
8604// TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
8605void InstructionCodeGeneratorARMVIXL::GenerateOrrConst(vixl32::Register out,
8606 vixl32::Register first,
8607 uint32_t value) {
8608 // Optimize special cases for individual halfs of `or-long` (`or` is simplified earlier).
8609 if (value == 0u) {
8610 if (!out.Is(first)) {
8611 __ Mov(out, first);
8612 }
8613 return;
8614 }
8615 if (value == 0xffffffffu) {
8616 __ Mvn(out, 0);
8617 return;
8618 }
8619 if (GetAssembler()->ShifterOperandCanHold(ORR, value)) {
8620 __ Orr(out, first, value);
8621 } else {
8622 DCHECK(GetAssembler()->ShifterOperandCanHold(ORN, ~value));
8623 __ Orn(out, first, ~value);
8624 }
8625}
8626
8627// TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
8628void InstructionCodeGeneratorARMVIXL::GenerateEorConst(vixl32::Register out,
8629 vixl32::Register first,
8630 uint32_t value) {
8631 // Optimize special case for individual halfs of `xor-long` (`xor` is simplified earlier).
8632 if (value == 0u) {
8633 if (!out.Is(first)) {
8634 __ Mov(out, first);
8635 }
8636 return;
8637 }
8638 __ Eor(out, first, value);
8639}
8640
Anton Kirilovdda43962016-11-21 19:55:20 +00008641void InstructionCodeGeneratorARMVIXL::GenerateAddLongConst(Location out,
8642 Location first,
8643 uint64_t value) {
8644 vixl32::Register out_low = LowRegisterFrom(out);
8645 vixl32::Register out_high = HighRegisterFrom(out);
8646 vixl32::Register first_low = LowRegisterFrom(first);
8647 vixl32::Register first_high = HighRegisterFrom(first);
8648 uint32_t value_low = Low32Bits(value);
8649 uint32_t value_high = High32Bits(value);
8650 if (value_low == 0u) {
8651 if (!out_low.Is(first_low)) {
8652 __ Mov(out_low, first_low);
8653 }
8654 __ Add(out_high, first_high, value_high);
8655 return;
8656 }
8657 __ Adds(out_low, first_low, value_low);
Vladimir Markof0a6a1d2018-01-08 14:23:56 +00008658 if (GetAssembler()->ShifterOperandCanHold(ADC, value_high)) {
Anton Kirilovdda43962016-11-21 19:55:20 +00008659 __ Adc(out_high, first_high, value_high);
Anton Kirilovdda43962016-11-21 19:55:20 +00008660 } else {
Vladimir Markof0a6a1d2018-01-08 14:23:56 +00008661 DCHECK(GetAssembler()->ShifterOperandCanHold(SBC, ~value_high));
8662 __ Sbc(out_high, first_high, ~value_high);
Anton Kirilovdda43962016-11-21 19:55:20 +00008663 }
8664}
8665
Artem Serov02109dd2016-09-23 17:17:54 +01008666void InstructionCodeGeneratorARMVIXL::HandleBitwiseOperation(HBinaryOperation* instruction) {
8667 LocationSummary* locations = instruction->GetLocations();
8668 Location first = locations->InAt(0);
8669 Location second = locations->InAt(1);
8670 Location out = locations->Out();
8671
8672 if (second.IsConstant()) {
8673 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
8674 uint32_t value_low = Low32Bits(value);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008675 if (instruction->GetResultType() == DataType::Type::kInt32) {
Artem Serov02109dd2016-09-23 17:17:54 +01008676 vixl32::Register first_reg = InputRegisterAt(instruction, 0);
8677 vixl32::Register out_reg = OutputRegister(instruction);
8678 if (instruction->IsAnd()) {
8679 GenerateAndConst(out_reg, first_reg, value_low);
8680 } else if (instruction->IsOr()) {
8681 GenerateOrrConst(out_reg, first_reg, value_low);
8682 } else {
8683 DCHECK(instruction->IsXor());
8684 GenerateEorConst(out_reg, first_reg, value_low);
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 uint32_t value_high = High32Bits(value);
8689 vixl32::Register first_low = LowRegisterFrom(first);
8690 vixl32::Register first_high = HighRegisterFrom(first);
8691 vixl32::Register out_low = LowRegisterFrom(out);
8692 vixl32::Register out_high = HighRegisterFrom(out);
8693 if (instruction->IsAnd()) {
8694 GenerateAndConst(out_low, first_low, value_low);
8695 GenerateAndConst(out_high, first_high, value_high);
8696 } else if (instruction->IsOr()) {
8697 GenerateOrrConst(out_low, first_low, value_low);
8698 GenerateOrrConst(out_high, first_high, value_high);
8699 } else {
8700 DCHECK(instruction->IsXor());
8701 GenerateEorConst(out_low, first_low, value_low);
8702 GenerateEorConst(out_high, first_high, value_high);
8703 }
8704 }
8705 return;
8706 }
8707
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008708 if (instruction->GetResultType() == DataType::Type::kInt32) {
Artem Serov02109dd2016-09-23 17:17:54 +01008709 vixl32::Register first_reg = InputRegisterAt(instruction, 0);
8710 vixl32::Register second_reg = InputRegisterAt(instruction, 1);
8711 vixl32::Register out_reg = OutputRegister(instruction);
8712 if (instruction->IsAnd()) {
8713 __ And(out_reg, first_reg, second_reg);
8714 } else if (instruction->IsOr()) {
8715 __ Orr(out_reg, first_reg, second_reg);
8716 } else {
8717 DCHECK(instruction->IsXor());
8718 __ Eor(out_reg, first_reg, second_reg);
8719 }
8720 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008721 DCHECK_EQ(instruction->GetResultType(), DataType::Type::kInt64);
Artem Serov02109dd2016-09-23 17:17:54 +01008722 vixl32::Register first_low = LowRegisterFrom(first);
8723 vixl32::Register first_high = HighRegisterFrom(first);
8724 vixl32::Register second_low = LowRegisterFrom(second);
8725 vixl32::Register second_high = HighRegisterFrom(second);
8726 vixl32::Register out_low = LowRegisterFrom(out);
8727 vixl32::Register out_high = HighRegisterFrom(out);
8728 if (instruction->IsAnd()) {
8729 __ And(out_low, first_low, second_low);
8730 __ And(out_high, first_high, second_high);
8731 } else if (instruction->IsOr()) {
8732 __ Orr(out_low, first_low, second_low);
8733 __ Orr(out_high, first_high, second_high);
8734 } else {
8735 DCHECK(instruction->IsXor());
8736 __ Eor(out_low, first_low, second_low);
8737 __ Eor(out_high, first_high, second_high);
8738 }
8739 }
8740}
8741
Artem Serovcfbe9132016-10-14 15:58:56 +01008742void InstructionCodeGeneratorARMVIXL::GenerateReferenceLoadOneRegister(
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008743 HInstruction* instruction,
Artem Serovcfbe9132016-10-14 15:58:56 +01008744 Location out,
8745 uint32_t offset,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008746 Location maybe_temp,
8747 ReadBarrierOption read_barrier_option) {
Artem Serovcfbe9132016-10-14 15:58:56 +01008748 vixl32::Register out_reg = RegisterFrom(out);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008749 if (read_barrier_option == kWithReadBarrier) {
8750 CHECK(kEmitCompilerReadBarrier);
8751 DCHECK(maybe_temp.IsRegister()) << maybe_temp;
8752 if (kUseBakerReadBarrier) {
8753 // Load with fast path based Baker's read barrier.
8754 // /* HeapReference<Object> */ out = *(out + offset)
8755 codegen_->GenerateFieldLoadWithBakerReadBarrier(
8756 instruction, out, out_reg, offset, maybe_temp, /* needs_null_check */ false);
8757 } else {
8758 // Load with slow path based read barrier.
8759 // Save the value of `out` into `maybe_temp` before overwriting it
8760 // in the following move operation, as we will need it for the
8761 // read barrier below.
8762 __ Mov(RegisterFrom(maybe_temp), out_reg);
8763 // /* HeapReference<Object> */ out = *(out + offset)
8764 GetAssembler()->LoadFromOffset(kLoadWord, out_reg, out_reg, offset);
8765 codegen_->GenerateReadBarrierSlow(instruction, out, out, maybe_temp, offset);
8766 }
Artem Serovcfbe9132016-10-14 15:58:56 +01008767 } else {
8768 // Plain load with no read barrier.
8769 // /* HeapReference<Object> */ out = *(out + offset)
8770 GetAssembler()->LoadFromOffset(kLoadWord, out_reg, out_reg, offset);
8771 GetAssembler()->MaybeUnpoisonHeapReference(out_reg);
8772 }
8773}
8774
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008775void InstructionCodeGeneratorARMVIXL::GenerateReferenceLoadTwoRegisters(
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008776 HInstruction* instruction,
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008777 Location out,
8778 Location obj,
8779 uint32_t offset,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008780 Location maybe_temp,
8781 ReadBarrierOption read_barrier_option) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008782 vixl32::Register out_reg = RegisterFrom(out);
8783 vixl32::Register obj_reg = RegisterFrom(obj);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008784 if (read_barrier_option == kWithReadBarrier) {
8785 CHECK(kEmitCompilerReadBarrier);
8786 if (kUseBakerReadBarrier) {
8787 DCHECK(maybe_temp.IsRegister()) << maybe_temp;
8788 // Load with fast path based Baker's read barrier.
8789 // /* HeapReference<Object> */ out = *(obj + offset)
8790 codegen_->GenerateFieldLoadWithBakerReadBarrier(
8791 instruction, out, obj_reg, offset, maybe_temp, /* needs_null_check */ false);
8792 } else {
8793 // Load with slow path based read barrier.
8794 // /* HeapReference<Object> */ out = *(obj + offset)
8795 GetAssembler()->LoadFromOffset(kLoadWord, out_reg, obj_reg, offset);
8796 codegen_->GenerateReadBarrierSlow(instruction, out, out, obj, offset);
8797 }
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008798 } else {
8799 // Plain load with no read barrier.
8800 // /* HeapReference<Object> */ out = *(obj + offset)
8801 GetAssembler()->LoadFromOffset(kLoadWord, out_reg, obj_reg, offset);
8802 GetAssembler()->MaybeUnpoisonHeapReference(out_reg);
8803 }
8804}
8805
Vladimir Markoca1e0382018-04-11 09:58:41 +00008806void CodeGeneratorARMVIXL::GenerateGcRootFieldLoad(
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008807 HInstruction* instruction,
Scott Wakelinga7812ae2016-10-17 10:03:36 +01008808 Location root,
8809 vixl32::Register obj,
8810 uint32_t offset,
Artem Serovd4cc5b22016-11-04 11:19:09 +00008811 ReadBarrierOption read_barrier_option) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01008812 vixl32::Register root_reg = RegisterFrom(root);
Artem Serovd4cc5b22016-11-04 11:19:09 +00008813 if (read_barrier_option == kWithReadBarrier) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008814 DCHECK(kEmitCompilerReadBarrier);
8815 if (kUseBakerReadBarrier) {
8816 // Fast path implementation of art::ReadBarrier::BarrierForRoot when
Roland Levillainba650a42017-03-06 13:52:32 +00008817 // Baker's read barrier are used.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008818 if (kBakerReadBarrierLinkTimeThunksEnableForGcRoots &&
8819 !Runtime::Current()->UseJitCompilation()) {
Roland Levillain6d729a72017-06-30 18:34:01 +01008820 // Query `art::Thread::Current()->GetIsGcMarking()` (stored in
8821 // the Marking Register) to decide whether we need to enter
8822 // the slow path to mark the GC root.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008823 //
8824 // We use link-time generated thunks for the slow path. That thunk
8825 // checks the reference and jumps to the entrypoint if needed.
8826 //
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008827 // lr = &return_address;
8828 // GcRoot<mirror::Object> root = *(obj+offset); // Original reference load.
Roland Levillain6d729a72017-06-30 18:34:01 +01008829 // if (mr) { // Thread::Current()->GetIsGcMarking()
8830 // goto gc_root_thunk<root_reg>(lr)
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008831 // }
8832 // return_address:
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008833
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008834 UseScratchRegisterScope temps(GetVIXLAssembler());
Vladimir Markodcd117e2018-04-19 11:54:00 +01008835 temps.Exclude(ip);
Vladimir Marko88abba22017-05-03 17:09:25 +01008836 bool narrow = CanEmitNarrowLdr(root_reg, obj, offset);
Vladimir Markoca1e0382018-04-11 09:58:41 +00008837 uint32_t custom_data = EncodeBakerReadBarrierGcRootData(root_reg.GetCode(), narrow);
8838 vixl32::Label* bne_label = NewBakerReadBarrierPatch(custom_data);
Roland Levillainba650a42017-03-06 13:52:32 +00008839
Roland Levillain6d729a72017-06-30 18:34:01 +01008840 vixl::EmissionCheckScope guard(GetVIXLAssembler(), 4 * vixl32::kMaxInstructionSizeInBytes);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008841 vixl32::Label return_address;
8842 EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
Roland Levillain6d729a72017-06-30 18:34:01 +01008843 __ cmp(mr, Operand(0));
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008844 // Currently the offset is always within range. If that changes,
8845 // we shall have to split the load the same way as for fields.
8846 DCHECK_LT(offset, kReferenceLoadMinFarOffset);
Vladimir Marko88abba22017-05-03 17:09:25 +01008847 ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
8848 __ ldr(EncodingSize(narrow ? Narrow : Wide), root_reg, MemOperand(obj, offset));
Vladimir Markoca1e0382018-04-11 09:58:41 +00008849 EmitPlaceholderBne(this, bne_label);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008850 __ Bind(&return_address);
Vladimir Marko88abba22017-05-03 17:09:25 +01008851 DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
8852 narrow ? BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_OFFSET
8853 : BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_OFFSET);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008854 } else {
Roland Levillain6d729a72017-06-30 18:34:01 +01008855 // Query `art::Thread::Current()->GetIsGcMarking()` (stored in
8856 // the Marking Register) to decide whether we need to enter
8857 // the slow path to mark the GC root.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008858 //
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008859 // GcRoot<mirror::Object> root = *(obj+offset); // Original reference load.
Roland Levillain6d729a72017-06-30 18:34:01 +01008860 // if (mr) { // Thread::Current()->GetIsGcMarking()
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008861 // // Slow path.
Roland Levillain6d729a72017-06-30 18:34:01 +01008862 // entrypoint = Thread::Current()->pReadBarrierMarkReg ## root.reg()
8863 // root = entrypoint(root); // root = ReadBarrier::Mark(root); // Entry point call.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008864 // }
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008865
Roland Levillain6d729a72017-06-30 18:34:01 +01008866 // Slow path marking the GC root `root`. The entrypoint will
8867 // be loaded by the slow path code.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008868 SlowPathCodeARMVIXL* slow_path =
Vladimir Markoca1e0382018-04-11 09:58:41 +00008869 new (GetScopedAllocator()) ReadBarrierMarkSlowPathARMVIXL(instruction, root);
8870 AddSlowPath(slow_path);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008871
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008872 // /* GcRoot<mirror::Object> */ root = *(obj + offset)
8873 GetAssembler()->LoadFromOffset(kLoadWord, root_reg, obj, offset);
8874 static_assert(
8875 sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(GcRoot<mirror::Object>),
8876 "art::mirror::CompressedReference<mirror::Object> and art::GcRoot<mirror::Object> "
8877 "have different sizes.");
8878 static_assert(sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(int32_t),
8879 "art::mirror::CompressedReference<mirror::Object> and int32_t "
8880 "have different sizes.");
8881
Roland Levillain6d729a72017-06-30 18:34:01 +01008882 __ CompareAndBranchIfNonZero(mr, slow_path->GetEntryLabel());
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008883 __ Bind(slow_path->GetExitLabel());
8884 }
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008885 } else {
8886 // GC root loaded through a slow path for read barriers other
8887 // than Baker's.
8888 // /* GcRoot<mirror::Object>* */ root = obj + offset
8889 __ Add(root_reg, obj, offset);
8890 // /* mirror::Object* */ root = root->Read()
Vladimir Markoca1e0382018-04-11 09:58:41 +00008891 GenerateReadBarrierForRootSlow(instruction, root, root);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008892 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01008893 } else {
8894 // Plain GC root load with no read barrier.
8895 // /* GcRoot<mirror::Object> */ root = *(obj + offset)
8896 GetAssembler()->LoadFromOffset(kLoadWord, root_reg, obj, offset);
8897 // Note that GC roots are not affected by heap poisoning, thus we
8898 // do not have to unpoison `root_reg` here.
8899 }
Orion Hodson4c8e12e2018-05-18 08:33:20 +01008900 MaybeGenerateMarkingRegisterCheck(/* code */ 19);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01008901}
8902
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008903void CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
8904 Location ref,
8905 vixl32::Register obj,
8906 uint32_t offset,
8907 Location temp,
8908 bool needs_null_check) {
8909 DCHECK(kEmitCompilerReadBarrier);
8910 DCHECK(kUseBakerReadBarrier);
8911
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008912 if (kBakerReadBarrierLinkTimeThunksEnableForFields &&
8913 !Runtime::Current()->UseJitCompilation()) {
Roland Levillain6d729a72017-06-30 18:34:01 +01008914 // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
8915 // Marking Register) to decide whether we need to enter the slow
8916 // path to mark the reference. Then, in the slow path, check the
8917 // gray bit in the lock word of the reference's holder (`obj`) to
8918 // decide whether to mark `ref` or not.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008919 //
8920 // We use link-time generated thunks for the slow path. That thunk checks
8921 // the holder and jumps to the entrypoint if needed. If the holder is not
8922 // gray, it creates a fake dependency and returns to the LDR instruction.
8923 //
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008924 // lr = &gray_return_address;
Roland Levillain6d729a72017-06-30 18:34:01 +01008925 // if (mr) { // Thread::Current()->GetIsGcMarking()
8926 // goto field_thunk<holder_reg, base_reg>(lr)
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008927 // }
8928 // not_gray_return_address:
8929 // // Original reference load. If the offset is too large to fit
8930 // // into LDR, we use an adjusted base register here.
Vladimir Marko88abba22017-05-03 17:09:25 +01008931 // HeapReference<mirror::Object> reference = *(obj+offset);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008932 // gray_return_address:
8933
8934 DCHECK_ALIGNED(offset, sizeof(mirror::HeapReference<mirror::Object>));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008935 vixl32::Register ref_reg = RegisterFrom(ref, DataType::Type::kReference);
Vladimir Marko88abba22017-05-03 17:09:25 +01008936 bool narrow = CanEmitNarrowLdr(ref_reg, obj, offset);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008937 vixl32::Register base = obj;
8938 if (offset >= kReferenceLoadMinFarOffset) {
8939 base = RegisterFrom(temp);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008940 static_assert(IsPowerOfTwo(kReferenceLoadMinFarOffset), "Expecting a power of 2.");
8941 __ Add(base, obj, Operand(offset & ~(kReferenceLoadMinFarOffset - 1u)));
8942 offset &= (kReferenceLoadMinFarOffset - 1u);
Vladimir Marko88abba22017-05-03 17:09:25 +01008943 // Use narrow LDR only for small offsets. Generating narrow encoding LDR for the large
8944 // offsets with `(offset & (kReferenceLoadMinFarOffset - 1u)) < 32u` would most likely
8945 // increase the overall code size when taking the generated thunks into account.
8946 DCHECK(!narrow);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008947 }
8948 UseScratchRegisterScope temps(GetVIXLAssembler());
Vladimir Markodcd117e2018-04-19 11:54:00 +01008949 temps.Exclude(ip);
Vladimir Markoca1e0382018-04-11 09:58:41 +00008950 uint32_t custom_data = EncodeBakerReadBarrierFieldData(base.GetCode(), obj.GetCode(), narrow);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008951 vixl32::Label* bne_label = NewBakerReadBarrierPatch(custom_data);
8952
Roland Levillain5daa4952017-07-03 17:23:56 +01008953 {
8954 vixl::EmissionCheckScope guard(
8955 GetVIXLAssembler(),
8956 (kPoisonHeapReferences ? 5u : 4u) * vixl32::kMaxInstructionSizeInBytes);
8957 vixl32::Label return_address;
8958 EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
8959 __ cmp(mr, Operand(0));
8960 EmitPlaceholderBne(this, bne_label);
8961 ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
8962 __ ldr(EncodingSize(narrow ? Narrow : Wide), ref_reg, MemOperand(base, offset));
8963 if (needs_null_check) {
8964 MaybeRecordImplicitNullCheck(instruction);
Vladimir Marko88abba22017-05-03 17:09:25 +01008965 }
Roland Levillain5daa4952017-07-03 17:23:56 +01008966 // Note: We need a specific width for the unpoisoning NEG.
8967 if (kPoisonHeapReferences) {
8968 if (narrow) {
8969 // The only 16-bit encoding is T1 which sets flags outside IT block (i.e. RSBS, not RSB).
8970 __ rsbs(EncodingSize(Narrow), ref_reg, ref_reg, Operand(0));
8971 } else {
8972 __ rsb(EncodingSize(Wide), ref_reg, ref_reg, Operand(0));
8973 }
8974 }
8975 __ Bind(&return_address);
8976 DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
8977 narrow ? BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET
8978 : BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008979 }
Orion Hodson4c8e12e2018-05-18 08:33:20 +01008980 MaybeGenerateMarkingRegisterCheck(/* code */ 20, /* temp_loc */ LocationFrom(ip));
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008981 return;
8982 }
8983
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008984 // /* HeapReference<Object> */ ref = *(obj + offset)
8985 Location no_index = Location::NoLocation();
8986 ScaleFactor no_scale_factor = TIMES_1;
8987 GenerateReferenceLoadWithBakerReadBarrier(
8988 instruction, ref, obj, offset, no_index, no_scale_factor, temp, needs_null_check);
Roland Levillain6070e882016-11-03 17:51:58 +00008989}
8990
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008991void CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction,
8992 Location ref,
8993 vixl32::Register obj,
8994 uint32_t data_offset,
8995 Location index,
8996 Location temp,
8997 bool needs_null_check) {
8998 DCHECK(kEmitCompilerReadBarrier);
8999 DCHECK(kUseBakerReadBarrier);
9000
9001 static_assert(
9002 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
9003 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009004 ScaleFactor scale_factor = TIMES_4;
9005
9006 if (kBakerReadBarrierLinkTimeThunksEnableForArrays &&
9007 !Runtime::Current()->UseJitCompilation()) {
Roland Levillain6d729a72017-06-30 18:34:01 +01009008 // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
9009 // Marking Register) to decide whether we need to enter the slow
9010 // path to mark the reference. Then, in the slow path, check the
9011 // gray bit in the lock word of the reference's holder (`obj`) to
9012 // decide whether to mark `ref` or not.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009013 //
9014 // We use link-time generated thunks for the slow path. That thunk checks
9015 // the holder and jumps to the entrypoint if needed. If the holder is not
9016 // gray, it creates a fake dependency and returns to the LDR instruction.
9017 //
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009018 // lr = &gray_return_address;
Roland Levillain6d729a72017-06-30 18:34:01 +01009019 // if (mr) { // Thread::Current()->GetIsGcMarking()
9020 // goto array_thunk<base_reg>(lr)
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009021 // }
9022 // not_gray_return_address:
9023 // // Original reference load. If the offset is too large to fit
9024 // // into LDR, we use an adjusted base register here.
Vladimir Marko88abba22017-05-03 17:09:25 +01009025 // HeapReference<mirror::Object> reference = data[index];
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009026 // gray_return_address:
9027
9028 DCHECK(index.IsValid());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01009029 vixl32::Register index_reg = RegisterFrom(index, DataType::Type::kInt32);
9030 vixl32::Register ref_reg = RegisterFrom(ref, DataType::Type::kReference);
9031 vixl32::Register data_reg = RegisterFrom(temp, DataType::Type::kInt32); // Raw pointer.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009032
9033 UseScratchRegisterScope temps(GetVIXLAssembler());
Vladimir Markodcd117e2018-04-19 11:54:00 +01009034 temps.Exclude(ip);
Vladimir Markoca1e0382018-04-11 09:58:41 +00009035 uint32_t custom_data = EncodeBakerReadBarrierArrayData(data_reg.GetCode());
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009036 vixl32::Label* bne_label = NewBakerReadBarrierPatch(custom_data);
9037
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009038 __ Add(data_reg, obj, Operand(data_offset));
Roland Levillain5daa4952017-07-03 17:23:56 +01009039 {
9040 vixl::EmissionCheckScope guard(
9041 GetVIXLAssembler(),
9042 (kPoisonHeapReferences ? 5u : 4u) * vixl32::kMaxInstructionSizeInBytes);
9043 vixl32::Label return_address;
9044 EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
9045 __ cmp(mr, Operand(0));
9046 EmitPlaceholderBne(this, bne_label);
9047 ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
9048 __ ldr(ref_reg, MemOperand(data_reg, index_reg, vixl32::LSL, scale_factor));
9049 DCHECK(!needs_null_check); // The thunk cannot handle the null check.
9050 // Note: We need a Wide NEG for the unpoisoning.
9051 if (kPoisonHeapReferences) {
9052 __ rsb(EncodingSize(Wide), ref_reg, ref_reg, Operand(0));
9053 }
9054 __ Bind(&return_address);
9055 DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
9056 BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009057 }
Orion Hodson4c8e12e2018-05-18 08:33:20 +01009058 MaybeGenerateMarkingRegisterCheck(/* code */ 21, /* temp_loc */ LocationFrom(ip));
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009059 return;
9060 }
9061
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009062 // /* HeapReference<Object> */ ref =
9063 // *(obj + data_offset + index * sizeof(HeapReference<Object>))
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009064 GenerateReferenceLoadWithBakerReadBarrier(
9065 instruction, ref, obj, data_offset, index, scale_factor, temp, needs_null_check);
Roland Levillain6070e882016-11-03 17:51:58 +00009066}
9067
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009068void CodeGeneratorARMVIXL::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
9069 Location ref,
9070 vixl32::Register obj,
9071 uint32_t offset,
9072 Location index,
9073 ScaleFactor scale_factor,
9074 Location temp,
Roland Levillainff487002017-03-07 16:50:01 +00009075 bool needs_null_check) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009076 DCHECK(kEmitCompilerReadBarrier);
9077 DCHECK(kUseBakerReadBarrier);
9078
Roland Levillain6d729a72017-06-30 18:34:01 +01009079 // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
9080 // Marking Register) to decide whether we need to enter the slow
9081 // path to mark the reference. Then, in the slow path, check the
9082 // gray bit in the lock word of the reference's holder (`obj`) to
9083 // decide whether to mark `ref` or not.
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009084 //
Roland Levillain6d729a72017-06-30 18:34:01 +01009085 // if (mr) { // Thread::Current()->GetIsGcMarking()
Roland Levillainff487002017-03-07 16:50:01 +00009086 // // Slow path.
9087 // uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
9088 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
9089 // HeapReference<mirror::Object> ref = *src; // Original reference load.
9090 // bool is_gray = (rb_state == ReadBarrier::GrayState());
9091 // if (is_gray) {
Roland Levillain6d729a72017-06-30 18:34:01 +01009092 // entrypoint = Thread::Current()->pReadBarrierMarkReg ## root.reg()
9093 // ref = entrypoint(ref); // ref = ReadBarrier::Mark(ref); // Runtime entry point call.
Roland Levillainff487002017-03-07 16:50:01 +00009094 // }
9095 // } else {
9096 // HeapReference<mirror::Object> ref = *src; // Original reference load.
9097 // }
9098
9099 vixl32::Register temp_reg = RegisterFrom(temp);
9100
9101 // Slow path marking the object `ref` when the GC is marking. The
Roland Levillain6d729a72017-06-30 18:34:01 +01009102 // entrypoint will be loaded by the slow path code.
Roland Levillainff487002017-03-07 16:50:01 +00009103 SlowPathCodeARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01009104 new (GetScopedAllocator()) LoadReferenceWithBakerReadBarrierSlowPathARMVIXL(
Roland Levillain6d729a72017-06-30 18:34:01 +01009105 instruction, ref, obj, offset, index, scale_factor, needs_null_check, temp_reg);
Roland Levillainff487002017-03-07 16:50:01 +00009106 AddSlowPath(slow_path);
9107
Roland Levillain6d729a72017-06-30 18:34:01 +01009108 __ CompareAndBranchIfNonZero(mr, slow_path->GetEntryLabel());
Roland Levillainff487002017-03-07 16:50:01 +00009109 // Fast path: the GC is not marking: just load the reference.
9110 GenerateRawReferenceLoad(instruction, ref, obj, offset, index, scale_factor, needs_null_check);
9111 __ Bind(slow_path->GetExitLabel());
Orion Hodson4c8e12e2018-05-18 08:33:20 +01009112 MaybeGenerateMarkingRegisterCheck(/* code */ 22);
Roland Levillainff487002017-03-07 16:50:01 +00009113}
9114
9115void CodeGeneratorARMVIXL::UpdateReferenceFieldWithBakerReadBarrier(HInstruction* instruction,
9116 Location ref,
9117 vixl32::Register obj,
9118 Location field_offset,
9119 Location temp,
9120 bool needs_null_check,
9121 vixl32::Register temp2) {
9122 DCHECK(kEmitCompilerReadBarrier);
9123 DCHECK(kUseBakerReadBarrier);
9124
Roland Levillain6d729a72017-06-30 18:34:01 +01009125 // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
9126 // Marking Register) to decide whether we need to enter the slow
9127 // path to update the reference field within `obj`. Then, in the
9128 // slow path, check the gray bit in the lock word of the reference's
9129 // holder (`obj`) to decide whether to mark `ref` and update the
9130 // field or not.
Roland Levillainff487002017-03-07 16:50:01 +00009131 //
Roland Levillain6d729a72017-06-30 18:34:01 +01009132 // if (mr) { // Thread::Current()->GetIsGcMarking()
Roland Levillainba650a42017-03-06 13:52:32 +00009133 // // Slow path.
Roland Levillain54f869e2017-03-06 13:54:11 +00009134 // uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
9135 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
Roland Levillainff487002017-03-07 16:50:01 +00009136 // HeapReference<mirror::Object> ref = *(obj + field_offset); // Reference load.
Roland Levillain54f869e2017-03-06 13:54:11 +00009137 // bool is_gray = (rb_state == ReadBarrier::GrayState());
9138 // if (is_gray) {
Roland Levillainff487002017-03-07 16:50:01 +00009139 // old_ref = ref;
Roland Levillain6d729a72017-06-30 18:34:01 +01009140 // entrypoint = Thread::Current()->pReadBarrierMarkReg ## root.reg()
9141 // ref = entrypoint(ref); // ref = ReadBarrier::Mark(ref); // Runtime entry point call.
Roland Levillainff487002017-03-07 16:50:01 +00009142 // compareAndSwapObject(obj, field_offset, old_ref, ref);
Roland Levillain54f869e2017-03-06 13:54:11 +00009143 // }
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009144 // }
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009145
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009146 vixl32::Register temp_reg = RegisterFrom(temp);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009147
Roland Levillainff487002017-03-07 16:50:01 +00009148 // Slow path updating the object reference at address `obj + field_offset`
Roland Levillain6d729a72017-06-30 18:34:01 +01009149 // when the GC is marking. The entrypoint will be loaded by the slow path code.
Vladimir Marko174b2e22017-10-12 13:34:49 +01009150 SlowPathCodeARMVIXL* slow_path =
9151 new (GetScopedAllocator()) LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARMVIXL(
Roland Levillainff487002017-03-07 16:50:01 +00009152 instruction,
9153 ref,
9154 obj,
9155 /* offset */ 0u,
9156 /* index */ field_offset,
9157 /* scale_factor */ ScaleFactor::TIMES_1,
9158 needs_null_check,
9159 temp_reg,
Roland Levillain6d729a72017-06-30 18:34:01 +01009160 temp2);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009161 AddSlowPath(slow_path);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009162
Roland Levillain6d729a72017-06-30 18:34:01 +01009163 __ CompareAndBranchIfNonZero(mr, slow_path->GetEntryLabel());
Roland Levillainff487002017-03-07 16:50:01 +00009164 // Fast path: the GC is not marking: nothing to do (the field is
9165 // up-to-date, and we don't need to load the reference).
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009166 __ Bind(slow_path->GetExitLabel());
Orion Hodson4c8e12e2018-05-18 08:33:20 +01009167 MaybeGenerateMarkingRegisterCheck(/* code */ 23);
Roland Levillain844e6532016-11-03 16:09:47 +00009168}
Scott Wakelingfe885462016-09-22 10:24:38 +01009169
Roland Levillainba650a42017-03-06 13:52:32 +00009170void CodeGeneratorARMVIXL::GenerateRawReferenceLoad(HInstruction* instruction,
9171 Location ref,
Vladimir Markoca1e0382018-04-11 09:58:41 +00009172 vixl32::Register obj,
Roland Levillainba650a42017-03-06 13:52:32 +00009173 uint32_t offset,
9174 Location index,
9175 ScaleFactor scale_factor,
9176 bool needs_null_check) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01009177 DataType::Type type = DataType::Type::kReference;
Roland Levillainba650a42017-03-06 13:52:32 +00009178 vixl32::Register ref_reg = RegisterFrom(ref, type);
9179
9180 // If needed, vixl::EmissionCheckScope guards are used to ensure
9181 // that no pools are emitted between the load (macro) instruction
9182 // and MaybeRecordImplicitNullCheck.
9183
Scott Wakelingfe885462016-09-22 10:24:38 +01009184 if (index.IsValid()) {
9185 // Load types involving an "index": ArrayGet,
9186 // UnsafeGetObject/UnsafeGetObjectVolatile and UnsafeCASObject
9187 // intrinsics.
Roland Levillainba650a42017-03-06 13:52:32 +00009188 // /* HeapReference<mirror::Object> */ ref = *(obj + offset + (index << scale_factor))
Scott Wakelingfe885462016-09-22 10:24:38 +01009189 if (index.IsConstant()) {
9190 size_t computed_offset =
9191 (Int32ConstantFrom(index) << scale_factor) + offset;
Roland Levillainba650a42017-03-06 13:52:32 +00009192 vixl::EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
Scott Wakelingfe885462016-09-22 10:24:38 +01009193 GetAssembler()->LoadFromOffset(kLoadWord, ref_reg, obj, computed_offset);
Roland Levillainba650a42017-03-06 13:52:32 +00009194 if (needs_null_check) {
9195 MaybeRecordImplicitNullCheck(instruction);
9196 }
Scott Wakelingfe885462016-09-22 10:24:38 +01009197 } else {
9198 // Handle the special case of the
9199 // UnsafeGetObject/UnsafeGetObjectVolatile and UnsafeCASObject
9200 // intrinsics, which use a register pair as index ("long
9201 // offset"), of which only the low part contains data.
9202 vixl32::Register index_reg = index.IsRegisterPair()
9203 ? LowRegisterFrom(index)
9204 : RegisterFrom(index);
9205 UseScratchRegisterScope temps(GetVIXLAssembler());
Roland Levillainba650a42017-03-06 13:52:32 +00009206 vixl32::Register temp = temps.Acquire();
9207 __ Add(temp, obj, Operand(index_reg, ShiftType::LSL, scale_factor));
9208 {
9209 vixl::EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
9210 GetAssembler()->LoadFromOffset(kLoadWord, ref_reg, temp, offset);
9211 if (needs_null_check) {
9212 MaybeRecordImplicitNullCheck(instruction);
9213 }
9214 }
Scott Wakelingfe885462016-09-22 10:24:38 +01009215 }
9216 } else {
Roland Levillainba650a42017-03-06 13:52:32 +00009217 // /* HeapReference<mirror::Object> */ ref = *(obj + offset)
9218 vixl::EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
Scott Wakelingfe885462016-09-22 10:24:38 +01009219 GetAssembler()->LoadFromOffset(kLoadWord, ref_reg, obj, offset);
Roland Levillainba650a42017-03-06 13:52:32 +00009220 if (needs_null_check) {
9221 MaybeRecordImplicitNullCheck(instruction);
9222 }
Scott Wakelingfe885462016-09-22 10:24:38 +01009223 }
9224
Roland Levillain844e6532016-11-03 16:09:47 +00009225 // Object* ref = ref_addr->AsMirrorPtr()
9226 GetAssembler()->MaybeUnpoisonHeapReference(ref_reg);
Roland Levillain844e6532016-11-03 16:09:47 +00009227}
9228
Roland Levillain5daa4952017-07-03 17:23:56 +01009229void CodeGeneratorARMVIXL::MaybeGenerateMarkingRegisterCheck(int code, Location temp_loc) {
9230 // The following condition is a compile-time one, so it does not have a run-time cost.
9231 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier && kIsDebugBuild) {
9232 // The following condition is a run-time one; it is executed after the
9233 // previous compile-time test, to avoid penalizing non-debug builds.
9234 if (GetCompilerOptions().EmitRunTimeChecksInDebugMode()) {
9235 UseScratchRegisterScope temps(GetVIXLAssembler());
9236 vixl32::Register temp = temp_loc.IsValid() ? RegisterFrom(temp_loc) : temps.Acquire();
9237 GetAssembler()->GenerateMarkingRegisterCheck(temp,
9238 kMarkingRegisterCheckBreakCodeBaseCode + code);
9239 }
9240 }
9241}
9242
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009243void CodeGeneratorARMVIXL::GenerateReadBarrierSlow(HInstruction* instruction,
9244 Location out,
9245 Location ref,
9246 Location obj,
9247 uint32_t offset,
9248 Location index) {
9249 DCHECK(kEmitCompilerReadBarrier);
9250
9251 // Insert a slow path based read barrier *after* the reference load.
9252 //
9253 // If heap poisoning is enabled, the unpoisoning of the loaded
9254 // reference will be carried out by the runtime within the slow
9255 // path.
9256 //
9257 // Note that `ref` currently does not get unpoisoned (when heap
9258 // poisoning is enabled), which is alright as the `ref` argument is
9259 // not used by the artReadBarrierSlow entry point.
9260 //
9261 // TODO: Unpoison `ref` when it is used by artReadBarrierSlow.
Vladimir Marko174b2e22017-10-12 13:34:49 +01009262 SlowPathCodeARMVIXL* slow_path = new (GetScopedAllocator())
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009263 ReadBarrierForHeapReferenceSlowPathARMVIXL(instruction, out, ref, obj, offset, index);
9264 AddSlowPath(slow_path);
9265
9266 __ B(slow_path->GetEntryLabel());
9267 __ Bind(slow_path->GetExitLabel());
9268}
9269
9270void CodeGeneratorARMVIXL::MaybeGenerateReadBarrierSlow(HInstruction* instruction,
Artem Serov02d37832016-10-25 15:25:33 +01009271 Location out,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009272 Location ref,
9273 Location obj,
9274 uint32_t offset,
9275 Location index) {
Artem Serov02d37832016-10-25 15:25:33 +01009276 if (kEmitCompilerReadBarrier) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009277 // Baker's read barriers shall be handled by the fast path
Roland Levillain9983e302017-07-14 14:34:22 +01009278 // (CodeGeneratorARMVIXL::GenerateReferenceLoadWithBakerReadBarrier).
Artem Serov02d37832016-10-25 15:25:33 +01009279 DCHECK(!kUseBakerReadBarrier);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009280 // If heap poisoning is enabled, unpoisoning will be taken care of
9281 // by the runtime within the slow path.
9282 GenerateReadBarrierSlow(instruction, out, ref, obj, offset, index);
Artem Serov02d37832016-10-25 15:25:33 +01009283 } else if (kPoisonHeapReferences) {
9284 GetAssembler()->UnpoisonHeapReference(RegisterFrom(out));
9285 }
9286}
9287
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009288void CodeGeneratorARMVIXL::GenerateReadBarrierForRootSlow(HInstruction* instruction,
9289 Location out,
9290 Location root) {
9291 DCHECK(kEmitCompilerReadBarrier);
9292
9293 // Insert a slow path based read barrier *after* the GC root load.
9294 //
9295 // Note that GC roots are not affected by heap poisoning, so we do
9296 // not need to do anything special for this here.
9297 SlowPathCodeARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01009298 new (GetScopedAllocator()) ReadBarrierForRootSlowPathARMVIXL(instruction, out, root);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009299 AddSlowPath(slow_path);
9300
9301 __ B(slow_path->GetEntryLabel());
9302 __ Bind(slow_path->GetExitLabel());
9303}
9304
Artem Serov02d37832016-10-25 15:25:33 +01009305// Check if the desired_dispatch_info is supported. If it is, return it,
9306// otherwise return a fall-back info that should be used instead.
9307HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARMVIXL::GetSupportedInvokeStaticOrDirectDispatch(
Artem Serovd4cc5b22016-11-04 11:19:09 +00009308 const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
Nicolas Geoffrayc1a42cf2016-12-18 15:52:36 +00009309 HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
Nicolas Geoffraye807ff72017-01-23 09:03:12 +00009310 return desired_dispatch_info;
Artem Serov02d37832016-10-25 15:25:33 +01009311}
9312
Scott Wakelingfe885462016-09-22 10:24:38 +01009313vixl32::Register CodeGeneratorARMVIXL::GetInvokeStaticOrDirectExtraParameter(
9314 HInvokeStaticOrDirect* invoke, vixl32::Register temp) {
9315 DCHECK_EQ(invoke->InputCount(), invoke->GetNumberOfArguments() + 1u);
9316 Location location = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
9317 if (!invoke->GetLocations()->Intrinsified()) {
9318 return RegisterFrom(location);
9319 }
9320 // For intrinsics we allow any location, so it may be on the stack.
9321 if (!location.IsRegister()) {
9322 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, location.GetStackIndex());
9323 return temp;
9324 }
9325 // For register locations, check if the register was saved. If so, get it from the stack.
9326 // Note: There is a chance that the register was saved but not overwritten, so we could
9327 // save one load. However, since this is just an intrinsic slow path we prefer this
9328 // simple and more robust approach rather that trying to determine if that's the case.
9329 SlowPathCode* slow_path = GetCurrentSlowPath();
Scott Wakelingd5cd4972017-02-03 11:38:35 +00009330 if (slow_path != nullptr && slow_path->IsCoreRegisterSaved(RegisterFrom(location).GetCode())) {
Scott Wakelingfe885462016-09-22 10:24:38 +01009331 int stack_offset = slow_path->GetStackOffsetOfCoreRegister(RegisterFrom(location).GetCode());
9332 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, stack_offset);
9333 return temp;
9334 }
9335 return RegisterFrom(location);
9336}
9337
Vladimir Markod254f5c2017-06-02 15:18:36 +00009338void CodeGeneratorARMVIXL::GenerateStaticOrDirectCall(
Vladimir Markoe7197bf2017-06-02 17:00:23 +01009339 HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00009340 Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp.
Scott Wakelingfe885462016-09-22 10:24:38 +01009341 switch (invoke->GetMethodLoadKind()) {
9342 case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: {
9343 uint32_t offset =
9344 GetThreadOffset<kArmPointerSize>(invoke->GetStringInitEntryPoint()).Int32Value();
9345 // temp = thread->string_init_entrypoint
Artem Serovd4cc5b22016-11-04 11:19:09 +00009346 GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(temp), tr, offset);
9347 break;
9348 }
9349 case HInvokeStaticOrDirect::MethodLoadKind::kRecursive:
9350 callee_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
9351 break;
Vladimir Marko65979462017-05-19 17:25:12 +01009352 case HInvokeStaticOrDirect::MethodLoadKind::kBootImageLinkTimePcRelative: {
9353 DCHECK(GetCompilerOptions().IsBootImage());
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009354 PcRelativePatchInfo* labels = NewBootImageMethodPatch(invoke->GetTargetMethod());
Vladimir Marko65979462017-05-19 17:25:12 +01009355 vixl32::Register temp_reg = RegisterFrom(temp);
9356 EmitMovwMovtPlaceholder(labels, temp_reg);
9357 break;
9358 }
Artem Serovd4cc5b22016-11-04 11:19:09 +00009359 case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress:
9360 __ Mov(RegisterFrom(temp), Operand::From(invoke->GetMethodAddress()));
9361 break;
Vladimir Markob066d432018-01-03 13:14:37 +00009362 case HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo: {
Vladimir Markoe47f60c2018-02-21 13:43:28 +00009363 uint32_t boot_image_offset = GetBootImageOffset(invoke);
Vladimir Markob066d432018-01-03 13:14:37 +00009364 PcRelativePatchInfo* labels = NewBootImageRelRoPatch(boot_image_offset);
9365 vixl32::Register temp_reg = RegisterFrom(temp);
9366 EmitMovwMovtPlaceholder(labels, temp_reg);
9367 GetAssembler()->LoadFromOffset(kLoadWord, temp_reg, temp_reg, /* offset*/ 0);
9368 break;
9369 }
Vladimir Marko0eb882b2017-05-15 13:39:18 +01009370 case HInvokeStaticOrDirect::MethodLoadKind::kBssEntry: {
9371 PcRelativePatchInfo* labels = NewMethodBssEntryPatch(
9372 MethodReference(&GetGraph()->GetDexFile(), invoke->GetDexMethodIndex()));
9373 vixl32::Register temp_reg = RegisterFrom(temp);
9374 EmitMovwMovtPlaceholder(labels, temp_reg);
9375 GetAssembler()->LoadFromOffset(kLoadWord, temp_reg, temp_reg, /* offset*/ 0);
Scott Wakelingfe885462016-09-22 10:24:38 +01009376 break;
9377 }
Vladimir Markoe7197bf2017-06-02 17:00:23 +01009378 case HInvokeStaticOrDirect::MethodLoadKind::kRuntimeCall: {
9379 GenerateInvokeStaticOrDirectRuntimeCall(invoke, temp, slow_path);
9380 return; // No code pointer retrieval; the runtime performs the call directly.
Scott Wakelingfe885462016-09-22 10:24:38 +01009381 }
Scott Wakelingfe885462016-09-22 10:24:38 +01009382 }
9383
Artem Serovd4cc5b22016-11-04 11:19:09 +00009384 switch (invoke->GetCodePtrLocation()) {
9385 case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf:
Vladimir Markoe7197bf2017-06-02 17:00:23 +01009386 {
9387 // Use a scope to help guarantee that `RecordPcInfo()` records the correct pc.
9388 ExactAssemblyScope aas(GetVIXLAssembler(),
9389 vixl32::k32BitT32InstructionSizeInBytes,
9390 CodeBufferCheckScope::kMaximumSize);
9391 __ bl(GetFrameEntryLabel());
9392 RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
9393 }
Artem Serovd4cc5b22016-11-04 11:19:09 +00009394 break;
Artem Serovd4cc5b22016-11-04 11:19:09 +00009395 case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod:
9396 // LR = callee_method->entry_point_from_quick_compiled_code_
9397 GetAssembler()->LoadFromOffset(
9398 kLoadWord,
9399 lr,
9400 RegisterFrom(callee_method),
9401 ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value());
Alexandre Rames374ddf32016-11-04 10:40:49 +00009402 {
Vladimir Markoe7197bf2017-06-02 17:00:23 +01009403 // Use a scope to help guarantee that `RecordPcInfo()` records the correct pc.
Alexandre Rames374ddf32016-11-04 10:40:49 +00009404 // 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 +00009405 ExactAssemblyScope aas(GetVIXLAssembler(),
9406 vixl32::k16BitT32InstructionSizeInBytes,
9407 CodeBufferCheckScope::kExactSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00009408 // LR()
9409 __ blx(lr);
Vladimir Markoe7197bf2017-06-02 17:00:23 +01009410 RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
Alexandre Rames374ddf32016-11-04 10:40:49 +00009411 }
Artem Serovd4cc5b22016-11-04 11:19:09 +00009412 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01009413 }
9414
Scott Wakelingfe885462016-09-22 10:24:38 +01009415 DCHECK(!IsLeafMethod());
9416}
9417
Vladimir Markoe7197bf2017-06-02 17:00:23 +01009418void CodeGeneratorARMVIXL::GenerateVirtualCall(
9419 HInvokeVirtual* invoke, Location temp_location, SlowPathCode* slow_path) {
Scott Wakelingfe885462016-09-22 10:24:38 +01009420 vixl32::Register temp = RegisterFrom(temp_location);
9421 uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
9422 invoke->GetVTableIndex(), kArmPointerSize).Uint32Value();
9423
9424 // Use the calling convention instead of the location of the receiver, as
9425 // intrinsics may have put the receiver in a different register. In the intrinsics
9426 // slow path, the arguments have been moved to the right place, so here we are
9427 // guaranteed that the receiver is the first register of the calling convention.
9428 InvokeDexCallingConventionARMVIXL calling_convention;
9429 vixl32::Register receiver = calling_convention.GetRegisterAt(0);
9430 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
Alexandre Rames374ddf32016-11-04 10:40:49 +00009431 {
9432 // Make sure the pc is recorded immediately after the `ldr` instruction.
Artem Serov0fb37192016-12-06 18:13:40 +00009433 ExactAssemblyScope aas(GetVIXLAssembler(),
9434 vixl32::kMaxInstructionSizeInBytes,
9435 CodeBufferCheckScope::kMaximumSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00009436 // /* HeapReference<Class> */ temp = receiver->klass_
9437 __ ldr(temp, MemOperand(receiver, class_offset));
9438 MaybeRecordImplicitNullCheck(invoke);
9439 }
Scott Wakelingfe885462016-09-22 10:24:38 +01009440 // Instead of simply (possibly) unpoisoning `temp` here, we should
9441 // emit a read barrier for the previous class reference load.
9442 // However this is not required in practice, as this is an
9443 // intermediate/temporary reference and because the current
9444 // concurrent copying collector keeps the from-space memory
9445 // intact/accessible until the end of the marking phase (the
9446 // concurrent copying collector may not in the future).
9447 GetAssembler()->MaybeUnpoisonHeapReference(temp);
9448
9449 // temp = temp->GetMethodAt(method_offset);
9450 uint32_t entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(
9451 kArmPointerSize).Int32Value();
9452 GetAssembler()->LoadFromOffset(kLoadWord, temp, temp, method_offset);
9453 // LR = temp->GetEntryPoint();
9454 GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, entry_point);
Vladimir Markoe7197bf2017-06-02 17:00:23 +01009455 {
9456 // Use a scope to help guarantee that `RecordPcInfo()` records the correct pc.
9457 // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
9458 ExactAssemblyScope aas(GetVIXLAssembler(),
9459 vixl32::k16BitT32InstructionSizeInBytes,
9460 CodeBufferCheckScope::kExactSize);
9461 // LR();
9462 __ blx(lr);
9463 RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
9464 }
Scott Wakelingfe885462016-09-22 10:24:38 +01009465}
9466
Vladimir Marko6fd16062018-06-26 11:02:04 +01009467CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewBootImageIntrinsicPatch(
9468 uint32_t intrinsic_data) {
9469 return NewPcRelativePatch(/* dex_file */ nullptr, intrinsic_data, &boot_image_intrinsic_patches_);
9470}
9471
Vladimir Markob066d432018-01-03 13:14:37 +00009472CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewBootImageRelRoPatch(
9473 uint32_t boot_image_offset) {
9474 return NewPcRelativePatch(/* dex_file */ nullptr,
9475 boot_image_offset,
9476 &boot_image_method_patches_);
9477}
9478
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009479CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewBootImageMethodPatch(
Vladimir Marko65979462017-05-19 17:25:12 +01009480 MethodReference target_method) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009481 return NewPcRelativePatch(
9482 target_method.dex_file, target_method.index, &boot_image_method_patches_);
Artem Serovd4cc5b22016-11-04 11:19:09 +00009483}
9484
Vladimir Marko0eb882b2017-05-15 13:39:18 +01009485CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewMethodBssEntryPatch(
9486 MethodReference target_method) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009487 return NewPcRelativePatch(
9488 target_method.dex_file, target_method.index, &method_bss_entry_patches_);
Vladimir Marko0eb882b2017-05-15 13:39:18 +01009489}
9490
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009491CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewBootImageTypePatch(
Artem Serovd4cc5b22016-11-04 11:19:09 +00009492 const DexFile& dex_file, dex::TypeIndex type_index) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009493 return NewPcRelativePatch(&dex_file, type_index.index_, &boot_image_type_patches_);
Artem Serovd4cc5b22016-11-04 11:19:09 +00009494}
9495
Vladimir Marko1998cd02017-01-13 13:02:58 +00009496CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewTypeBssEntryPatch(
9497 const DexFile& dex_file, dex::TypeIndex type_index) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009498 return NewPcRelativePatch(&dex_file, type_index.index_, &type_bss_entry_patches_);
Vladimir Marko1998cd02017-01-13 13:02:58 +00009499}
9500
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009501CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewBootImageStringPatch(
Vladimir Marko65979462017-05-19 17:25:12 +01009502 const DexFile& dex_file, dex::StringIndex string_index) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009503 return NewPcRelativePatch(&dex_file, string_index.index_, &boot_image_string_patches_);
Vladimir Marko65979462017-05-19 17:25:12 +01009504}
9505
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01009506CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewStringBssEntryPatch(
9507 const DexFile& dex_file, dex::StringIndex string_index) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009508 return NewPcRelativePatch(&dex_file, string_index.index_, &string_bss_entry_patches_);
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01009509}
9510
Artem Serovd4cc5b22016-11-04 11:19:09 +00009511CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewPcRelativePatch(
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009512 const DexFile* dex_file, uint32_t offset_or_index, ArenaDeque<PcRelativePatchInfo>* patches) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00009513 patches->emplace_back(dex_file, offset_or_index);
9514 return &patches->back();
9515}
9516
Vladimir Markoca1e0382018-04-11 09:58:41 +00009517vixl32::Label* CodeGeneratorARMVIXL::NewBakerReadBarrierPatch(uint32_t custom_data) {
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009518 baker_read_barrier_patches_.emplace_back(custom_data);
9519 return &baker_read_barrier_patches_.back().label;
9520}
9521
Artem Serovc5fcb442016-12-02 19:19:58 +00009522VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateBootImageAddressLiteral(uint32_t address) {
Richard Uhlerc52f3032017-03-02 13:45:45 +00009523 return DeduplicateUint32Literal(dchecked_integral_cast<uint32_t>(address), &uint32_literals_);
Artem Serovc5fcb442016-12-02 19:19:58 +00009524}
9525
Nicolas Geoffrayf0acfe72017-01-09 20:54:52 +00009526VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateJitStringLiteral(
9527 const DexFile& dex_file,
9528 dex::StringIndex string_index,
9529 Handle<mirror::String> handle) {
Vladimir Marko174b2e22017-10-12 13:34:49 +01009530 ReserveJitStringRoot(StringReference(&dex_file, string_index), handle);
Artem Serovc5fcb442016-12-02 19:19:58 +00009531 return jit_string_patches_.GetOrCreate(
9532 StringReference(&dex_file, string_index),
9533 [this]() {
9534 return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u);
9535 });
9536}
9537
9538VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateJitClassLiteral(const DexFile& dex_file,
9539 dex::TypeIndex type_index,
Nicolas Geoffray5247c082017-01-13 14:17:29 +00009540 Handle<mirror::Class> handle) {
Vladimir Marko174b2e22017-10-12 13:34:49 +01009541 ReserveJitClassRoot(TypeReference(&dex_file, type_index), handle);
Artem Serovc5fcb442016-12-02 19:19:58 +00009542 return jit_class_patches_.GetOrCreate(
9543 TypeReference(&dex_file, type_index),
9544 [this]() {
9545 return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u);
9546 });
9547}
9548
Vladimir Marko6fd16062018-06-26 11:02:04 +01009549void CodeGeneratorARMVIXL::LoadBootImageAddress(vixl32::Register reg,
9550 uint32_t boot_image_reference) {
9551 if (GetCompilerOptions().IsBootImage()) {
9552 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
9553 NewBootImageIntrinsicPatch(boot_image_reference);
9554 EmitMovwMovtPlaceholder(labels, reg);
9555 } else if (GetCompilerOptions().GetCompilePic()) {
Vladimir Markoeebb8212018-06-05 14:57:24 +01009556 DCHECK(Runtime::Current()->IsAotCompiler());
Vladimir Marko6fd16062018-06-26 11:02:04 +01009557 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
9558 NewBootImageRelRoPatch(boot_image_reference);
Vladimir Markoeebb8212018-06-05 14:57:24 +01009559 EmitMovwMovtPlaceholder(labels, reg);
9560 __ Ldr(reg, MemOperand(reg, /* offset */ 0));
9561 } else {
9562 gc::Heap* heap = Runtime::Current()->GetHeap();
9563 DCHECK(!heap->GetBootImageSpaces().empty());
9564 uintptr_t address =
Vladimir Marko6fd16062018-06-26 11:02:04 +01009565 reinterpret_cast<uintptr_t>(heap->GetBootImageSpaces()[0]->Begin() + boot_image_reference);
Vladimir Markoeebb8212018-06-05 14:57:24 +01009566 __ Ldr(reg, DeduplicateBootImageAddressLiteral(dchecked_integral_cast<uint32_t>(address)));
9567 }
9568}
9569
Vladimir Marko6fd16062018-06-26 11:02:04 +01009570void CodeGeneratorARMVIXL::AllocateInstanceForIntrinsic(HInvokeStaticOrDirect* invoke,
9571 uint32_t boot_image_offset) {
9572 DCHECK(invoke->IsStatic());
9573 InvokeRuntimeCallingConventionARMVIXL calling_convention;
9574 vixl32::Register argument = calling_convention.GetRegisterAt(0);
9575 if (GetCompilerOptions().IsBootImage()) {
9576 DCHECK_EQ(boot_image_offset, IntrinsicVisitor::IntegerValueOfInfo::kInvalidReference);
9577 // Load the class the same way as for HLoadClass::LoadKind::kBootImageLinkTimePcRelative.
9578 MethodReference target_method = invoke->GetTargetMethod();
9579 dex::TypeIndex type_idx = target_method.dex_file->GetMethodId(target_method.index).class_idx_;
9580 PcRelativePatchInfo* labels = NewBootImageTypePatch(*target_method.dex_file, type_idx);
9581 EmitMovwMovtPlaceholder(labels, argument);
9582 } else {
9583 LoadBootImageAddress(argument, boot_image_offset);
9584 }
9585 InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc());
9586 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
9587}
9588
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009589template <linker::LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)>
Artem Serovd4cc5b22016-11-04 11:19:09 +00009590inline void CodeGeneratorARMVIXL::EmitPcRelativeLinkerPatches(
9591 const ArenaDeque<PcRelativePatchInfo>& infos,
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009592 ArenaVector<linker::LinkerPatch>* linker_patches) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00009593 for (const PcRelativePatchInfo& info : infos) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009594 const DexFile* dex_file = info.target_dex_file;
Artem Serovd4cc5b22016-11-04 11:19:09 +00009595 size_t offset_or_index = info.offset_or_index;
9596 DCHECK(info.add_pc_label.IsBound());
9597 uint32_t add_pc_offset = dchecked_integral_cast<uint32_t>(info.add_pc_label.GetLocation());
9598 // Add MOVW patch.
9599 DCHECK(info.movw_label.IsBound());
9600 uint32_t movw_offset = dchecked_integral_cast<uint32_t>(info.movw_label.GetLocation());
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009601 linker_patches->push_back(Factory(movw_offset, dex_file, add_pc_offset, offset_or_index));
Artem Serovd4cc5b22016-11-04 11:19:09 +00009602 // Add MOVT patch.
9603 DCHECK(info.movt_label.IsBound());
9604 uint32_t movt_offset = dchecked_integral_cast<uint32_t>(info.movt_label.GetLocation());
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009605 linker_patches->push_back(Factory(movt_offset, dex_file, add_pc_offset, offset_or_index));
Artem Serovd4cc5b22016-11-04 11:19:09 +00009606 }
9607}
9608
Vladimir Marko6fd16062018-06-26 11:02:04 +01009609template <linker::LinkerPatch (*Factory)(size_t, uint32_t, uint32_t)>
9610linker::LinkerPatch NoDexFileAdapter(size_t literal_offset,
9611 const DexFile* target_dex_file,
9612 uint32_t pc_insn_offset,
9613 uint32_t boot_image_offset) {
9614 DCHECK(target_dex_file == nullptr); // Unused for these patches, should be null.
9615 return Factory(literal_offset, pc_insn_offset, boot_image_offset);
Vladimir Markob066d432018-01-03 13:14:37 +00009616}
9617
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009618void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* linker_patches) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00009619 DCHECK(linker_patches->empty());
9620 size_t size =
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009621 /* MOVW+MOVT for each entry */ 2u * boot_image_method_patches_.size() +
Vladimir Marko0eb882b2017-05-15 13:39:18 +01009622 /* MOVW+MOVT for each entry */ 2u * method_bss_entry_patches_.size() +
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009623 /* MOVW+MOVT for each entry */ 2u * boot_image_type_patches_.size() +
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009624 /* MOVW+MOVT for each entry */ 2u * type_bss_entry_patches_.size() +
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009625 /* MOVW+MOVT for each entry */ 2u * boot_image_string_patches_.size() +
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01009626 /* MOVW+MOVT for each entry */ 2u * string_bss_entry_patches_.size() +
Vladimir Marko6fd16062018-06-26 11:02:04 +01009627 /* MOVW+MOVT for each entry */ 2u * boot_image_intrinsic_patches_.size() +
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009628 baker_read_barrier_patches_.size();
Artem Serovd4cc5b22016-11-04 11:19:09 +00009629 linker_patches->reserve(size);
Vladimir Marko65979462017-05-19 17:25:12 +01009630 if (GetCompilerOptions().IsBootImage()) {
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009631 EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeMethodPatch>(
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009632 boot_image_method_patches_, linker_patches);
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009633 EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeTypePatch>(
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009634 boot_image_type_patches_, linker_patches);
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009635 EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeStringPatch>(
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009636 boot_image_string_patches_, linker_patches);
Vladimir Marko6fd16062018-06-26 11:02:04 +01009637 EmitPcRelativeLinkerPatches<NoDexFileAdapter<linker::LinkerPatch::IntrinsicReferencePatch>>(
9638 boot_image_intrinsic_patches_, linker_patches);
Vladimir Marko65979462017-05-19 17:25:12 +01009639 } else {
Vladimir Marko6fd16062018-06-26 11:02:04 +01009640 EmitPcRelativeLinkerPatches<NoDexFileAdapter<linker::LinkerPatch::DataBimgRelRoPatch>>(
Vladimir Markob066d432018-01-03 13:14:37 +00009641 boot_image_method_patches_, linker_patches);
Vladimir Markoe47f60c2018-02-21 13:43:28 +00009642 DCHECK(boot_image_type_patches_.empty());
9643 DCHECK(boot_image_string_patches_.empty());
Vladimir Marko6fd16062018-06-26 11:02:04 +01009644 DCHECK(boot_image_intrinsic_patches_.empty());
Artem Serovd4cc5b22016-11-04 11:19:09 +00009645 }
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009646 EmitPcRelativeLinkerPatches<linker::LinkerPatch::MethodBssEntryPatch>(
9647 method_bss_entry_patches_, linker_patches);
9648 EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeBssEntryPatch>(
9649 type_bss_entry_patches_, linker_patches);
9650 EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringBssEntryPatch>(
9651 string_bss_entry_patches_, linker_patches);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009652 for (const BakerReadBarrierPatchInfo& info : baker_read_barrier_patches_) {
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009653 linker_patches->push_back(linker::LinkerPatch::BakerReadBarrierBranchPatch(
9654 info.label.GetLocation(), info.custom_data));
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009655 }
Vladimir Marko1998cd02017-01-13 13:02:58 +00009656 DCHECK_EQ(size, linker_patches->size());
Artem Serovc5fcb442016-12-02 19:19:58 +00009657}
9658
Vladimir Markoca1e0382018-04-11 09:58:41 +00009659bool CodeGeneratorARMVIXL::NeedsThunkCode(const linker::LinkerPatch& patch) const {
9660 return patch.GetType() == linker::LinkerPatch::Type::kBakerReadBarrierBranch ||
9661 patch.GetType() == linker::LinkerPatch::Type::kCallRelative;
9662}
9663
9664void CodeGeneratorARMVIXL::EmitThunkCode(const linker::LinkerPatch& patch,
9665 /*out*/ ArenaVector<uint8_t>* code,
9666 /*out*/ std::string* debug_name) {
9667 arm::ArmVIXLAssembler assembler(GetGraph()->GetAllocator());
9668 switch (patch.GetType()) {
9669 case linker::LinkerPatch::Type::kCallRelative:
9670 // The thunk just uses the entry point in the ArtMethod. This works even for calls
9671 // to the generic JNI and interpreter trampolines.
9672 assembler.LoadFromOffset(
9673 arm::kLoadWord,
9674 vixl32::pc,
9675 vixl32::r0,
9676 ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value());
9677 assembler.GetVIXLAssembler()->Bkpt(0);
9678 if (GetCompilerOptions().GenerateAnyDebugInfo()) {
9679 *debug_name = "MethodCallThunk";
9680 }
9681 break;
9682 case linker::LinkerPatch::Type::kBakerReadBarrierBranch:
9683 DCHECK_EQ(patch.GetBakerCustomValue2(), 0u);
9684 CompileBakerReadBarrierThunk(assembler, patch.GetBakerCustomValue1(), debug_name);
9685 break;
9686 default:
9687 LOG(FATAL) << "Unexpected patch type " << patch.GetType();
9688 UNREACHABLE();
9689 }
9690
9691 // Ensure we emit the literal pool if any.
9692 assembler.FinalizeCode();
9693 code->resize(assembler.CodeSize());
9694 MemoryRegion code_region(code->data(), code->size());
9695 assembler.FinalizeInstructions(code_region);
9696}
9697
Artem Serovc5fcb442016-12-02 19:19:58 +00009698VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateUint32Literal(
9699 uint32_t value,
9700 Uint32ToLiteralMap* map) {
9701 return map->GetOrCreate(
9702 value,
9703 [this, value]() {
9704 return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ value);
9705 });
9706}
9707
Artem Serov2bbc9532016-10-21 11:51:50 +01009708void LocationsBuilderARMVIXL::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
9709 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01009710 new (GetGraph()->GetAllocator()) LocationSummary(instr, LocationSummary::kNoCall);
Artem Serov2bbc9532016-10-21 11:51:50 +01009711 locations->SetInAt(HMultiplyAccumulate::kInputAccumulatorIndex,
9712 Location::RequiresRegister());
9713 locations->SetInAt(HMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister());
9714 locations->SetInAt(HMultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister());
9715 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
9716}
9717
9718void InstructionCodeGeneratorARMVIXL::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
9719 vixl32::Register res = OutputRegister(instr);
9720 vixl32::Register accumulator =
9721 InputRegisterAt(instr, HMultiplyAccumulate::kInputAccumulatorIndex);
9722 vixl32::Register mul_left =
9723 InputRegisterAt(instr, HMultiplyAccumulate::kInputMulLeftIndex);
9724 vixl32::Register mul_right =
9725 InputRegisterAt(instr, HMultiplyAccumulate::kInputMulRightIndex);
9726
9727 if (instr->GetOpKind() == HInstruction::kAdd) {
9728 __ Mla(res, mul_left, mul_right, accumulator);
9729 } else {
9730 __ Mls(res, mul_left, mul_right, accumulator);
9731 }
9732}
9733
Artem Serov551b28f2016-10-18 19:11:30 +01009734void LocationsBuilderARMVIXL::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
9735 // Nothing to do, this should be removed during prepare for register allocator.
9736 LOG(FATAL) << "Unreachable";
9737}
9738
9739void InstructionCodeGeneratorARMVIXL::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
9740 // Nothing to do, this should be removed during prepare for register allocator.
9741 LOG(FATAL) << "Unreachable";
9742}
9743
9744// Simple implementation of packed switch - generate cascaded compare/jumps.
9745void LocationsBuilderARMVIXL::VisitPackedSwitch(HPackedSwitch* switch_instr) {
9746 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01009747 new (GetGraph()->GetAllocator()) LocationSummary(switch_instr, LocationSummary::kNoCall);
Artem Serov551b28f2016-10-18 19:11:30 +01009748 locations->SetInAt(0, Location::RequiresRegister());
9749 if (switch_instr->GetNumEntries() > kPackedSwitchCompareJumpThreshold &&
9750 codegen_->GetAssembler()->GetVIXLAssembler()->IsUsingT32()) {
9751 locations->AddTemp(Location::RequiresRegister()); // We need a temp for the table base.
9752 if (switch_instr->GetStartValue() != 0) {
9753 locations->AddTemp(Location::RequiresRegister()); // We need a temp for the bias.
9754 }
9755 }
9756}
9757
9758// TODO(VIXL): Investigate and reach the parity with old arm codegen.
9759void InstructionCodeGeneratorARMVIXL::VisitPackedSwitch(HPackedSwitch* switch_instr) {
9760 int32_t lower_bound = switch_instr->GetStartValue();
9761 uint32_t num_entries = switch_instr->GetNumEntries();
9762 LocationSummary* locations = switch_instr->GetLocations();
9763 vixl32::Register value_reg = InputRegisterAt(switch_instr, 0);
9764 HBasicBlock* default_block = switch_instr->GetDefaultBlock();
9765
9766 if (num_entries <= kPackedSwitchCompareJumpThreshold ||
9767 !codegen_->GetAssembler()->GetVIXLAssembler()->IsUsingT32()) {
9768 // Create a series of compare/jumps.
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009769 UseScratchRegisterScope temps(GetVIXLAssembler());
Artem Serov551b28f2016-10-18 19:11:30 +01009770 vixl32::Register temp_reg = temps.Acquire();
9771 // Note: It is fine for the below AddConstantSetFlags() using IP register to temporarily store
9772 // the immediate, because IP is used as the destination register. For the other
9773 // AddConstantSetFlags() and GenerateCompareWithImmediate(), the immediate values are constant,
9774 // and they can be encoded in the instruction without making use of IP register.
9775 __ Adds(temp_reg, value_reg, -lower_bound);
9776
9777 const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors();
9778 // Jump to successors[0] if value == lower_bound.
9779 __ B(eq, codegen_->GetLabelOf(successors[0]));
9780 int32_t last_index = 0;
9781 for (; num_entries - last_index > 2; last_index += 2) {
9782 __ Adds(temp_reg, temp_reg, -2);
9783 // Jump to successors[last_index + 1] if value < case_value[last_index + 2].
9784 __ B(lo, codegen_->GetLabelOf(successors[last_index + 1]));
9785 // Jump to successors[last_index + 2] if value == case_value[last_index + 2].
9786 __ B(eq, codegen_->GetLabelOf(successors[last_index + 2]));
9787 }
9788 if (num_entries - last_index == 2) {
9789 // The last missing case_value.
9790 __ Cmp(temp_reg, 1);
9791 __ B(eq, codegen_->GetLabelOf(successors[last_index + 1]));
9792 }
9793
9794 // And the default for any other value.
9795 if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) {
9796 __ B(codegen_->GetLabelOf(default_block));
9797 }
9798 } else {
9799 // Create a table lookup.
9800 vixl32::Register table_base = RegisterFrom(locations->GetTemp(0));
9801
9802 JumpTableARMVIXL* jump_table = codegen_->CreateJumpTable(switch_instr);
9803
9804 // Remove the bias.
9805 vixl32::Register key_reg;
9806 if (lower_bound != 0) {
9807 key_reg = RegisterFrom(locations->GetTemp(1));
9808 __ Sub(key_reg, value_reg, lower_bound);
9809 } else {
9810 key_reg = value_reg;
9811 }
9812
9813 // Check whether the value is in the table, jump to default block if not.
9814 __ Cmp(key_reg, num_entries - 1);
9815 __ B(hi, codegen_->GetLabelOf(default_block));
9816
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009817 UseScratchRegisterScope temps(GetVIXLAssembler());
Artem Serov551b28f2016-10-18 19:11:30 +01009818 vixl32::Register jump_offset = temps.Acquire();
9819
9820 // Load jump offset from the table.
Scott Wakeling86e9d262017-01-18 15:59:24 +00009821 {
9822 const size_t jump_size = switch_instr->GetNumEntries() * sizeof(int32_t);
9823 ExactAssemblyScope aas(GetVIXLAssembler(),
9824 (vixl32::kMaxInstructionSizeInBytes * 4) + jump_size,
9825 CodeBufferCheckScope::kMaximumSize);
9826 __ adr(table_base, jump_table->GetTableStartLabel());
9827 __ ldr(jump_offset, MemOperand(table_base, key_reg, vixl32::LSL, 2));
Artem Serov551b28f2016-10-18 19:11:30 +01009828
Scott Wakeling86e9d262017-01-18 15:59:24 +00009829 // Jump to target block by branching to table_base(pc related) + offset.
9830 vixl32::Register target_address = table_base;
9831 __ add(target_address, table_base, jump_offset);
9832 __ bx(target_address);
Artem Serov09a940d2016-11-11 16:15:11 +00009833
Scott Wakeling86e9d262017-01-18 15:59:24 +00009834 jump_table->EmitTable(codegen_);
9835 }
Artem Serov551b28f2016-10-18 19:11:30 +01009836 }
9837}
9838
Artem Serov02d37832016-10-25 15:25:33 +01009839// Copy the result of a call into the given target.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01009840void CodeGeneratorARMVIXL::MoveFromReturnRegister(Location trg, DataType::Type type) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009841 if (!trg.IsValid()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01009842 DCHECK_EQ(type, DataType::Type::kVoid);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009843 return;
9844 }
9845
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01009846 DCHECK_NE(type, DataType::Type::kVoid);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009847
Artem Serovd4cc5b22016-11-04 11:19:09 +00009848 Location return_loc = InvokeDexCallingConventionVisitorARMVIXL().GetReturnLocation(type);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009849 if (return_loc.Equals(trg)) {
9850 return;
9851 }
9852
9853 // TODO: Consider pairs in the parallel move resolver, then this could be nicely merged
9854 // with the last branch.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01009855 if (type == DataType::Type::kInt64) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009856 TODO_VIXL32(FATAL);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01009857 } else if (type == DataType::Type::kFloat64) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009858 TODO_VIXL32(FATAL);
9859 } else {
9860 // Let the parallel move resolver take care of all of this.
Vladimir Markoca6fff82017-10-03 14:49:14 +01009861 HParallelMove parallel_move(GetGraph()->GetAllocator());
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009862 parallel_move.AddMove(return_loc, trg, type, nullptr);
9863 GetMoveResolver()->EmitNativeCode(&parallel_move);
9864 }
Scott Wakelingfe885462016-09-22 10:24:38 +01009865}
9866
xueliang.zhong8d2c4592016-11-23 17:05:25 +00009867void LocationsBuilderARMVIXL::VisitClassTableGet(HClassTableGet* instruction) {
9868 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01009869 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
xueliang.zhong8d2c4592016-11-23 17:05:25 +00009870 locations->SetInAt(0, Location::RequiresRegister());
9871 locations->SetOut(Location::RequiresRegister());
Artem Serov551b28f2016-10-18 19:11:30 +01009872}
9873
xueliang.zhong8d2c4592016-11-23 17:05:25 +00009874void InstructionCodeGeneratorARMVIXL::VisitClassTableGet(HClassTableGet* instruction) {
9875 if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) {
9876 uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
9877 instruction->GetIndex(), kArmPointerSize).SizeValue();
9878 GetAssembler()->LoadFromOffset(kLoadWord,
9879 OutputRegister(instruction),
9880 InputRegisterAt(instruction, 0),
9881 method_offset);
9882 } else {
9883 uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement(
9884 instruction->GetIndex(), kArmPointerSize));
9885 GetAssembler()->LoadFromOffset(kLoadWord,
9886 OutputRegister(instruction),
9887 InputRegisterAt(instruction, 0),
9888 mirror::Class::ImtPtrOffset(kArmPointerSize).Uint32Value());
9889 GetAssembler()->LoadFromOffset(kLoadWord,
9890 OutputRegister(instruction),
9891 OutputRegister(instruction),
9892 method_offset);
9893 }
Artem Serov551b28f2016-10-18 19:11:30 +01009894}
9895
Artem Serovc5fcb442016-12-02 19:19:58 +00009896static void PatchJitRootUse(uint8_t* code,
9897 const uint8_t* roots_data,
9898 VIXLUInt32Literal* literal,
9899 uint64_t index_in_table) {
9900 DCHECK(literal->IsBound());
9901 uint32_t literal_offset = literal->GetLocation();
9902 uintptr_t address =
9903 reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>);
9904 uint8_t* data = code + literal_offset;
9905 reinterpret_cast<uint32_t*>(data)[0] = dchecked_integral_cast<uint32_t>(address);
9906}
9907
9908void CodeGeneratorARMVIXL::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) {
9909 for (const auto& entry : jit_string_patches_) {
Vladimir Marko7d157fc2017-05-10 16:29:23 +01009910 const StringReference& string_reference = entry.first;
9911 VIXLUInt32Literal* table_entry_literal = entry.second;
Vladimir Marko174b2e22017-10-12 13:34:49 +01009912 uint64_t index_in_table = GetJitStringRootIndex(string_reference);
Vladimir Marko7d157fc2017-05-10 16:29:23 +01009913 PatchJitRootUse(code, roots_data, table_entry_literal, index_in_table);
Artem Serovc5fcb442016-12-02 19:19:58 +00009914 }
9915 for (const auto& entry : jit_class_patches_) {
Vladimir Marko7d157fc2017-05-10 16:29:23 +01009916 const TypeReference& type_reference = entry.first;
9917 VIXLUInt32Literal* table_entry_literal = entry.second;
Vladimir Marko174b2e22017-10-12 13:34:49 +01009918 uint64_t index_in_table = GetJitClassRootIndex(type_reference);
Vladimir Marko7d157fc2017-05-10 16:29:23 +01009919 PatchJitRootUse(code, roots_data, table_entry_literal, index_in_table);
Artem Serovc5fcb442016-12-02 19:19:58 +00009920 }
9921}
9922
Artem Serovd4cc5b22016-11-04 11:19:09 +00009923void CodeGeneratorARMVIXL::EmitMovwMovtPlaceholder(
9924 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels,
9925 vixl32::Register out) {
Artem Serov0fb37192016-12-06 18:13:40 +00009926 ExactAssemblyScope aas(GetVIXLAssembler(),
9927 3 * vixl32::kMaxInstructionSizeInBytes,
9928 CodeBufferCheckScope::kMaximumSize);
Artem Serovd4cc5b22016-11-04 11:19:09 +00009929 // TODO(VIXL): Think about using mov instead of movw.
9930 __ bind(&labels->movw_label);
9931 __ movw(out, /* placeholder */ 0u);
9932 __ bind(&labels->movt_label);
9933 __ movt(out, /* placeholder */ 0u);
9934 __ bind(&labels->add_pc_label);
9935 __ add(out, out, pc);
9936}
9937
Scott Wakelingfe885462016-09-22 10:24:38 +01009938#undef __
9939#undef QUICK_ENTRY_POINT
9940#undef TODO_VIXL32
9941
Vladimir Markoca1e0382018-04-11 09:58:41 +00009942#define __ assembler.GetVIXLAssembler()->
9943
9944static void EmitGrayCheckAndFastPath(ArmVIXLAssembler& assembler,
9945 vixl32::Register base_reg,
9946 vixl32::MemOperand& lock_word,
9947 vixl32::Label* slow_path,
Vladimir Marko7a695052018-04-12 10:26:50 +01009948 int32_t raw_ldr_offset,
9949 vixl32::Label* throw_npe = nullptr) {
Vladimir Markoca1e0382018-04-11 09:58:41 +00009950 // Load the lock word containing the rb_state.
9951 __ Ldr(ip, lock_word);
9952 // Given the numeric representation, it's enough to check the low bit of the rb_state.
9953 static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
9954 static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
9955 __ Tst(ip, Operand(LockWord::kReadBarrierStateMaskShifted));
9956 __ B(ne, slow_path, /* is_far_target */ false);
Vladimir Marko7a695052018-04-12 10:26:50 +01009957 // To throw NPE, we return to the fast path; the artificial dependence below does not matter.
9958 if (throw_npe != nullptr) {
9959 __ Bind(throw_npe);
9960 }
Vladimir Markoca1e0382018-04-11 09:58:41 +00009961 __ Add(lr, lr, raw_ldr_offset);
9962 // Introduce a dependency on the lock_word including rb_state,
9963 // to prevent load-load reordering, and without using
9964 // a memory barrier (which would be more expensive).
9965 __ Add(base_reg, base_reg, Operand(ip, LSR, 32));
9966 __ Bx(lr); // And return back to the function.
9967 // Note: The fake dependency is unnecessary for the slow path.
9968}
9969
9970// Load the read barrier introspection entrypoint in register `entrypoint`
Vladimir Markodcd117e2018-04-19 11:54:00 +01009971static vixl32::Register LoadReadBarrierMarkIntrospectionEntrypoint(ArmVIXLAssembler& assembler) {
Vladimir Markoca1e0382018-04-11 09:58:41 +00009972 // The register where the read barrier introspection entrypoint is loaded
Vladimir Markodcd117e2018-04-19 11:54:00 +01009973 // is the marking register. We clobber it here and the entrypoint restores it to 1.
9974 vixl32::Register entrypoint = mr;
Vladimir Markoca1e0382018-04-11 09:58:41 +00009975 // entrypoint = Thread::Current()->pReadBarrierMarkReg12, i.e. pReadBarrierMarkIntrospection.
9976 DCHECK_EQ(ip.GetCode(), 12u);
9977 const int32_t entry_point_offset =
9978 Thread::ReadBarrierMarkEntryPointsOffset<kArmPointerSize>(ip.GetCode());
9979 __ Ldr(entrypoint, MemOperand(tr, entry_point_offset));
Vladimir Markodcd117e2018-04-19 11:54:00 +01009980 return entrypoint;
Vladimir Markoca1e0382018-04-11 09:58:41 +00009981}
9982
9983void CodeGeneratorARMVIXL::CompileBakerReadBarrierThunk(ArmVIXLAssembler& assembler,
9984 uint32_t encoded_data,
9985 /*out*/ std::string* debug_name) {
9986 BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data);
9987 switch (kind) {
9988 case BakerReadBarrierKind::kField: {
Vladimir Markoca1e0382018-04-11 09:58:41 +00009989 vixl32::Register base_reg(BakerReadBarrierFirstRegField::Decode(encoded_data));
9990 CheckValidReg(base_reg.GetCode());
9991 vixl32::Register holder_reg(BakerReadBarrierSecondRegField::Decode(encoded_data));
9992 CheckValidReg(holder_reg.GetCode());
9993 BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data);
9994 UseScratchRegisterScope temps(assembler.GetVIXLAssembler());
9995 temps.Exclude(ip);
Vladimir Marko7a695052018-04-12 10:26:50 +01009996 // If base_reg differs from holder_reg, the offset was too large and we must have emitted
9997 // an explicit null check before the load. Otherwise, for implicit null checks, we need to
9998 // null-check the holder as we do not necessarily do that check before going to the thunk.
9999 vixl32::Label throw_npe_label;
10000 vixl32::Label* throw_npe = nullptr;
10001 if (GetCompilerOptions().GetImplicitNullChecks() && holder_reg.Is(base_reg)) {
10002 throw_npe = &throw_npe_label;
10003 __ CompareAndBranchIfZero(holder_reg, throw_npe, /* is_far_target */ false);
Vladimir Markoca1e0382018-04-11 09:58:41 +000010004 }
Vladimir Marko7a695052018-04-12 10:26:50 +010010005 // Check if the holder is gray and, if not, add fake dependency to the base register
10006 // and return to the LDR instruction to load the reference. Otherwise, use introspection
10007 // to load the reference and call the entrypoint that performs further checks on the
10008 // reference and marks it if needed.
Vladimir Markoca1e0382018-04-11 09:58:41 +000010009 vixl32::Label slow_path;
10010 MemOperand lock_word(holder_reg, mirror::Object::MonitorOffset().Int32Value());
10011 const int32_t raw_ldr_offset = (width == BakerReadBarrierWidth::kWide)
10012 ? BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET
10013 : BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET;
Vladimir Marko7a695052018-04-12 10:26:50 +010010014 EmitGrayCheckAndFastPath(
10015 assembler, base_reg, lock_word, &slow_path, raw_ldr_offset, throw_npe);
Vladimir Markoca1e0382018-04-11 09:58:41 +000010016 __ Bind(&slow_path);
10017 const int32_t ldr_offset = /* Thumb state adjustment (LR contains Thumb state). */ -1 +
10018 raw_ldr_offset;
Vladimir Markodcd117e2018-04-19 11:54:00 +010010019 vixl32::Register ep_reg = LoadReadBarrierMarkIntrospectionEntrypoint(assembler);
Vladimir Markoca1e0382018-04-11 09:58:41 +000010020 if (width == BakerReadBarrierWidth::kWide) {
10021 MemOperand ldr_half_address(lr, ldr_offset + 2);
10022 __ Ldrh(ip, ldr_half_address); // Load the LDR immediate half-word with "Rt | imm12".
10023 __ Ubfx(ip, ip, 0, 12); // Extract the offset imm12.
10024 __ Ldr(ip, MemOperand(base_reg, ip)); // Load the reference.
10025 } else {
10026 MemOperand ldr_address(lr, ldr_offset);
10027 __ Ldrh(ip, ldr_address); // Load the LDR immediate, encoding T1.
10028 __ Add(ep_reg, // Adjust the entrypoint address to the entrypoint
10029 ep_reg, // for narrow LDR.
10030 Operand(BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_ENTRYPOINT_OFFSET));
10031 __ Ubfx(ip, ip, 6, 5); // Extract the imm5, i.e. offset / 4.
10032 __ Ldr(ip, MemOperand(base_reg, ip, LSL, 2)); // Load the reference.
10033 }
10034 // Do not unpoison. With heap poisoning enabled, the entrypoint expects a poisoned reference.
10035 __ Bx(ep_reg); // Jump to the entrypoint.
Vladimir Markoca1e0382018-04-11 09:58:41 +000010036 break;
10037 }
10038 case BakerReadBarrierKind::kArray: {
10039 vixl32::Register base_reg(BakerReadBarrierFirstRegField::Decode(encoded_data));
10040 CheckValidReg(base_reg.GetCode());
10041 DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg,
10042 BakerReadBarrierSecondRegField::Decode(encoded_data));
10043 DCHECK(BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide);
10044 UseScratchRegisterScope temps(assembler.GetVIXLAssembler());
10045 temps.Exclude(ip);
10046 vixl32::Label slow_path;
10047 int32_t data_offset =
10048 mirror::Array::DataOffset(Primitive::ComponentSize(Primitive::kPrimNot)).Int32Value();
10049 MemOperand lock_word(base_reg, mirror::Object::MonitorOffset().Int32Value() - data_offset);
10050 DCHECK_LT(lock_word.GetOffsetImmediate(), 0);
10051 const int32_t raw_ldr_offset = BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET;
10052 EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path, raw_ldr_offset);
10053 __ Bind(&slow_path);
10054 const int32_t ldr_offset = /* Thumb state adjustment (LR contains Thumb state). */ -1 +
10055 raw_ldr_offset;
10056 MemOperand ldr_address(lr, ldr_offset + 2);
10057 __ Ldrb(ip, ldr_address); // Load the LDR (register) byte with "00 | imm2 | Rm",
10058 // i.e. Rm+32 because the scale in imm2 is 2.
Vladimir Markodcd117e2018-04-19 11:54:00 +010010059 vixl32::Register ep_reg = LoadReadBarrierMarkIntrospectionEntrypoint(assembler);
Vladimir Markoca1e0382018-04-11 09:58:41 +000010060 __ Bfi(ep_reg, ip, 3, 6); // Insert ip to the entrypoint address to create
10061 // a switch case target based on the index register.
10062 __ Mov(ip, base_reg); // Move the base register to ip0.
10063 __ Bx(ep_reg); // Jump to the entrypoint's array switch case.
10064 break;
10065 }
10066 case BakerReadBarrierKind::kGcRoot: {
10067 // Check if the reference needs to be marked and if so (i.e. not null, not marked yet
10068 // and it does not have a forwarding address), call the correct introspection entrypoint;
10069 // otherwise return the reference (or the extracted forwarding address).
10070 // There is no gray bit check for GC roots.
10071 vixl32::Register root_reg(BakerReadBarrierFirstRegField::Decode(encoded_data));
10072 CheckValidReg(root_reg.GetCode());
10073 DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg,
10074 BakerReadBarrierSecondRegField::Decode(encoded_data));
10075 BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data);
10076 UseScratchRegisterScope temps(assembler.GetVIXLAssembler());
10077 temps.Exclude(ip);
10078 vixl32::Label return_label, not_marked, forwarding_address;
10079 __ CompareAndBranchIfZero(root_reg, &return_label, /* is_far_target */ false);
10080 MemOperand lock_word(root_reg, mirror::Object::MonitorOffset().Int32Value());
10081 __ Ldr(ip, lock_word);
10082 __ Tst(ip, LockWord::kMarkBitStateMaskShifted);
10083 __ B(eq, &not_marked);
10084 __ Bind(&return_label);
10085 __ Bx(lr);
10086 __ Bind(&not_marked);
10087 static_assert(LockWord::kStateShift == 30 && LockWord::kStateForwardingAddress == 3,
10088 "To use 'CMP ip, #modified-immediate; BHS', we need the lock word state in "
10089 " the highest bits and the 'forwarding address' state to have all bits set");
10090 __ Cmp(ip, Operand(0xc0000000));
10091 __ B(hs, &forwarding_address);
Vladimir Markodcd117e2018-04-19 11:54:00 +010010092 vixl32::Register ep_reg = LoadReadBarrierMarkIntrospectionEntrypoint(assembler);
Vladimir Markoca1e0382018-04-11 09:58:41 +000010093 // Adjust the art_quick_read_barrier_mark_introspection address in kBakerCcEntrypointRegister
10094 // to art_quick_read_barrier_mark_introspection_gc_roots.
10095 int32_t entrypoint_offset = (width == BakerReadBarrierWidth::kWide)
10096 ? BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_ENTRYPOINT_OFFSET
10097 : BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_ENTRYPOINT_OFFSET;
10098 __ Add(ep_reg, ep_reg, Operand(entrypoint_offset));
10099 __ Mov(ip, root_reg);
10100 __ Bx(ep_reg);
10101 __ Bind(&forwarding_address);
10102 __ Lsl(root_reg, ip, LockWord::kForwardingAddressShift);
10103 __ Bx(lr);
10104 break;
10105 }
10106 default:
10107 LOG(FATAL) << "Unexpected kind: " << static_cast<uint32_t>(kind);
10108 UNREACHABLE();
10109 }
10110
10111 if (GetCompilerOptions().GenerateAnyDebugInfo()) {
10112 std::ostringstream oss;
10113 oss << "BakerReadBarrierThunk";
10114 switch (kind) {
10115 case BakerReadBarrierKind::kField:
10116 oss << "Field";
10117 if (BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide) {
10118 oss << "Wide";
10119 }
10120 oss << "_r" << BakerReadBarrierFirstRegField::Decode(encoded_data)
10121 << "_r" << BakerReadBarrierSecondRegField::Decode(encoded_data);
10122 break;
10123 case BakerReadBarrierKind::kArray:
10124 oss << "Array_r" << BakerReadBarrierFirstRegField::Decode(encoded_data);
10125 DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg,
10126 BakerReadBarrierSecondRegField::Decode(encoded_data));
10127 DCHECK(BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide);
10128 break;
10129 case BakerReadBarrierKind::kGcRoot:
10130 oss << "GcRoot";
10131 if (BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide) {
10132 oss << "Wide";
10133 }
10134 oss << "_r" << BakerReadBarrierFirstRegField::Decode(encoded_data);
10135 DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg,
10136 BakerReadBarrierSecondRegField::Decode(encoded_data));
10137 break;
10138 }
10139 *debug_name = oss.str();
10140 }
10141}
10142
10143#undef __
10144
Scott Wakelingfe885462016-09-22 10:24:38 +010010145} // namespace arm
10146} // namespace art