blob: b3141bfa4e61f3a867a8cc11fe91336bb2f71f3e [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 Wakelinga7812ae2016-10-17 10:03:36 +010050using helpers::HighRegisterFrom;
Donghui Bai426b49c2016-11-08 14:55:38 +080051using helpers::InputDRegisterAt;
Scott Wakelingfe885462016-09-22 10:24:38 +010052using helpers::InputOperandAt;
Scott Wakelingc34dba72016-10-03 10:14:44 +010053using helpers::InputRegister;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010054using helpers::InputRegisterAt;
Scott Wakelingfe885462016-09-22 10:24:38 +010055using helpers::InputSRegisterAt;
Anton Kirilov644032c2016-12-06 17:51:43 +000056using helpers::InputVRegister;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010057using helpers::InputVRegisterAt;
Scott Wakelingb77051e2016-11-21 19:46:00 +000058using helpers::Int32ConstantFrom;
Anton Kirilov644032c2016-12-06 17:51:43 +000059using helpers::Int64ConstantFrom;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010060using helpers::LocationFrom;
61using helpers::LowRegisterFrom;
62using helpers::LowSRegisterFrom;
Donghui Bai426b49c2016-11-08 14:55:38 +080063using helpers::OperandFrom;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010064using helpers::OutputRegister;
65using helpers::OutputSRegister;
66using helpers::OutputVRegister;
67using helpers::RegisterFrom;
68using helpers::SRegisterFrom;
Anton Kirilov644032c2016-12-06 17:51:43 +000069using helpers::Uint64ConstantFrom;
Scott Wakelingfe885462016-09-22 10:24:38 +010070
Artem Serov0fb37192016-12-06 18:13:40 +000071using vixl::ExactAssemblyScope;
72using vixl::CodeBufferCheckScope;
73
Scott Wakelingfe885462016-09-22 10:24:38 +010074using RegisterList = vixl32::RegisterList;
75
76static bool ExpectedPairLayout(Location location) {
77 // We expected this for both core and fpu register pairs.
78 return ((location.low() & 1) == 0) && (location.low() + 1 == location.high());
79}
Artem Serovd4cc5b22016-11-04 11:19:09 +000080// Use a local definition to prevent copying mistakes.
81static constexpr size_t kArmWordSize = static_cast<size_t>(kArmPointerSize);
82static constexpr size_t kArmBitsPerWord = kArmWordSize * kBitsPerByte;
Artem Serov551b28f2016-10-18 19:11:30 +010083static constexpr uint32_t kPackedSwitchCompareJumpThreshold = 7;
Scott Wakelingfe885462016-09-22 10:24:38 +010084
Vladimir Markoeee1c0e2017-04-21 17:58:41 +010085// Reference load (except object array loads) is using LDR Rt, [Rn, #offset] which can handle
86// offset < 4KiB. For offsets >= 4KiB, the load shall be emitted as two or more instructions.
Vladimir Marko008e09f32018-08-06 15:42:43 +010087// For the Baker read barrier implementation using link-time generated thunks we need to split
Vladimir Markoeee1c0e2017-04-21 17:58:41 +010088// the offset explicitly.
89constexpr uint32_t kReferenceLoadMinFarOffset = 4 * KB;
90
Roland Levillain5daa4952017-07-03 17:23:56 +010091// Using a base helps identify when we hit Marking Register check breakpoints.
92constexpr int kMarkingRegisterCheckBreakCodeBaseCode = 0x10;
93
Scott Wakelingfe885462016-09-22 10:24:38 +010094#ifdef __
95#error "ARM Codegen VIXL macro-assembler macro already defined."
96#endif
97
Scott Wakelingfe885462016-09-22 10:24:38 +010098// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
99#define __ down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler()-> // NOLINT
100#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArmPointerSize, x).Int32Value()
101
102// Marker that code is yet to be, and must, be implemented.
103#define TODO_VIXL32(level) LOG(level) << __PRETTY_FUNCTION__ << " unimplemented "
104
Vladimir Marko88abba22017-05-03 17:09:25 +0100105static inline bool CanEmitNarrowLdr(vixl32::Register rt, vixl32::Register rn, uint32_t offset) {
106 return rt.IsLow() && rn.IsLow() && offset < 32u;
107}
108
Vladimir Markoeee1c0e2017-04-21 17:58:41 +0100109class EmitAdrCode {
110 public:
111 EmitAdrCode(ArmVIXLMacroAssembler* assembler, vixl32::Register rd, vixl32::Label* label)
112 : assembler_(assembler), rd_(rd), label_(label) {
Vladimir Markod887ed82018-08-14 13:52:12 +0000113 DCHECK(!assembler->AllowMacroInstructions()); // In ExactAssemblyScope.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +0100114 adr_location_ = assembler->GetCursorOffset();
115 assembler->adr(EncodingSize(Wide), rd, label);
116 }
117
118 ~EmitAdrCode() {
119 DCHECK(label_->IsBound());
120 // The ADR emitted by the assembler does not set the Thumb mode bit we need.
121 // TODO: Maybe extend VIXL to allow ADR for return address?
122 uint8_t* raw_adr = assembler_->GetBuffer()->GetOffsetAddress<uint8_t*>(adr_location_);
123 // Expecting ADR encoding T3 with `(offset & 1) == 0`.
124 DCHECK_EQ(raw_adr[1] & 0xfbu, 0xf2u); // Check bits 24-31, except 26.
125 DCHECK_EQ(raw_adr[0] & 0xffu, 0x0fu); // Check bits 16-23.
126 DCHECK_EQ(raw_adr[3] & 0x8fu, rd_.GetCode()); // Check bits 8-11 and 15.
127 DCHECK_EQ(raw_adr[2] & 0x01u, 0x00u); // Check bit 0, i.e. the `offset & 1`.
128 // Add the Thumb mode bit.
129 raw_adr[2] |= 0x01u;
130 }
131
132 private:
133 ArmVIXLMacroAssembler* const assembler_;
134 vixl32::Register rd_;
135 vixl32::Label* const label_;
136 int32_t adr_location_;
137};
138
Vladimir Marko3232dbb2018-07-25 15:42:46 +0100139static RegisterSet OneRegInReferenceOutSaveEverythingCallerSaves() {
140 InvokeRuntimeCallingConventionARMVIXL calling_convention;
141 RegisterSet caller_saves = RegisterSet::Empty();
142 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0)));
143 // TODO: Add GetReturnLocation() to the calling convention so that we can DCHECK()
144 // that the the kPrimNot result register is the same as the first argument register.
145 return caller_saves;
146}
147
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100148// SaveLiveRegisters and RestoreLiveRegisters from SlowPathCodeARM operate on sets of S registers,
149// for each live D registers they treat two corresponding S registers as live ones.
150//
151// Two following functions (SaveContiguousSRegisterList, RestoreContiguousSRegisterList) build
152// from a list of contiguous S registers a list of contiguous D registers (processing first/last
153// S registers corner cases) and save/restore this new list treating them as D registers.
154// - decreasing code size
155// - avoiding hazards on Cortex-A57, when a pair of S registers for an actual live D register is
156// restored and then used in regular non SlowPath code as D register.
157//
158// For the following example (v means the S register is live):
159// D names: | D0 | D1 | D2 | D4 | ...
160// S names: | S0 | S1 | S2 | S3 | S4 | S5 | S6 | S7 | ...
161// Live? | | v | v | v | v | v | v | | ...
162//
163// S1 and S6 will be saved/restored independently; D registers list (D1, D2) will be processed
164// as D registers.
165//
166// TODO(VIXL): All this code should be unnecessary once the VIXL AArch32 backend provides helpers
167// for lists of floating-point registers.
168static size_t SaveContiguousSRegisterList(size_t first,
169 size_t last,
170 CodeGenerator* codegen,
171 size_t stack_offset) {
172 static_assert(kSRegSizeInBytes == kArmWordSize, "Broken assumption on reg/word sizes.");
173 static_assert(kDRegSizeInBytes == 2 * kArmWordSize, "Broken assumption on reg/word sizes.");
174 DCHECK_LE(first, last);
175 if ((first == last) && (first == 0)) {
176 __ Vstr(vixl32::SRegister(first), MemOperand(sp, stack_offset));
177 return stack_offset + kSRegSizeInBytes;
178 }
179 if (first % 2 == 1) {
180 __ Vstr(vixl32::SRegister(first++), MemOperand(sp, stack_offset));
181 stack_offset += kSRegSizeInBytes;
182 }
183
184 bool save_last = false;
185 if (last % 2 == 0) {
186 save_last = true;
187 --last;
188 }
189
190 if (first < last) {
191 vixl32::DRegister d_reg = vixl32::DRegister(first / 2);
192 DCHECK_EQ((last - first + 1) % 2, 0u);
193 size_t number_of_d_regs = (last - first + 1) / 2;
194
195 if (number_of_d_regs == 1) {
196 __ Vstr(d_reg, MemOperand(sp, stack_offset));
197 } else if (number_of_d_regs > 1) {
198 UseScratchRegisterScope temps(down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler());
199 vixl32::Register base = sp;
200 if (stack_offset != 0) {
201 base = temps.Acquire();
Scott Wakelingb77051e2016-11-21 19:46:00 +0000202 __ Add(base, sp, Operand::From(stack_offset));
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100203 }
204 __ Vstm(F64, base, NO_WRITE_BACK, DRegisterList(d_reg, number_of_d_regs));
205 }
206 stack_offset += number_of_d_regs * kDRegSizeInBytes;
207 }
208
209 if (save_last) {
210 __ Vstr(vixl32::SRegister(last + 1), MemOperand(sp, stack_offset));
211 stack_offset += kSRegSizeInBytes;
212 }
213
214 return stack_offset;
215}
216
217static size_t RestoreContiguousSRegisterList(size_t first,
218 size_t last,
219 CodeGenerator* codegen,
220 size_t stack_offset) {
221 static_assert(kSRegSizeInBytes == kArmWordSize, "Broken assumption on reg/word sizes.");
222 static_assert(kDRegSizeInBytes == 2 * kArmWordSize, "Broken assumption on reg/word sizes.");
223 DCHECK_LE(first, last);
224 if ((first == last) && (first == 0)) {
225 __ Vldr(vixl32::SRegister(first), MemOperand(sp, stack_offset));
226 return stack_offset + kSRegSizeInBytes;
227 }
228 if (first % 2 == 1) {
229 __ Vldr(vixl32::SRegister(first++), MemOperand(sp, stack_offset));
230 stack_offset += kSRegSizeInBytes;
231 }
232
233 bool restore_last = false;
234 if (last % 2 == 0) {
235 restore_last = true;
236 --last;
237 }
238
239 if (first < last) {
240 vixl32::DRegister d_reg = vixl32::DRegister(first / 2);
241 DCHECK_EQ((last - first + 1) % 2, 0u);
242 size_t number_of_d_regs = (last - first + 1) / 2;
243 if (number_of_d_regs == 1) {
244 __ Vldr(d_reg, MemOperand(sp, stack_offset));
245 } else if (number_of_d_regs > 1) {
246 UseScratchRegisterScope temps(down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler());
247 vixl32::Register base = sp;
248 if (stack_offset != 0) {
249 base = temps.Acquire();
Scott Wakelingb77051e2016-11-21 19:46:00 +0000250 __ Add(base, sp, Operand::From(stack_offset));
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100251 }
252 __ Vldm(F64, base, NO_WRITE_BACK, DRegisterList(d_reg, number_of_d_regs));
253 }
254 stack_offset += number_of_d_regs * kDRegSizeInBytes;
255 }
256
257 if (restore_last) {
258 __ Vldr(vixl32::SRegister(last + 1), MemOperand(sp, stack_offset));
259 stack_offset += kSRegSizeInBytes;
260 }
261
262 return stack_offset;
263}
264
Vladimir Markod5d2f2c2017-09-26 12:37:26 +0100265static LoadOperandType GetLoadOperandType(DataType::Type type) {
266 switch (type) {
267 case DataType::Type::kReference:
268 return kLoadWord;
269 case DataType::Type::kBool:
270 case DataType::Type::kUint8:
271 return kLoadUnsignedByte;
272 case DataType::Type::kInt8:
273 return kLoadSignedByte;
274 case DataType::Type::kUint16:
275 return kLoadUnsignedHalfword;
276 case DataType::Type::kInt16:
277 return kLoadSignedHalfword;
278 case DataType::Type::kInt32:
279 return kLoadWord;
280 case DataType::Type::kInt64:
281 return kLoadWordPair;
282 case DataType::Type::kFloat32:
283 return kLoadSWord;
284 case DataType::Type::kFloat64:
285 return kLoadDWord;
286 default:
287 LOG(FATAL) << "Unreachable type " << type;
288 UNREACHABLE();
289 }
290}
291
292static StoreOperandType GetStoreOperandType(DataType::Type type) {
293 switch (type) {
294 case DataType::Type::kReference:
295 return kStoreWord;
296 case DataType::Type::kBool:
297 case DataType::Type::kUint8:
298 case DataType::Type::kInt8:
299 return kStoreByte;
300 case DataType::Type::kUint16:
301 case DataType::Type::kInt16:
302 return kStoreHalfword;
303 case DataType::Type::kInt32:
304 return kStoreWord;
305 case DataType::Type::kInt64:
306 return kStoreWordPair;
307 case DataType::Type::kFloat32:
308 return kStoreSWord;
309 case DataType::Type::kFloat64:
310 return kStoreDWord;
311 default:
312 LOG(FATAL) << "Unreachable type " << type;
313 UNREACHABLE();
314 }
315}
316
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100317void SlowPathCodeARMVIXL::SaveLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
318 size_t stack_offset = codegen->GetFirstRegisterSlotInSlowPath();
319 size_t orig_offset = stack_offset;
320
Andreas Gampe3db70682018-12-26 15:12:03 -0800321 const uint32_t core_spills = codegen->GetSlowPathSpills(locations, /* core_registers= */ true);
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100322 for (uint32_t i : LowToHighBits(core_spills)) {
323 // If the register holds an object, update the stack mask.
324 if (locations->RegisterContainsObject(i)) {
325 locations->SetStackBit(stack_offset / kVRegSize);
326 }
327 DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
328 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
329 saved_core_stack_offsets_[i] = stack_offset;
330 stack_offset += kArmWordSize;
331 }
332
333 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
334 arm_codegen->GetAssembler()->StoreRegisterList(core_spills, orig_offset);
335
Andreas Gampe3db70682018-12-26 15:12:03 -0800336 uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers= */ false);
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100337 orig_offset = stack_offset;
338 for (uint32_t i : LowToHighBits(fp_spills)) {
339 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
340 saved_fpu_stack_offsets_[i] = stack_offset;
341 stack_offset += kArmWordSize;
342 }
343
344 stack_offset = orig_offset;
345 while (fp_spills != 0u) {
346 uint32_t begin = CTZ(fp_spills);
347 uint32_t tmp = fp_spills + (1u << begin);
348 fp_spills &= tmp; // Clear the contiguous range of 1s.
349 uint32_t end = (tmp == 0u) ? 32u : CTZ(tmp); // CTZ(0) is undefined.
350 stack_offset = SaveContiguousSRegisterList(begin, end - 1, codegen, stack_offset);
351 }
352 DCHECK_LE(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
353}
354
355void SlowPathCodeARMVIXL::RestoreLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
356 size_t stack_offset = codegen->GetFirstRegisterSlotInSlowPath();
357 size_t orig_offset = stack_offset;
358
Andreas Gampe3db70682018-12-26 15:12:03 -0800359 const uint32_t core_spills = codegen->GetSlowPathSpills(locations, /* core_registers= */ true);
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100360 for (uint32_t i : LowToHighBits(core_spills)) {
361 DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
362 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
363 stack_offset += kArmWordSize;
364 }
365
366 // TODO(VIXL): Check the coherency of stack_offset after this with a test.
367 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
368 arm_codegen->GetAssembler()->LoadRegisterList(core_spills, orig_offset);
369
Andreas Gampe3db70682018-12-26 15:12:03 -0800370 uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers= */ false);
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100371 while (fp_spills != 0u) {
372 uint32_t begin = CTZ(fp_spills);
373 uint32_t tmp = fp_spills + (1u << begin);
374 fp_spills &= tmp; // Clear the contiguous range of 1s.
375 uint32_t end = (tmp == 0u) ? 32u : CTZ(tmp); // CTZ(0) is undefined.
376 stack_offset = RestoreContiguousSRegisterList(begin, end - 1, codegen, stack_offset);
377 }
378 DCHECK_LE(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
379}
380
381class NullCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
382 public:
383 explicit NullCheckSlowPathARMVIXL(HNullCheck* instruction) : SlowPathCodeARMVIXL(instruction) {}
384
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100385 void EmitNativeCode(CodeGenerator* codegen) override {
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100386 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
387 __ Bind(GetEntryLabel());
388 if (instruction_->CanThrowIntoCatchBlock()) {
389 // Live registers will be restored in the catch block if caught.
390 SaveLiveRegisters(codegen, instruction_->GetLocations());
391 }
392 arm_codegen->InvokeRuntime(kQuickThrowNullPointer,
393 instruction_,
394 instruction_->GetDexPc(),
395 this);
396 CheckEntrypointTypes<kQuickThrowNullPointer, void, void>();
397 }
398
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100399 bool IsFatal() const override { return true; }
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100400
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100401 const char* GetDescription() const override { return "NullCheckSlowPathARMVIXL"; }
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100402
403 private:
404 DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARMVIXL);
405};
406
Scott Wakelingfe885462016-09-22 10:24:38 +0100407class DivZeroCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
408 public:
409 explicit DivZeroCheckSlowPathARMVIXL(HDivZeroCheck* instruction)
410 : SlowPathCodeARMVIXL(instruction) {}
411
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100412 void EmitNativeCode(CodeGenerator* codegen) override {
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100413 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
Scott Wakelingfe885462016-09-22 10:24:38 +0100414 __ Bind(GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100415 arm_codegen->InvokeRuntime(kQuickThrowDivZero, instruction_, instruction_->GetDexPc(), this);
Scott Wakelingfe885462016-09-22 10:24:38 +0100416 CheckEntrypointTypes<kQuickThrowDivZero, void, void>();
417 }
418
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100419 bool IsFatal() const override { return true; }
Scott Wakelingfe885462016-09-22 10:24:38 +0100420
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100421 const char* GetDescription() const override { return "DivZeroCheckSlowPathARMVIXL"; }
Scott Wakelingfe885462016-09-22 10:24:38 +0100422
423 private:
424 DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathARMVIXL);
425};
426
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100427class SuspendCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
428 public:
429 SuspendCheckSlowPathARMVIXL(HSuspendCheck* instruction, HBasicBlock* successor)
430 : SlowPathCodeARMVIXL(instruction), successor_(successor) {}
431
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100432 void EmitNativeCode(CodeGenerator* codegen) override {
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100433 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
434 __ Bind(GetEntryLabel());
435 arm_codegen->InvokeRuntime(kQuickTestSuspend, instruction_, instruction_->GetDexPc(), this);
436 CheckEntrypointTypes<kQuickTestSuspend, void, void>();
437 if (successor_ == nullptr) {
438 __ B(GetReturnLabel());
439 } else {
440 __ B(arm_codegen->GetLabelOf(successor_));
441 }
442 }
443
444 vixl32::Label* GetReturnLabel() {
445 DCHECK(successor_ == nullptr);
446 return &return_label_;
447 }
448
449 HBasicBlock* GetSuccessor() const {
450 return successor_;
451 }
452
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100453 const char* GetDescription() const override { return "SuspendCheckSlowPathARMVIXL"; }
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100454
455 private:
456 // If not null, the block to branch to after the suspend check.
457 HBasicBlock* const successor_;
458
459 // If `successor_` is null, the label to branch to after the suspend check.
460 vixl32::Label return_label_;
461
462 DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathARMVIXL);
463};
464
Scott Wakelingc34dba72016-10-03 10:14:44 +0100465class BoundsCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
466 public:
467 explicit BoundsCheckSlowPathARMVIXL(HBoundsCheck* instruction)
468 : SlowPathCodeARMVIXL(instruction) {}
469
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100470 void EmitNativeCode(CodeGenerator* codegen) override {
Scott Wakelingc34dba72016-10-03 10:14:44 +0100471 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
472 LocationSummary* locations = instruction_->GetLocations();
473
474 __ Bind(GetEntryLabel());
475 if (instruction_->CanThrowIntoCatchBlock()) {
476 // Live registers will be restored in the catch block if caught.
477 SaveLiveRegisters(codegen, instruction_->GetLocations());
478 }
479 // We're moving two locations to locations that could overlap, so we need a parallel
480 // move resolver.
481 InvokeRuntimeCallingConventionARMVIXL calling_convention;
482 codegen->EmitParallelMoves(
483 locations->InAt(0),
484 LocationFrom(calling_convention.GetRegisterAt(0)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100485 DataType::Type::kInt32,
Scott Wakelingc34dba72016-10-03 10:14:44 +0100486 locations->InAt(1),
487 LocationFrom(calling_convention.GetRegisterAt(1)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100488 DataType::Type::kInt32);
Scott Wakelingc34dba72016-10-03 10:14:44 +0100489 QuickEntrypointEnum entrypoint = instruction_->AsBoundsCheck()->IsStringCharAt()
490 ? kQuickThrowStringBounds
491 : kQuickThrowArrayBounds;
492 arm_codegen->InvokeRuntime(entrypoint, instruction_, instruction_->GetDexPc(), this);
493 CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>();
494 CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>();
495 }
496
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100497 bool IsFatal() const override { return true; }
Scott Wakelingc34dba72016-10-03 10:14:44 +0100498
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100499 const char* GetDescription() const override { return "BoundsCheckSlowPathARMVIXL"; }
Scott Wakelingc34dba72016-10-03 10:14:44 +0100500
501 private:
502 DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARMVIXL);
503};
504
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100505class LoadClassSlowPathARMVIXL : public SlowPathCodeARMVIXL {
506 public:
Vladimir Markoa9f303c2018-07-20 16:43:56 +0100507 LoadClassSlowPathARMVIXL(HLoadClass* cls, HInstruction* at)
508 : SlowPathCodeARMVIXL(at), cls_(cls) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100509 DCHECK(at->IsLoadClass() || at->IsClinitCheck());
Vladimir Markoa9f303c2018-07-20 16:43:56 +0100510 DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_);
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100511 }
512
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100513 void EmitNativeCode(CodeGenerator* codegen) override {
Vladimir Marko6bec91c2017-01-09 15:03:12 +0000514 LocationSummary* locations = instruction_->GetLocations();
Vladimir Markoea4c1262017-02-06 19:59:33 +0000515 Location out = locations->Out();
Vladimir Markoa9f303c2018-07-20 16:43:56 +0100516 const uint32_t dex_pc = instruction_->GetDexPc();
517 bool must_resolve_type = instruction_->IsLoadClass() && cls_->MustResolveTypeOnSlowPath();
518 bool must_do_clinit = instruction_->IsClinitCheck() || cls_->MustGenerateClinitCheck();
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100519
520 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
521 __ Bind(GetEntryLabel());
522 SaveLiveRegisters(codegen, locations);
523
524 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Vladimir Markoa9f303c2018-07-20 16:43:56 +0100525 if (must_resolve_type) {
526 DCHECK(IsSameDexFile(cls_->GetDexFile(), arm_codegen->GetGraph()->GetDexFile()));
527 dex::TypeIndex type_index = cls_->GetTypeIndex();
528 __ Mov(calling_convention.GetRegisterAt(0), type_index.index_);
Vladimir Marko9d479252018-07-24 11:35:20 +0100529 arm_codegen->InvokeRuntime(kQuickResolveType, instruction_, dex_pc, this);
530 CheckEntrypointTypes<kQuickResolveType, void*, uint32_t>();
Vladimir Markoa9f303c2018-07-20 16:43:56 +0100531 // If we also must_do_clinit, the resolved type is now in the correct register.
532 } else {
533 DCHECK(must_do_clinit);
534 Location source = instruction_->IsLoadClass() ? out : locations->InAt(0);
535 arm_codegen->Move32(LocationFrom(calling_convention.GetRegisterAt(0)), source);
536 }
537 if (must_do_clinit) {
538 arm_codegen->InvokeRuntime(kQuickInitializeStaticStorage, instruction_, dex_pc, this);
539 CheckEntrypointTypes<kQuickInitializeStaticStorage, void*, mirror::Class*>();
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100540 }
541
542 // Move the class to the desired location.
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100543 if (out.IsValid()) {
544 DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
545 arm_codegen->Move32(locations->Out(), LocationFrom(r0));
546 }
547 RestoreLiveRegisters(codegen, locations);
548 __ B(GetExitLabel());
549 }
550
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100551 const char* GetDescription() const override { return "LoadClassSlowPathARMVIXL"; }
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100552
553 private:
554 // The class this slow path will load.
555 HLoadClass* const cls_;
556
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100557 DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathARMVIXL);
558};
559
Artem Serovd4cc5b22016-11-04 11:19:09 +0000560class LoadStringSlowPathARMVIXL : public SlowPathCodeARMVIXL {
561 public:
562 explicit LoadStringSlowPathARMVIXL(HLoadString* instruction)
563 : SlowPathCodeARMVIXL(instruction) {}
564
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100565 void EmitNativeCode(CodeGenerator* codegen) override {
Vladimir Markoea4c1262017-02-06 19:59:33 +0000566 DCHECK(instruction_->IsLoadString());
567 DCHECK_EQ(instruction_->AsLoadString()->GetLoadKind(), HLoadString::LoadKind::kBssEntry);
Artem Serovd4cc5b22016-11-04 11:19:09 +0000568 LocationSummary* locations = instruction_->GetLocations();
569 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
Vladimir Markof3c52b42017-11-17 17:32:12 +0000570 const dex::StringIndex string_index = instruction_->AsLoadString()->GetStringIndex();
Artem Serovd4cc5b22016-11-04 11:19:09 +0000571
572 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
573 __ Bind(GetEntryLabel());
574 SaveLiveRegisters(codegen, locations);
575
576 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Vladimir Marko6bec91c2017-01-09 15:03:12 +0000577 __ Mov(calling_convention.GetRegisterAt(0), string_index.index_);
Artem Serovd4cc5b22016-11-04 11:19:09 +0000578 arm_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), this);
579 CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
580
Artem Serovd4cc5b22016-11-04 11:19:09 +0000581 arm_codegen->Move32(locations->Out(), LocationFrom(r0));
582 RestoreLiveRegisters(codegen, locations);
583
584 __ B(GetExitLabel());
585 }
586
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100587 const char* GetDescription() const override { return "LoadStringSlowPathARMVIXL"; }
Artem Serovd4cc5b22016-11-04 11:19:09 +0000588
589 private:
590 DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathARMVIXL);
591};
592
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100593class TypeCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
594 public:
595 TypeCheckSlowPathARMVIXL(HInstruction* instruction, bool is_fatal)
596 : SlowPathCodeARMVIXL(instruction), is_fatal_(is_fatal) {}
597
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100598 void EmitNativeCode(CodeGenerator* codegen) override {
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100599 LocationSummary* locations = instruction_->GetLocations();
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100600 DCHECK(instruction_->IsCheckCast()
601 || !locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
602
603 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
604 __ Bind(GetEntryLabel());
605
Vladimir Marko87584542017-12-12 17:47:52 +0000606 if (!is_fatal_ || instruction_->CanThrowIntoCatchBlock()) {
Artem Serovcfbe9132016-10-14 15:58:56 +0100607 SaveLiveRegisters(codegen, locations);
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100608 }
609
610 // We're moving two locations to locations that could overlap, so we need a parallel
611 // move resolver.
612 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100613
Mathieu Chartier9fd8c602016-11-14 14:38:53 -0800614 codegen->EmitParallelMoves(locations->InAt(0),
Mathieu Chartierb99f4d62016-11-07 16:17:26 -0800615 LocationFrom(calling_convention.GetRegisterAt(0)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100616 DataType::Type::kReference,
Mathieu Chartier9fd8c602016-11-14 14:38:53 -0800617 locations->InAt(1),
Mathieu Chartierb99f4d62016-11-07 16:17:26 -0800618 LocationFrom(calling_convention.GetRegisterAt(1)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100619 DataType::Type::kReference);
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100620 if (instruction_->IsInstanceOf()) {
Artem Serovcfbe9132016-10-14 15:58:56 +0100621 arm_codegen->InvokeRuntime(kQuickInstanceofNonTrivial,
622 instruction_,
623 instruction_->GetDexPc(),
624 this);
Mathieu Chartier9fd8c602016-11-14 14:38:53 -0800625 CheckEntrypointTypes<kQuickInstanceofNonTrivial, size_t, mirror::Object*, mirror::Class*>();
Artem Serovcfbe9132016-10-14 15:58:56 +0100626 arm_codegen->Move32(locations->Out(), LocationFrom(r0));
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100627 } else {
628 DCHECK(instruction_->IsCheckCast());
Mathieu Chartierb99f4d62016-11-07 16:17:26 -0800629 arm_codegen->InvokeRuntime(kQuickCheckInstanceOf,
630 instruction_,
631 instruction_->GetDexPc(),
632 this);
633 CheckEntrypointTypes<kQuickCheckInstanceOf, void, mirror::Object*, mirror::Class*>();
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100634 }
635
636 if (!is_fatal_) {
Artem Serovcfbe9132016-10-14 15:58:56 +0100637 RestoreLiveRegisters(codegen, locations);
638 __ B(GetExitLabel());
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100639 }
640 }
641
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100642 const char* GetDescription() const override { return "TypeCheckSlowPathARMVIXL"; }
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100643
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100644 bool IsFatal() const override { return is_fatal_; }
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100645
646 private:
647 const bool is_fatal_;
648
649 DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathARMVIXL);
650};
651
Scott Wakelingc34dba72016-10-03 10:14:44 +0100652class DeoptimizationSlowPathARMVIXL : public SlowPathCodeARMVIXL {
653 public:
654 explicit DeoptimizationSlowPathARMVIXL(HDeoptimize* instruction)
655 : SlowPathCodeARMVIXL(instruction) {}
656
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100657 void EmitNativeCode(CodeGenerator* codegen) override {
Scott Wakelingc34dba72016-10-03 10:14:44 +0100658 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
659 __ Bind(GetEntryLabel());
Nicolas Geoffray4e92c3c2017-05-08 09:34:26 +0100660 LocationSummary* locations = instruction_->GetLocations();
661 SaveLiveRegisters(codegen, locations);
662 InvokeRuntimeCallingConventionARMVIXL calling_convention;
663 __ Mov(calling_convention.GetRegisterAt(0),
664 static_cast<uint32_t>(instruction_->AsDeoptimize()->GetDeoptimizationKind()));
665
Scott Wakelingc34dba72016-10-03 10:14:44 +0100666 arm_codegen->InvokeRuntime(kQuickDeoptimize, instruction_, instruction_->GetDexPc(), this);
Nicolas Geoffray4e92c3c2017-05-08 09:34:26 +0100667 CheckEntrypointTypes<kQuickDeoptimize, void, DeoptimizationKind>();
Scott Wakelingc34dba72016-10-03 10:14:44 +0100668 }
669
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100670 const char* GetDescription() const override { return "DeoptimizationSlowPathARMVIXL"; }
Scott Wakelingc34dba72016-10-03 10:14:44 +0100671
672 private:
673 DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathARMVIXL);
674};
675
676class ArraySetSlowPathARMVIXL : public SlowPathCodeARMVIXL {
677 public:
678 explicit ArraySetSlowPathARMVIXL(HInstruction* instruction) : SlowPathCodeARMVIXL(instruction) {}
679
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100680 void EmitNativeCode(CodeGenerator* codegen) override {
Scott Wakelingc34dba72016-10-03 10:14:44 +0100681 LocationSummary* locations = instruction_->GetLocations();
682 __ Bind(GetEntryLabel());
683 SaveLiveRegisters(codegen, locations);
684
685 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Vladimir Markoca6fff82017-10-03 14:49:14 +0100686 HParallelMove parallel_move(codegen->GetGraph()->GetAllocator());
Scott Wakelingc34dba72016-10-03 10:14:44 +0100687 parallel_move.AddMove(
688 locations->InAt(0),
689 LocationFrom(calling_convention.GetRegisterAt(0)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100690 DataType::Type::kReference,
Scott Wakelingc34dba72016-10-03 10:14:44 +0100691 nullptr);
692 parallel_move.AddMove(
693 locations->InAt(1),
694 LocationFrom(calling_convention.GetRegisterAt(1)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100695 DataType::Type::kInt32,
Scott Wakelingc34dba72016-10-03 10:14:44 +0100696 nullptr);
697 parallel_move.AddMove(
698 locations->InAt(2),
699 LocationFrom(calling_convention.GetRegisterAt(2)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100700 DataType::Type::kReference,
Scott Wakelingc34dba72016-10-03 10:14:44 +0100701 nullptr);
702 codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
703
704 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
705 arm_codegen->InvokeRuntime(kQuickAputObject, instruction_, instruction_->GetDexPc(), this);
706 CheckEntrypointTypes<kQuickAputObject, void, mirror::Array*, int32_t, mirror::Object*>();
707 RestoreLiveRegisters(codegen, locations);
708 __ B(GetExitLabel());
709 }
710
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100711 const char* GetDescription() const override { return "ArraySetSlowPathARMVIXL"; }
Scott Wakelingc34dba72016-10-03 10:14:44 +0100712
713 private:
714 DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathARMVIXL);
715};
716
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000717// Slow path generating a read barrier for a heap reference.
718class ReadBarrierForHeapReferenceSlowPathARMVIXL : public SlowPathCodeARMVIXL {
719 public:
720 ReadBarrierForHeapReferenceSlowPathARMVIXL(HInstruction* instruction,
721 Location out,
722 Location ref,
723 Location obj,
724 uint32_t offset,
725 Location index)
726 : SlowPathCodeARMVIXL(instruction),
727 out_(out),
728 ref_(ref),
729 obj_(obj),
730 offset_(offset),
731 index_(index) {
732 DCHECK(kEmitCompilerReadBarrier);
733 // If `obj` is equal to `out` or `ref`, it means the initial object
734 // has been overwritten by (or after) the heap object reference load
735 // to be instrumented, e.g.:
736 //
737 // __ LoadFromOffset(kLoadWord, out, out, offset);
738 // codegen_->GenerateReadBarrierSlow(instruction, out_loc, out_loc, out_loc, offset);
739 //
740 // In that case, we have lost the information about the original
741 // object, and the emitted read barrier cannot work properly.
742 DCHECK(!obj.Equals(out)) << "obj=" << obj << " out=" << out;
743 DCHECK(!obj.Equals(ref)) << "obj=" << obj << " ref=" << ref;
744 }
745
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100746 void EmitNativeCode(CodeGenerator* codegen) override {
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000747 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
748 LocationSummary* locations = instruction_->GetLocations();
749 vixl32::Register reg_out = RegisterFrom(out_);
750 DCHECK(locations->CanCall());
751 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out.GetCode()));
752 DCHECK(instruction_->IsInstanceFieldGet() ||
753 instruction_->IsStaticFieldGet() ||
754 instruction_->IsArrayGet() ||
755 instruction_->IsInstanceOf() ||
756 instruction_->IsCheckCast() ||
Andreas Gamped9911ee2017-03-27 13:27:24 -0700757 (instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()))
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000758 << "Unexpected instruction in read barrier for heap reference slow path: "
759 << instruction_->DebugName();
760 // The read barrier instrumentation of object ArrayGet
761 // instructions does not support the HIntermediateAddress
762 // instruction.
763 DCHECK(!(instruction_->IsArrayGet() &&
764 instruction_->AsArrayGet()->GetArray()->IsIntermediateAddress()));
765
766 __ Bind(GetEntryLabel());
767 SaveLiveRegisters(codegen, locations);
768
769 // We may have to change the index's value, but as `index_` is a
770 // constant member (like other "inputs" of this slow path),
771 // introduce a copy of it, `index`.
772 Location index = index_;
773 if (index_.IsValid()) {
774 // Handle `index_` for HArrayGet and UnsafeGetObject/UnsafeGetObjectVolatile intrinsics.
775 if (instruction_->IsArrayGet()) {
776 // Compute the actual memory offset and store it in `index`.
777 vixl32::Register index_reg = RegisterFrom(index_);
778 DCHECK(locations->GetLiveRegisters()->ContainsCoreRegister(index_reg.GetCode()));
779 if (codegen->IsCoreCalleeSaveRegister(index_reg.GetCode())) {
780 // We are about to change the value of `index_reg` (see the
Roland Levillain9983e302017-07-14 14:34:22 +0100781 // calls to art::arm::ArmVIXLMacroAssembler::Lsl and
782 // art::arm::ArmVIXLMacroAssembler::Add below), but it has
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000783 // not been saved by the previous call to
784 // art::SlowPathCode::SaveLiveRegisters, as it is a
785 // callee-save register --
786 // art::SlowPathCode::SaveLiveRegisters does not consider
787 // callee-save registers, as it has been designed with the
788 // assumption that callee-save registers are supposed to be
789 // handled by the called function. So, as a callee-save
790 // register, `index_reg` _would_ eventually be saved onto
791 // the stack, but it would be too late: we would have
792 // changed its value earlier. Therefore, we manually save
793 // it here into another freely available register,
794 // `free_reg`, chosen of course among the caller-save
795 // registers (as a callee-save `free_reg` register would
796 // exhibit the same problem).
797 //
798 // Note we could have requested a temporary register from
799 // the register allocator instead; but we prefer not to, as
800 // this is a slow path, and we know we can find a
801 // caller-save register that is available.
802 vixl32::Register free_reg = FindAvailableCallerSaveRegister(codegen);
803 __ Mov(free_reg, index_reg);
804 index_reg = free_reg;
805 index = LocationFrom(index_reg);
806 } else {
807 // The initial register stored in `index_` has already been
808 // saved in the call to art::SlowPathCode::SaveLiveRegisters
809 // (as it is not a callee-save register), so we can freely
810 // use it.
811 }
812 // Shifting the index value contained in `index_reg` by the scale
813 // factor (2) cannot overflow in practice, as the runtime is
814 // unable to allocate object arrays with a size larger than
815 // 2^26 - 1 (that is, 2^28 - 4 bytes).
816 __ Lsl(index_reg, index_reg, TIMES_4);
817 static_assert(
818 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
819 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
820 __ Add(index_reg, index_reg, offset_);
821 } else {
822 // In the case of the UnsafeGetObject/UnsafeGetObjectVolatile
823 // intrinsics, `index_` is not shifted by a scale factor of 2
824 // (as in the case of ArrayGet), as it is actually an offset
825 // to an object field within an object.
826 DCHECK(instruction_->IsInvoke()) << instruction_->DebugName();
827 DCHECK(instruction_->GetLocations()->Intrinsified());
828 DCHECK((instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObject) ||
829 (instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile))
830 << instruction_->AsInvoke()->GetIntrinsic();
831 DCHECK_EQ(offset_, 0U);
832 DCHECK(index_.IsRegisterPair());
833 // UnsafeGet's offset location is a register pair, the low
834 // part contains the correct offset.
835 index = index_.ToLow();
836 }
837 }
838
839 // We're moving two or three locations to locations that could
840 // overlap, so we need a parallel move resolver.
841 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Vladimir Markoca6fff82017-10-03 14:49:14 +0100842 HParallelMove parallel_move(codegen->GetGraph()->GetAllocator());
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000843 parallel_move.AddMove(ref_,
844 LocationFrom(calling_convention.GetRegisterAt(0)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100845 DataType::Type::kReference,
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000846 nullptr);
847 parallel_move.AddMove(obj_,
848 LocationFrom(calling_convention.GetRegisterAt(1)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100849 DataType::Type::kReference,
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000850 nullptr);
851 if (index.IsValid()) {
852 parallel_move.AddMove(index,
853 LocationFrom(calling_convention.GetRegisterAt(2)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100854 DataType::Type::kInt32,
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000855 nullptr);
856 codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
857 } else {
858 codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
859 __ Mov(calling_convention.GetRegisterAt(2), offset_);
860 }
861 arm_codegen->InvokeRuntime(kQuickReadBarrierSlow, instruction_, instruction_->GetDexPc(), this);
862 CheckEntrypointTypes<
863 kQuickReadBarrierSlow, mirror::Object*, mirror::Object*, mirror::Object*, uint32_t>();
864 arm_codegen->Move32(out_, LocationFrom(r0));
865
866 RestoreLiveRegisters(codegen, locations);
867 __ B(GetExitLabel());
868 }
869
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100870 const char* GetDescription() const override {
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000871 return "ReadBarrierForHeapReferenceSlowPathARMVIXL";
872 }
873
874 private:
875 vixl32::Register FindAvailableCallerSaveRegister(CodeGenerator* codegen) {
876 uint32_t ref = RegisterFrom(ref_).GetCode();
877 uint32_t obj = RegisterFrom(obj_).GetCode();
878 for (uint32_t i = 0, e = codegen->GetNumberOfCoreRegisters(); i < e; ++i) {
879 if (i != ref && i != obj && !codegen->IsCoreCalleeSaveRegister(i)) {
880 return vixl32::Register(i);
881 }
882 }
883 // We shall never fail to find a free caller-save register, as
884 // there are more than two core caller-save registers on ARM
885 // (meaning it is possible to find one which is different from
886 // `ref` and `obj`).
887 DCHECK_GT(codegen->GetNumberOfCoreCallerSaveRegisters(), 2u);
888 LOG(FATAL) << "Could not find a free caller-save register";
889 UNREACHABLE();
890 }
891
892 const Location out_;
893 const Location ref_;
894 const Location obj_;
895 const uint32_t offset_;
896 // An additional location containing an index to an array.
897 // Only used for HArrayGet and the UnsafeGetObject &
898 // UnsafeGetObjectVolatile intrinsics.
899 const Location index_;
900
901 DISALLOW_COPY_AND_ASSIGN(ReadBarrierForHeapReferenceSlowPathARMVIXL);
902};
903
904// Slow path generating a read barrier for a GC root.
905class ReadBarrierForRootSlowPathARMVIXL : public SlowPathCodeARMVIXL {
906 public:
907 ReadBarrierForRootSlowPathARMVIXL(HInstruction* instruction, Location out, Location root)
908 : SlowPathCodeARMVIXL(instruction), out_(out), root_(root) {
909 DCHECK(kEmitCompilerReadBarrier);
910 }
911
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100912 void EmitNativeCode(CodeGenerator* codegen) override {
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000913 LocationSummary* locations = instruction_->GetLocations();
914 vixl32::Register reg_out = RegisterFrom(out_);
915 DCHECK(locations->CanCall());
916 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out.GetCode()));
917 DCHECK(instruction_->IsLoadClass() || instruction_->IsLoadString())
918 << "Unexpected instruction in read barrier for GC root slow path: "
919 << instruction_->DebugName();
920
921 __ Bind(GetEntryLabel());
922 SaveLiveRegisters(codegen, locations);
923
924 InvokeRuntimeCallingConventionARMVIXL calling_convention;
925 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
926 arm_codegen->Move32(LocationFrom(calling_convention.GetRegisterAt(0)), root_);
927 arm_codegen->InvokeRuntime(kQuickReadBarrierForRootSlow,
928 instruction_,
929 instruction_->GetDexPc(),
930 this);
931 CheckEntrypointTypes<kQuickReadBarrierForRootSlow, mirror::Object*, GcRoot<mirror::Object>*>();
932 arm_codegen->Move32(out_, LocationFrom(r0));
933
934 RestoreLiveRegisters(codegen, locations);
935 __ B(GetExitLabel());
936 }
937
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100938 const char* GetDescription() const override { return "ReadBarrierForRootSlowPathARMVIXL"; }
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000939
940 private:
941 const Location out_;
942 const Location root_;
943
944 DISALLOW_COPY_AND_ASSIGN(ReadBarrierForRootSlowPathARMVIXL);
945};
Scott Wakelingc34dba72016-10-03 10:14:44 +0100946
Scott Wakelingfe885462016-09-22 10:24:38 +0100947inline vixl32::Condition ARMCondition(IfCondition cond) {
948 switch (cond) {
949 case kCondEQ: return eq;
950 case kCondNE: return ne;
951 case kCondLT: return lt;
952 case kCondLE: return le;
953 case kCondGT: return gt;
954 case kCondGE: return ge;
955 case kCondB: return lo;
956 case kCondBE: return ls;
957 case kCondA: return hi;
958 case kCondAE: return hs;
959 }
960 LOG(FATAL) << "Unreachable";
961 UNREACHABLE();
962}
963
964// Maps signed condition to unsigned condition.
965inline vixl32::Condition ARMUnsignedCondition(IfCondition cond) {
966 switch (cond) {
967 case kCondEQ: return eq;
968 case kCondNE: return ne;
969 // Signed to unsigned.
970 case kCondLT: return lo;
971 case kCondLE: return ls;
972 case kCondGT: return hi;
973 case kCondGE: return hs;
974 // Unsigned remain unchanged.
975 case kCondB: return lo;
976 case kCondBE: return ls;
977 case kCondA: return hi;
978 case kCondAE: return hs;
979 }
980 LOG(FATAL) << "Unreachable";
981 UNREACHABLE();
982}
983
984inline vixl32::Condition ARMFPCondition(IfCondition cond, bool gt_bias) {
985 // The ARM condition codes can express all the necessary branches, see the
986 // "Meaning (floating-point)" column in the table A8-1 of the ARMv7 reference manual.
987 // There is no dex instruction or HIR that would need the missing conditions
988 // "equal or unordered" or "not equal".
989 switch (cond) {
990 case kCondEQ: return eq;
991 case kCondNE: return ne /* unordered */;
992 case kCondLT: return gt_bias ? cc : lt /* unordered */;
993 case kCondLE: return gt_bias ? ls : le /* unordered */;
994 case kCondGT: return gt_bias ? hi /* unordered */ : gt;
995 case kCondGE: return gt_bias ? cs /* unordered */ : ge;
996 default:
997 LOG(FATAL) << "UNREACHABLE";
998 UNREACHABLE();
999 }
1000}
1001
Anton Kirilov74234da2017-01-13 14:42:47 +00001002inline ShiftType ShiftFromOpKind(HDataProcWithShifterOp::OpKind op_kind) {
1003 switch (op_kind) {
1004 case HDataProcWithShifterOp::kASR: return ShiftType::ASR;
1005 case HDataProcWithShifterOp::kLSL: return ShiftType::LSL;
1006 case HDataProcWithShifterOp::kLSR: return ShiftType::LSR;
1007 default:
1008 LOG(FATAL) << "Unexpected op kind " << op_kind;
1009 UNREACHABLE();
1010 }
1011}
1012
Scott Wakelingfe885462016-09-22 10:24:38 +01001013void CodeGeneratorARMVIXL::DumpCoreRegister(std::ostream& stream, int reg) const {
1014 stream << vixl32::Register(reg);
1015}
1016
1017void CodeGeneratorARMVIXL::DumpFloatingPointRegister(std::ostream& stream, int reg) const {
1018 stream << vixl32::SRegister(reg);
1019}
1020
Vladimir Markoa0431112018-06-25 09:32:54 +01001021const ArmInstructionSetFeatures& CodeGeneratorARMVIXL::GetInstructionSetFeatures() const {
1022 return *GetCompilerOptions().GetInstructionSetFeatures()->AsArmInstructionSetFeatures();
1023}
1024
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001025static uint32_t ComputeSRegisterListMask(const SRegisterList& regs) {
Scott Wakelingfe885462016-09-22 10:24:38 +01001026 uint32_t mask = 0;
1027 for (uint32_t i = regs.GetFirstSRegister().GetCode();
1028 i <= regs.GetLastSRegister().GetCode();
1029 ++i) {
1030 mask |= (1 << i);
1031 }
1032 return mask;
1033}
1034
Artem Serovd4cc5b22016-11-04 11:19:09 +00001035// Saves the register in the stack. Returns the size taken on stack.
1036size_t CodeGeneratorARMVIXL::SaveCoreRegister(size_t stack_index ATTRIBUTE_UNUSED,
1037 uint32_t reg_id ATTRIBUTE_UNUSED) {
1038 TODO_VIXL32(FATAL);
Elliott Hughesc1896c92018-11-29 11:33:18 -08001039 UNREACHABLE();
Artem Serovd4cc5b22016-11-04 11:19:09 +00001040}
1041
1042// Restores the register from the stack. Returns the size taken on stack.
1043size_t CodeGeneratorARMVIXL::RestoreCoreRegister(size_t stack_index ATTRIBUTE_UNUSED,
1044 uint32_t reg_id ATTRIBUTE_UNUSED) {
1045 TODO_VIXL32(FATAL);
Elliott Hughesc1896c92018-11-29 11:33:18 -08001046 UNREACHABLE();
Artem Serovd4cc5b22016-11-04 11:19:09 +00001047}
1048
1049size_t CodeGeneratorARMVIXL::SaveFloatingPointRegister(size_t stack_index ATTRIBUTE_UNUSED,
1050 uint32_t reg_id ATTRIBUTE_UNUSED) {
1051 TODO_VIXL32(FATAL);
Elliott Hughesc1896c92018-11-29 11:33:18 -08001052 UNREACHABLE();
Artem Serovd4cc5b22016-11-04 11:19:09 +00001053}
1054
1055size_t CodeGeneratorARMVIXL::RestoreFloatingPointRegister(size_t stack_index ATTRIBUTE_UNUSED,
1056 uint32_t reg_id ATTRIBUTE_UNUSED) {
1057 TODO_VIXL32(FATAL);
Elliott Hughesc1896c92018-11-29 11:33:18 -08001058 UNREACHABLE();
Anton Kirilove28d9ae2016-10-25 18:17:23 +01001059}
1060
Anton Kirilov74234da2017-01-13 14:42:47 +00001061static void GenerateDataProcInstruction(HInstruction::InstructionKind kind,
1062 vixl32::Register out,
1063 vixl32::Register first,
1064 const Operand& second,
1065 CodeGeneratorARMVIXL* codegen) {
1066 if (second.IsImmediate() && second.GetImmediate() == 0) {
1067 const Operand in = kind == HInstruction::kAnd
1068 ? Operand(0)
1069 : Operand(first);
1070
1071 __ Mov(out, in);
1072 } else {
1073 switch (kind) {
1074 case HInstruction::kAdd:
1075 __ Add(out, first, second);
1076 break;
1077 case HInstruction::kAnd:
1078 __ And(out, first, second);
1079 break;
1080 case HInstruction::kOr:
1081 __ Orr(out, first, second);
1082 break;
1083 case HInstruction::kSub:
1084 __ Sub(out, first, second);
1085 break;
1086 case HInstruction::kXor:
1087 __ Eor(out, first, second);
1088 break;
1089 default:
1090 LOG(FATAL) << "Unexpected instruction kind: " << kind;
1091 UNREACHABLE();
1092 }
1093 }
1094}
1095
1096static void GenerateDataProc(HInstruction::InstructionKind kind,
1097 const Location& out,
1098 const Location& first,
1099 const Operand& second_lo,
1100 const Operand& second_hi,
1101 CodeGeneratorARMVIXL* codegen) {
1102 const vixl32::Register first_hi = HighRegisterFrom(first);
1103 const vixl32::Register first_lo = LowRegisterFrom(first);
1104 const vixl32::Register out_hi = HighRegisterFrom(out);
1105 const vixl32::Register out_lo = LowRegisterFrom(out);
1106
1107 if (kind == HInstruction::kAdd) {
1108 __ Adds(out_lo, first_lo, second_lo);
1109 __ Adc(out_hi, first_hi, second_hi);
1110 } else if (kind == HInstruction::kSub) {
1111 __ Subs(out_lo, first_lo, second_lo);
1112 __ Sbc(out_hi, first_hi, second_hi);
1113 } else {
1114 GenerateDataProcInstruction(kind, out_lo, first_lo, second_lo, codegen);
1115 GenerateDataProcInstruction(kind, out_hi, first_hi, second_hi, codegen);
1116 }
1117}
1118
1119static Operand GetShifterOperand(vixl32::Register rm, ShiftType shift, uint32_t shift_imm) {
1120 return shift_imm == 0 ? Operand(rm) : Operand(rm, shift, shift_imm);
1121}
1122
1123static void GenerateLongDataProc(HDataProcWithShifterOp* instruction,
1124 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001125 DCHECK_EQ(instruction->GetType(), DataType::Type::kInt64);
Anton Kirilov74234da2017-01-13 14:42:47 +00001126 DCHECK(HDataProcWithShifterOp::IsShiftOp(instruction->GetOpKind()));
1127
1128 const LocationSummary* const locations = instruction->GetLocations();
1129 const uint32_t shift_value = instruction->GetShiftAmount();
1130 const HInstruction::InstructionKind kind = instruction->GetInstrKind();
1131 const Location first = locations->InAt(0);
1132 const Location second = locations->InAt(1);
1133 const Location out = locations->Out();
1134 const vixl32::Register first_hi = HighRegisterFrom(first);
1135 const vixl32::Register first_lo = LowRegisterFrom(first);
1136 const vixl32::Register out_hi = HighRegisterFrom(out);
1137 const vixl32::Register out_lo = LowRegisterFrom(out);
1138 const vixl32::Register second_hi = HighRegisterFrom(second);
1139 const vixl32::Register second_lo = LowRegisterFrom(second);
1140 const ShiftType shift = ShiftFromOpKind(instruction->GetOpKind());
1141
1142 if (shift_value >= 32) {
1143 if (shift == ShiftType::LSL) {
1144 GenerateDataProcInstruction(kind,
1145 out_hi,
1146 first_hi,
1147 Operand(second_lo, ShiftType::LSL, shift_value - 32),
1148 codegen);
1149 GenerateDataProcInstruction(kind, out_lo, first_lo, 0, codegen);
1150 } else if (shift == ShiftType::ASR) {
1151 GenerateDataProc(kind,
1152 out,
1153 first,
1154 GetShifterOperand(second_hi, ShiftType::ASR, shift_value - 32),
1155 Operand(second_hi, ShiftType::ASR, 31),
1156 codegen);
1157 } else {
1158 DCHECK_EQ(shift, ShiftType::LSR);
1159 GenerateDataProc(kind,
1160 out,
1161 first,
1162 GetShifterOperand(second_hi, ShiftType::LSR, shift_value - 32),
1163 0,
1164 codegen);
1165 }
1166 } else {
1167 DCHECK_GT(shift_value, 1U);
1168 DCHECK_LT(shift_value, 32U);
1169
1170 UseScratchRegisterScope temps(codegen->GetVIXLAssembler());
1171
1172 if (shift == ShiftType::LSL) {
1173 // We are not doing this for HInstruction::kAdd because the output will require
1174 // Location::kOutputOverlap; not applicable to other cases.
1175 if (kind == HInstruction::kOr || kind == HInstruction::kXor) {
1176 GenerateDataProcInstruction(kind,
1177 out_hi,
1178 first_hi,
1179 Operand(second_hi, ShiftType::LSL, shift_value),
1180 codegen);
1181 GenerateDataProcInstruction(kind,
1182 out_hi,
1183 out_hi,
1184 Operand(second_lo, ShiftType::LSR, 32 - shift_value),
1185 codegen);
1186 GenerateDataProcInstruction(kind,
1187 out_lo,
1188 first_lo,
1189 Operand(second_lo, ShiftType::LSL, shift_value),
1190 codegen);
1191 } else {
1192 const vixl32::Register temp = temps.Acquire();
1193
1194 __ Lsl(temp, second_hi, shift_value);
1195 __ Orr(temp, temp, Operand(second_lo, ShiftType::LSR, 32 - shift_value));
1196 GenerateDataProc(kind,
1197 out,
1198 first,
1199 Operand(second_lo, ShiftType::LSL, shift_value),
1200 temp,
1201 codegen);
1202 }
1203 } else {
1204 DCHECK(shift == ShiftType::ASR || shift == ShiftType::LSR);
1205
1206 // We are not doing this for HInstruction::kAdd because the output will require
1207 // Location::kOutputOverlap; not applicable to other cases.
1208 if (kind == HInstruction::kOr || kind == HInstruction::kXor) {
1209 GenerateDataProcInstruction(kind,
1210 out_lo,
1211 first_lo,
1212 Operand(second_lo, ShiftType::LSR, shift_value),
1213 codegen);
1214 GenerateDataProcInstruction(kind,
1215 out_lo,
1216 out_lo,
1217 Operand(second_hi, ShiftType::LSL, 32 - shift_value),
1218 codegen);
1219 GenerateDataProcInstruction(kind,
1220 out_hi,
1221 first_hi,
1222 Operand(second_hi, shift, shift_value),
1223 codegen);
1224 } else {
1225 const vixl32::Register temp = temps.Acquire();
1226
1227 __ Lsr(temp, second_lo, shift_value);
1228 __ Orr(temp, temp, Operand(second_hi, ShiftType::LSL, 32 - shift_value));
1229 GenerateDataProc(kind,
1230 out,
1231 first,
1232 temp,
1233 Operand(second_hi, shift, shift_value),
1234 codegen);
1235 }
1236 }
1237 }
1238}
1239
Donghui Bai426b49c2016-11-08 14:55:38 +08001240static void GenerateVcmp(HInstruction* instruction, CodeGeneratorARMVIXL* codegen) {
1241 const Location rhs_loc = instruction->GetLocations()->InAt(1);
1242 if (rhs_loc.IsConstant()) {
1243 // 0.0 is the only immediate that can be encoded directly in
1244 // a VCMP instruction.
1245 //
1246 // Both the JLS (section 15.20.1) and the JVMS (section 6.5)
1247 // specify that in a floating-point comparison, positive zero
1248 // and negative zero are considered equal, so we can use the
1249 // literal 0.0 for both cases here.
1250 //
1251 // Note however that some methods (Float.equal, Float.compare,
1252 // Float.compareTo, Double.equal, Double.compare,
1253 // Double.compareTo, Math.max, Math.min, StrictMath.max,
1254 // StrictMath.min) consider 0.0 to be (strictly) greater than
1255 // -0.0. So if we ever translate calls to these methods into a
1256 // HCompare instruction, we must handle the -0.0 case with
1257 // care here.
1258 DCHECK(rhs_loc.GetConstant()->IsArithmeticZero());
1259
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001260 const DataType::Type type = instruction->InputAt(0)->GetType();
Donghui Bai426b49c2016-11-08 14:55:38 +08001261
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001262 if (type == DataType::Type::kFloat32) {
Donghui Bai426b49c2016-11-08 14:55:38 +08001263 __ Vcmp(F32, InputSRegisterAt(instruction, 0), 0.0);
1264 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001265 DCHECK_EQ(type, DataType::Type::kFloat64);
Donghui Bai426b49c2016-11-08 14:55:38 +08001266 __ Vcmp(F64, InputDRegisterAt(instruction, 0), 0.0);
1267 }
1268 } else {
1269 __ Vcmp(InputVRegisterAt(instruction, 0), InputVRegisterAt(instruction, 1));
1270 }
1271}
1272
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001273static int64_t AdjustConstantForCondition(int64_t value,
1274 IfCondition* condition,
1275 IfCondition* opposite) {
1276 if (value == 1) {
1277 if (*condition == kCondB) {
1278 value = 0;
1279 *condition = kCondEQ;
1280 *opposite = kCondNE;
1281 } else if (*condition == kCondAE) {
1282 value = 0;
1283 *condition = kCondNE;
1284 *opposite = kCondEQ;
1285 }
1286 } else if (value == -1) {
1287 if (*condition == kCondGT) {
1288 value = 0;
1289 *condition = kCondGE;
1290 *opposite = kCondLT;
1291 } else if (*condition == kCondLE) {
1292 value = 0;
1293 *condition = kCondLT;
1294 *opposite = kCondGE;
1295 }
1296 }
1297
1298 return value;
1299}
1300
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001301static std::pair<vixl32::Condition, vixl32::Condition> GenerateLongTestConstant(
1302 HCondition* condition,
1303 bool invert,
1304 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001305 DCHECK_EQ(condition->GetLeft()->GetType(), DataType::Type::kInt64);
Donghui Bai426b49c2016-11-08 14:55:38 +08001306
1307 const LocationSummary* const locations = condition->GetLocations();
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001308 IfCondition cond = condition->GetCondition();
1309 IfCondition opposite = condition->GetOppositeCondition();
1310
1311 if (invert) {
1312 std::swap(cond, opposite);
1313 }
1314
1315 std::pair<vixl32::Condition, vixl32::Condition> ret(eq, ne);
Donghui Bai426b49c2016-11-08 14:55:38 +08001316 const Location left = locations->InAt(0);
1317 const Location right = locations->InAt(1);
1318
1319 DCHECK(right.IsConstant());
1320
1321 const vixl32::Register left_high = HighRegisterFrom(left);
1322 const vixl32::Register left_low = LowRegisterFrom(left);
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001323 int64_t value = AdjustConstantForCondition(Int64ConstantFrom(right), &cond, &opposite);
1324 UseScratchRegisterScope temps(codegen->GetVIXLAssembler());
1325
1326 // Comparisons against 0 are common enough to deserve special attention.
1327 if (value == 0) {
1328 switch (cond) {
1329 case kCondNE:
1330 // x > 0 iff x != 0 when the comparison is unsigned.
1331 case kCondA:
1332 ret = std::make_pair(ne, eq);
1333 FALLTHROUGH_INTENDED;
1334 case kCondEQ:
1335 // x <= 0 iff x == 0 when the comparison is unsigned.
1336 case kCondBE:
1337 __ Orrs(temps.Acquire(), left_low, left_high);
1338 return ret;
1339 case kCondLT:
1340 case kCondGE:
1341 __ Cmp(left_high, 0);
1342 return std::make_pair(ARMCondition(cond), ARMCondition(opposite));
1343 // Trivially true or false.
1344 case kCondB:
1345 ret = std::make_pair(ne, eq);
1346 FALLTHROUGH_INTENDED;
1347 case kCondAE:
1348 __ Cmp(left_low, left_low);
1349 return ret;
1350 default:
1351 break;
1352 }
1353 }
Donghui Bai426b49c2016-11-08 14:55:38 +08001354
1355 switch (cond) {
1356 case kCondEQ:
1357 case kCondNE:
1358 case kCondB:
1359 case kCondBE:
1360 case kCondA:
1361 case kCondAE: {
Anton Kirilov23b752b2017-07-20 14:40:44 +01001362 const uint32_t value_low = Low32Bits(value);
1363 Operand operand_low(value_low);
1364
Donghui Bai426b49c2016-11-08 14:55:38 +08001365 __ Cmp(left_high, High32Bits(value));
1366
Anton Kirilov23b752b2017-07-20 14:40:44 +01001367 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
1368 // we must ensure that the operands corresponding to the least significant
1369 // halves of the inputs fit into a 16-bit CMP encoding.
1370 if (!left_low.IsLow() || !IsUint<8>(value_low)) {
1371 operand_low = Operand(temps.Acquire());
1372 __ Mov(LeaveFlags, operand_low.GetBaseRegister(), value_low);
1373 }
1374
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001375 // We use the scope because of the IT block that follows.
Donghui Bai426b49c2016-11-08 14:55:38 +08001376 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
1377 2 * vixl32::k16BitT32InstructionSizeInBytes,
1378 CodeBufferCheckScope::kExactSize);
1379
1380 __ it(eq);
Anton Kirilov23b752b2017-07-20 14:40:44 +01001381 __ cmp(eq, left_low, operand_low);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001382 ret = std::make_pair(ARMUnsignedCondition(cond), ARMUnsignedCondition(opposite));
Donghui Bai426b49c2016-11-08 14:55:38 +08001383 break;
1384 }
1385 case kCondLE:
1386 case kCondGT:
1387 // Trivially true or false.
1388 if (value == std::numeric_limits<int64_t>::max()) {
1389 __ Cmp(left_low, left_low);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001390 ret = cond == kCondLE ? std::make_pair(eq, ne) : std::make_pair(ne, eq);
Donghui Bai426b49c2016-11-08 14:55:38 +08001391 break;
1392 }
1393
1394 if (cond == kCondLE) {
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001395 DCHECK_EQ(opposite, kCondGT);
Donghui Bai426b49c2016-11-08 14:55:38 +08001396 cond = kCondLT;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001397 opposite = kCondGE;
Donghui Bai426b49c2016-11-08 14:55:38 +08001398 } else {
1399 DCHECK_EQ(cond, kCondGT);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001400 DCHECK_EQ(opposite, kCondLE);
Donghui Bai426b49c2016-11-08 14:55:38 +08001401 cond = kCondGE;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001402 opposite = kCondLT;
Donghui Bai426b49c2016-11-08 14:55:38 +08001403 }
1404
1405 value++;
1406 FALLTHROUGH_INTENDED;
1407 case kCondGE:
1408 case kCondLT: {
Donghui Bai426b49c2016-11-08 14:55:38 +08001409 __ Cmp(left_low, Low32Bits(value));
1410 __ Sbcs(temps.Acquire(), left_high, High32Bits(value));
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001411 ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite));
Donghui Bai426b49c2016-11-08 14:55:38 +08001412 break;
1413 }
1414 default:
1415 LOG(FATAL) << "Unreachable";
1416 UNREACHABLE();
1417 }
1418
1419 return ret;
1420}
1421
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001422static std::pair<vixl32::Condition, vixl32::Condition> GenerateLongTest(
1423 HCondition* condition,
1424 bool invert,
1425 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001426 DCHECK_EQ(condition->GetLeft()->GetType(), DataType::Type::kInt64);
Donghui Bai426b49c2016-11-08 14:55:38 +08001427
1428 const LocationSummary* const locations = condition->GetLocations();
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001429 IfCondition cond = condition->GetCondition();
1430 IfCondition opposite = condition->GetOppositeCondition();
1431
1432 if (invert) {
1433 std::swap(cond, opposite);
1434 }
1435
1436 std::pair<vixl32::Condition, vixl32::Condition> ret(eq, ne);
Donghui Bai426b49c2016-11-08 14:55:38 +08001437 Location left = locations->InAt(0);
1438 Location right = locations->InAt(1);
1439
1440 DCHECK(right.IsRegisterPair());
1441
1442 switch (cond) {
1443 case kCondEQ:
1444 case kCondNE:
1445 case kCondB:
1446 case kCondBE:
1447 case kCondA:
1448 case kCondAE: {
1449 __ Cmp(HighRegisterFrom(left), HighRegisterFrom(right));
1450
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001451 // We use the scope because of the IT block that follows.
Donghui Bai426b49c2016-11-08 14:55:38 +08001452 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
1453 2 * vixl32::k16BitT32InstructionSizeInBytes,
1454 CodeBufferCheckScope::kExactSize);
1455
1456 __ it(eq);
1457 __ cmp(eq, LowRegisterFrom(left), LowRegisterFrom(right));
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001458 ret = std::make_pair(ARMUnsignedCondition(cond), ARMUnsignedCondition(opposite));
Donghui Bai426b49c2016-11-08 14:55:38 +08001459 break;
1460 }
1461 case kCondLE:
1462 case kCondGT:
1463 if (cond == kCondLE) {
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001464 DCHECK_EQ(opposite, kCondGT);
Donghui Bai426b49c2016-11-08 14:55:38 +08001465 cond = kCondGE;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001466 opposite = kCondLT;
Donghui Bai426b49c2016-11-08 14:55:38 +08001467 } else {
1468 DCHECK_EQ(cond, kCondGT);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001469 DCHECK_EQ(opposite, kCondLE);
Donghui Bai426b49c2016-11-08 14:55:38 +08001470 cond = kCondLT;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001471 opposite = kCondGE;
Donghui Bai426b49c2016-11-08 14:55:38 +08001472 }
1473
1474 std::swap(left, right);
1475 FALLTHROUGH_INTENDED;
1476 case kCondGE:
1477 case kCondLT: {
1478 UseScratchRegisterScope temps(codegen->GetVIXLAssembler());
1479
1480 __ Cmp(LowRegisterFrom(left), LowRegisterFrom(right));
1481 __ Sbcs(temps.Acquire(), HighRegisterFrom(left), HighRegisterFrom(right));
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001482 ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite));
Donghui Bai426b49c2016-11-08 14:55:38 +08001483 break;
1484 }
1485 default:
1486 LOG(FATAL) << "Unreachable";
1487 UNREACHABLE();
1488 }
1489
1490 return ret;
1491}
1492
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001493static std::pair<vixl32::Condition, vixl32::Condition> GenerateTest(HCondition* condition,
1494 bool invert,
1495 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001496 const DataType::Type type = condition->GetLeft()->GetType();
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001497 IfCondition cond = condition->GetCondition();
1498 IfCondition opposite = condition->GetOppositeCondition();
1499 std::pair<vixl32::Condition, vixl32::Condition> ret(eq, ne);
Donghui Bai426b49c2016-11-08 14:55:38 +08001500
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001501 if (invert) {
1502 std::swap(cond, opposite);
1503 }
Donghui Bai426b49c2016-11-08 14:55:38 +08001504
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001505 if (type == DataType::Type::kInt64) {
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001506 ret = condition->GetLocations()->InAt(1).IsConstant()
1507 ? GenerateLongTestConstant(condition, invert, codegen)
1508 : GenerateLongTest(condition, invert, codegen);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001509 } else if (DataType::IsFloatingPointType(type)) {
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001510 GenerateVcmp(condition, codegen);
1511 __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
1512 ret = std::make_pair(ARMFPCondition(cond, condition->IsGtBias()),
1513 ARMFPCondition(opposite, condition->IsGtBias()));
Donghui Bai426b49c2016-11-08 14:55:38 +08001514 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001515 DCHECK(DataType::IsIntegralType(type) || type == DataType::Type::kReference) << type;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001516 __ Cmp(InputRegisterAt(condition, 0), InputOperandAt(condition, 1));
1517 ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite));
Donghui Bai426b49c2016-11-08 14:55:38 +08001518 }
1519
1520 return ret;
1521}
1522
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001523static void GenerateConditionGeneric(HCondition* cond, CodeGeneratorARMVIXL* codegen) {
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001524 const vixl32::Register out = OutputRegister(cond);
1525 const auto condition = GenerateTest(cond, false, codegen);
1526
1527 __ Mov(LeaveFlags, out, 0);
1528
1529 if (out.IsLow()) {
1530 // We use the scope because of the IT block that follows.
1531 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
1532 2 * vixl32::k16BitT32InstructionSizeInBytes,
1533 CodeBufferCheckScope::kExactSize);
1534
1535 __ it(condition.first);
1536 __ mov(condition.first, out, 1);
1537 } else {
1538 vixl32::Label done_label;
1539 vixl32::Label* const final_label = codegen->GetFinalLabel(cond, &done_label);
1540
Andreas Gampe3db70682018-12-26 15:12:03 -08001541 __ B(condition.second, final_label, /* is_far_target= */ false);
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001542 __ Mov(out, 1);
1543
1544 if (done_label.IsReferenced()) {
1545 __ Bind(&done_label);
1546 }
1547 }
1548}
1549
1550static void GenerateEqualLong(HCondition* cond, CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001551 DCHECK_EQ(cond->GetLeft()->GetType(), DataType::Type::kInt64);
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001552
1553 const LocationSummary* const locations = cond->GetLocations();
1554 IfCondition condition = cond->GetCondition();
1555 const vixl32::Register out = OutputRegister(cond);
1556 const Location left = locations->InAt(0);
1557 const Location right = locations->InAt(1);
1558 vixl32::Register left_high = HighRegisterFrom(left);
1559 vixl32::Register left_low = LowRegisterFrom(left);
1560 vixl32::Register temp;
1561 UseScratchRegisterScope temps(codegen->GetVIXLAssembler());
1562
1563 if (right.IsConstant()) {
1564 IfCondition opposite = cond->GetOppositeCondition();
1565 const int64_t value = AdjustConstantForCondition(Int64ConstantFrom(right),
1566 &condition,
1567 &opposite);
1568 Operand right_high = High32Bits(value);
1569 Operand right_low = Low32Bits(value);
1570
1571 // The output uses Location::kNoOutputOverlap.
1572 if (out.Is(left_high)) {
1573 std::swap(left_low, left_high);
1574 std::swap(right_low, right_high);
1575 }
1576
1577 __ Sub(out, left_low, right_low);
1578 temp = temps.Acquire();
1579 __ Sub(temp, left_high, right_high);
1580 } else {
1581 DCHECK(right.IsRegisterPair());
1582 temp = temps.Acquire();
1583 __ Sub(temp, left_high, HighRegisterFrom(right));
1584 __ Sub(out, left_low, LowRegisterFrom(right));
1585 }
1586
1587 // Need to check after calling AdjustConstantForCondition().
1588 DCHECK(condition == kCondEQ || condition == kCondNE) << condition;
1589
1590 if (condition == kCondNE && out.IsLow()) {
1591 __ Orrs(out, out, temp);
1592
1593 // We use the scope because of the IT block that follows.
1594 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
1595 2 * vixl32::k16BitT32InstructionSizeInBytes,
1596 CodeBufferCheckScope::kExactSize);
1597
1598 __ it(ne);
1599 __ mov(ne, out, 1);
1600 } else {
1601 __ Orr(out, out, temp);
1602 codegen->GenerateConditionWithZero(condition, out, out, temp);
1603 }
1604}
1605
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001606static void GenerateConditionLong(HCondition* cond, CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001607 DCHECK_EQ(cond->GetLeft()->GetType(), DataType::Type::kInt64);
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001608
1609 const LocationSummary* const locations = cond->GetLocations();
1610 IfCondition condition = cond->GetCondition();
1611 const vixl32::Register out = OutputRegister(cond);
1612 const Location left = locations->InAt(0);
1613 const Location right = locations->InAt(1);
1614
1615 if (right.IsConstant()) {
1616 IfCondition opposite = cond->GetOppositeCondition();
1617
1618 // Comparisons against 0 are common enough to deserve special attention.
1619 if (AdjustConstantForCondition(Int64ConstantFrom(right), &condition, &opposite) == 0) {
1620 switch (condition) {
1621 case kCondNE:
1622 case kCondA:
1623 if (out.IsLow()) {
1624 // We only care if both input registers are 0 or not.
1625 __ Orrs(out, LowRegisterFrom(left), HighRegisterFrom(left));
1626
1627 // We use the scope because of the IT block that follows.
1628 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
1629 2 * vixl32::k16BitT32InstructionSizeInBytes,
1630 CodeBufferCheckScope::kExactSize);
1631
1632 __ it(ne);
1633 __ mov(ne, out, 1);
1634 return;
1635 }
1636
1637 FALLTHROUGH_INTENDED;
1638 case kCondEQ:
1639 case kCondBE:
1640 // We only care if both input registers are 0 or not.
1641 __ Orr(out, LowRegisterFrom(left), HighRegisterFrom(left));
1642 codegen->GenerateConditionWithZero(condition, out, out);
1643 return;
1644 case kCondLT:
1645 case kCondGE:
1646 // We only care about the sign bit.
1647 FALLTHROUGH_INTENDED;
1648 case kCondAE:
1649 case kCondB:
1650 codegen->GenerateConditionWithZero(condition, out, HighRegisterFrom(left));
1651 return;
1652 case kCondLE:
1653 case kCondGT:
1654 default:
1655 break;
1656 }
1657 }
1658 }
1659
Anton Kirilov23b752b2017-07-20 14:40:44 +01001660 // If `out` is a low register, then the GenerateConditionGeneric()
1661 // function generates a shorter code sequence that is still branchless.
1662 if ((condition == kCondEQ || condition == kCondNE) && !out.IsLow()) {
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001663 GenerateEqualLong(cond, codegen);
1664 return;
1665 }
1666
Anton Kirilov23b752b2017-07-20 14:40:44 +01001667 GenerateConditionGeneric(cond, codegen);
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001668}
1669
Roland Levillain6d729a72017-06-30 18:34:01 +01001670static void GenerateConditionIntegralOrNonPrimitive(HCondition* cond,
1671 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001672 const DataType::Type type = cond->GetLeft()->GetType();
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001673
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001674 DCHECK(DataType::IsIntegralType(type) || type == DataType::Type::kReference) << type;
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001675
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001676 if (type == DataType::Type::kInt64) {
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001677 GenerateConditionLong(cond, codegen);
1678 return;
1679 }
1680
1681 IfCondition condition = cond->GetCondition();
1682 vixl32::Register in = InputRegisterAt(cond, 0);
1683 const vixl32::Register out = OutputRegister(cond);
1684 const Location right = cond->GetLocations()->InAt(1);
1685 int64_t value;
1686
1687 if (right.IsConstant()) {
1688 IfCondition opposite = cond->GetOppositeCondition();
1689
1690 value = AdjustConstantForCondition(Int64ConstantFrom(right), &condition, &opposite);
1691
1692 // Comparisons against 0 are common enough to deserve special attention.
1693 if (value == 0) {
1694 switch (condition) {
1695 case kCondNE:
1696 case kCondA:
1697 if (out.IsLow() && out.Is(in)) {
1698 __ Cmp(out, 0);
1699
1700 // We use the scope because of the IT block that follows.
1701 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
1702 2 * vixl32::k16BitT32InstructionSizeInBytes,
1703 CodeBufferCheckScope::kExactSize);
1704
1705 __ it(ne);
1706 __ mov(ne, out, 1);
1707 return;
1708 }
1709
1710 FALLTHROUGH_INTENDED;
1711 case kCondEQ:
1712 case kCondBE:
1713 case kCondLT:
1714 case kCondGE:
1715 case kCondAE:
1716 case kCondB:
1717 codegen->GenerateConditionWithZero(condition, out, in);
1718 return;
1719 case kCondLE:
1720 case kCondGT:
1721 default:
1722 break;
1723 }
1724 }
1725 }
1726
1727 if (condition == kCondEQ || condition == kCondNE) {
1728 Operand operand(0);
1729
1730 if (right.IsConstant()) {
1731 operand = Operand::From(value);
1732 } else if (out.Is(RegisterFrom(right))) {
1733 // Avoid 32-bit instructions if possible.
1734 operand = InputOperandAt(cond, 0);
1735 in = RegisterFrom(right);
1736 } else {
1737 operand = InputOperandAt(cond, 1);
1738 }
1739
1740 if (condition == kCondNE && out.IsLow()) {
1741 __ Subs(out, in, operand);
1742
1743 // We use the scope because of the IT block that follows.
1744 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
1745 2 * vixl32::k16BitT32InstructionSizeInBytes,
1746 CodeBufferCheckScope::kExactSize);
1747
1748 __ it(ne);
1749 __ mov(ne, out, 1);
1750 } else {
1751 __ Sub(out, in, operand);
1752 codegen->GenerateConditionWithZero(condition, out, out);
1753 }
1754
1755 return;
1756 }
1757
1758 GenerateConditionGeneric(cond, codegen);
1759}
1760
Donghui Bai426b49c2016-11-08 14:55:38 +08001761static bool CanEncodeConstantAs8BitImmediate(HConstant* constant) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001762 const DataType::Type type = constant->GetType();
Donghui Bai426b49c2016-11-08 14:55:38 +08001763 bool ret = false;
1764
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001765 DCHECK(DataType::IsIntegralType(type) || type == DataType::Type::kReference) << type;
Donghui Bai426b49c2016-11-08 14:55:38 +08001766
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001767 if (type == DataType::Type::kInt64) {
Donghui Bai426b49c2016-11-08 14:55:38 +08001768 const uint64_t value = Uint64ConstantFrom(constant);
1769
1770 ret = IsUint<8>(Low32Bits(value)) && IsUint<8>(High32Bits(value));
1771 } else {
1772 ret = IsUint<8>(Int32ConstantFrom(constant));
1773 }
1774
1775 return ret;
1776}
1777
1778static Location Arm8BitEncodableConstantOrRegister(HInstruction* constant) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001779 DCHECK(!DataType::IsFloatingPointType(constant->GetType()));
Donghui Bai426b49c2016-11-08 14:55:38 +08001780
1781 if (constant->IsConstant() && CanEncodeConstantAs8BitImmediate(constant->AsConstant())) {
1782 return Location::ConstantLocation(constant->AsConstant());
1783 }
1784
1785 return Location::RequiresRegister();
1786}
1787
1788static bool CanGenerateConditionalMove(const Location& out, const Location& src) {
1789 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
1790 // we check that we are not dealing with floating-point output (there is no
1791 // 16-bit VMOV encoding).
1792 if (!out.IsRegister() && !out.IsRegisterPair()) {
1793 return false;
1794 }
1795
1796 // For constants, we also check that the output is in one or two low registers,
1797 // and that the constants fit in an 8-bit unsigned integer, so that a 16-bit
1798 // MOV encoding can be used.
1799 if (src.IsConstant()) {
1800 if (!CanEncodeConstantAs8BitImmediate(src.GetConstant())) {
1801 return false;
1802 }
1803
1804 if (out.IsRegister()) {
1805 if (!RegisterFrom(out).IsLow()) {
1806 return false;
1807 }
1808 } else {
1809 DCHECK(out.IsRegisterPair());
1810
1811 if (!HighRegisterFrom(out).IsLow()) {
1812 return false;
1813 }
1814 }
1815 }
1816
1817 return true;
1818}
1819
Scott Wakelingfe885462016-09-22 10:24:38 +01001820#undef __
1821
Donghui Bai426b49c2016-11-08 14:55:38 +08001822vixl32::Label* CodeGeneratorARMVIXL::GetFinalLabel(HInstruction* instruction,
1823 vixl32::Label* final_label) {
1824 DCHECK(!instruction->IsControlFlow() && !instruction->IsSuspendCheck());
Anton Kirilov6f644202017-02-27 18:29:45 +00001825 DCHECK(!instruction->IsInvoke() || !instruction->GetLocations()->CanCall());
Donghui Bai426b49c2016-11-08 14:55:38 +08001826
1827 const HBasicBlock* const block = instruction->GetBlock();
1828 const HLoopInformation* const info = block->GetLoopInformation();
1829 HInstruction* const next = instruction->GetNext();
1830
1831 // Avoid a branch to a branch.
1832 if (next->IsGoto() && (info == nullptr ||
1833 !info->IsBackEdge(*block) ||
1834 !info->HasSuspendCheck())) {
1835 final_label = GetLabelOf(next->AsGoto()->GetSuccessor());
1836 }
1837
1838 return final_label;
1839}
1840
Scott Wakelingfe885462016-09-22 10:24:38 +01001841CodeGeneratorARMVIXL::CodeGeneratorARMVIXL(HGraph* graph,
Scott Wakelingfe885462016-09-22 10:24:38 +01001842 const CompilerOptions& compiler_options,
1843 OptimizingCompilerStats* stats)
1844 : CodeGenerator(graph,
1845 kNumberOfCoreRegisters,
1846 kNumberOfSRegisters,
1847 kNumberOfRegisterPairs,
1848 kCoreCalleeSaves.GetList(),
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001849 ComputeSRegisterListMask(kFpuCalleeSaves),
Scott Wakelingfe885462016-09-22 10:24:38 +01001850 compiler_options,
1851 stats),
Vladimir Markoca6fff82017-10-03 14:49:14 +01001852 block_labels_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1853 jump_tables_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Scott Wakelingfe885462016-09-22 10:24:38 +01001854 location_builder_(graph, this),
1855 instruction_visitor_(graph, this),
Vladimir Markoca6fff82017-10-03 14:49:14 +01001856 move_resolver_(graph->GetAllocator(), this),
1857 assembler_(graph->GetAllocator()),
Vladimir Marko59eb30f2018-02-20 11:52:34 +00001858 boot_image_method_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Markoca6fff82017-10-03 14:49:14 +01001859 method_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Marko59eb30f2018-02-20 11:52:34 +00001860 boot_image_type_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Markoca6fff82017-10-03 14:49:14 +01001861 type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Marko59eb30f2018-02-20 11:52:34 +00001862 boot_image_string_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Markoca6fff82017-10-03 14:49:14 +01001863 string_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Marko2d06e022019-07-08 15:45:19 +01001864 boot_image_other_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Markof6675082019-05-17 12:05:28 +01001865 call_entrypoint_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Markoca6fff82017-10-03 14:49:14 +01001866 baker_read_barrier_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Markof6675082019-05-17 12:05:28 +01001867 uint32_literals_(std::less<uint32_t>(),
1868 graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Artem Serovc5fcb442016-12-02 19:19:58 +00001869 jit_string_patches_(StringReferenceValueComparator(),
Vladimir Markoca6fff82017-10-03 14:49:14 +01001870 graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Artem Serovc5fcb442016-12-02 19:19:58 +00001871 jit_class_patches_(TypeReferenceValueComparator(),
Vladimir Marko966b46f2018-08-03 10:20:19 +00001872 graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1873 jit_baker_read_barrier_slow_paths_(std::less<uint32_t>(),
1874 graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)) {
Scott Wakelingfe885462016-09-22 10:24:38 +01001875 // Always save the LR register to mimic Quick.
1876 AddAllocatedRegister(Location::RegisterLocation(LR));
Nicolas Geoffray13a797b2017-03-15 16:41:31 +00001877 // Give D30 and D31 as scratch register to VIXL. The register allocator only works on
1878 // S0-S31, which alias to D0-D15.
1879 GetVIXLAssembler()->GetScratchVRegisterList()->Combine(d31);
1880 GetVIXLAssembler()->GetScratchVRegisterList()->Combine(d30);
Scott Wakelingfe885462016-09-22 10:24:38 +01001881}
1882
Artem Serov551b28f2016-10-18 19:11:30 +01001883void JumpTableARMVIXL::EmitTable(CodeGeneratorARMVIXL* codegen) {
1884 uint32_t num_entries = switch_instr_->GetNumEntries();
1885 DCHECK_GE(num_entries, kPackedSwitchCompareJumpThreshold);
1886
1887 // We are about to use the assembler to place literals directly. Make sure we have enough
Scott Wakelingb77051e2016-11-21 19:46:00 +00001888 // underlying code buffer and we have generated a jump table of the right size, using
1889 // codegen->GetVIXLAssembler()->GetBuffer().Align();
Artem Serov0fb37192016-12-06 18:13:40 +00001890 ExactAssemblyScope aas(codegen->GetVIXLAssembler(),
1891 num_entries * sizeof(int32_t),
1892 CodeBufferCheckScope::kMaximumSize);
Artem Serov551b28f2016-10-18 19:11:30 +01001893 // TODO(VIXL): Check that using lower case bind is fine here.
1894 codegen->GetVIXLAssembler()->bind(&table_start_);
Artem Serov09a940d2016-11-11 16:15:11 +00001895 for (uint32_t i = 0; i < num_entries; i++) {
1896 codegen->GetVIXLAssembler()->place(bb_addresses_[i].get());
1897 }
1898}
1899
1900void JumpTableARMVIXL::FixTable(CodeGeneratorARMVIXL* codegen) {
1901 uint32_t num_entries = switch_instr_->GetNumEntries();
1902 DCHECK_GE(num_entries, kPackedSwitchCompareJumpThreshold);
1903
Artem Serov551b28f2016-10-18 19:11:30 +01001904 const ArenaVector<HBasicBlock*>& successors = switch_instr_->GetBlock()->GetSuccessors();
1905 for (uint32_t i = 0; i < num_entries; i++) {
1906 vixl32::Label* target_label = codegen->GetLabelOf(successors[i]);
1907 DCHECK(target_label->IsBound());
1908 int32_t jump_offset = target_label->GetLocation() - table_start_.GetLocation();
1909 // When doing BX to address we need to have lower bit set to 1 in T32.
1910 if (codegen->GetVIXLAssembler()->IsUsingT32()) {
1911 jump_offset++;
1912 }
1913 DCHECK_GT(jump_offset, std::numeric_limits<int32_t>::min());
1914 DCHECK_LE(jump_offset, std::numeric_limits<int32_t>::max());
Artem Serov09a940d2016-11-11 16:15:11 +00001915
Scott Wakelingb77051e2016-11-21 19:46:00 +00001916 bb_addresses_[i].get()->UpdateValue(jump_offset, codegen->GetVIXLAssembler()->GetBuffer());
Artem Serov551b28f2016-10-18 19:11:30 +01001917 }
1918}
1919
Artem Serov09a940d2016-11-11 16:15:11 +00001920void CodeGeneratorARMVIXL::FixJumpTables() {
Artem Serov551b28f2016-10-18 19:11:30 +01001921 for (auto&& jump_table : jump_tables_) {
Artem Serov09a940d2016-11-11 16:15:11 +00001922 jump_table->FixTable(this);
Artem Serov551b28f2016-10-18 19:11:30 +01001923 }
1924}
1925
Andreas Gampeca620d72016-11-08 08:09:33 -08001926#define __ reinterpret_cast<ArmVIXLAssembler*>(GetAssembler())->GetVIXLAssembler()-> // NOLINT
Scott Wakelingfe885462016-09-22 10:24:38 +01001927
1928void CodeGeneratorARMVIXL::Finalize(CodeAllocator* allocator) {
Artem Serov09a940d2016-11-11 16:15:11 +00001929 FixJumpTables();
Vladimir Marko966b46f2018-08-03 10:20:19 +00001930
1931 // Emit JIT baker read barrier slow paths.
1932 DCHECK(Runtime::Current()->UseJitCompilation() || jit_baker_read_barrier_slow_paths_.empty());
1933 for (auto& entry : jit_baker_read_barrier_slow_paths_) {
1934 uint32_t encoded_data = entry.first;
1935 vixl::aarch32::Label* slow_path_entry = &entry.second.label;
1936 __ Bind(slow_path_entry);
Andreas Gampe3db70682018-12-26 15:12:03 -08001937 CompileBakerReadBarrierThunk(*GetAssembler(), encoded_data, /* debug_name= */ nullptr);
Vladimir Marko966b46f2018-08-03 10:20:19 +00001938 }
1939
Scott Wakelingfe885462016-09-22 10:24:38 +01001940 GetAssembler()->FinalizeCode();
1941 CodeGenerator::Finalize(allocator);
Vladimir Markoca1e0382018-04-11 09:58:41 +00001942
1943 // Verify Baker read barrier linker patches.
1944 if (kIsDebugBuild) {
1945 ArrayRef<const uint8_t> code = allocator->GetMemory();
1946 for (const BakerReadBarrierPatchInfo& info : baker_read_barrier_patches_) {
1947 DCHECK(info.label.IsBound());
1948 uint32_t literal_offset = info.label.GetLocation();
1949 DCHECK_ALIGNED(literal_offset, 2u);
1950
1951 auto GetInsn16 = [&code](uint32_t offset) {
1952 DCHECK_ALIGNED(offset, 2u);
1953 return (static_cast<uint32_t>(code[offset + 0]) << 0) +
1954 (static_cast<uint32_t>(code[offset + 1]) << 8);
1955 };
1956 auto GetInsn32 = [=](uint32_t offset) {
1957 return (GetInsn16(offset) << 16) + (GetInsn16(offset + 2u) << 0);
1958 };
1959
1960 uint32_t encoded_data = info.custom_data;
1961 BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data);
1962 // Check that the next instruction matches the expected LDR.
1963 switch (kind) {
1964 case BakerReadBarrierKind::kField: {
1965 BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data);
1966 if (width == BakerReadBarrierWidth::kWide) {
1967 DCHECK_GE(code.size() - literal_offset, 8u);
1968 uint32_t next_insn = GetInsn32(literal_offset + 4u);
1969 // LDR (immediate), encoding T3, with correct base_reg.
1970 CheckValidReg((next_insn >> 12) & 0xfu); // Check destination register.
1971 const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
1972 CHECK_EQ(next_insn & 0xffff0000u, 0xf8d00000u | (base_reg << 16));
1973 } else {
1974 DCHECK_GE(code.size() - literal_offset, 6u);
1975 uint32_t next_insn = GetInsn16(literal_offset + 4u);
1976 // LDR (immediate), encoding T1, with correct base_reg.
1977 CheckValidReg(next_insn & 0x7u); // Check destination register.
1978 const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
1979 CHECK_EQ(next_insn & 0xf838u, 0x6800u | (base_reg << 3));
1980 }
1981 break;
1982 }
1983 case BakerReadBarrierKind::kArray: {
1984 DCHECK_GE(code.size() - literal_offset, 8u);
1985 uint32_t next_insn = GetInsn32(literal_offset + 4u);
1986 // LDR (register) with correct base_reg, S=1 and option=011 (LDR Wt, [Xn, Xm, LSL #2]).
1987 CheckValidReg((next_insn >> 12) & 0xfu); // Check destination register.
1988 const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
1989 CHECK_EQ(next_insn & 0xffff0ff0u, 0xf8500020u | (base_reg << 16));
1990 CheckValidReg(next_insn & 0xf); // Check index register
1991 break;
1992 }
1993 case BakerReadBarrierKind::kGcRoot: {
1994 BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data);
1995 if (width == BakerReadBarrierWidth::kWide) {
1996 DCHECK_GE(literal_offset, 4u);
1997 uint32_t prev_insn = GetInsn32(literal_offset - 4u);
Vladimir Markof28be432018-08-14 12:20:51 +00001998 // LDR (immediate), encoding T3, with correct root_reg.
Vladimir Markoca1e0382018-04-11 09:58:41 +00001999 const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
Vladimir Markof28be432018-08-14 12:20:51 +00002000 CHECK_EQ(prev_insn & 0xfff0f000u, 0xf8d00000u | (root_reg << 12));
Vladimir Markoca1e0382018-04-11 09:58:41 +00002001 } else {
2002 DCHECK_GE(literal_offset, 2u);
2003 uint32_t prev_insn = GetInsn16(literal_offset - 2u);
2004 // LDR (immediate), encoding T1, with correct root_reg.
2005 const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
2006 CHECK_EQ(prev_insn & 0xf807u, 0x6800u | root_reg);
2007 }
2008 break;
2009 }
Vladimir Markod887ed82018-08-14 13:52:12 +00002010 case BakerReadBarrierKind::kUnsafeCas: {
2011 DCHECK_GE(literal_offset, 4u);
2012 uint32_t prev_insn = GetInsn32(literal_offset - 4u);
2013 // ADD (register), encoding T3, with correct root_reg.
2014 const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
2015 CHECK_EQ(prev_insn & 0xfff0fff0u, 0xeb000000u | (root_reg << 8));
2016 break;
2017 }
Vladimir Markoca1e0382018-04-11 09:58:41 +00002018 default:
2019 LOG(FATAL) << "Unexpected kind: " << static_cast<uint32_t>(kind);
2020 UNREACHABLE();
2021 }
2022 }
2023 }
Scott Wakelingfe885462016-09-22 10:24:38 +01002024}
2025
2026void CodeGeneratorARMVIXL::SetupBlockedRegisters() const {
Scott Wakelingfe885462016-09-22 10:24:38 +01002027 // Stack register, LR and PC are always reserved.
2028 blocked_core_registers_[SP] = true;
2029 blocked_core_registers_[LR] = true;
2030 blocked_core_registers_[PC] = true;
2031
Roland Levillain6d729a72017-06-30 18:34:01 +01002032 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2033 // Reserve marking register.
2034 blocked_core_registers_[MR] = true;
2035 }
2036
Scott Wakelingfe885462016-09-22 10:24:38 +01002037 // Reserve thread register.
2038 blocked_core_registers_[TR] = true;
2039
2040 // Reserve temp register.
2041 blocked_core_registers_[IP] = true;
2042
2043 if (GetGraph()->IsDebuggable()) {
2044 // Stubs do not save callee-save floating point registers. If the graph
2045 // is debuggable, we need to deal with these registers differently. For
2046 // now, just block them.
2047 for (uint32_t i = kFpuCalleeSaves.GetFirstSRegister().GetCode();
2048 i <= kFpuCalleeSaves.GetLastSRegister().GetCode();
2049 ++i) {
2050 blocked_fpu_registers_[i] = true;
2051 }
2052 }
Scott Wakelingfe885462016-09-22 10:24:38 +01002053}
2054
Scott Wakelingfe885462016-09-22 10:24:38 +01002055InstructionCodeGeneratorARMVIXL::InstructionCodeGeneratorARMVIXL(HGraph* graph,
2056 CodeGeneratorARMVIXL* codegen)
2057 : InstructionCodeGenerator(graph, codegen),
2058 assembler_(codegen->GetAssembler()),
2059 codegen_(codegen) {}
2060
2061void CodeGeneratorARMVIXL::ComputeSpillMask() {
2062 core_spill_mask_ = allocated_registers_.GetCoreRegisters() & core_callee_save_mask_;
Vladimir Marko460f0542019-07-04 14:02:08 +01002063 DCHECK_NE(core_spill_mask_ & (1u << kLrCode), 0u)
2064 << "At least the return address register must be saved";
2065 // 16-bit PUSH/POP (T1) can save/restore just the LR/PC.
2066 DCHECK(GetVIXLAssembler()->IsUsingT32());
Scott Wakelingfe885462016-09-22 10:24:38 +01002067 fpu_spill_mask_ = allocated_registers_.GetFloatingPointRegisters() & fpu_callee_save_mask_;
2068 // We use vpush and vpop for saving and restoring floating point registers, which take
2069 // a SRegister and the number of registers to save/restore after that SRegister. We
2070 // therefore update the `fpu_spill_mask_` to also contain those registers not allocated,
2071 // but in the range.
2072 if (fpu_spill_mask_ != 0) {
2073 uint32_t least_significant_bit = LeastSignificantBit(fpu_spill_mask_);
2074 uint32_t most_significant_bit = MostSignificantBit(fpu_spill_mask_);
2075 for (uint32_t i = least_significant_bit + 1 ; i < most_significant_bit; ++i) {
2076 fpu_spill_mask_ |= (1 << i);
2077 }
2078 }
2079}
2080
2081void CodeGeneratorARMVIXL::GenerateFrameEntry() {
2082 bool skip_overflow_check =
2083 IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kArm);
2084 DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks());
2085 __ Bind(&frame_entry_label_);
2086
Nicolas Geoffray8d728322018-01-18 22:44:32 +00002087 if (GetCompilerOptions().CountHotnessInCompiledCode()) {
2088 UseScratchRegisterScope temps(GetVIXLAssembler());
2089 vixl32::Register temp = temps.Acquire();
2090 __ Ldrh(temp, MemOperand(kMethodRegister, ArtMethod::HotnessCountOffset().Int32Value()));
2091 __ Add(temp, temp, 1);
2092 __ Strh(temp, MemOperand(kMethodRegister, ArtMethod::HotnessCountOffset().Int32Value()));
2093 }
2094
Scott Wakelingfe885462016-09-22 10:24:38 +01002095 if (HasEmptyFrame()) {
David Srbecky30021842019-02-13 14:19:36 +00002096 // Ensure that the CFI opcode list is not empty.
2097 GetAssembler()->cfi().Nop();
Scott Wakelingfe885462016-09-22 10:24:38 +01002098 return;
2099 }
2100
Scott Wakelingfe885462016-09-22 10:24:38 +01002101 if (!skip_overflow_check) {
xueliang.zhong10049552018-01-31 17:10:36 +00002102 // Using r4 instead of IP saves 2 bytes.
Nicolas Geoffray1a4f3ca2018-01-25 14:07:15 +00002103 UseScratchRegisterScope temps(GetVIXLAssembler());
xueliang.zhong10049552018-01-31 17:10:36 +00002104 vixl32::Register temp;
2105 // TODO: Remove this check when R4 is made a callee-save register
2106 // in ART compiled code (b/72801708). Currently we need to make
2107 // sure r4 is not blocked, e.g. in special purpose
2108 // TestCodeGeneratorARMVIXL; also asserting that r4 is available
2109 // here.
2110 if (!blocked_core_registers_[R4]) {
2111 for (vixl32::Register reg : kParameterCoreRegistersVIXL) {
2112 DCHECK(!reg.Is(r4));
2113 }
2114 DCHECK(!kCoreCalleeSaves.Includes(r4));
2115 temp = r4;
2116 } else {
2117 temp = temps.Acquire();
2118 }
Vladimir Marko33bff252017-11-01 14:35:42 +00002119 __ Sub(temp, sp, Operand::From(GetStackOverflowReservedBytes(InstructionSet::kArm)));
Scott Wakelingfe885462016-09-22 10:24:38 +01002120 // The load must immediately precede RecordPcInfo.
Artem Serov0fb37192016-12-06 18:13:40 +00002121 ExactAssemblyScope aas(GetVIXLAssembler(),
2122 vixl32::kMaxInstructionSizeInBytes,
2123 CodeBufferCheckScope::kMaximumSize);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002124 __ ldr(temp, MemOperand(temp));
2125 RecordPcInfo(nullptr, 0);
Scott Wakelingfe885462016-09-22 10:24:38 +01002126 }
2127
Vladimir Marko1a225a72019-07-05 13:37:42 +01002128 uint32_t frame_size = GetFrameSize();
2129 uint32_t core_spills_offset = frame_size - GetCoreSpillSize();
2130 uint32_t fp_spills_offset = frame_size - FrameEntrySpillSize();
2131 if ((fpu_spill_mask_ == 0u || IsPowerOfTwo(fpu_spill_mask_)) &&
2132 core_spills_offset <= 3u * kArmWordSize) {
2133 // Do a single PUSH for core registers including the method and up to two
2134 // filler registers. Then store the single FP spill if any.
2135 // (The worst case is when the method is not required and we actually
2136 // store 3 extra registers but they are stored in the same properly
2137 // aligned 16-byte chunk where we're already writing anyway.)
2138 DCHECK_EQ(kMethodRegister.GetCode(), 0u);
2139 uint32_t extra_regs = MaxInt<uint32_t>(core_spills_offset / kArmWordSize);
2140 DCHECK_LT(MostSignificantBit(extra_regs), LeastSignificantBit(core_spill_mask_));
2141 __ Push(RegisterList(core_spill_mask_ | extra_regs));
2142 GetAssembler()->cfi().AdjustCFAOffset(frame_size);
2143 GetAssembler()->cfi().RelOffsetForMany(DWARFReg(kMethodRegister),
2144 core_spills_offset,
2145 core_spill_mask_,
2146 kArmWordSize);
2147 if (fpu_spill_mask_ != 0u) {
2148 DCHECK(IsPowerOfTwo(fpu_spill_mask_));
2149 vixl::aarch32::SRegister sreg(LeastSignificantBit(fpu_spill_mask_));
2150 GetAssembler()->StoreSToOffset(sreg, sp, fp_spills_offset);
2151 GetAssembler()->cfi().RelOffset(DWARFReg(sreg), /*offset=*/ fp_spills_offset);
2152 }
2153 } else {
2154 __ Push(RegisterList(core_spill_mask_));
2155 GetAssembler()->cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(core_spill_mask_));
2156 GetAssembler()->cfi().RelOffsetForMany(DWARFReg(kMethodRegister),
2157 /*offset=*/ 0,
2158 core_spill_mask_,
2159 kArmWordSize);
2160 if (fpu_spill_mask_ != 0) {
2161 uint32_t first = LeastSignificantBit(fpu_spill_mask_);
Scott Wakelingfe885462016-09-22 10:24:38 +01002162
Vladimir Marko1a225a72019-07-05 13:37:42 +01002163 // Check that list is contiguous.
2164 DCHECK_EQ(fpu_spill_mask_ >> CTZ(fpu_spill_mask_), ~0u >> (32 - POPCOUNT(fpu_spill_mask_)));
Scott Wakelingfe885462016-09-22 10:24:38 +01002165
Vladimir Marko1a225a72019-07-05 13:37:42 +01002166 __ Vpush(SRegisterList(vixl32::SRegister(first), POPCOUNT(fpu_spill_mask_)));
2167 GetAssembler()->cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(fpu_spill_mask_));
2168 GetAssembler()->cfi().RelOffsetForMany(DWARFReg(s0),
2169 /*offset=*/ 0,
2170 fpu_spill_mask_,
2171 kArmWordSize);
2172 }
Scott Wakelingbffdc702016-12-07 17:46:03 +00002173
Vladimir Marko1a225a72019-07-05 13:37:42 +01002174 // Adjust SP and save the current method if we need it. Note that we do
2175 // not save the method in HCurrentMethod, as the instruction might have
2176 // been removed in the SSA graph.
2177 if (RequiresCurrentMethod() && fp_spills_offset <= 3 * kArmWordSize) {
2178 DCHECK_EQ(kMethodRegister.GetCode(), 0u);
2179 __ Push(RegisterList(MaxInt<uint32_t>(fp_spills_offset / kArmWordSize)));
2180 GetAssembler()->cfi().AdjustCFAOffset(fp_spills_offset);
2181 } else {
2182 __ Sub(sp, sp, dchecked_integral_cast<int32_t>(fp_spills_offset));
2183 GetAssembler()->cfi().AdjustCFAOffset(fp_spills_offset);
2184 if (RequiresCurrentMethod()) {
2185 GetAssembler()->StoreToOffset(kStoreWord, kMethodRegister, sp, 0);
2186 }
2187 }
Scott Wakelingbffdc702016-12-07 17:46:03 +00002188 }
Nicolas Geoffrayf7893532017-06-15 12:34:36 +01002189
2190 if (GetGraph()->HasShouldDeoptimizeFlag()) {
2191 UseScratchRegisterScope temps(GetVIXLAssembler());
2192 vixl32::Register temp = temps.Acquire();
2193 // Initialize should_deoptimize flag to 0.
2194 __ Mov(temp, 0);
2195 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, GetStackOffsetOfShouldDeoptimizeFlag());
2196 }
Roland Levillain5daa4952017-07-03 17:23:56 +01002197
Andreas Gampe3db70682018-12-26 15:12:03 -08002198 MaybeGenerateMarkingRegisterCheck(/* code= */ 1);
Scott Wakelingfe885462016-09-22 10:24:38 +01002199}
2200
2201void CodeGeneratorARMVIXL::GenerateFrameExit() {
2202 if (HasEmptyFrame()) {
2203 __ Bx(lr);
2204 return;
2205 }
Scott Wakelingfe885462016-09-22 10:24:38 +01002206
Scott Wakelingfe885462016-09-22 10:24:38 +01002207 // Pop LR into PC to return.
2208 DCHECK_NE(core_spill_mask_ & (1 << kLrCode), 0U);
2209 uint32_t pop_mask = (core_spill_mask_ & (~(1 << kLrCode))) | 1 << kPcCode;
Vladimir Marko1a225a72019-07-05 13:37:42 +01002210
2211 uint32_t frame_size = GetFrameSize();
2212 uint32_t core_spills_offset = frame_size - GetCoreSpillSize();
2213 uint32_t fp_spills_offset = frame_size - FrameEntrySpillSize();
2214 if ((fpu_spill_mask_ == 0u || IsPowerOfTwo(fpu_spill_mask_)) &&
2215 // r4 is blocked by TestCodeGeneratorARMVIXL used by some tests.
2216 core_spills_offset <= (blocked_core_registers_[r4.GetCode()] ? 2u : 3u) * kArmWordSize) {
2217 // Load the FP spill if any and then do a single POP including the method
2218 // and up to two filler registers. If we have no FP spills, this also has
2219 // the advantage that we do not need to emit CFI directives.
2220 if (fpu_spill_mask_ != 0u) {
2221 DCHECK(IsPowerOfTwo(fpu_spill_mask_));
2222 vixl::aarch32::SRegister sreg(LeastSignificantBit(fpu_spill_mask_));
2223 GetAssembler()->cfi().RememberState();
2224 GetAssembler()->LoadSFromOffset(sreg, sp, fp_spills_offset);
2225 GetAssembler()->cfi().Restore(DWARFReg(sreg));
2226 }
2227 // Clobber registers r2-r4 as they are caller-save in ART managed ABI and
2228 // never hold the return value.
2229 uint32_t extra_regs = MaxInt<uint32_t>(core_spills_offset / kArmWordSize) << r2.GetCode();
2230 DCHECK_EQ(extra_regs & kCoreCalleeSaves.GetList(), 0u);
2231 DCHECK_LT(MostSignificantBit(extra_regs), LeastSignificantBit(pop_mask));
2232 __ Pop(RegisterList(pop_mask | extra_regs));
2233 if (fpu_spill_mask_ != 0u) {
2234 GetAssembler()->cfi().RestoreState();
2235 }
2236 } else {
2237 GetAssembler()->cfi().RememberState();
2238 __ Add(sp, sp, fp_spills_offset);
2239 GetAssembler()->cfi().AdjustCFAOffset(-dchecked_integral_cast<int32_t>(fp_spills_offset));
2240 if (fpu_spill_mask_ != 0) {
2241 uint32_t first = LeastSignificantBit(fpu_spill_mask_);
2242
2243 // Check that list is contiguous.
2244 DCHECK_EQ(fpu_spill_mask_ >> CTZ(fpu_spill_mask_), ~0u >> (32 - POPCOUNT(fpu_spill_mask_)));
2245
2246 __ Vpop(SRegisterList(vixl32::SRegister(first), POPCOUNT(fpu_spill_mask_)));
2247 GetAssembler()->cfi().AdjustCFAOffset(
2248 -static_cast<int>(kArmWordSize) * POPCOUNT(fpu_spill_mask_));
2249 GetAssembler()->cfi().RestoreMany(DWARFReg(vixl32::SRegister(0)), fpu_spill_mask_);
2250 }
2251 __ Pop(RegisterList(pop_mask));
2252 GetAssembler()->cfi().RestoreState();
2253 GetAssembler()->cfi().DefCFAOffset(GetFrameSize());
2254 }
Scott Wakelingfe885462016-09-22 10:24:38 +01002255}
2256
2257void CodeGeneratorARMVIXL::Bind(HBasicBlock* block) {
2258 __ Bind(GetLabelOf(block));
2259}
2260
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002261Location InvokeDexCallingConventionVisitorARMVIXL::GetNextLocation(DataType::Type type) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002262 switch (type) {
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01002263 case DataType::Type::kReference:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002264 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01002265 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002266 case DataType::Type::kInt8:
2267 case DataType::Type::kUint16:
2268 case DataType::Type::kInt16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01002269 case DataType::Type::kInt32: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002270 uint32_t index = gp_index_++;
2271 uint32_t stack_index = stack_index_++;
2272 if (index < calling_convention.GetNumberOfRegisters()) {
2273 return LocationFrom(calling_convention.GetRegisterAt(index));
2274 } else {
2275 return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index));
2276 }
2277 }
2278
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002279 case DataType::Type::kInt64: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002280 uint32_t index = gp_index_;
2281 uint32_t stack_index = stack_index_;
2282 gp_index_ += 2;
2283 stack_index_ += 2;
2284 if (index + 1 < calling_convention.GetNumberOfRegisters()) {
2285 if (calling_convention.GetRegisterAt(index).Is(r1)) {
2286 // Skip R1, and use R2_R3 instead.
2287 gp_index_++;
2288 index++;
2289 }
2290 }
2291 if (index + 1 < calling_convention.GetNumberOfRegisters()) {
2292 DCHECK_EQ(calling_convention.GetRegisterAt(index).GetCode() + 1,
2293 calling_convention.GetRegisterAt(index + 1).GetCode());
2294
2295 return LocationFrom(calling_convention.GetRegisterAt(index),
2296 calling_convention.GetRegisterAt(index + 1));
2297 } else {
2298 return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index));
2299 }
2300 }
2301
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002302 case DataType::Type::kFloat32: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002303 uint32_t stack_index = stack_index_++;
2304 if (float_index_ % 2 == 0) {
2305 float_index_ = std::max(double_index_, float_index_);
2306 }
2307 if (float_index_ < calling_convention.GetNumberOfFpuRegisters()) {
2308 return LocationFrom(calling_convention.GetFpuRegisterAt(float_index_++));
2309 } else {
2310 return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index));
2311 }
2312 }
2313
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002314 case DataType::Type::kFloat64: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002315 double_index_ = std::max(double_index_, RoundUp(float_index_, 2));
2316 uint32_t stack_index = stack_index_;
2317 stack_index_ += 2;
2318 if (double_index_ + 1 < calling_convention.GetNumberOfFpuRegisters()) {
2319 uint32_t index = double_index_;
2320 double_index_ += 2;
2321 Location result = LocationFrom(
2322 calling_convention.GetFpuRegisterAt(index),
2323 calling_convention.GetFpuRegisterAt(index + 1));
2324 DCHECK(ExpectedPairLayout(result));
2325 return result;
2326 } else {
2327 return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index));
2328 }
2329 }
2330
Aart Bik66c158e2018-01-31 12:55:04 -08002331 case DataType::Type::kUint32:
2332 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002333 case DataType::Type::kVoid:
Artem Serovd4cc5b22016-11-04 11:19:09 +00002334 LOG(FATAL) << "Unexpected parameter type " << type;
Elliott Hughesc1896c92018-11-29 11:33:18 -08002335 UNREACHABLE();
Artem Serovd4cc5b22016-11-04 11:19:09 +00002336 }
2337 return Location::NoLocation();
2338}
2339
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002340Location InvokeDexCallingConventionVisitorARMVIXL::GetReturnLocation(DataType::Type type) const {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002341 switch (type) {
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01002342 case DataType::Type::kReference:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002343 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01002344 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002345 case DataType::Type::kInt8:
2346 case DataType::Type::kUint16:
2347 case DataType::Type::kInt16:
Aart Bik66c158e2018-01-31 12:55:04 -08002348 case DataType::Type::kUint32:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01002349 case DataType::Type::kInt32: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002350 return LocationFrom(r0);
2351 }
2352
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002353 case DataType::Type::kFloat32: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002354 return LocationFrom(s0);
2355 }
2356
Aart Bik66c158e2018-01-31 12:55:04 -08002357 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002358 case DataType::Type::kInt64: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002359 return LocationFrom(r0, r1);
2360 }
2361
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002362 case DataType::Type::kFloat64: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002363 return LocationFrom(s0, s1);
2364 }
2365
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002366 case DataType::Type::kVoid:
Artem Serovd4cc5b22016-11-04 11:19:09 +00002367 return Location::NoLocation();
2368 }
2369
2370 UNREACHABLE();
2371}
2372
2373Location InvokeDexCallingConventionVisitorARMVIXL::GetMethodLocation() const {
2374 return LocationFrom(kMethodRegister);
2375}
2376
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002377void CodeGeneratorARMVIXL::Move32(Location destination, Location source) {
2378 if (source.Equals(destination)) {
2379 return;
2380 }
2381 if (destination.IsRegister()) {
2382 if (source.IsRegister()) {
2383 __ Mov(RegisterFrom(destination), RegisterFrom(source));
2384 } else if (source.IsFpuRegister()) {
2385 __ Vmov(RegisterFrom(destination), SRegisterFrom(source));
2386 } else {
2387 GetAssembler()->LoadFromOffset(kLoadWord,
2388 RegisterFrom(destination),
2389 sp,
2390 source.GetStackIndex());
2391 }
2392 } else if (destination.IsFpuRegister()) {
2393 if (source.IsRegister()) {
2394 __ Vmov(SRegisterFrom(destination), RegisterFrom(source));
2395 } else if (source.IsFpuRegister()) {
2396 __ Vmov(SRegisterFrom(destination), SRegisterFrom(source));
2397 } else {
2398 GetAssembler()->LoadSFromOffset(SRegisterFrom(destination), sp, source.GetStackIndex());
2399 }
2400 } else {
2401 DCHECK(destination.IsStackSlot()) << destination;
2402 if (source.IsRegister()) {
2403 GetAssembler()->StoreToOffset(kStoreWord,
2404 RegisterFrom(source),
2405 sp,
2406 destination.GetStackIndex());
2407 } else if (source.IsFpuRegister()) {
2408 GetAssembler()->StoreSToOffset(SRegisterFrom(source), sp, destination.GetStackIndex());
2409 } else {
2410 DCHECK(source.IsStackSlot()) << source;
2411 UseScratchRegisterScope temps(GetVIXLAssembler());
2412 vixl32::Register temp = temps.Acquire();
2413 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, source.GetStackIndex());
2414 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
2415 }
2416 }
2417}
2418
Artem Serovcfbe9132016-10-14 15:58:56 +01002419void CodeGeneratorARMVIXL::MoveConstant(Location location, int32_t value) {
2420 DCHECK(location.IsRegister());
2421 __ Mov(RegisterFrom(location), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01002422}
2423
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002424void CodeGeneratorARMVIXL::MoveLocation(Location dst, Location src, DataType::Type dst_type) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002425 // TODO(VIXL): Maybe refactor to have the 'move' implementation here and use it in
2426 // `ParallelMoveResolverARMVIXL::EmitMove`, as is done in the `arm64` backend.
Vladimir Markoca6fff82017-10-03 14:49:14 +01002427 HParallelMove move(GetGraph()->GetAllocator());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002428 move.AddMove(src, dst, dst_type, nullptr);
2429 GetMoveResolver()->EmitNativeCode(&move);
Scott Wakelingfe885462016-09-22 10:24:38 +01002430}
2431
Artem Serovcfbe9132016-10-14 15:58:56 +01002432void CodeGeneratorARMVIXL::AddLocationAsTemp(Location location, LocationSummary* locations) {
2433 if (location.IsRegister()) {
2434 locations->AddTemp(location);
2435 } else if (location.IsRegisterPair()) {
2436 locations->AddTemp(LocationFrom(LowRegisterFrom(location)));
2437 locations->AddTemp(LocationFrom(HighRegisterFrom(location)));
2438 } else {
2439 UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location;
2440 }
Scott Wakelingfe885462016-09-22 10:24:38 +01002441}
2442
2443void CodeGeneratorARMVIXL::InvokeRuntime(QuickEntrypointEnum entrypoint,
2444 HInstruction* instruction,
2445 uint32_t dex_pc,
2446 SlowPathCode* slow_path) {
2447 ValidateInvokeRuntime(entrypoint, instruction, slow_path);
Vladimir Markof6675082019-05-17 12:05:28 +01002448
2449 ThreadOffset32 entrypoint_offset = GetThreadOffset<kArmPointerSize>(entrypoint);
2450 // Reduce code size for AOT by using shared trampolines for slow path runtime calls across the
2451 // entire oat file. This adds an extra branch and we do not want to slow down the main path.
2452 // For JIT, thunk sharing is per-method, so the gains would be smaller or even negative.
2453 if (slow_path == nullptr || Runtime::Current()->UseJitCompilation()) {
2454 __ Ldr(lr, MemOperand(tr, entrypoint_offset.Int32Value()));
2455 // Ensure the pc position is recorded immediately after the `blx` instruction.
2456 // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
2457 ExactAssemblyScope aas(GetVIXLAssembler(),
2458 vixl32::k16BitT32InstructionSizeInBytes,
2459 CodeBufferCheckScope::kExactSize);
2460 __ blx(lr);
2461 if (EntrypointRequiresStackMap(entrypoint)) {
2462 RecordPcInfo(instruction, dex_pc, slow_path);
2463 }
2464 } else {
2465 // Ensure the pc position is recorded immediately after the `bl` instruction.
2466 ExactAssemblyScope aas(GetVIXLAssembler(),
2467 vixl32::k32BitT32InstructionSizeInBytes,
2468 CodeBufferCheckScope::kExactSize);
2469 EmitEntrypointThunkCall(entrypoint_offset);
2470 if (EntrypointRequiresStackMap(entrypoint)) {
2471 RecordPcInfo(instruction, dex_pc, slow_path);
2472 }
Scott Wakelingfe885462016-09-22 10:24:38 +01002473 }
2474}
2475
2476void CodeGeneratorARMVIXL::InvokeRuntimeWithoutRecordingPcInfo(int32_t entry_point_offset,
2477 HInstruction* instruction,
2478 SlowPathCode* slow_path) {
2479 ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction, slow_path);
Alexandre Rames374ddf32016-11-04 10:40:49 +00002480 __ Ldr(lr, MemOperand(tr, entry_point_offset));
Scott Wakelingfe885462016-09-22 10:24:38 +01002481 __ Blx(lr);
2482}
2483
Scott Wakelingfe885462016-09-22 10:24:38 +01002484void InstructionCodeGeneratorARMVIXL::HandleGoto(HInstruction* got, HBasicBlock* successor) {
Aart Bika8b8e9b2018-01-09 11:01:02 -08002485 if (successor->IsExitBlock()) {
2486 DCHECK(got->GetPrevious()->AlwaysThrows());
2487 return; // no code needed
2488 }
2489
Scott Wakelingfe885462016-09-22 10:24:38 +01002490 HBasicBlock* block = got->GetBlock();
2491 HInstruction* previous = got->GetPrevious();
2492 HLoopInformation* info = block->GetLoopInformation();
2493
2494 if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) {
Nicolas Geoffray8d728322018-01-18 22:44:32 +00002495 if (codegen_->GetCompilerOptions().CountHotnessInCompiledCode()) {
2496 UseScratchRegisterScope temps(GetVIXLAssembler());
2497 vixl32::Register temp = temps.Acquire();
2498 __ Push(vixl32::Register(kMethodRegister));
2499 GetAssembler()->LoadFromOffset(kLoadWord, kMethodRegister, sp, kArmWordSize);
2500 __ Ldrh(temp, MemOperand(kMethodRegister, ArtMethod::HotnessCountOffset().Int32Value()));
2501 __ Add(temp, temp, 1);
2502 __ Strh(temp, MemOperand(kMethodRegister, ArtMethod::HotnessCountOffset().Int32Value()));
2503 __ Pop(vixl32::Register(kMethodRegister));
2504 }
Scott Wakelingfe885462016-09-22 10:24:38 +01002505 GenerateSuspendCheck(info->GetSuspendCheck(), successor);
2506 return;
2507 }
2508 if (block->IsEntryBlock() && (previous != nullptr) && previous->IsSuspendCheck()) {
2509 GenerateSuspendCheck(previous->AsSuspendCheck(), nullptr);
Andreas Gampe3db70682018-12-26 15:12:03 -08002510 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 2);
Scott Wakelingfe885462016-09-22 10:24:38 +01002511 }
2512 if (!codegen_->GoesToNextBlock(block, successor)) {
2513 __ B(codegen_->GetLabelOf(successor));
2514 }
2515}
2516
2517void LocationsBuilderARMVIXL::VisitGoto(HGoto* got) {
2518 got->SetLocations(nullptr);
2519}
2520
2521void InstructionCodeGeneratorARMVIXL::VisitGoto(HGoto* got) {
2522 HandleGoto(got, got->GetSuccessor());
2523}
2524
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002525void LocationsBuilderARMVIXL::VisitTryBoundary(HTryBoundary* try_boundary) {
2526 try_boundary->SetLocations(nullptr);
2527}
2528
2529void InstructionCodeGeneratorARMVIXL::VisitTryBoundary(HTryBoundary* try_boundary) {
2530 HBasicBlock* successor = try_boundary->GetNormalFlowSuccessor();
2531 if (!successor->IsExitBlock()) {
2532 HandleGoto(try_boundary, successor);
2533 }
2534}
2535
Scott Wakelingfe885462016-09-22 10:24:38 +01002536void LocationsBuilderARMVIXL::VisitExit(HExit* exit) {
2537 exit->SetLocations(nullptr);
2538}
2539
2540void InstructionCodeGeneratorARMVIXL::VisitExit(HExit* exit ATTRIBUTE_UNUSED) {
2541}
2542
Scott Wakelingfe885462016-09-22 10:24:38 +01002543void InstructionCodeGeneratorARMVIXL::GenerateCompareTestAndBranch(HCondition* condition,
Anton Kirilov23b752b2017-07-20 14:40:44 +01002544 vixl32::Label* true_target,
2545 vixl32::Label* false_target,
Anton Kirilovfd522532017-05-10 12:46:57 +01002546 bool is_far_target) {
Anton Kirilov23b752b2017-07-20 14:40:44 +01002547 if (true_target == false_target) {
2548 DCHECK(true_target != nullptr);
2549 __ B(true_target);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00002550 return;
2551 }
2552
Anton Kirilov23b752b2017-07-20 14:40:44 +01002553 vixl32::Label* non_fallthrough_target;
2554 bool invert;
2555 bool emit_both_branches;
Scott Wakelingfe885462016-09-22 10:24:38 +01002556
Anton Kirilov23b752b2017-07-20 14:40:44 +01002557 if (true_target == nullptr) {
2558 // The true target is fallthrough.
2559 DCHECK(false_target != nullptr);
2560 non_fallthrough_target = false_target;
2561 invert = true;
2562 emit_both_branches = false;
2563 } else {
2564 non_fallthrough_target = true_target;
2565 invert = false;
2566 // Either the false target is fallthrough, or there is no fallthrough
2567 // and both branches must be emitted.
2568 emit_both_branches = (false_target != nullptr);
Scott Wakelingfe885462016-09-22 10:24:38 +01002569 }
2570
Anton Kirilov23b752b2017-07-20 14:40:44 +01002571 const auto cond = GenerateTest(condition, invert, codegen_);
2572
2573 __ B(cond.first, non_fallthrough_target, is_far_target);
2574
2575 if (emit_both_branches) {
2576 // No target falls through, we need to branch.
2577 __ B(false_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01002578 }
2579}
2580
2581void InstructionCodeGeneratorARMVIXL::GenerateTestAndBranch(HInstruction* instruction,
2582 size_t condition_input_index,
2583 vixl32::Label* true_target,
xueliang.zhongf51bc622016-11-04 09:23:32 +00002584 vixl32::Label* false_target,
2585 bool far_target) {
Scott Wakelingfe885462016-09-22 10:24:38 +01002586 HInstruction* cond = instruction->InputAt(condition_input_index);
2587
2588 if (true_target == nullptr && false_target == nullptr) {
2589 // Nothing to do. The code always falls through.
2590 return;
2591 } else if (cond->IsIntConstant()) {
2592 // Constant condition, statically compared against "true" (integer value 1).
2593 if (cond->AsIntConstant()->IsTrue()) {
2594 if (true_target != nullptr) {
2595 __ B(true_target);
2596 }
2597 } else {
Anton Kirilov644032c2016-12-06 17:51:43 +00002598 DCHECK(cond->AsIntConstant()->IsFalse()) << Int32ConstantFrom(cond);
Scott Wakelingfe885462016-09-22 10:24:38 +01002599 if (false_target != nullptr) {
2600 __ B(false_target);
2601 }
2602 }
2603 return;
2604 }
2605
2606 // The following code generates these patterns:
2607 // (1) true_target == nullptr && false_target != nullptr
2608 // - opposite condition true => branch to false_target
2609 // (2) true_target != nullptr && false_target == nullptr
2610 // - condition true => branch to true_target
2611 // (3) true_target != nullptr && false_target != nullptr
2612 // - condition true => branch to true_target
2613 // - branch to false_target
2614 if (IsBooleanValueOrMaterializedCondition(cond)) {
2615 // Condition has been materialized, compare the output to 0.
2616 if (kIsDebugBuild) {
2617 Location cond_val = instruction->GetLocations()->InAt(condition_input_index);
2618 DCHECK(cond_val.IsRegister());
2619 }
2620 if (true_target == nullptr) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00002621 __ CompareAndBranchIfZero(InputRegisterAt(instruction, condition_input_index),
2622 false_target,
2623 far_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01002624 } else {
xueliang.zhongf51bc622016-11-04 09:23:32 +00002625 __ CompareAndBranchIfNonZero(InputRegisterAt(instruction, condition_input_index),
2626 true_target,
2627 far_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01002628 }
2629 } else {
2630 // Condition has not been materialized. Use its inputs as the comparison and
2631 // its condition as the branch condition.
2632 HCondition* condition = cond->AsCondition();
2633
2634 // If this is a long or FP comparison that has been folded into
2635 // the HCondition, generate the comparison directly.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002636 DataType::Type type = condition->InputAt(0)->GetType();
2637 if (type == DataType::Type::kInt64 || DataType::IsFloatingPointType(type)) {
Anton Kirilovfd522532017-05-10 12:46:57 +01002638 GenerateCompareTestAndBranch(condition, true_target, false_target, far_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01002639 return;
2640 }
2641
Donghui Bai426b49c2016-11-08 14:55:38 +08002642 vixl32::Label* non_fallthrough_target;
2643 vixl32::Condition arm_cond = vixl32::Condition::None();
2644 const vixl32::Register left = InputRegisterAt(cond, 0);
2645 const Operand right = InputOperandAt(cond, 1);
2646
Scott Wakelingfe885462016-09-22 10:24:38 +01002647 if (true_target == nullptr) {
Donghui Bai426b49c2016-11-08 14:55:38 +08002648 arm_cond = ARMCondition(condition->GetOppositeCondition());
2649 non_fallthrough_target = false_target;
Scott Wakelingfe885462016-09-22 10:24:38 +01002650 } else {
Donghui Bai426b49c2016-11-08 14:55:38 +08002651 arm_cond = ARMCondition(condition->GetCondition());
2652 non_fallthrough_target = true_target;
2653 }
2654
2655 if (right.IsImmediate() && right.GetImmediate() == 0 && (arm_cond.Is(ne) || arm_cond.Is(eq))) {
2656 if (arm_cond.Is(eq)) {
Anton Kirilovfd522532017-05-10 12:46:57 +01002657 __ CompareAndBranchIfZero(left, non_fallthrough_target, far_target);
Donghui Bai426b49c2016-11-08 14:55:38 +08002658 } else {
2659 DCHECK(arm_cond.Is(ne));
Anton Kirilovfd522532017-05-10 12:46:57 +01002660 __ CompareAndBranchIfNonZero(left, non_fallthrough_target, far_target);
Donghui Bai426b49c2016-11-08 14:55:38 +08002661 }
2662 } else {
2663 __ Cmp(left, right);
Anton Kirilovfd522532017-05-10 12:46:57 +01002664 __ B(arm_cond, non_fallthrough_target, far_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01002665 }
2666 }
2667
2668 // If neither branch falls through (case 3), the conditional branch to `true_target`
2669 // was already emitted (case 2) and we need to emit a jump to `false_target`.
2670 if (true_target != nullptr && false_target != nullptr) {
2671 __ B(false_target);
2672 }
2673}
2674
2675void LocationsBuilderARMVIXL::VisitIf(HIf* if_instr) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002676 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(if_instr);
Scott Wakelingfe885462016-09-22 10:24:38 +01002677 if (IsBooleanValueOrMaterializedCondition(if_instr->InputAt(0))) {
2678 locations->SetInAt(0, Location::RequiresRegister());
2679 }
2680}
2681
2682void InstructionCodeGeneratorARMVIXL::VisitIf(HIf* if_instr) {
2683 HBasicBlock* true_successor = if_instr->IfTrueSuccessor();
2684 HBasicBlock* false_successor = if_instr->IfFalseSuccessor();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002685 vixl32::Label* true_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), true_successor) ?
2686 nullptr : codegen_->GetLabelOf(true_successor);
2687 vixl32::Label* false_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), false_successor) ?
2688 nullptr : codegen_->GetLabelOf(false_successor);
Andreas Gampe3db70682018-12-26 15:12:03 -08002689 GenerateTestAndBranch(if_instr, /* condition_input_index= */ 0, true_target, false_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01002690}
2691
Scott Wakelingc34dba72016-10-03 10:14:44 +01002692void LocationsBuilderARMVIXL::VisitDeoptimize(HDeoptimize* deoptimize) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002693 LocationSummary* locations = new (GetGraph()->GetAllocator())
Scott Wakelingc34dba72016-10-03 10:14:44 +01002694 LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath);
Nicolas Geoffray4e92c3c2017-05-08 09:34:26 +01002695 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2696 RegisterSet caller_saves = RegisterSet::Empty();
2697 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0)));
2698 locations->SetCustomSlowPathCallerSaves(caller_saves);
Scott Wakelingc34dba72016-10-03 10:14:44 +01002699 if (IsBooleanValueOrMaterializedCondition(deoptimize->InputAt(0))) {
2700 locations->SetInAt(0, Location::RequiresRegister());
2701 }
2702}
2703
2704void InstructionCodeGeneratorARMVIXL::VisitDeoptimize(HDeoptimize* deoptimize) {
2705 SlowPathCodeARMVIXL* slow_path =
2706 deopt_slow_paths_.NewSlowPath<DeoptimizationSlowPathARMVIXL>(deoptimize);
2707 GenerateTestAndBranch(deoptimize,
Andreas Gampe3db70682018-12-26 15:12:03 -08002708 /* condition_input_index= */ 0,
Scott Wakelingc34dba72016-10-03 10:14:44 +01002709 slow_path->GetEntryLabel(),
Andreas Gampe3db70682018-12-26 15:12:03 -08002710 /* false_target= */ nullptr);
Scott Wakelingc34dba72016-10-03 10:14:44 +01002711}
2712
Artem Serovd4cc5b22016-11-04 11:19:09 +00002713void LocationsBuilderARMVIXL::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002714 LocationSummary* locations = new (GetGraph()->GetAllocator())
Artem Serovd4cc5b22016-11-04 11:19:09 +00002715 LocationSummary(flag, LocationSummary::kNoCall);
2716 locations->SetOut(Location::RequiresRegister());
2717}
2718
2719void InstructionCodeGeneratorARMVIXL::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
2720 GetAssembler()->LoadFromOffset(kLoadWord,
2721 OutputRegister(flag),
2722 sp,
2723 codegen_->GetStackOffsetOfShouldDeoptimizeFlag());
2724}
2725
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002726void LocationsBuilderARMVIXL::VisitSelect(HSelect* select) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002727 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(select);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002728 const bool is_floating_point = DataType::IsFloatingPointType(select->GetType());
Donghui Bai426b49c2016-11-08 14:55:38 +08002729
2730 if (is_floating_point) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002731 locations->SetInAt(0, Location::RequiresFpuRegister());
Donghui Bai426b49c2016-11-08 14:55:38 +08002732 locations->SetInAt(1, Location::FpuRegisterOrConstant(select->GetTrueValue()));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002733 } else {
2734 locations->SetInAt(0, Location::RequiresRegister());
Donghui Bai426b49c2016-11-08 14:55:38 +08002735 locations->SetInAt(1, Arm8BitEncodableConstantOrRegister(select->GetTrueValue()));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002736 }
Donghui Bai426b49c2016-11-08 14:55:38 +08002737
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002738 if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) {
Donghui Bai426b49c2016-11-08 14:55:38 +08002739 locations->SetInAt(2, Location::RegisterOrConstant(select->GetCondition()));
2740 // The code generator handles overlap with the values, but not with the condition.
2741 locations->SetOut(Location::SameAsFirstInput());
2742 } else if (is_floating_point) {
2743 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2744 } else {
2745 if (!locations->InAt(1).IsConstant()) {
2746 locations->SetInAt(0, Arm8BitEncodableConstantOrRegister(select->GetFalseValue()));
2747 }
2748
2749 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002750 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002751}
2752
2753void InstructionCodeGeneratorARMVIXL::VisitSelect(HSelect* select) {
Donghui Bai426b49c2016-11-08 14:55:38 +08002754 HInstruction* const condition = select->GetCondition();
2755 const LocationSummary* const locations = select->GetLocations();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002756 const DataType::Type type = select->GetType();
Donghui Bai426b49c2016-11-08 14:55:38 +08002757 const Location first = locations->InAt(0);
2758 const Location out = locations->Out();
2759 const Location second = locations->InAt(1);
Nicolas Geoffray7b05c5f2018-09-21 11:31:38 +01002760
2761 // In the unlucky case the output of this instruction overlaps
2762 // with an input of an "emitted-at-use-site" condition, and
2763 // the output of this instruction is not one of its inputs, we'll
2764 // need to fallback to branches instead of conditional ARM instructions.
2765 bool output_overlaps_with_condition_inputs =
2766 !IsBooleanValueOrMaterializedCondition(condition) &&
2767 !out.Equals(first) &&
2768 !out.Equals(second) &&
2769 (condition->GetLocations()->InAt(0).Equals(out) ||
2770 condition->GetLocations()->InAt(1).Equals(out));
2771 DCHECK(!output_overlaps_with_condition_inputs || condition->IsCondition());
Donghui Bai426b49c2016-11-08 14:55:38 +08002772 Location src;
2773
2774 if (condition->IsIntConstant()) {
2775 if (condition->AsIntConstant()->IsFalse()) {
2776 src = first;
2777 } else {
2778 src = second;
2779 }
2780
2781 codegen_->MoveLocation(out, src, type);
2782 return;
2783 }
2784
Nicolas Geoffray7b05c5f2018-09-21 11:31:38 +01002785 if (!DataType::IsFloatingPointType(type) && !output_overlaps_with_condition_inputs) {
Donghui Bai426b49c2016-11-08 14:55:38 +08002786 bool invert = false;
2787
2788 if (out.Equals(second)) {
2789 src = first;
2790 invert = true;
2791 } else if (out.Equals(first)) {
2792 src = second;
2793 } else if (second.IsConstant()) {
2794 DCHECK(CanEncodeConstantAs8BitImmediate(second.GetConstant()));
2795 src = second;
2796 } else if (first.IsConstant()) {
2797 DCHECK(CanEncodeConstantAs8BitImmediate(first.GetConstant()));
2798 src = first;
2799 invert = true;
2800 } else {
2801 src = second;
2802 }
2803
2804 if (CanGenerateConditionalMove(out, src)) {
2805 if (!out.Equals(first) && !out.Equals(second)) {
2806 codegen_->MoveLocation(out, src.Equals(first) ? second : first, type);
2807 }
2808
Anton Kirilov217b2ce2017-03-16 11:47:12 +00002809 std::pair<vixl32::Condition, vixl32::Condition> cond(eq, ne);
2810
2811 if (IsBooleanValueOrMaterializedCondition(condition)) {
2812 __ Cmp(InputRegisterAt(select, 2), 0);
2813 cond = invert ? std::make_pair(eq, ne) : std::make_pair(ne, eq);
2814 } else {
2815 cond = GenerateTest(condition->AsCondition(), invert, codegen_);
2816 }
2817
Donghui Bai426b49c2016-11-08 14:55:38 +08002818 const size_t instr_count = out.IsRegisterPair() ? 4 : 2;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00002819 // We use the scope because of the IT block that follows.
Donghui Bai426b49c2016-11-08 14:55:38 +08002820 ExactAssemblyScope guard(GetVIXLAssembler(),
2821 instr_count * vixl32::k16BitT32InstructionSizeInBytes,
2822 CodeBufferCheckScope::kExactSize);
2823
2824 if (out.IsRegister()) {
Anton Kirilov217b2ce2017-03-16 11:47:12 +00002825 __ it(cond.first);
2826 __ mov(cond.first, RegisterFrom(out), OperandFrom(src, type));
Donghui Bai426b49c2016-11-08 14:55:38 +08002827 } else {
2828 DCHECK(out.IsRegisterPair());
2829
2830 Operand operand_high(0);
2831 Operand operand_low(0);
2832
2833 if (src.IsConstant()) {
2834 const int64_t value = Int64ConstantFrom(src);
2835
2836 operand_high = High32Bits(value);
2837 operand_low = Low32Bits(value);
2838 } else {
2839 DCHECK(src.IsRegisterPair());
2840 operand_high = HighRegisterFrom(src);
2841 operand_low = LowRegisterFrom(src);
2842 }
2843
Anton Kirilov217b2ce2017-03-16 11:47:12 +00002844 __ it(cond.first);
2845 __ mov(cond.first, LowRegisterFrom(out), operand_low);
2846 __ it(cond.first);
2847 __ mov(cond.first, HighRegisterFrom(out), operand_high);
Donghui Bai426b49c2016-11-08 14:55:38 +08002848 }
2849
2850 return;
2851 }
2852 }
2853
2854 vixl32::Label* false_target = nullptr;
2855 vixl32::Label* true_target = nullptr;
2856 vixl32::Label select_end;
Nicolas Geoffray7b05c5f2018-09-21 11:31:38 +01002857 vixl32::Label other_case;
Donghui Bai426b49c2016-11-08 14:55:38 +08002858 vixl32::Label* const target = codegen_->GetFinalLabel(select, &select_end);
2859
2860 if (out.Equals(second)) {
2861 true_target = target;
2862 src = first;
2863 } else {
2864 false_target = target;
2865 src = second;
2866
2867 if (!out.Equals(first)) {
Nicolas Geoffray7b05c5f2018-09-21 11:31:38 +01002868 if (output_overlaps_with_condition_inputs) {
2869 false_target = &other_case;
2870 } else {
2871 codegen_->MoveLocation(out, first, type);
2872 }
Donghui Bai426b49c2016-11-08 14:55:38 +08002873 }
2874 }
2875
Andreas Gampe3db70682018-12-26 15:12:03 -08002876 GenerateTestAndBranch(select, 2, true_target, false_target, /* far_target= */ false);
Donghui Bai426b49c2016-11-08 14:55:38 +08002877 codegen_->MoveLocation(out, src, type);
Nicolas Geoffray7b05c5f2018-09-21 11:31:38 +01002878 if (output_overlaps_with_condition_inputs) {
2879 __ B(target);
2880 __ Bind(&other_case);
2881 codegen_->MoveLocation(out, first, type);
2882 }
Donghui Bai426b49c2016-11-08 14:55:38 +08002883
2884 if (select_end.IsReferenced()) {
2885 __ Bind(&select_end);
2886 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002887}
2888
Artem Serov551b28f2016-10-18 19:11:30 +01002889void LocationsBuilderARMVIXL::VisitNativeDebugInfo(HNativeDebugInfo* info) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002890 new (GetGraph()->GetAllocator()) LocationSummary(info);
Artem Serov551b28f2016-10-18 19:11:30 +01002891}
2892
2893void InstructionCodeGeneratorARMVIXL::VisitNativeDebugInfo(HNativeDebugInfo*) {
2894 // MaybeRecordNativeDebugInfo is already called implicitly in CodeGenerator::Compile.
2895}
2896
Scott Wakelingfe885462016-09-22 10:24:38 +01002897void CodeGeneratorARMVIXL::GenerateNop() {
2898 __ Nop();
2899}
2900
Anton Kirilov5601d4e2017-05-11 19:33:50 +01002901// `temp` is an extra temporary register that is used for some conditions;
2902// callers may not specify it, in which case the method will use a scratch
2903// register instead.
2904void CodeGeneratorARMVIXL::GenerateConditionWithZero(IfCondition condition,
2905 vixl32::Register out,
2906 vixl32::Register in,
2907 vixl32::Register temp) {
2908 switch (condition) {
2909 case kCondEQ:
2910 // x <= 0 iff x == 0 when the comparison is unsigned.
2911 case kCondBE:
2912 if (!temp.IsValid() || (out.IsLow() && !out.Is(in))) {
2913 temp = out;
2914 }
2915
2916 // Avoid 32-bit instructions if possible; note that `in` and `temp` must be
2917 // different as well.
2918 if (in.IsLow() && temp.IsLow() && !in.Is(temp)) {
2919 // temp = - in; only 0 sets the carry flag.
2920 __ Rsbs(temp, in, 0);
2921
2922 if (out.Is(in)) {
2923 std::swap(in, temp);
2924 }
2925
2926 // out = - in + in + carry = carry
2927 __ Adc(out, temp, in);
2928 } else {
2929 // If `in` is 0, then it has 32 leading zeros, and less than that otherwise.
2930 __ Clz(out, in);
2931 // Any number less than 32 logically shifted right by 5 bits results in 0;
2932 // the same operation on 32 yields 1.
2933 __ Lsr(out, out, 5);
2934 }
2935
2936 break;
2937 case kCondNE:
2938 // x > 0 iff x != 0 when the comparison is unsigned.
2939 case kCondA: {
2940 UseScratchRegisterScope temps(GetVIXLAssembler());
2941
2942 if (out.Is(in)) {
2943 if (!temp.IsValid() || in.Is(temp)) {
2944 temp = temps.Acquire();
2945 }
2946 } else if (!temp.IsValid() || !temp.IsLow()) {
2947 temp = out;
2948 }
2949
2950 // temp = in - 1; only 0 does not set the carry flag.
2951 __ Subs(temp, in, 1);
2952 // out = in + ~temp + carry = in + (-(in - 1) - 1) + carry = in - in + 1 - 1 + carry = carry
2953 __ Sbc(out, in, temp);
2954 break;
2955 }
2956 case kCondGE:
2957 __ Mvn(out, in);
2958 in = out;
2959 FALLTHROUGH_INTENDED;
2960 case kCondLT:
2961 // We only care about the sign bit.
2962 __ Lsr(out, in, 31);
2963 break;
2964 case kCondAE:
2965 // Trivially true.
2966 __ Mov(out, 1);
2967 break;
2968 case kCondB:
2969 // Trivially false.
2970 __ Mov(out, 0);
2971 break;
2972 default:
2973 LOG(FATAL) << "Unexpected condition " << condition;
2974 UNREACHABLE();
2975 }
2976}
2977
Scott Wakelingfe885462016-09-22 10:24:38 +01002978void LocationsBuilderARMVIXL::HandleCondition(HCondition* cond) {
2979 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01002980 new (GetGraph()->GetAllocator()) LocationSummary(cond, LocationSummary::kNoCall);
Nicolas Geoffray7b05c5f2018-09-21 11:31:38 +01002981 const DataType::Type type = cond->InputAt(0)->GetType();
2982 if (DataType::IsFloatingPointType(type)) {
2983 locations->SetInAt(0, Location::RequiresFpuRegister());
2984 locations->SetInAt(1, ArithmeticZeroOrFpuRegister(cond->InputAt(1)));
2985 } else {
2986 locations->SetInAt(0, Location::RequiresRegister());
2987 locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
2988 }
2989 if (!cond->IsEmittedAtUseSite()) {
2990 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Scott Wakelingfe885462016-09-22 10:24:38 +01002991 }
2992}
2993
2994void InstructionCodeGeneratorARMVIXL::HandleCondition(HCondition* cond) {
2995 if (cond->IsEmittedAtUseSite()) {
2996 return;
2997 }
2998
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002999 const DataType::Type type = cond->GetLeft()->GetType();
Scott Wakelingfe885462016-09-22 10:24:38 +01003000
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003001 if (DataType::IsFloatingPointType(type)) {
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003002 GenerateConditionGeneric(cond, codegen_);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003003 return;
Scott Wakelingfe885462016-09-22 10:24:38 +01003004 }
3005
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003006 DCHECK(DataType::IsIntegralType(type) || type == DataType::Type::kReference) << type;
Scott Wakelingfe885462016-09-22 10:24:38 +01003007
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003008 const IfCondition condition = cond->GetCondition();
Scott Wakelingfe885462016-09-22 10:24:38 +01003009
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003010 // A condition with only one boolean input, or two boolean inputs without being equality or
3011 // inequality results from transformations done by the instruction simplifier, and is handled
3012 // as a regular condition with integral inputs.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003013 if (type == DataType::Type::kBool &&
3014 cond->GetRight()->GetType() == DataType::Type::kBool &&
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003015 (condition == kCondEQ || condition == kCondNE)) {
3016 vixl32::Register left = InputRegisterAt(cond, 0);
3017 const vixl32::Register out = OutputRegister(cond);
3018 const Location right_loc = cond->GetLocations()->InAt(1);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003019
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003020 // The constant case is handled by the instruction simplifier.
3021 DCHECK(!right_loc.IsConstant());
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003022
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003023 vixl32::Register right = RegisterFrom(right_loc);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003024
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003025 // Avoid 32-bit instructions if possible.
3026 if (out.Is(right)) {
3027 std::swap(left, right);
3028 }
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003029
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003030 __ Eor(out, left, right);
3031
3032 if (condition == kCondEQ) {
3033 __ Eor(out, out, 1);
3034 }
3035
3036 return;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003037 }
Anton Kirilov6f644202017-02-27 18:29:45 +00003038
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003039 GenerateConditionIntegralOrNonPrimitive(cond, codegen_);
Scott Wakelingfe885462016-09-22 10:24:38 +01003040}
3041
3042void LocationsBuilderARMVIXL::VisitEqual(HEqual* comp) {
3043 HandleCondition(comp);
3044}
3045
3046void InstructionCodeGeneratorARMVIXL::VisitEqual(HEqual* comp) {
3047 HandleCondition(comp);
3048}
3049
3050void LocationsBuilderARMVIXL::VisitNotEqual(HNotEqual* comp) {
3051 HandleCondition(comp);
3052}
3053
3054void InstructionCodeGeneratorARMVIXL::VisitNotEqual(HNotEqual* comp) {
3055 HandleCondition(comp);
3056}
3057
3058void LocationsBuilderARMVIXL::VisitLessThan(HLessThan* comp) {
3059 HandleCondition(comp);
3060}
3061
3062void InstructionCodeGeneratorARMVIXL::VisitLessThan(HLessThan* comp) {
3063 HandleCondition(comp);
3064}
3065
3066void LocationsBuilderARMVIXL::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
3067 HandleCondition(comp);
3068}
3069
3070void InstructionCodeGeneratorARMVIXL::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
3071 HandleCondition(comp);
3072}
3073
3074void LocationsBuilderARMVIXL::VisitGreaterThan(HGreaterThan* comp) {
3075 HandleCondition(comp);
3076}
3077
3078void InstructionCodeGeneratorARMVIXL::VisitGreaterThan(HGreaterThan* comp) {
3079 HandleCondition(comp);
3080}
3081
3082void LocationsBuilderARMVIXL::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
3083 HandleCondition(comp);
3084}
3085
3086void InstructionCodeGeneratorARMVIXL::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
3087 HandleCondition(comp);
3088}
3089
3090void LocationsBuilderARMVIXL::VisitBelow(HBelow* comp) {
3091 HandleCondition(comp);
3092}
3093
3094void InstructionCodeGeneratorARMVIXL::VisitBelow(HBelow* comp) {
3095 HandleCondition(comp);
3096}
3097
3098void LocationsBuilderARMVIXL::VisitBelowOrEqual(HBelowOrEqual* comp) {
3099 HandleCondition(comp);
3100}
3101
3102void InstructionCodeGeneratorARMVIXL::VisitBelowOrEqual(HBelowOrEqual* comp) {
3103 HandleCondition(comp);
3104}
3105
3106void LocationsBuilderARMVIXL::VisitAbove(HAbove* comp) {
3107 HandleCondition(comp);
3108}
3109
3110void InstructionCodeGeneratorARMVIXL::VisitAbove(HAbove* comp) {
3111 HandleCondition(comp);
3112}
3113
3114void LocationsBuilderARMVIXL::VisitAboveOrEqual(HAboveOrEqual* comp) {
3115 HandleCondition(comp);
3116}
3117
3118void InstructionCodeGeneratorARMVIXL::VisitAboveOrEqual(HAboveOrEqual* comp) {
3119 HandleCondition(comp);
3120}
3121
3122void LocationsBuilderARMVIXL::VisitIntConstant(HIntConstant* constant) {
3123 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003124 new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
Scott Wakelingfe885462016-09-22 10:24:38 +01003125 locations->SetOut(Location::ConstantLocation(constant));
3126}
3127
3128void InstructionCodeGeneratorARMVIXL::VisitIntConstant(HIntConstant* constant ATTRIBUTE_UNUSED) {
3129 // Will be generated at use site.
3130}
3131
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003132void LocationsBuilderARMVIXL::VisitNullConstant(HNullConstant* constant) {
3133 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003134 new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003135 locations->SetOut(Location::ConstantLocation(constant));
3136}
3137
3138void InstructionCodeGeneratorARMVIXL::VisitNullConstant(HNullConstant* constant ATTRIBUTE_UNUSED) {
3139 // Will be generated at use site.
3140}
3141
Scott Wakelingfe885462016-09-22 10:24:38 +01003142void LocationsBuilderARMVIXL::VisitLongConstant(HLongConstant* constant) {
3143 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003144 new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
Scott Wakelingfe885462016-09-22 10:24:38 +01003145 locations->SetOut(Location::ConstantLocation(constant));
3146}
3147
3148void InstructionCodeGeneratorARMVIXL::VisitLongConstant(HLongConstant* constant ATTRIBUTE_UNUSED) {
3149 // Will be generated at use site.
3150}
3151
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01003152void LocationsBuilderARMVIXL::VisitFloatConstant(HFloatConstant* constant) {
3153 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003154 new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01003155 locations->SetOut(Location::ConstantLocation(constant));
3156}
3157
Scott Wakelingc34dba72016-10-03 10:14:44 +01003158void InstructionCodeGeneratorARMVIXL::VisitFloatConstant(
3159 HFloatConstant* constant ATTRIBUTE_UNUSED) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01003160 // Will be generated at use site.
3161}
3162
3163void LocationsBuilderARMVIXL::VisitDoubleConstant(HDoubleConstant* constant) {
3164 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003165 new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01003166 locations->SetOut(Location::ConstantLocation(constant));
3167}
3168
Scott Wakelingc34dba72016-10-03 10:14:44 +01003169void InstructionCodeGeneratorARMVIXL::VisitDoubleConstant(
3170 HDoubleConstant* constant ATTRIBUTE_UNUSED) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01003171 // Will be generated at use site.
3172}
3173
Igor Murashkind01745e2017-04-05 16:40:31 -07003174void LocationsBuilderARMVIXL::VisitConstructorFence(HConstructorFence* constructor_fence) {
3175 constructor_fence->SetLocations(nullptr);
3176}
3177
3178void InstructionCodeGeneratorARMVIXL::VisitConstructorFence(
3179 HConstructorFence* constructor_fence ATTRIBUTE_UNUSED) {
3180 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
3181}
3182
Scott Wakelingfe885462016-09-22 10:24:38 +01003183void LocationsBuilderARMVIXL::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
3184 memory_barrier->SetLocations(nullptr);
3185}
3186
3187void InstructionCodeGeneratorARMVIXL::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
3188 codegen_->GenerateMemoryBarrier(memory_barrier->GetBarrierKind());
3189}
3190
3191void LocationsBuilderARMVIXL::VisitReturnVoid(HReturnVoid* ret) {
3192 ret->SetLocations(nullptr);
3193}
3194
3195void InstructionCodeGeneratorARMVIXL::VisitReturnVoid(HReturnVoid* ret ATTRIBUTE_UNUSED) {
3196 codegen_->GenerateFrameExit();
3197}
3198
3199void LocationsBuilderARMVIXL::VisitReturn(HReturn* ret) {
3200 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003201 new (GetGraph()->GetAllocator()) LocationSummary(ret, LocationSummary::kNoCall);
Scott Wakelingfe885462016-09-22 10:24:38 +01003202 locations->SetInAt(0, parameter_visitor_.GetReturnLocation(ret->InputAt(0)->GetType()));
3203}
3204
3205void InstructionCodeGeneratorARMVIXL::VisitReturn(HReturn* ret ATTRIBUTE_UNUSED) {
3206 codegen_->GenerateFrameExit();
3207}
3208
Artem Serovcfbe9132016-10-14 15:58:56 +01003209void LocationsBuilderARMVIXL::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
3210 // The trampoline uses the same calling convention as dex calling conventions,
3211 // except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain
3212 // the method_idx.
3213 HandleInvoke(invoke);
3214}
3215
3216void InstructionCodeGeneratorARMVIXL::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
3217 codegen_->GenerateInvokeUnresolvedRuntimeCall(invoke);
Andreas Gampe3db70682018-12-26 15:12:03 -08003218 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 3);
Artem Serovcfbe9132016-10-14 15:58:56 +01003219}
3220
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003221void LocationsBuilderARMVIXL::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
3222 // Explicit clinit checks triggered by static invokes must have been pruned by
3223 // art::PrepareForRegisterAllocation.
3224 DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
3225
Anton Kirilov5ec62182016-10-13 20:16:02 +01003226 IntrinsicLocationsBuilderARMVIXL intrinsic(codegen_);
3227 if (intrinsic.TryDispatch(invoke)) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01003228 return;
3229 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003230
3231 HandleInvoke(invoke);
3232}
3233
Anton Kirilov5ec62182016-10-13 20:16:02 +01003234static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorARMVIXL* codegen) {
3235 if (invoke->GetLocations()->Intrinsified()) {
3236 IntrinsicCodeGeneratorARMVIXL intrinsic(codegen);
3237 intrinsic.Dispatch(invoke);
3238 return true;
3239 }
3240 return false;
3241}
3242
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003243void InstructionCodeGeneratorARMVIXL::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
3244 // Explicit clinit checks triggered by static invokes must have been pruned by
3245 // art::PrepareForRegisterAllocation.
3246 DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
3247
Anton Kirilov5ec62182016-10-13 20:16:02 +01003248 if (TryGenerateIntrinsicCode(invoke, codegen_)) {
Andreas Gampe3db70682018-12-26 15:12:03 -08003249 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 4);
Anton Kirilov5ec62182016-10-13 20:16:02 +01003250 return;
3251 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003252
3253 LocationSummary* locations = invoke->GetLocations();
Artem Serovd4cc5b22016-11-04 11:19:09 +00003254 codegen_->GenerateStaticOrDirectCall(
3255 invoke, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation());
Roland Levillain5daa4952017-07-03 17:23:56 +01003256
Andreas Gampe3db70682018-12-26 15:12:03 -08003257 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 5);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003258}
3259
3260void LocationsBuilderARMVIXL::HandleInvoke(HInvoke* invoke) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00003261 InvokeDexCallingConventionVisitorARMVIXL calling_convention_visitor;
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003262 CodeGenerator::CreateCommonInvokeLocationSummary(invoke, &calling_convention_visitor);
3263}
3264
3265void LocationsBuilderARMVIXL::VisitInvokeVirtual(HInvokeVirtual* invoke) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01003266 IntrinsicLocationsBuilderARMVIXL intrinsic(codegen_);
3267 if (intrinsic.TryDispatch(invoke)) {
3268 return;
3269 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003270
3271 HandleInvoke(invoke);
3272}
3273
3274void InstructionCodeGeneratorARMVIXL::VisitInvokeVirtual(HInvokeVirtual* invoke) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01003275 if (TryGenerateIntrinsicCode(invoke, codegen_)) {
Andreas Gampe3db70682018-12-26 15:12:03 -08003276 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 6);
Anton Kirilov5ec62182016-10-13 20:16:02 +01003277 return;
3278 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003279
3280 codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0));
Alexandre Rames374ddf32016-11-04 10:40:49 +00003281 DCHECK(!codegen_->IsLeafMethod());
Roland Levillain5daa4952017-07-03 17:23:56 +01003282
Andreas Gampe3db70682018-12-26 15:12:03 -08003283 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 7);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003284}
3285
Artem Serovcfbe9132016-10-14 15:58:56 +01003286void LocationsBuilderARMVIXL::VisitInvokeInterface(HInvokeInterface* invoke) {
3287 HandleInvoke(invoke);
3288 // Add the hidden argument.
3289 invoke->GetLocations()->AddTemp(LocationFrom(r12));
3290}
3291
3292void InstructionCodeGeneratorARMVIXL::VisitInvokeInterface(HInvokeInterface* invoke) {
3293 // TODO: b/18116999, our IMTs can miss an IncompatibleClassChangeError.
3294 LocationSummary* locations = invoke->GetLocations();
3295 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
3296 vixl32::Register hidden_reg = RegisterFrom(locations->GetTemp(1));
3297 Location receiver = locations->InAt(0);
3298 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
3299
3300 DCHECK(!receiver.IsStackSlot());
3301
Alexandre Rames374ddf32016-11-04 10:40:49 +00003302 // Ensure the pc position is recorded immediately after the `ldr` instruction.
3303 {
Artem Serov0fb37192016-12-06 18:13:40 +00003304 ExactAssemblyScope aas(GetVIXLAssembler(),
3305 vixl32::kMaxInstructionSizeInBytes,
3306 CodeBufferCheckScope::kMaximumSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00003307 // /* HeapReference<Class> */ temp = receiver->klass_
3308 __ ldr(temp, MemOperand(RegisterFrom(receiver), class_offset));
3309 codegen_->MaybeRecordImplicitNullCheck(invoke);
3310 }
Artem Serovcfbe9132016-10-14 15:58:56 +01003311 // Instead of simply (possibly) unpoisoning `temp` here, we should
3312 // emit a read barrier for the previous class reference load.
3313 // However this is not required in practice, as this is an
3314 // intermediate/temporary reference and because the current
3315 // concurrent copying collector keeps the from-space memory
3316 // intact/accessible until the end of the marking phase (the
3317 // concurrent copying collector may not in the future).
3318 GetAssembler()->MaybeUnpoisonHeapReference(temp);
3319 GetAssembler()->LoadFromOffset(kLoadWord,
3320 temp,
3321 temp,
3322 mirror::Class::ImtPtrOffset(kArmPointerSize).Uint32Value());
3323 uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement(
3324 invoke->GetImtIndex(), kArmPointerSize));
3325 // temp = temp->GetImtEntryAt(method_offset);
3326 GetAssembler()->LoadFromOffset(kLoadWord, temp, temp, method_offset);
3327 uint32_t entry_point =
3328 ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value();
3329 // LR = temp->GetEntryPoint();
3330 GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, entry_point);
3331
3332 // Set the hidden (in r12) argument. It is done here, right before a BLX to prevent other
3333 // instruction from clobbering it as they might use r12 as a scratch register.
3334 DCHECK(hidden_reg.Is(r12));
Scott Wakelingb77051e2016-11-21 19:46:00 +00003335
3336 {
3337 // The VIXL macro assembler may clobber any of the scratch registers that are available to it,
3338 // so it checks if the application is using them (by passing them to the macro assembler
3339 // methods). The following application of UseScratchRegisterScope corrects VIXL's notion of
3340 // what is available, and is the opposite of the standard usage: Instead of requesting a
3341 // temporary location, it imposes an external constraint (i.e. a specific register is reserved
3342 // for the hidden argument). Note that this works even if VIXL needs a scratch register itself
3343 // (to materialize the constant), since the destination register becomes available for such use
3344 // internally for the duration of the macro instruction.
3345 UseScratchRegisterScope temps(GetVIXLAssembler());
3346 temps.Exclude(hidden_reg);
3347 __ Mov(hidden_reg, invoke->GetDexMethodIndex());
3348 }
Artem Serovcfbe9132016-10-14 15:58:56 +01003349 {
Alexandre Rames374ddf32016-11-04 10:40:49 +00003350 // Ensure the pc position is recorded immediately after the `blx` instruction.
3351 // 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 +00003352 ExactAssemblyScope aas(GetVIXLAssembler(),
Alexandre Rames374ddf32016-11-04 10:40:49 +00003353 vixl32::k16BitT32InstructionSizeInBytes,
3354 CodeBufferCheckScope::kExactSize);
Artem Serovcfbe9132016-10-14 15:58:56 +01003355 // LR();
3356 __ blx(lr);
Artem Serovcfbe9132016-10-14 15:58:56 +01003357 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
Alexandre Rames374ddf32016-11-04 10:40:49 +00003358 DCHECK(!codegen_->IsLeafMethod());
Artem Serovcfbe9132016-10-14 15:58:56 +01003359 }
Roland Levillain5daa4952017-07-03 17:23:56 +01003360
Andreas Gampe3db70682018-12-26 15:12:03 -08003361 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 8);
Artem Serovcfbe9132016-10-14 15:58:56 +01003362}
3363
Orion Hodsonac141392017-01-13 11:53:47 +00003364void LocationsBuilderARMVIXL::VisitInvokePolymorphic(HInvokePolymorphic* invoke) {
3365 HandleInvoke(invoke);
3366}
3367
3368void InstructionCodeGeneratorARMVIXL::VisitInvokePolymorphic(HInvokePolymorphic* invoke) {
3369 codegen_->GenerateInvokePolymorphicCall(invoke);
Andreas Gampe3db70682018-12-26 15:12:03 -08003370 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 9);
Orion Hodsonac141392017-01-13 11:53:47 +00003371}
3372
Orion Hodson4c8e12e2018-05-18 08:33:20 +01003373void LocationsBuilderARMVIXL::VisitInvokeCustom(HInvokeCustom* invoke) {
3374 HandleInvoke(invoke);
3375}
3376
3377void InstructionCodeGeneratorARMVIXL::VisitInvokeCustom(HInvokeCustom* invoke) {
3378 codegen_->GenerateInvokeCustomCall(invoke);
Andreas Gampe3db70682018-12-26 15:12:03 -08003379 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 10);
Orion Hodson4c8e12e2018-05-18 08:33:20 +01003380}
3381
Artem Serov02109dd2016-09-23 17:17:54 +01003382void LocationsBuilderARMVIXL::VisitNeg(HNeg* neg) {
3383 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003384 new (GetGraph()->GetAllocator()) LocationSummary(neg, LocationSummary::kNoCall);
Artem Serov02109dd2016-09-23 17:17:54 +01003385 switch (neg->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003386 case DataType::Type::kInt32: {
Artem Serov02109dd2016-09-23 17:17:54 +01003387 locations->SetInAt(0, Location::RequiresRegister());
3388 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3389 break;
3390 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003391 case DataType::Type::kInt64: {
Artem Serov02109dd2016-09-23 17:17:54 +01003392 locations->SetInAt(0, Location::RequiresRegister());
3393 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
3394 break;
3395 }
3396
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003397 case DataType::Type::kFloat32:
3398 case DataType::Type::kFloat64:
Artem Serov02109dd2016-09-23 17:17:54 +01003399 locations->SetInAt(0, Location::RequiresFpuRegister());
3400 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3401 break;
3402
3403 default:
3404 LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
3405 }
3406}
3407
3408void InstructionCodeGeneratorARMVIXL::VisitNeg(HNeg* neg) {
3409 LocationSummary* locations = neg->GetLocations();
3410 Location out = locations->Out();
3411 Location in = locations->InAt(0);
3412 switch (neg->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003413 case DataType::Type::kInt32:
Artem Serov02109dd2016-09-23 17:17:54 +01003414 __ Rsb(OutputRegister(neg), InputRegisterAt(neg, 0), 0);
3415 break;
3416
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003417 case DataType::Type::kInt64:
Artem Serov02109dd2016-09-23 17:17:54 +01003418 // out.lo = 0 - in.lo (and update the carry/borrow (C) flag)
3419 __ Rsbs(LowRegisterFrom(out), LowRegisterFrom(in), 0);
3420 // We cannot emit an RSC (Reverse Subtract with Carry)
3421 // instruction here, as it does not exist in the Thumb-2
3422 // instruction set. We use the following approach
3423 // using SBC and SUB instead.
3424 //
3425 // out.hi = -C
3426 __ Sbc(HighRegisterFrom(out), HighRegisterFrom(out), HighRegisterFrom(out));
3427 // out.hi = out.hi - in.hi
3428 __ Sub(HighRegisterFrom(out), HighRegisterFrom(out), HighRegisterFrom(in));
3429 break;
3430
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003431 case DataType::Type::kFloat32:
3432 case DataType::Type::kFloat64:
Anton Kirilov644032c2016-12-06 17:51:43 +00003433 __ Vneg(OutputVRegister(neg), InputVRegister(neg));
Artem Serov02109dd2016-09-23 17:17:54 +01003434 break;
3435
3436 default:
3437 LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
3438 }
3439}
3440
Scott Wakelingfe885462016-09-22 10:24:38 +01003441void LocationsBuilderARMVIXL::VisitTypeConversion(HTypeConversion* conversion) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003442 DataType::Type result_type = conversion->GetResultType();
3443 DataType::Type input_type = conversion->GetInputType();
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003444 DCHECK(!DataType::IsTypeConversionImplicit(input_type, result_type))
3445 << input_type << " -> " << result_type;
Scott Wakelingfe885462016-09-22 10:24:38 +01003446
3447 // The float-to-long, double-to-long and long-to-float type conversions
3448 // rely on a call to the runtime.
3449 LocationSummary::CallKind call_kind =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003450 (((input_type == DataType::Type::kFloat32 || input_type == DataType::Type::kFloat64)
3451 && result_type == DataType::Type::kInt64)
3452 || (input_type == DataType::Type::kInt64 && result_type == DataType::Type::kFloat32))
Scott Wakelingfe885462016-09-22 10:24:38 +01003453 ? LocationSummary::kCallOnMainOnly
3454 : LocationSummary::kNoCall;
3455 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003456 new (GetGraph()->GetAllocator()) LocationSummary(conversion, call_kind);
Scott Wakelingfe885462016-09-22 10:24:38 +01003457
Scott Wakelingfe885462016-09-22 10:24:38 +01003458 switch (result_type) {
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003459 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003460 case DataType::Type::kInt8:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003461 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003462 case DataType::Type::kInt16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003463 DCHECK(DataType::IsIntegralType(input_type)) << input_type;
3464 locations->SetInAt(0, Location::RequiresRegister());
3465 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Scott Wakelingfe885462016-09-22 10:24:38 +01003466 break;
3467
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003468 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003469 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003470 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003471 locations->SetInAt(0, Location::Any());
3472 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3473 break;
3474
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003475 case DataType::Type::kFloat32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003476 locations->SetInAt(0, Location::RequiresFpuRegister());
3477 locations->SetOut(Location::RequiresRegister());
3478 locations->AddTemp(Location::RequiresFpuRegister());
3479 break;
3480
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003481 case DataType::Type::kFloat64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003482 locations->SetInAt(0, Location::RequiresFpuRegister());
3483 locations->SetOut(Location::RequiresRegister());
3484 locations->AddTemp(Location::RequiresFpuRegister());
3485 break;
3486
3487 default:
3488 LOG(FATAL) << "Unexpected type conversion from " << input_type
3489 << " to " << result_type;
3490 }
3491 break;
3492
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003493 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003494 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003495 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003496 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003497 case DataType::Type::kInt8:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003498 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003499 case DataType::Type::kInt16:
3500 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003501 locations->SetInAt(0, Location::RequiresRegister());
3502 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3503 break;
3504
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003505 case DataType::Type::kFloat32: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003506 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3507 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
3508 locations->SetOut(LocationFrom(r0, r1));
Scott Wakelingfe885462016-09-22 10:24:38 +01003509 break;
3510 }
3511
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003512 case DataType::Type::kFloat64: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003513 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3514 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0),
3515 calling_convention.GetFpuRegisterAt(1)));
3516 locations->SetOut(LocationFrom(r0, r1));
Scott Wakelingfe885462016-09-22 10:24:38 +01003517 break;
3518 }
3519
3520 default:
3521 LOG(FATAL) << "Unexpected type conversion from " << input_type
3522 << " to " << result_type;
3523 }
3524 break;
3525
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003526 case DataType::Type::kFloat32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003527 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003528 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003529 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003530 case DataType::Type::kInt8:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003531 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003532 case DataType::Type::kInt16:
3533 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003534 locations->SetInAt(0, Location::RequiresRegister());
3535 locations->SetOut(Location::RequiresFpuRegister());
3536 break;
3537
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003538 case DataType::Type::kInt64: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003539 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3540 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0),
3541 calling_convention.GetRegisterAt(1)));
3542 locations->SetOut(LocationFrom(calling_convention.GetFpuRegisterAt(0)));
Scott Wakelingfe885462016-09-22 10:24:38 +01003543 break;
3544 }
3545
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003546 case DataType::Type::kFloat64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003547 locations->SetInAt(0, Location::RequiresFpuRegister());
3548 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3549 break;
3550
3551 default:
3552 LOG(FATAL) << "Unexpected type conversion from " << input_type
3553 << " to " << result_type;
Igor Murashkin2ffb7032017-11-08 13:35:21 -08003554 }
Scott Wakelingfe885462016-09-22 10:24:38 +01003555 break;
3556
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003557 case DataType::Type::kFloat64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003558 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003559 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003560 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003561 case DataType::Type::kInt8:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003562 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003563 case DataType::Type::kInt16:
3564 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003565 locations->SetInAt(0, Location::RequiresRegister());
3566 locations->SetOut(Location::RequiresFpuRegister());
3567 break;
3568
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003569 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003570 locations->SetInAt(0, Location::RequiresRegister());
3571 locations->SetOut(Location::RequiresFpuRegister());
3572 locations->AddTemp(Location::RequiresFpuRegister());
3573 locations->AddTemp(Location::RequiresFpuRegister());
3574 break;
3575
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003576 case DataType::Type::kFloat32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003577 locations->SetInAt(0, Location::RequiresFpuRegister());
3578 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3579 break;
3580
3581 default:
3582 LOG(FATAL) << "Unexpected type conversion from " << input_type
3583 << " to " << result_type;
Igor Murashkin2ffb7032017-11-08 13:35:21 -08003584 }
Scott Wakelingfe885462016-09-22 10:24:38 +01003585 break;
3586
3587 default:
3588 LOG(FATAL) << "Unexpected type conversion from " << input_type
3589 << " to " << result_type;
3590 }
3591}
3592
3593void InstructionCodeGeneratorARMVIXL::VisitTypeConversion(HTypeConversion* conversion) {
3594 LocationSummary* locations = conversion->GetLocations();
3595 Location out = locations->Out();
3596 Location in = locations->InAt(0);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003597 DataType::Type result_type = conversion->GetResultType();
3598 DataType::Type input_type = conversion->GetInputType();
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003599 DCHECK(!DataType::IsTypeConversionImplicit(input_type, result_type))
3600 << input_type << " -> " << result_type;
Scott Wakelingfe885462016-09-22 10:24:38 +01003601 switch (result_type) {
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003602 case DataType::Type::kUint8:
Scott Wakelingfe885462016-09-22 10:24:38 +01003603 switch (input_type) {
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003604 case DataType::Type::kInt8:
3605 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003606 case DataType::Type::kInt16:
3607 case DataType::Type::kInt32:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003608 __ Ubfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 8);
3609 break;
3610 case DataType::Type::kInt64:
3611 __ Ubfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 8);
3612 break;
3613
3614 default:
3615 LOG(FATAL) << "Unexpected type conversion from " << input_type
3616 << " to " << result_type;
3617 }
3618 break;
3619
3620 case DataType::Type::kInt8:
3621 switch (input_type) {
3622 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003623 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003624 case DataType::Type::kInt16:
3625 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003626 __ Sbfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 8);
3627 break;
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003628 case DataType::Type::kInt64:
3629 __ Sbfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 8);
3630 break;
3631
3632 default:
3633 LOG(FATAL) << "Unexpected type conversion from " << input_type
3634 << " to " << result_type;
3635 }
3636 break;
3637
3638 case DataType::Type::kUint16:
3639 switch (input_type) {
3640 case DataType::Type::kInt8:
3641 case DataType::Type::kInt16:
3642 case DataType::Type::kInt32:
3643 __ Ubfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 16);
3644 break;
3645 case DataType::Type::kInt64:
3646 __ Ubfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 16);
3647 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01003648
3649 default:
3650 LOG(FATAL) << "Unexpected type conversion from " << input_type
3651 << " to " << result_type;
3652 }
3653 break;
3654
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003655 case DataType::Type::kInt16:
Scott Wakelingfe885462016-09-22 10:24:38 +01003656 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003657 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003658 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003659 __ Sbfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 16);
3660 break;
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003661 case DataType::Type::kInt64:
3662 __ Sbfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 16);
3663 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01003664
3665 default:
3666 LOG(FATAL) << "Unexpected type conversion from " << input_type
3667 << " to " << result_type;
3668 }
3669 break;
3670
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003671 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003672 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003673 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003674 DCHECK(out.IsRegister());
3675 if (in.IsRegisterPair()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003676 __ Mov(OutputRegister(conversion), LowRegisterFrom(in));
Scott Wakelingfe885462016-09-22 10:24:38 +01003677 } else if (in.IsDoubleStackSlot()) {
3678 GetAssembler()->LoadFromOffset(kLoadWord,
3679 OutputRegister(conversion),
3680 sp,
3681 in.GetStackIndex());
3682 } else {
3683 DCHECK(in.IsConstant());
3684 DCHECK(in.GetConstant()->IsLongConstant());
Vladimir Markoba1a48e2017-04-13 11:50:14 +01003685 int64_t value = in.GetConstant()->AsLongConstant()->GetValue();
3686 __ Mov(OutputRegister(conversion), static_cast<int32_t>(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01003687 }
3688 break;
3689
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003690 case DataType::Type::kFloat32: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003691 vixl32::SRegister temp = LowSRegisterFrom(locations->GetTemp(0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01003692 __ Vcvt(S32, F32, temp, InputSRegisterAt(conversion, 0));
Scott Wakelingfe885462016-09-22 10:24:38 +01003693 __ Vmov(OutputRegister(conversion), temp);
3694 break;
3695 }
3696
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003697 case DataType::Type::kFloat64: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003698 vixl32::SRegister temp_s = LowSRegisterFrom(locations->GetTemp(0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01003699 __ Vcvt(S32, F64, temp_s, DRegisterFrom(in));
Scott Wakelingfe885462016-09-22 10:24:38 +01003700 __ Vmov(OutputRegister(conversion), temp_s);
3701 break;
3702 }
3703
3704 default:
3705 LOG(FATAL) << "Unexpected type conversion from " << input_type
3706 << " to " << result_type;
3707 }
3708 break;
3709
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003710 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003711 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003712 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003713 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003714 case DataType::Type::kInt8:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003715 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003716 case DataType::Type::kInt16:
3717 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003718 DCHECK(out.IsRegisterPair());
3719 DCHECK(in.IsRegister());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003720 __ Mov(LowRegisterFrom(out), InputRegisterAt(conversion, 0));
Scott Wakelingfe885462016-09-22 10:24:38 +01003721 // Sign extension.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003722 __ Asr(HighRegisterFrom(out), LowRegisterFrom(out), 31);
Scott Wakelingfe885462016-09-22 10:24:38 +01003723 break;
3724
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003725 case DataType::Type::kFloat32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003726 codegen_->InvokeRuntime(kQuickF2l, conversion, conversion->GetDexPc());
3727 CheckEntrypointTypes<kQuickF2l, int64_t, float>();
3728 break;
3729
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003730 case DataType::Type::kFloat64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003731 codegen_->InvokeRuntime(kQuickD2l, conversion, conversion->GetDexPc());
3732 CheckEntrypointTypes<kQuickD2l, int64_t, double>();
3733 break;
3734
3735 default:
3736 LOG(FATAL) << "Unexpected type conversion from " << input_type
3737 << " to " << result_type;
3738 }
3739 break;
3740
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003741 case DataType::Type::kFloat32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003742 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003743 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003744 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003745 case DataType::Type::kInt8:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003746 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003747 case DataType::Type::kInt16:
3748 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003749 __ Vmov(OutputSRegister(conversion), InputRegisterAt(conversion, 0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01003750 __ Vcvt(F32, S32, OutputSRegister(conversion), OutputSRegister(conversion));
Scott Wakelingfe885462016-09-22 10:24:38 +01003751 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01003752
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003753 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003754 codegen_->InvokeRuntime(kQuickL2f, conversion, conversion->GetDexPc());
3755 CheckEntrypointTypes<kQuickL2f, float, int64_t>();
3756 break;
3757
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003758 case DataType::Type::kFloat64:
Scott Wakelingc34dba72016-10-03 10:14:44 +01003759 __ Vcvt(F32, F64, OutputSRegister(conversion), DRegisterFrom(in));
Scott Wakelingfe885462016-09-22 10:24:38 +01003760 break;
3761
3762 default:
3763 LOG(FATAL) << "Unexpected type conversion from " << input_type
3764 << " to " << result_type;
Igor Murashkin2ffb7032017-11-08 13:35:21 -08003765 }
Scott Wakelingfe885462016-09-22 10:24:38 +01003766 break;
3767
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003768 case DataType::Type::kFloat64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003769 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003770 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003771 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003772 case DataType::Type::kInt8:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003773 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003774 case DataType::Type::kInt16:
3775 case DataType::Type::kInt32:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003776 __ Vmov(LowSRegisterFrom(out), InputRegisterAt(conversion, 0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01003777 __ Vcvt(F64, S32, DRegisterFrom(out), LowSRegisterFrom(out));
Scott Wakelingfe885462016-09-22 10:24:38 +01003778 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01003779
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003780 case DataType::Type::kInt64: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003781 vixl32::Register low = LowRegisterFrom(in);
3782 vixl32::Register high = HighRegisterFrom(in);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003783 vixl32::SRegister out_s = LowSRegisterFrom(out);
Scott Wakelingc34dba72016-10-03 10:14:44 +01003784 vixl32::DRegister out_d = DRegisterFrom(out);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003785 vixl32::SRegister temp_s = LowSRegisterFrom(locations->GetTemp(0));
Scott Wakelingc34dba72016-10-03 10:14:44 +01003786 vixl32::DRegister temp_d = DRegisterFrom(locations->GetTemp(0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01003787 vixl32::DRegister constant_d = DRegisterFrom(locations->GetTemp(1));
Scott Wakelingfe885462016-09-22 10:24:38 +01003788
3789 // temp_d = int-to-double(high)
3790 __ Vmov(temp_s, high);
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01003791 __ Vcvt(F64, S32, temp_d, temp_s);
Scott Wakelingfe885462016-09-22 10:24:38 +01003792 // constant_d = k2Pow32EncodingForDouble
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003793 __ Vmov(constant_d, bit_cast<double, int64_t>(k2Pow32EncodingForDouble));
Scott Wakelingfe885462016-09-22 10:24:38 +01003794 // out_d = unsigned-to-double(low)
3795 __ Vmov(out_s, low);
3796 __ Vcvt(F64, U32, out_d, out_s);
3797 // out_d += temp_d * constant_d
3798 __ Vmla(F64, out_d, temp_d, constant_d);
3799 break;
3800 }
3801
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003802 case DataType::Type::kFloat32:
Scott Wakelingc34dba72016-10-03 10:14:44 +01003803 __ Vcvt(F64, F32, DRegisterFrom(out), InputSRegisterAt(conversion, 0));
Scott Wakelingfe885462016-09-22 10:24:38 +01003804 break;
3805
3806 default:
3807 LOG(FATAL) << "Unexpected type conversion from " << input_type
3808 << " to " << result_type;
Igor Murashkin2ffb7032017-11-08 13:35:21 -08003809 }
Scott Wakelingfe885462016-09-22 10:24:38 +01003810 break;
3811
3812 default:
3813 LOG(FATAL) << "Unexpected type conversion from " << input_type
3814 << " to " << result_type;
3815 }
3816}
3817
3818void LocationsBuilderARMVIXL::VisitAdd(HAdd* add) {
3819 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003820 new (GetGraph()->GetAllocator()) LocationSummary(add, LocationSummary::kNoCall);
Scott Wakelingfe885462016-09-22 10:24:38 +01003821 switch (add->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003822 case DataType::Type::kInt32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01003823 locations->SetInAt(0, Location::RequiresRegister());
3824 locations->SetInAt(1, Location::RegisterOrConstant(add->InputAt(1)));
3825 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3826 break;
3827 }
3828
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003829 case DataType::Type::kInt64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01003830 locations->SetInAt(0, Location::RequiresRegister());
Anton Kirilovdda43962016-11-21 19:55:20 +00003831 locations->SetInAt(1, ArmEncodableConstantOrRegister(add->InputAt(1), ADD));
Scott Wakelingfe885462016-09-22 10:24:38 +01003832 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3833 break;
3834 }
3835
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003836 case DataType::Type::kFloat32:
3837 case DataType::Type::kFloat64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01003838 locations->SetInAt(0, Location::RequiresFpuRegister());
3839 locations->SetInAt(1, Location::RequiresFpuRegister());
3840 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3841 break;
3842 }
3843
3844 default:
3845 LOG(FATAL) << "Unexpected add type " << add->GetResultType();
3846 }
3847}
3848
3849void InstructionCodeGeneratorARMVIXL::VisitAdd(HAdd* add) {
3850 LocationSummary* locations = add->GetLocations();
3851 Location out = locations->Out();
3852 Location first = locations->InAt(0);
3853 Location second = locations->InAt(1);
3854
3855 switch (add->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003856 case DataType::Type::kInt32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01003857 __ Add(OutputRegister(add), InputRegisterAt(add, 0), InputOperandAt(add, 1));
3858 }
3859 break;
3860
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003861 case DataType::Type::kInt64: {
Anton Kirilovdda43962016-11-21 19:55:20 +00003862 if (second.IsConstant()) {
3863 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
3864 GenerateAddLongConst(out, first, value);
3865 } else {
3866 DCHECK(second.IsRegisterPair());
3867 __ Adds(LowRegisterFrom(out), LowRegisterFrom(first), LowRegisterFrom(second));
3868 __ Adc(HighRegisterFrom(out), HighRegisterFrom(first), HighRegisterFrom(second));
3869 }
Scott Wakelingfe885462016-09-22 10:24:38 +01003870 break;
3871 }
3872
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003873 case DataType::Type::kFloat32:
3874 case DataType::Type::kFloat64:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003875 __ Vadd(OutputVRegister(add), InputVRegisterAt(add, 0), InputVRegisterAt(add, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01003876 break;
3877
3878 default:
3879 LOG(FATAL) << "Unexpected add type " << add->GetResultType();
3880 }
3881}
3882
3883void LocationsBuilderARMVIXL::VisitSub(HSub* sub) {
3884 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003885 new (GetGraph()->GetAllocator()) LocationSummary(sub, LocationSummary::kNoCall);
Scott Wakelingfe885462016-09-22 10:24:38 +01003886 switch (sub->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003887 case DataType::Type::kInt32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01003888 locations->SetInAt(0, Location::RequiresRegister());
3889 locations->SetInAt(1, Location::RegisterOrConstant(sub->InputAt(1)));
3890 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3891 break;
3892 }
3893
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003894 case DataType::Type::kInt64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01003895 locations->SetInAt(0, Location::RequiresRegister());
Anton Kirilovdda43962016-11-21 19:55:20 +00003896 locations->SetInAt(1, ArmEncodableConstantOrRegister(sub->InputAt(1), SUB));
Scott Wakelingfe885462016-09-22 10:24:38 +01003897 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3898 break;
3899 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003900 case DataType::Type::kFloat32:
3901 case DataType::Type::kFloat64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01003902 locations->SetInAt(0, Location::RequiresFpuRegister());
3903 locations->SetInAt(1, Location::RequiresFpuRegister());
3904 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3905 break;
3906 }
3907 default:
3908 LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
3909 }
3910}
3911
3912void InstructionCodeGeneratorARMVIXL::VisitSub(HSub* sub) {
3913 LocationSummary* locations = sub->GetLocations();
3914 Location out = locations->Out();
3915 Location first = locations->InAt(0);
3916 Location second = locations->InAt(1);
3917 switch (sub->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003918 case DataType::Type::kInt32: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003919 __ Sub(OutputRegister(sub), InputRegisterAt(sub, 0), InputOperandAt(sub, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01003920 break;
3921 }
3922
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003923 case DataType::Type::kInt64: {
Anton Kirilovdda43962016-11-21 19:55:20 +00003924 if (second.IsConstant()) {
3925 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
3926 GenerateAddLongConst(out, first, -value);
3927 } else {
3928 DCHECK(second.IsRegisterPair());
3929 __ Subs(LowRegisterFrom(out), LowRegisterFrom(first), LowRegisterFrom(second));
3930 __ Sbc(HighRegisterFrom(out), HighRegisterFrom(first), HighRegisterFrom(second));
3931 }
Scott Wakelingfe885462016-09-22 10:24:38 +01003932 break;
3933 }
3934
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003935 case DataType::Type::kFloat32:
3936 case DataType::Type::kFloat64:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003937 __ Vsub(OutputVRegister(sub), InputVRegisterAt(sub, 0), InputVRegisterAt(sub, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01003938 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01003939
3940 default:
3941 LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
3942 }
3943}
3944
3945void LocationsBuilderARMVIXL::VisitMul(HMul* mul) {
3946 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003947 new (GetGraph()->GetAllocator()) LocationSummary(mul, LocationSummary::kNoCall);
Scott Wakelingfe885462016-09-22 10:24:38 +01003948 switch (mul->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003949 case DataType::Type::kInt32:
3950 case DataType::Type::kInt64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01003951 locations->SetInAt(0, Location::RequiresRegister());
3952 locations->SetInAt(1, Location::RequiresRegister());
3953 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3954 break;
3955 }
3956
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003957 case DataType::Type::kFloat32:
3958 case DataType::Type::kFloat64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01003959 locations->SetInAt(0, Location::RequiresFpuRegister());
3960 locations->SetInAt(1, Location::RequiresFpuRegister());
3961 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3962 break;
3963 }
3964
3965 default:
3966 LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
3967 }
3968}
3969
3970void InstructionCodeGeneratorARMVIXL::VisitMul(HMul* mul) {
3971 LocationSummary* locations = mul->GetLocations();
3972 Location out = locations->Out();
3973 Location first = locations->InAt(0);
3974 Location second = locations->InAt(1);
3975 switch (mul->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003976 case DataType::Type::kInt32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01003977 __ Mul(OutputRegister(mul), InputRegisterAt(mul, 0), InputRegisterAt(mul, 1));
3978 break;
3979 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003980 case DataType::Type::kInt64: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003981 vixl32::Register out_hi = HighRegisterFrom(out);
3982 vixl32::Register out_lo = LowRegisterFrom(out);
3983 vixl32::Register in1_hi = HighRegisterFrom(first);
3984 vixl32::Register in1_lo = LowRegisterFrom(first);
3985 vixl32::Register in2_hi = HighRegisterFrom(second);
3986 vixl32::Register in2_lo = LowRegisterFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01003987
3988 // Extra checks to protect caused by the existence of R1_R2.
3989 // The algorithm is wrong if out.hi is either in1.lo or in2.lo:
3990 // (e.g. in1=r0_r1, in2=r2_r3 and out=r1_r2);
Anton Kirilov644032c2016-12-06 17:51:43 +00003991 DCHECK(!out_hi.Is(in1_lo));
3992 DCHECK(!out_hi.Is(in2_lo));
Scott Wakelingfe885462016-09-22 10:24:38 +01003993
3994 // input: in1 - 64 bits, in2 - 64 bits
3995 // output: out
3996 // formula: out.hi : out.lo = (in1.lo * in2.hi + in1.hi * in2.lo)* 2^32 + in1.lo * in2.lo
3997 // parts: out.hi = in1.lo * in2.hi + in1.hi * in2.lo + (in1.lo * in2.lo)[63:32]
3998 // parts: out.lo = (in1.lo * in2.lo)[31:0]
3999
4000 UseScratchRegisterScope temps(GetVIXLAssembler());
4001 vixl32::Register temp = temps.Acquire();
4002 // temp <- in1.lo * in2.hi
4003 __ Mul(temp, in1_lo, in2_hi);
4004 // out.hi <- in1.lo * in2.hi + in1.hi * in2.lo
4005 __ Mla(out_hi, in1_hi, in2_lo, temp);
4006 // out.lo <- (in1.lo * in2.lo)[31:0];
4007 __ Umull(out_lo, temp, in1_lo, in2_lo);
4008 // out.hi <- in2.hi * in1.lo + in2.lo * in1.hi + (in1.lo * in2.lo)[63:32]
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004009 __ Add(out_hi, out_hi, temp);
Scott Wakelingfe885462016-09-22 10:24:38 +01004010 break;
4011 }
4012
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004013 case DataType::Type::kFloat32:
4014 case DataType::Type::kFloat64:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004015 __ Vmul(OutputVRegister(mul), InputVRegisterAt(mul, 0), InputVRegisterAt(mul, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004016 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01004017
4018 default:
4019 LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
4020 }
4021}
4022
Scott Wakelingfe885462016-09-22 10:24:38 +01004023void InstructionCodeGeneratorARMVIXL::DivRemOneOrMinusOne(HBinaryOperation* instruction) {
4024 DCHECK(instruction->IsDiv() || instruction->IsRem());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004025 DCHECK(instruction->GetResultType() == DataType::Type::kInt32);
Scott Wakelingfe885462016-09-22 10:24:38 +01004026
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004027 Location second = instruction->GetLocations()->InAt(1);
Scott Wakelingfe885462016-09-22 10:24:38 +01004028 DCHECK(second.IsConstant());
4029
4030 vixl32::Register out = OutputRegister(instruction);
4031 vixl32::Register dividend = InputRegisterAt(instruction, 0);
Anton Kirilov644032c2016-12-06 17:51:43 +00004032 int32_t imm = Int32ConstantFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01004033 DCHECK(imm == 1 || imm == -1);
4034
4035 if (instruction->IsRem()) {
4036 __ Mov(out, 0);
4037 } else {
4038 if (imm == 1) {
4039 __ Mov(out, dividend);
4040 } else {
4041 __ Rsb(out, dividend, 0);
4042 }
4043 }
4044}
4045
4046void InstructionCodeGeneratorARMVIXL::DivRemByPowerOfTwo(HBinaryOperation* instruction) {
4047 DCHECK(instruction->IsDiv() || instruction->IsRem());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004048 DCHECK(instruction->GetResultType() == DataType::Type::kInt32);
Scott Wakelingfe885462016-09-22 10:24:38 +01004049
4050 LocationSummary* locations = instruction->GetLocations();
4051 Location second = locations->InAt(1);
4052 DCHECK(second.IsConstant());
4053
4054 vixl32::Register out = OutputRegister(instruction);
4055 vixl32::Register dividend = InputRegisterAt(instruction, 0);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004056 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
Anton Kirilov644032c2016-12-06 17:51:43 +00004057 int32_t imm = Int32ConstantFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01004058 uint32_t abs_imm = static_cast<uint32_t>(AbsOrMin(imm));
4059 int ctz_imm = CTZ(abs_imm);
4060
4061 if (ctz_imm == 1) {
4062 __ Lsr(temp, dividend, 32 - ctz_imm);
4063 } else {
4064 __ Asr(temp, dividend, 31);
4065 __ Lsr(temp, temp, 32 - ctz_imm);
4066 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004067 __ Add(out, temp, dividend);
Scott Wakelingfe885462016-09-22 10:24:38 +01004068
4069 if (instruction->IsDiv()) {
4070 __ Asr(out, out, ctz_imm);
4071 if (imm < 0) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004072 __ Rsb(out, out, 0);
Scott Wakelingfe885462016-09-22 10:24:38 +01004073 }
4074 } else {
4075 __ Ubfx(out, out, 0, ctz_imm);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004076 __ Sub(out, out, temp);
Scott Wakelingfe885462016-09-22 10:24:38 +01004077 }
4078}
4079
4080void InstructionCodeGeneratorARMVIXL::GenerateDivRemWithAnyConstant(HBinaryOperation* instruction) {
4081 DCHECK(instruction->IsDiv() || instruction->IsRem());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004082 DCHECK(instruction->GetResultType() == DataType::Type::kInt32);
Scott Wakelingfe885462016-09-22 10:24:38 +01004083
4084 LocationSummary* locations = instruction->GetLocations();
4085 Location second = locations->InAt(1);
4086 DCHECK(second.IsConstant());
4087
4088 vixl32::Register out = OutputRegister(instruction);
4089 vixl32::Register dividend = InputRegisterAt(instruction, 0);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004090 vixl32::Register temp1 = RegisterFrom(locations->GetTemp(0));
4091 vixl32::Register temp2 = RegisterFrom(locations->GetTemp(1));
Scott Wakelingb77051e2016-11-21 19:46:00 +00004092 int32_t imm = Int32ConstantFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01004093
4094 int64_t magic;
4095 int shift;
Andreas Gampe3db70682018-12-26 15:12:03 -08004096 CalculateMagicAndShiftForDivRem(imm, /* is_long= */ false, &magic, &shift);
Scott Wakelingfe885462016-09-22 10:24:38 +01004097
Anton Kirilovdda43962016-11-21 19:55:20 +00004098 // TODO(VIXL): Change the static cast to Operand::From() after VIXL is fixed.
4099 __ Mov(temp1, static_cast<int32_t>(magic));
Scott Wakelingfe885462016-09-22 10:24:38 +01004100 __ Smull(temp2, temp1, dividend, temp1);
4101
4102 if (imm > 0 && magic < 0) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004103 __ Add(temp1, temp1, dividend);
Scott Wakelingfe885462016-09-22 10:24:38 +01004104 } else if (imm < 0 && magic > 0) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004105 __ Sub(temp1, temp1, dividend);
Scott Wakelingfe885462016-09-22 10:24:38 +01004106 }
4107
4108 if (shift != 0) {
4109 __ Asr(temp1, temp1, shift);
4110 }
4111
4112 if (instruction->IsDiv()) {
4113 __ Sub(out, temp1, Operand(temp1, vixl32::Shift(ASR), 31));
4114 } else {
4115 __ Sub(temp1, temp1, Operand(temp1, vixl32::Shift(ASR), 31));
4116 // TODO: Strength reduction for mls.
4117 __ Mov(temp2, imm);
4118 __ Mls(out, temp1, temp2, dividend);
4119 }
4120}
4121
4122void InstructionCodeGeneratorARMVIXL::GenerateDivRemConstantIntegral(
4123 HBinaryOperation* instruction) {
4124 DCHECK(instruction->IsDiv() || instruction->IsRem());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004125 DCHECK(instruction->GetResultType() == DataType::Type::kInt32);
Scott Wakelingfe885462016-09-22 10:24:38 +01004126
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004127 Location second = instruction->GetLocations()->InAt(1);
Scott Wakelingfe885462016-09-22 10:24:38 +01004128 DCHECK(second.IsConstant());
4129
Anton Kirilov644032c2016-12-06 17:51:43 +00004130 int32_t imm = Int32ConstantFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01004131 if (imm == 0) {
4132 // Do not generate anything. DivZeroCheck would prevent any code to be executed.
4133 } else if (imm == 1 || imm == -1) {
4134 DivRemOneOrMinusOne(instruction);
4135 } else if (IsPowerOfTwo(AbsOrMin(imm))) {
4136 DivRemByPowerOfTwo(instruction);
4137 } else {
4138 DCHECK(imm <= -2 || imm >= 2);
4139 GenerateDivRemWithAnyConstant(instruction);
4140 }
4141}
4142
4143void LocationsBuilderARMVIXL::VisitDiv(HDiv* div) {
4144 LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004145 if (div->GetResultType() == DataType::Type::kInt64) {
Scott Wakelingfe885462016-09-22 10:24:38 +01004146 // pLdiv runtime call.
4147 call_kind = LocationSummary::kCallOnMainOnly;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004148 } else if (div->GetResultType() == DataType::Type::kInt32 && div->InputAt(1)->IsConstant()) {
Scott Wakelingfe885462016-09-22 10:24:38 +01004149 // sdiv will be replaced by other instruction sequence.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004150 } else if (div->GetResultType() == DataType::Type::kInt32 &&
Scott Wakelingfe885462016-09-22 10:24:38 +01004151 !codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4152 // pIdivmod runtime call.
4153 call_kind = LocationSummary::kCallOnMainOnly;
4154 }
4155
Vladimir Markoca6fff82017-10-03 14:49:14 +01004156 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(div, call_kind);
Scott Wakelingfe885462016-09-22 10:24:38 +01004157
4158 switch (div->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004159 case DataType::Type::kInt32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004160 if (div->InputAt(1)->IsConstant()) {
4161 locations->SetInAt(0, Location::RequiresRegister());
4162 locations->SetInAt(1, Location::ConstantLocation(div->InputAt(1)->AsConstant()));
4163 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Anton Kirilov644032c2016-12-06 17:51:43 +00004164 int32_t value = Int32ConstantFrom(div->InputAt(1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004165 if (value == 1 || value == 0 || value == -1) {
4166 // No temp register required.
4167 } else {
4168 locations->AddTemp(Location::RequiresRegister());
4169 if (!IsPowerOfTwo(AbsOrMin(value))) {
4170 locations->AddTemp(Location::RequiresRegister());
4171 }
4172 }
4173 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4174 locations->SetInAt(0, Location::RequiresRegister());
4175 locations->SetInAt(1, Location::RequiresRegister());
4176 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4177 } else {
Artem Serov551b28f2016-10-18 19:11:30 +01004178 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4179 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
4180 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
Roland Levillain5e8d5f02016-10-18 18:03:43 +01004181 // Note: divmod will compute both the quotient and the remainder as the pair R0 and R1, but
Artem Serov551b28f2016-10-18 19:11:30 +01004182 // we only need the former.
4183 locations->SetOut(LocationFrom(r0));
Scott Wakelingfe885462016-09-22 10:24:38 +01004184 }
4185 break;
4186 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004187 case DataType::Type::kInt64: {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004188 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4189 locations->SetInAt(0, LocationFrom(
4190 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
4191 locations->SetInAt(1, LocationFrom(
4192 calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3)));
4193 locations->SetOut(LocationFrom(r0, r1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004194 break;
4195 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004196 case DataType::Type::kFloat32:
4197 case DataType::Type::kFloat64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004198 locations->SetInAt(0, Location::RequiresFpuRegister());
4199 locations->SetInAt(1, Location::RequiresFpuRegister());
4200 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4201 break;
4202 }
4203
4204 default:
4205 LOG(FATAL) << "Unexpected div type " << div->GetResultType();
4206 }
4207}
4208
4209void InstructionCodeGeneratorARMVIXL::VisitDiv(HDiv* div) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004210 Location lhs = div->GetLocations()->InAt(0);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004211 Location rhs = div->GetLocations()->InAt(1);
Scott Wakelingfe885462016-09-22 10:24:38 +01004212
4213 switch (div->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004214 case DataType::Type::kInt32: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004215 if (rhs.IsConstant()) {
Scott Wakelingfe885462016-09-22 10:24:38 +01004216 GenerateDivRemConstantIntegral(div);
4217 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4218 __ Sdiv(OutputRegister(div), InputRegisterAt(div, 0), InputRegisterAt(div, 1));
4219 } else {
Artem Serov551b28f2016-10-18 19:11:30 +01004220 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4221 DCHECK(calling_convention.GetRegisterAt(0).Is(RegisterFrom(lhs)));
4222 DCHECK(calling_convention.GetRegisterAt(1).Is(RegisterFrom(rhs)));
4223 DCHECK(r0.Is(OutputRegister(div)));
4224
4225 codegen_->InvokeRuntime(kQuickIdivmod, div, div->GetDexPc());
4226 CheckEntrypointTypes<kQuickIdivmod, int32_t, int32_t, int32_t>();
Scott Wakelingfe885462016-09-22 10:24:38 +01004227 }
4228 break;
4229 }
4230
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004231 case DataType::Type::kInt64: {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004232 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4233 DCHECK(calling_convention.GetRegisterAt(0).Is(LowRegisterFrom(lhs)));
4234 DCHECK(calling_convention.GetRegisterAt(1).Is(HighRegisterFrom(lhs)));
4235 DCHECK(calling_convention.GetRegisterAt(2).Is(LowRegisterFrom(rhs)));
4236 DCHECK(calling_convention.GetRegisterAt(3).Is(HighRegisterFrom(rhs)));
4237 DCHECK(LowRegisterFrom(div->GetLocations()->Out()).Is(r0));
4238 DCHECK(HighRegisterFrom(div->GetLocations()->Out()).Is(r1));
4239
4240 codegen_->InvokeRuntime(kQuickLdiv, div, div->GetDexPc());
4241 CheckEntrypointTypes<kQuickLdiv, int64_t, int64_t, int64_t>();
Scott Wakelingfe885462016-09-22 10:24:38 +01004242 break;
4243 }
4244
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004245 case DataType::Type::kFloat32:
4246 case DataType::Type::kFloat64:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004247 __ Vdiv(OutputVRegister(div), InputVRegisterAt(div, 0), InputVRegisterAt(div, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004248 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01004249
4250 default:
4251 LOG(FATAL) << "Unexpected div type " << div->GetResultType();
4252 }
4253}
4254
Artem Serov551b28f2016-10-18 19:11:30 +01004255void LocationsBuilderARMVIXL::VisitRem(HRem* rem) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004256 DataType::Type type = rem->GetResultType();
Artem Serov551b28f2016-10-18 19:11:30 +01004257
4258 // Most remainders are implemented in the runtime.
4259 LocationSummary::CallKind call_kind = LocationSummary::kCallOnMainOnly;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004260 if (rem->GetResultType() == DataType::Type::kInt32 && rem->InputAt(1)->IsConstant()) {
Artem Serov551b28f2016-10-18 19:11:30 +01004261 // sdiv will be replaced by other instruction sequence.
4262 call_kind = LocationSummary::kNoCall;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004263 } else if ((rem->GetResultType() == DataType::Type::kInt32)
Artem Serov551b28f2016-10-18 19:11:30 +01004264 && codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4265 // Have hardware divide instruction for int, do it with three instructions.
4266 call_kind = LocationSummary::kNoCall;
4267 }
4268
Vladimir Markoca6fff82017-10-03 14:49:14 +01004269 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(rem, call_kind);
Artem Serov551b28f2016-10-18 19:11:30 +01004270
4271 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004272 case DataType::Type::kInt32: {
Artem Serov551b28f2016-10-18 19:11:30 +01004273 if (rem->InputAt(1)->IsConstant()) {
4274 locations->SetInAt(0, Location::RequiresRegister());
4275 locations->SetInAt(1, Location::ConstantLocation(rem->InputAt(1)->AsConstant()));
4276 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Anton Kirilov644032c2016-12-06 17:51:43 +00004277 int32_t value = Int32ConstantFrom(rem->InputAt(1));
Artem Serov551b28f2016-10-18 19:11:30 +01004278 if (value == 1 || value == 0 || value == -1) {
4279 // No temp register required.
4280 } else {
4281 locations->AddTemp(Location::RequiresRegister());
4282 if (!IsPowerOfTwo(AbsOrMin(value))) {
4283 locations->AddTemp(Location::RequiresRegister());
4284 }
4285 }
4286 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4287 locations->SetInAt(0, Location::RequiresRegister());
4288 locations->SetInAt(1, Location::RequiresRegister());
4289 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4290 locations->AddTemp(Location::RequiresRegister());
4291 } else {
4292 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4293 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
4294 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
Roland Levillain5e8d5f02016-10-18 18:03:43 +01004295 // Note: divmod will compute both the quotient and the remainder as the pair R0 and R1, but
Artem Serov551b28f2016-10-18 19:11:30 +01004296 // we only need the latter.
4297 locations->SetOut(LocationFrom(r1));
4298 }
4299 break;
4300 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004301 case DataType::Type::kInt64: {
Artem Serov551b28f2016-10-18 19:11:30 +01004302 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4303 locations->SetInAt(0, LocationFrom(
4304 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
4305 locations->SetInAt(1, LocationFrom(
4306 calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3)));
4307 // The runtime helper puts the output in R2,R3.
4308 locations->SetOut(LocationFrom(r2, r3));
4309 break;
4310 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004311 case DataType::Type::kFloat32: {
Artem Serov551b28f2016-10-18 19:11:30 +01004312 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4313 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
4314 locations->SetInAt(1, LocationFrom(calling_convention.GetFpuRegisterAt(1)));
4315 locations->SetOut(LocationFrom(s0));
4316 break;
4317 }
4318
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004319 case DataType::Type::kFloat64: {
Artem Serov551b28f2016-10-18 19:11:30 +01004320 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4321 locations->SetInAt(0, LocationFrom(
4322 calling_convention.GetFpuRegisterAt(0), calling_convention.GetFpuRegisterAt(1)));
4323 locations->SetInAt(1, LocationFrom(
4324 calling_convention.GetFpuRegisterAt(2), calling_convention.GetFpuRegisterAt(3)));
4325 locations->SetOut(LocationFrom(s0, s1));
4326 break;
4327 }
4328
4329 default:
4330 LOG(FATAL) << "Unexpected rem type " << type;
4331 }
4332}
4333
4334void InstructionCodeGeneratorARMVIXL::VisitRem(HRem* rem) {
4335 LocationSummary* locations = rem->GetLocations();
4336 Location second = locations->InAt(1);
4337
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004338 DataType::Type type = rem->GetResultType();
Artem Serov551b28f2016-10-18 19:11:30 +01004339 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004340 case DataType::Type::kInt32: {
Artem Serov551b28f2016-10-18 19:11:30 +01004341 vixl32::Register reg1 = InputRegisterAt(rem, 0);
4342 vixl32::Register out_reg = OutputRegister(rem);
4343 if (second.IsConstant()) {
4344 GenerateDivRemConstantIntegral(rem);
4345 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4346 vixl32::Register reg2 = RegisterFrom(second);
4347 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
4348
4349 // temp = reg1 / reg2 (integer division)
4350 // dest = reg1 - temp * reg2
4351 __ Sdiv(temp, reg1, reg2);
4352 __ Mls(out_reg, temp, reg2, reg1);
4353 } else {
4354 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4355 DCHECK(reg1.Is(calling_convention.GetRegisterAt(0)));
4356 DCHECK(RegisterFrom(second).Is(calling_convention.GetRegisterAt(1)));
4357 DCHECK(out_reg.Is(r1));
4358
4359 codegen_->InvokeRuntime(kQuickIdivmod, rem, rem->GetDexPc());
4360 CheckEntrypointTypes<kQuickIdivmod, int32_t, int32_t, int32_t>();
4361 }
4362 break;
4363 }
4364
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004365 case DataType::Type::kInt64: {
Artem Serov551b28f2016-10-18 19:11:30 +01004366 codegen_->InvokeRuntime(kQuickLmod, rem, rem->GetDexPc());
4367 CheckEntrypointTypes<kQuickLmod, int64_t, int64_t, int64_t>();
4368 break;
4369 }
4370
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004371 case DataType::Type::kFloat32: {
Artem Serov551b28f2016-10-18 19:11:30 +01004372 codegen_->InvokeRuntime(kQuickFmodf, rem, rem->GetDexPc());
4373 CheckEntrypointTypes<kQuickFmodf, float, float, float>();
4374 break;
4375 }
4376
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004377 case DataType::Type::kFloat64: {
Artem Serov551b28f2016-10-18 19:11:30 +01004378 codegen_->InvokeRuntime(kQuickFmod, rem, rem->GetDexPc());
4379 CheckEntrypointTypes<kQuickFmod, double, double, double>();
4380 break;
4381 }
4382
4383 default:
4384 LOG(FATAL) << "Unexpected rem type " << type;
4385 }
4386}
4387
Aart Bik1f8d51b2018-02-15 10:42:37 -08004388static void CreateMinMaxLocations(ArenaAllocator* allocator, HBinaryOperation* minmax) {
4389 LocationSummary* locations = new (allocator) LocationSummary(minmax);
4390 switch (minmax->GetResultType()) {
4391 case DataType::Type::kInt32:
4392 locations->SetInAt(0, Location::RequiresRegister());
4393 locations->SetInAt(1, Location::RequiresRegister());
4394 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4395 break;
4396 case DataType::Type::kInt64:
4397 locations->SetInAt(0, Location::RequiresRegister());
4398 locations->SetInAt(1, Location::RequiresRegister());
4399 locations->SetOut(Location::SameAsFirstInput());
4400 break;
4401 case DataType::Type::kFloat32:
4402 locations->SetInAt(0, Location::RequiresFpuRegister());
4403 locations->SetInAt(1, Location::RequiresFpuRegister());
4404 locations->SetOut(Location::SameAsFirstInput());
4405 locations->AddTemp(Location::RequiresRegister());
4406 break;
4407 case DataType::Type::kFloat64:
4408 locations->SetInAt(0, Location::RequiresFpuRegister());
4409 locations->SetInAt(1, Location::RequiresFpuRegister());
4410 locations->SetOut(Location::SameAsFirstInput());
4411 break;
4412 default:
4413 LOG(FATAL) << "Unexpected type for HMinMax " << minmax->GetResultType();
4414 }
4415}
4416
Aart Bik351df3e2018-03-07 11:54:57 -08004417void InstructionCodeGeneratorARMVIXL::GenerateMinMaxInt(LocationSummary* locations, bool is_min) {
Aart Bik1f8d51b2018-02-15 10:42:37 -08004418 Location op1_loc = locations->InAt(0);
4419 Location op2_loc = locations->InAt(1);
4420 Location out_loc = locations->Out();
4421
4422 vixl32::Register op1 = RegisterFrom(op1_loc);
4423 vixl32::Register op2 = RegisterFrom(op2_loc);
4424 vixl32::Register out = RegisterFrom(out_loc);
4425
4426 __ Cmp(op1, op2);
4427
4428 {
4429 ExactAssemblyScope aas(GetVIXLAssembler(),
4430 3 * kMaxInstructionSizeInBytes,
4431 CodeBufferCheckScope::kMaximumSize);
4432
4433 __ ite(is_min ? lt : gt);
4434 __ mov(is_min ? lt : gt, out, op1);
4435 __ mov(is_min ? ge : le, out, op2);
4436 }
4437}
4438
4439void InstructionCodeGeneratorARMVIXL::GenerateMinMaxLong(LocationSummary* locations, bool is_min) {
4440 Location op1_loc = locations->InAt(0);
4441 Location op2_loc = locations->InAt(1);
4442 Location out_loc = locations->Out();
4443
4444 // Optimization: don't generate any code if inputs are the same.
4445 if (op1_loc.Equals(op2_loc)) {
4446 DCHECK(out_loc.Equals(op1_loc)); // out_loc is set as SameAsFirstInput() in location builder.
4447 return;
4448 }
4449
4450 vixl32::Register op1_lo = LowRegisterFrom(op1_loc);
4451 vixl32::Register op1_hi = HighRegisterFrom(op1_loc);
4452 vixl32::Register op2_lo = LowRegisterFrom(op2_loc);
4453 vixl32::Register op2_hi = HighRegisterFrom(op2_loc);
4454 vixl32::Register out_lo = LowRegisterFrom(out_loc);
4455 vixl32::Register out_hi = HighRegisterFrom(out_loc);
4456 UseScratchRegisterScope temps(GetVIXLAssembler());
4457 const vixl32::Register temp = temps.Acquire();
4458
4459 DCHECK(op1_lo.Is(out_lo));
4460 DCHECK(op1_hi.Is(out_hi));
4461
4462 // Compare op1 >= op2, or op1 < op2.
4463 __ Cmp(out_lo, op2_lo);
4464 __ Sbcs(temp, out_hi, op2_hi);
4465
4466 // Now GE/LT condition code is correct for the long comparison.
4467 {
4468 vixl32::ConditionType cond = is_min ? ge : lt;
4469 ExactAssemblyScope it_scope(GetVIXLAssembler(),
4470 3 * kMaxInstructionSizeInBytes,
4471 CodeBufferCheckScope::kMaximumSize);
4472 __ itt(cond);
4473 __ mov(cond, out_lo, op2_lo);
4474 __ mov(cond, out_hi, op2_hi);
4475 }
4476}
4477
Aart Bik351df3e2018-03-07 11:54:57 -08004478void InstructionCodeGeneratorARMVIXL::GenerateMinMaxFloat(HInstruction* minmax, bool is_min) {
4479 LocationSummary* locations = minmax->GetLocations();
Aart Bik1f8d51b2018-02-15 10:42:37 -08004480 Location op1_loc = locations->InAt(0);
4481 Location op2_loc = locations->InAt(1);
4482 Location out_loc = locations->Out();
4483
4484 // Optimization: don't generate any code if inputs are the same.
4485 if (op1_loc.Equals(op2_loc)) {
4486 DCHECK(out_loc.Equals(op1_loc)); // out_loc is set as SameAsFirstInput() in location builder.
4487 return;
4488 }
4489
4490 vixl32::SRegister op1 = SRegisterFrom(op1_loc);
4491 vixl32::SRegister op2 = SRegisterFrom(op2_loc);
4492 vixl32::SRegister out = SRegisterFrom(out_loc);
4493
4494 UseScratchRegisterScope temps(GetVIXLAssembler());
4495 const vixl32::Register temp1 = temps.Acquire();
4496 vixl32::Register temp2 = RegisterFrom(locations->GetTemp(0));
4497 vixl32::Label nan, done;
Aart Bik351df3e2018-03-07 11:54:57 -08004498 vixl32::Label* final_label = codegen_->GetFinalLabel(minmax, &done);
Aart Bik1f8d51b2018-02-15 10:42:37 -08004499
4500 DCHECK(op1.Is(out));
4501
4502 __ Vcmp(op1, op2);
4503 __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
Andreas Gampe3db70682018-12-26 15:12:03 -08004504 __ B(vs, &nan, /* is_far_target= */ false); // if un-ordered, go to NaN handling.
Aart Bik1f8d51b2018-02-15 10:42:37 -08004505
4506 // op1 <> op2
4507 vixl32::ConditionType cond = is_min ? gt : lt;
4508 {
4509 ExactAssemblyScope it_scope(GetVIXLAssembler(),
4510 2 * kMaxInstructionSizeInBytes,
4511 CodeBufferCheckScope::kMaximumSize);
4512 __ it(cond);
4513 __ vmov(cond, F32, out, op2);
4514 }
4515 // for <>(not equal), we've done min/max calculation.
Andreas Gampe3db70682018-12-26 15:12:03 -08004516 __ B(ne, final_label, /* is_far_target= */ false);
Aart Bik1f8d51b2018-02-15 10:42:37 -08004517
4518 // handle op1 == op2, max(+0.0,-0.0), min(+0.0,-0.0).
4519 __ Vmov(temp1, op1);
4520 __ Vmov(temp2, op2);
4521 if (is_min) {
4522 __ Orr(temp1, temp1, temp2);
4523 } else {
4524 __ And(temp1, temp1, temp2);
4525 }
4526 __ Vmov(out, temp1);
4527 __ B(final_label);
4528
4529 // handle NaN input.
4530 __ Bind(&nan);
4531 __ Movt(temp1, High16Bits(kNanFloat)); // 0x7FC0xxxx is a NaN.
4532 __ Vmov(out, temp1);
4533
4534 if (done.IsReferenced()) {
4535 __ Bind(&done);
4536 }
4537}
4538
Aart Bik351df3e2018-03-07 11:54:57 -08004539void InstructionCodeGeneratorARMVIXL::GenerateMinMaxDouble(HInstruction* minmax, bool is_min) {
4540 LocationSummary* locations = minmax->GetLocations();
Aart Bik1f8d51b2018-02-15 10:42:37 -08004541 Location op1_loc = locations->InAt(0);
4542 Location op2_loc = locations->InAt(1);
4543 Location out_loc = locations->Out();
4544
4545 // Optimization: don't generate any code if inputs are the same.
4546 if (op1_loc.Equals(op2_loc)) {
4547 DCHECK(out_loc.Equals(op1_loc)); // out_loc is set as SameAsFirstInput() in.
4548 return;
4549 }
4550
4551 vixl32::DRegister op1 = DRegisterFrom(op1_loc);
4552 vixl32::DRegister op2 = DRegisterFrom(op2_loc);
4553 vixl32::DRegister out = DRegisterFrom(out_loc);
4554 vixl32::Label handle_nan_eq, done;
Aart Bik351df3e2018-03-07 11:54:57 -08004555 vixl32::Label* final_label = codegen_->GetFinalLabel(minmax, &done);
Aart Bik1f8d51b2018-02-15 10:42:37 -08004556
4557 DCHECK(op1.Is(out));
4558
4559 __ Vcmp(op1, op2);
4560 __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
Andreas Gampe3db70682018-12-26 15:12:03 -08004561 __ B(vs, &handle_nan_eq, /* is_far_target= */ false); // if un-ordered, go to NaN handling.
Aart Bik1f8d51b2018-02-15 10:42:37 -08004562
4563 // op1 <> op2
4564 vixl32::ConditionType cond = is_min ? gt : lt;
4565 {
4566 ExactAssemblyScope it_scope(GetVIXLAssembler(),
4567 2 * kMaxInstructionSizeInBytes,
4568 CodeBufferCheckScope::kMaximumSize);
4569 __ it(cond);
4570 __ vmov(cond, F64, out, op2);
4571 }
4572 // for <>(not equal), we've done min/max calculation.
Andreas Gampe3db70682018-12-26 15:12:03 -08004573 __ B(ne, final_label, /* is_far_target= */ false);
Aart Bik1f8d51b2018-02-15 10:42:37 -08004574
4575 // handle op1 == op2, max(+0.0,-0.0).
4576 if (!is_min) {
4577 __ Vand(F64, out, op1, op2);
4578 __ B(final_label);
4579 }
4580
4581 // handle op1 == op2, min(+0.0,-0.0), NaN input.
4582 __ Bind(&handle_nan_eq);
4583 __ Vorr(F64, out, op1, op2); // assemble op1/-0.0/NaN.
4584
4585 if (done.IsReferenced()) {
4586 __ Bind(&done);
4587 }
4588}
4589
Aart Bik351df3e2018-03-07 11:54:57 -08004590void InstructionCodeGeneratorARMVIXL::GenerateMinMax(HBinaryOperation* minmax, bool is_min) {
4591 DataType::Type type = minmax->GetResultType();
4592 switch (type) {
4593 case DataType::Type::kInt32:
4594 GenerateMinMaxInt(minmax->GetLocations(), is_min);
4595 break;
4596 case DataType::Type::kInt64:
4597 GenerateMinMaxLong(minmax->GetLocations(), is_min);
4598 break;
4599 case DataType::Type::kFloat32:
4600 GenerateMinMaxFloat(minmax, is_min);
4601 break;
4602 case DataType::Type::kFloat64:
4603 GenerateMinMaxDouble(minmax, is_min);
4604 break;
4605 default:
4606 LOG(FATAL) << "Unexpected type for HMinMax " << type;
4607 }
4608}
4609
Aart Bik1f8d51b2018-02-15 10:42:37 -08004610void LocationsBuilderARMVIXL::VisitMin(HMin* min) {
4611 CreateMinMaxLocations(GetGraph()->GetAllocator(), min);
4612}
4613
4614void InstructionCodeGeneratorARMVIXL::VisitMin(HMin* min) {
Aart Bik351df3e2018-03-07 11:54:57 -08004615 GenerateMinMax(min, /*is_min*/ true);
Aart Bik1f8d51b2018-02-15 10:42:37 -08004616}
4617
4618void LocationsBuilderARMVIXL::VisitMax(HMax* max) {
4619 CreateMinMaxLocations(GetGraph()->GetAllocator(), max);
4620}
4621
4622void InstructionCodeGeneratorARMVIXL::VisitMax(HMax* max) {
Aart Bik351df3e2018-03-07 11:54:57 -08004623 GenerateMinMax(max, /*is_min*/ false);
Aart Bik1f8d51b2018-02-15 10:42:37 -08004624}
4625
Aart Bik3dad3412018-02-28 12:01:46 -08004626void LocationsBuilderARMVIXL::VisitAbs(HAbs* abs) {
4627 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(abs);
4628 switch (abs->GetResultType()) {
4629 case DataType::Type::kInt32:
4630 case DataType::Type::kInt64:
4631 locations->SetInAt(0, Location::RequiresRegister());
4632 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4633 locations->AddTemp(Location::RequiresRegister());
4634 break;
4635 case DataType::Type::kFloat32:
4636 case DataType::Type::kFloat64:
4637 locations->SetInAt(0, Location::RequiresFpuRegister());
4638 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4639 break;
4640 default:
4641 LOG(FATAL) << "Unexpected type for abs operation " << abs->GetResultType();
4642 }
4643}
4644
4645void InstructionCodeGeneratorARMVIXL::VisitAbs(HAbs* abs) {
4646 LocationSummary* locations = abs->GetLocations();
4647 switch (abs->GetResultType()) {
4648 case DataType::Type::kInt32: {
4649 vixl32::Register in_reg = RegisterFrom(locations->InAt(0));
4650 vixl32::Register out_reg = RegisterFrom(locations->Out());
4651 vixl32::Register mask = RegisterFrom(locations->GetTemp(0));
4652 __ Asr(mask, in_reg, 31);
4653 __ Add(out_reg, in_reg, mask);
4654 __ Eor(out_reg, out_reg, mask);
4655 break;
4656 }
4657 case DataType::Type::kInt64: {
4658 Location in = locations->InAt(0);
4659 vixl32::Register in_reg_lo = LowRegisterFrom(in);
4660 vixl32::Register in_reg_hi = HighRegisterFrom(in);
4661 Location output = locations->Out();
4662 vixl32::Register out_reg_lo = LowRegisterFrom(output);
4663 vixl32::Register out_reg_hi = HighRegisterFrom(output);
4664 DCHECK(!out_reg_lo.Is(in_reg_hi)) << "Diagonal overlap unexpected.";
4665 vixl32::Register mask = RegisterFrom(locations->GetTemp(0));
4666 __ Asr(mask, in_reg_hi, 31);
4667 __ Adds(out_reg_lo, in_reg_lo, mask);
4668 __ Adc(out_reg_hi, in_reg_hi, mask);
4669 __ Eor(out_reg_lo, out_reg_lo, mask);
4670 __ Eor(out_reg_hi, out_reg_hi, mask);
4671 break;
4672 }
4673 case DataType::Type::kFloat32:
4674 case DataType::Type::kFloat64:
4675 __ Vabs(OutputVRegister(abs), InputVRegisterAt(abs, 0));
4676 break;
4677 default:
4678 LOG(FATAL) << "Unexpected type for abs operation " << abs->GetResultType();
4679 }
4680}
Artem Serov551b28f2016-10-18 19:11:30 +01004681
Scott Wakelingfe885462016-09-22 10:24:38 +01004682void LocationsBuilderARMVIXL::VisitDivZeroCheck(HDivZeroCheck* instruction) {
Artem Serov657022c2016-11-23 14:19:38 +00004683 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
Scott Wakelingfe885462016-09-22 10:24:38 +01004684 locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0)));
Scott Wakelingfe885462016-09-22 10:24:38 +01004685}
4686
4687void InstructionCodeGeneratorARMVIXL::VisitDivZeroCheck(HDivZeroCheck* instruction) {
4688 DivZeroCheckSlowPathARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01004689 new (codegen_->GetScopedAllocator()) DivZeroCheckSlowPathARMVIXL(instruction);
Scott Wakelingfe885462016-09-22 10:24:38 +01004690 codegen_->AddSlowPath(slow_path);
4691
4692 LocationSummary* locations = instruction->GetLocations();
4693 Location value = locations->InAt(0);
4694
4695 switch (instruction->GetType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004696 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01004697 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004698 case DataType::Type::kInt8:
4699 case DataType::Type::kUint16:
4700 case DataType::Type::kInt16:
4701 case DataType::Type::kInt32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004702 if (value.IsRegister()) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00004703 __ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
Scott Wakelingfe885462016-09-22 10:24:38 +01004704 } else {
4705 DCHECK(value.IsConstant()) << value;
Anton Kirilov644032c2016-12-06 17:51:43 +00004706 if (Int32ConstantFrom(value) == 0) {
Scott Wakelingfe885462016-09-22 10:24:38 +01004707 __ B(slow_path->GetEntryLabel());
4708 }
4709 }
4710 break;
4711 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004712 case DataType::Type::kInt64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004713 if (value.IsRegisterPair()) {
4714 UseScratchRegisterScope temps(GetVIXLAssembler());
4715 vixl32::Register temp = temps.Acquire();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004716 __ Orrs(temp, LowRegisterFrom(value), HighRegisterFrom(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01004717 __ B(eq, slow_path->GetEntryLabel());
4718 } else {
4719 DCHECK(value.IsConstant()) << value;
Anton Kirilov644032c2016-12-06 17:51:43 +00004720 if (Int64ConstantFrom(value) == 0) {
Scott Wakelingfe885462016-09-22 10:24:38 +01004721 __ B(slow_path->GetEntryLabel());
4722 }
4723 }
4724 break;
4725 }
4726 default:
4727 LOG(FATAL) << "Unexpected type for HDivZeroCheck " << instruction->GetType();
4728 }
4729}
4730
Artem Serov02109dd2016-09-23 17:17:54 +01004731void InstructionCodeGeneratorARMVIXL::HandleIntegerRotate(HRor* ror) {
4732 LocationSummary* locations = ror->GetLocations();
4733 vixl32::Register in = InputRegisterAt(ror, 0);
4734 Location rhs = locations->InAt(1);
4735 vixl32::Register out = OutputRegister(ror);
4736
4737 if (rhs.IsConstant()) {
4738 // Arm32 and Thumb2 assemblers require a rotation on the interval [1,31],
4739 // so map all rotations to a +ve. equivalent in that range.
4740 // (e.g. left *or* right by -2 bits == 30 bits in the same direction.)
4741 uint32_t rot = CodeGenerator::GetInt32ValueOf(rhs.GetConstant()) & 0x1F;
4742 if (rot) {
4743 // Rotate, mapping left rotations to right equivalents if necessary.
4744 // (e.g. left by 2 bits == right by 30.)
4745 __ Ror(out, in, rot);
4746 } else if (!out.Is(in)) {
4747 __ Mov(out, in);
4748 }
4749 } else {
4750 __ Ror(out, in, RegisterFrom(rhs));
4751 }
4752}
4753
4754// Gain some speed by mapping all Long rotates onto equivalent pairs of Integer
4755// rotates by swapping input regs (effectively rotating by the first 32-bits of
4756// a larger rotation) or flipping direction (thus treating larger right/left
4757// rotations as sub-word sized rotations in the other direction) as appropriate.
4758void InstructionCodeGeneratorARMVIXL::HandleLongRotate(HRor* ror) {
4759 LocationSummary* locations = ror->GetLocations();
4760 vixl32::Register in_reg_lo = LowRegisterFrom(locations->InAt(0));
4761 vixl32::Register in_reg_hi = HighRegisterFrom(locations->InAt(0));
4762 Location rhs = locations->InAt(1);
4763 vixl32::Register out_reg_lo = LowRegisterFrom(locations->Out());
4764 vixl32::Register out_reg_hi = HighRegisterFrom(locations->Out());
4765
4766 if (rhs.IsConstant()) {
4767 uint64_t rot = CodeGenerator::GetInt64ValueOf(rhs.GetConstant());
4768 // Map all rotations to +ve. equivalents on the interval [0,63].
4769 rot &= kMaxLongShiftDistance;
4770 // For rotates over a word in size, 'pre-rotate' by 32-bits to keep rotate
4771 // logic below to a simple pair of binary orr.
4772 // (e.g. 34 bits == in_reg swap + 2 bits right.)
4773 if (rot >= kArmBitsPerWord) {
4774 rot -= kArmBitsPerWord;
4775 std::swap(in_reg_hi, in_reg_lo);
4776 }
4777 // Rotate, or mov to out for zero or word size rotations.
4778 if (rot != 0u) {
Scott Wakelingb77051e2016-11-21 19:46:00 +00004779 __ Lsr(out_reg_hi, in_reg_hi, Operand::From(rot));
Artem Serov02109dd2016-09-23 17:17:54 +01004780 __ Orr(out_reg_hi, out_reg_hi, Operand(in_reg_lo, ShiftType::LSL, kArmBitsPerWord - rot));
Scott Wakelingb77051e2016-11-21 19:46:00 +00004781 __ Lsr(out_reg_lo, in_reg_lo, Operand::From(rot));
Artem Serov02109dd2016-09-23 17:17:54 +01004782 __ Orr(out_reg_lo, out_reg_lo, Operand(in_reg_hi, ShiftType::LSL, kArmBitsPerWord - rot));
4783 } else {
4784 __ Mov(out_reg_lo, in_reg_lo);
4785 __ Mov(out_reg_hi, in_reg_hi);
4786 }
4787 } else {
4788 vixl32::Register shift_right = RegisterFrom(locations->GetTemp(0));
4789 vixl32::Register shift_left = RegisterFrom(locations->GetTemp(1));
4790 vixl32::Label end;
4791 vixl32::Label shift_by_32_plus_shift_right;
Anton Kirilov6f644202017-02-27 18:29:45 +00004792 vixl32::Label* final_label = codegen_->GetFinalLabel(ror, &end);
Artem Serov02109dd2016-09-23 17:17:54 +01004793
4794 __ And(shift_right, RegisterFrom(rhs), 0x1F);
4795 __ Lsrs(shift_left, RegisterFrom(rhs), 6);
Scott Wakelingbffdc702016-12-07 17:46:03 +00004796 __ Rsb(LeaveFlags, shift_left, shift_right, Operand::From(kArmBitsPerWord));
Andreas Gampe3db70682018-12-26 15:12:03 -08004797 __ B(cc, &shift_by_32_plus_shift_right, /* is_far_target= */ false);
Artem Serov02109dd2016-09-23 17:17:54 +01004798
4799 // out_reg_hi = (reg_hi << shift_left) | (reg_lo >> shift_right).
4800 // out_reg_lo = (reg_lo << shift_left) | (reg_hi >> shift_right).
4801 __ Lsl(out_reg_hi, in_reg_hi, shift_left);
4802 __ Lsr(out_reg_lo, in_reg_lo, shift_right);
4803 __ Add(out_reg_hi, out_reg_hi, out_reg_lo);
4804 __ Lsl(out_reg_lo, in_reg_lo, shift_left);
4805 __ Lsr(shift_left, in_reg_hi, shift_right);
4806 __ Add(out_reg_lo, out_reg_lo, shift_left);
Anton Kirilov6f644202017-02-27 18:29:45 +00004807 __ B(final_label);
Artem Serov02109dd2016-09-23 17:17:54 +01004808
4809 __ Bind(&shift_by_32_plus_shift_right); // Shift by 32+shift_right.
4810 // out_reg_hi = (reg_hi >> shift_right) | (reg_lo << shift_left).
4811 // out_reg_lo = (reg_lo >> shift_right) | (reg_hi << shift_left).
4812 __ Lsr(out_reg_hi, in_reg_hi, shift_right);
4813 __ Lsl(out_reg_lo, in_reg_lo, shift_left);
4814 __ Add(out_reg_hi, out_reg_hi, out_reg_lo);
4815 __ Lsr(out_reg_lo, in_reg_lo, shift_right);
4816 __ Lsl(shift_right, in_reg_hi, shift_left);
4817 __ Add(out_reg_lo, out_reg_lo, shift_right);
4818
Anton Kirilov6f644202017-02-27 18:29:45 +00004819 if (end.IsReferenced()) {
4820 __ Bind(&end);
4821 }
Artem Serov02109dd2016-09-23 17:17:54 +01004822 }
4823}
4824
4825void LocationsBuilderARMVIXL::VisitRor(HRor* ror) {
4826 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01004827 new (GetGraph()->GetAllocator()) LocationSummary(ror, LocationSummary::kNoCall);
Artem Serov02109dd2016-09-23 17:17:54 +01004828 switch (ror->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004829 case DataType::Type::kInt32: {
Artem Serov02109dd2016-09-23 17:17:54 +01004830 locations->SetInAt(0, Location::RequiresRegister());
4831 locations->SetInAt(1, Location::RegisterOrConstant(ror->InputAt(1)));
4832 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4833 break;
4834 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004835 case DataType::Type::kInt64: {
Artem Serov02109dd2016-09-23 17:17:54 +01004836 locations->SetInAt(0, Location::RequiresRegister());
4837 if (ror->InputAt(1)->IsConstant()) {
4838 locations->SetInAt(1, Location::ConstantLocation(ror->InputAt(1)->AsConstant()));
4839 } else {
4840 locations->SetInAt(1, Location::RequiresRegister());
4841 locations->AddTemp(Location::RequiresRegister());
4842 locations->AddTemp(Location::RequiresRegister());
4843 }
4844 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
4845 break;
4846 }
4847 default:
4848 LOG(FATAL) << "Unexpected operation type " << ror->GetResultType();
4849 }
4850}
4851
4852void InstructionCodeGeneratorARMVIXL::VisitRor(HRor* ror) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004853 DataType::Type type = ror->GetResultType();
Artem Serov02109dd2016-09-23 17:17:54 +01004854 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004855 case DataType::Type::kInt32: {
Artem Serov02109dd2016-09-23 17:17:54 +01004856 HandleIntegerRotate(ror);
4857 break;
4858 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004859 case DataType::Type::kInt64: {
Artem Serov02109dd2016-09-23 17:17:54 +01004860 HandleLongRotate(ror);
4861 break;
4862 }
4863 default:
4864 LOG(FATAL) << "Unexpected operation type " << type;
4865 UNREACHABLE();
4866 }
4867}
4868
Artem Serov02d37832016-10-25 15:25:33 +01004869void LocationsBuilderARMVIXL::HandleShift(HBinaryOperation* op) {
4870 DCHECK(op->IsShl() || op->IsShr() || op->IsUShr());
4871
4872 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01004873 new (GetGraph()->GetAllocator()) LocationSummary(op, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01004874
4875 switch (op->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004876 case DataType::Type::kInt32: {
Artem Serov02d37832016-10-25 15:25:33 +01004877 locations->SetInAt(0, Location::RequiresRegister());
4878 if (op->InputAt(1)->IsConstant()) {
4879 locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant()));
4880 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4881 } else {
4882 locations->SetInAt(1, Location::RequiresRegister());
4883 // Make the output overlap, as it will be used to hold the masked
4884 // second input.
4885 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
4886 }
4887 break;
4888 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004889 case DataType::Type::kInt64: {
Artem Serov02d37832016-10-25 15:25:33 +01004890 locations->SetInAt(0, Location::RequiresRegister());
4891 if (op->InputAt(1)->IsConstant()) {
4892 locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant()));
4893 // For simplicity, use kOutputOverlap even though we only require that low registers
4894 // don't clash with high registers which the register allocator currently guarantees.
4895 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
4896 } else {
4897 locations->SetInAt(1, Location::RequiresRegister());
4898 locations->AddTemp(Location::RequiresRegister());
4899 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
4900 }
4901 break;
4902 }
4903 default:
4904 LOG(FATAL) << "Unexpected operation type " << op->GetResultType();
4905 }
4906}
4907
4908void InstructionCodeGeneratorARMVIXL::HandleShift(HBinaryOperation* op) {
4909 DCHECK(op->IsShl() || op->IsShr() || op->IsUShr());
4910
4911 LocationSummary* locations = op->GetLocations();
4912 Location out = locations->Out();
4913 Location first = locations->InAt(0);
4914 Location second = locations->InAt(1);
4915
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004916 DataType::Type type = op->GetResultType();
Artem Serov02d37832016-10-25 15:25:33 +01004917 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004918 case DataType::Type::kInt32: {
Artem Serov02d37832016-10-25 15:25:33 +01004919 vixl32::Register out_reg = OutputRegister(op);
4920 vixl32::Register first_reg = InputRegisterAt(op, 0);
4921 if (second.IsRegister()) {
4922 vixl32::Register second_reg = RegisterFrom(second);
4923 // ARM doesn't mask the shift count so we need to do it ourselves.
4924 __ And(out_reg, second_reg, kMaxIntShiftDistance);
4925 if (op->IsShl()) {
4926 __ Lsl(out_reg, first_reg, out_reg);
4927 } else if (op->IsShr()) {
4928 __ Asr(out_reg, first_reg, out_reg);
4929 } else {
4930 __ Lsr(out_reg, first_reg, out_reg);
4931 }
4932 } else {
Anton Kirilov644032c2016-12-06 17:51:43 +00004933 int32_t cst = Int32ConstantFrom(second);
Artem Serov02d37832016-10-25 15:25:33 +01004934 uint32_t shift_value = cst & kMaxIntShiftDistance;
4935 if (shift_value == 0) { // ARM does not support shifting with 0 immediate.
4936 __ Mov(out_reg, first_reg);
4937 } else if (op->IsShl()) {
4938 __ Lsl(out_reg, first_reg, shift_value);
4939 } else if (op->IsShr()) {
4940 __ Asr(out_reg, first_reg, shift_value);
4941 } else {
4942 __ Lsr(out_reg, first_reg, shift_value);
4943 }
4944 }
4945 break;
4946 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004947 case DataType::Type::kInt64: {
Artem Serov02d37832016-10-25 15:25:33 +01004948 vixl32::Register o_h = HighRegisterFrom(out);
4949 vixl32::Register o_l = LowRegisterFrom(out);
4950
4951 vixl32::Register high = HighRegisterFrom(first);
4952 vixl32::Register low = LowRegisterFrom(first);
4953
4954 if (second.IsRegister()) {
4955 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
4956
4957 vixl32::Register second_reg = RegisterFrom(second);
4958
4959 if (op->IsShl()) {
4960 __ And(o_l, second_reg, kMaxLongShiftDistance);
4961 // Shift the high part
4962 __ Lsl(o_h, high, o_l);
4963 // Shift the low part and `or` what overflew on the high part
Scott Wakelingb77051e2016-11-21 19:46:00 +00004964 __ Rsb(temp, o_l, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01004965 __ Lsr(temp, low, temp);
4966 __ Orr(o_h, o_h, temp);
4967 // If the shift is > 32 bits, override the high part
Scott Wakelingb77051e2016-11-21 19:46:00 +00004968 __ Subs(temp, o_l, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01004969 {
Artem Serov0fb37192016-12-06 18:13:40 +00004970 ExactAssemblyScope guard(GetVIXLAssembler(),
4971 2 * vixl32::kMaxInstructionSizeInBytes,
4972 CodeBufferCheckScope::kMaximumSize);
Artem Serov02d37832016-10-25 15:25:33 +01004973 __ it(pl);
4974 __ lsl(pl, o_h, low, temp);
4975 }
4976 // Shift the low part
4977 __ Lsl(o_l, low, o_l);
4978 } else if (op->IsShr()) {
4979 __ And(o_h, second_reg, kMaxLongShiftDistance);
4980 // Shift the low part
4981 __ Lsr(o_l, low, o_h);
4982 // Shift the high part and `or` what underflew on the low part
Scott Wakelingb77051e2016-11-21 19:46:00 +00004983 __ Rsb(temp, o_h, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01004984 __ Lsl(temp, high, temp);
4985 __ Orr(o_l, o_l, temp);
4986 // If the shift is > 32 bits, override the low part
Scott Wakelingb77051e2016-11-21 19:46:00 +00004987 __ Subs(temp, o_h, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01004988 {
Artem Serov0fb37192016-12-06 18:13:40 +00004989 ExactAssemblyScope guard(GetVIXLAssembler(),
4990 2 * vixl32::kMaxInstructionSizeInBytes,
4991 CodeBufferCheckScope::kMaximumSize);
Artem Serov02d37832016-10-25 15:25:33 +01004992 __ it(pl);
4993 __ asr(pl, o_l, high, temp);
4994 }
4995 // Shift the high part
4996 __ Asr(o_h, high, o_h);
4997 } else {
4998 __ And(o_h, second_reg, kMaxLongShiftDistance);
4999 // same as Shr except we use `Lsr`s and not `Asr`s
5000 __ Lsr(o_l, low, o_h);
Scott Wakelingb77051e2016-11-21 19:46:00 +00005001 __ Rsb(temp, o_h, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01005002 __ Lsl(temp, high, temp);
5003 __ Orr(o_l, o_l, temp);
Scott Wakelingb77051e2016-11-21 19:46:00 +00005004 __ Subs(temp, o_h, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01005005 {
Artem Serov0fb37192016-12-06 18:13:40 +00005006 ExactAssemblyScope guard(GetVIXLAssembler(),
5007 2 * vixl32::kMaxInstructionSizeInBytes,
5008 CodeBufferCheckScope::kMaximumSize);
Artem Serov02d37832016-10-25 15:25:33 +01005009 __ it(pl);
5010 __ lsr(pl, o_l, high, temp);
5011 }
5012 __ Lsr(o_h, high, o_h);
5013 }
5014 } else {
5015 // Register allocator doesn't create partial overlap.
5016 DCHECK(!o_l.Is(high));
5017 DCHECK(!o_h.Is(low));
Anton Kirilov644032c2016-12-06 17:51:43 +00005018 int32_t cst = Int32ConstantFrom(second);
Artem Serov02d37832016-10-25 15:25:33 +01005019 uint32_t shift_value = cst & kMaxLongShiftDistance;
5020 if (shift_value > 32) {
5021 if (op->IsShl()) {
5022 __ Lsl(o_h, low, shift_value - 32);
5023 __ Mov(o_l, 0);
5024 } else if (op->IsShr()) {
5025 __ Asr(o_l, high, shift_value - 32);
5026 __ Asr(o_h, high, 31);
5027 } else {
5028 __ Lsr(o_l, high, shift_value - 32);
5029 __ Mov(o_h, 0);
5030 }
5031 } else if (shift_value == 32) {
5032 if (op->IsShl()) {
5033 __ Mov(o_h, low);
5034 __ Mov(o_l, 0);
5035 } else if (op->IsShr()) {
5036 __ Mov(o_l, high);
5037 __ Asr(o_h, high, 31);
5038 } else {
5039 __ Mov(o_l, high);
5040 __ Mov(o_h, 0);
5041 }
5042 } else if (shift_value == 1) {
5043 if (op->IsShl()) {
5044 __ Lsls(o_l, low, 1);
5045 __ Adc(o_h, high, high);
5046 } else if (op->IsShr()) {
5047 __ Asrs(o_h, high, 1);
5048 __ Rrx(o_l, low);
5049 } else {
5050 __ Lsrs(o_h, high, 1);
5051 __ Rrx(o_l, low);
5052 }
Nicolas Geoffray9b195cc2019-04-02 08:29:00 +01005053 } else if (shift_value == 0) {
5054 __ Mov(o_l, low);
5055 __ Mov(o_h, high);
Artem Serov02d37832016-10-25 15:25:33 +01005056 } else {
Nicolas Geoffray9b195cc2019-04-02 08:29:00 +01005057 DCHECK(0 < shift_value && shift_value < 32) << shift_value;
Artem Serov02d37832016-10-25 15:25:33 +01005058 if (op->IsShl()) {
5059 __ Lsl(o_h, high, shift_value);
5060 __ Orr(o_h, o_h, Operand(low, ShiftType::LSR, 32 - shift_value));
5061 __ Lsl(o_l, low, shift_value);
5062 } else if (op->IsShr()) {
5063 __ Lsr(o_l, low, shift_value);
5064 __ Orr(o_l, o_l, Operand(high, ShiftType::LSL, 32 - shift_value));
5065 __ Asr(o_h, high, shift_value);
5066 } else {
5067 __ Lsr(o_l, low, shift_value);
5068 __ Orr(o_l, o_l, Operand(high, ShiftType::LSL, 32 - shift_value));
5069 __ Lsr(o_h, high, shift_value);
5070 }
5071 }
5072 }
5073 break;
5074 }
5075 default:
5076 LOG(FATAL) << "Unexpected operation type " << type;
5077 UNREACHABLE();
5078 }
5079}
5080
5081void LocationsBuilderARMVIXL::VisitShl(HShl* shl) {
5082 HandleShift(shl);
5083}
5084
5085void InstructionCodeGeneratorARMVIXL::VisitShl(HShl* shl) {
5086 HandleShift(shl);
5087}
5088
5089void LocationsBuilderARMVIXL::VisitShr(HShr* shr) {
5090 HandleShift(shr);
5091}
5092
5093void InstructionCodeGeneratorARMVIXL::VisitShr(HShr* shr) {
5094 HandleShift(shr);
5095}
5096
5097void LocationsBuilderARMVIXL::VisitUShr(HUShr* ushr) {
5098 HandleShift(ushr);
5099}
5100
5101void InstructionCodeGeneratorARMVIXL::VisitUShr(HUShr* ushr) {
5102 HandleShift(ushr);
5103}
5104
5105void LocationsBuilderARMVIXL::VisitNewInstance(HNewInstance* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01005106 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
5107 instruction, LocationSummary::kCallOnMainOnly);
Alex Lightd109e302018-06-27 10:25:41 -07005108 InvokeRuntimeCallingConventionARMVIXL calling_convention;
5109 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
Artem Serov02d37832016-10-25 15:25:33 +01005110 locations->SetOut(LocationFrom(r0));
5111}
5112
5113void InstructionCodeGeneratorARMVIXL::VisitNewInstance(HNewInstance* instruction) {
Alex Lightd109e302018-06-27 10:25:41 -07005114 codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
5115 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
Andreas Gampe3db70682018-12-26 15:12:03 -08005116 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 11);
Artem Serov02d37832016-10-25 15:25:33 +01005117}
5118
5119void LocationsBuilderARMVIXL::VisitNewArray(HNewArray* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01005120 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
5121 instruction, LocationSummary::kCallOnMainOnly);
Artem Serov02d37832016-10-25 15:25:33 +01005122 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Artem Serov02d37832016-10-25 15:25:33 +01005123 locations->SetOut(LocationFrom(r0));
Nicolas Geoffray8c7c4f12017-01-26 10:13:11 +00005124 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
5125 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
Artem Serov02d37832016-10-25 15:25:33 +01005126}
5127
5128void InstructionCodeGeneratorARMVIXL::VisitNewArray(HNewArray* instruction) {
Vladimir Markob5461632018-10-15 14:24:21 +01005129 // Note: if heap poisoning is enabled, the entry point takes care of poisoning the reference.
5130 QuickEntrypointEnum entrypoint = CodeGenerator::GetArrayAllocationEntrypoint(instruction);
Artem Serov7b3672e2017-02-03 17:30:34 +00005131 codegen_->InvokeRuntime(entrypoint, instruction, instruction->GetDexPc());
Nicolas Geoffraye761bcc2017-01-19 08:59:37 +00005132 CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>();
Artem Serov7b3672e2017-02-03 17:30:34 +00005133 DCHECK(!codegen_->IsLeafMethod());
Andreas Gampe3db70682018-12-26 15:12:03 -08005134 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 12);
Artem Serov02d37832016-10-25 15:25:33 +01005135}
5136
5137void LocationsBuilderARMVIXL::VisitParameterValue(HParameterValue* instruction) {
5138 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005139 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01005140 Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
5141 if (location.IsStackSlot()) {
5142 location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
5143 } else if (location.IsDoubleStackSlot()) {
5144 location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
5145 }
5146 locations->SetOut(location);
5147}
5148
5149void InstructionCodeGeneratorARMVIXL::VisitParameterValue(
5150 HParameterValue* instruction ATTRIBUTE_UNUSED) {
5151 // Nothing to do, the parameter is already at its location.
5152}
5153
5154void LocationsBuilderARMVIXL::VisitCurrentMethod(HCurrentMethod* instruction) {
5155 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005156 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01005157 locations->SetOut(LocationFrom(kMethodRegister));
5158}
5159
5160void InstructionCodeGeneratorARMVIXL::VisitCurrentMethod(
5161 HCurrentMethod* instruction ATTRIBUTE_UNUSED) {
5162 // Nothing to do, the method is already at its location.
5163}
5164
5165void LocationsBuilderARMVIXL::VisitNot(HNot* not_) {
5166 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005167 new (GetGraph()->GetAllocator()) LocationSummary(not_, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01005168 locations->SetInAt(0, Location::RequiresRegister());
5169 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5170}
5171
5172void InstructionCodeGeneratorARMVIXL::VisitNot(HNot* not_) {
5173 LocationSummary* locations = not_->GetLocations();
5174 Location out = locations->Out();
5175 Location in = locations->InAt(0);
5176 switch (not_->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005177 case DataType::Type::kInt32:
Artem Serov02d37832016-10-25 15:25:33 +01005178 __ Mvn(OutputRegister(not_), InputRegisterAt(not_, 0));
5179 break;
5180
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005181 case DataType::Type::kInt64:
Artem Serov02d37832016-10-25 15:25:33 +01005182 __ Mvn(LowRegisterFrom(out), LowRegisterFrom(in));
5183 __ Mvn(HighRegisterFrom(out), HighRegisterFrom(in));
5184 break;
5185
5186 default:
5187 LOG(FATAL) << "Unimplemented type for not operation " << not_->GetResultType();
5188 }
5189}
5190
Scott Wakelingc34dba72016-10-03 10:14:44 +01005191void LocationsBuilderARMVIXL::VisitBooleanNot(HBooleanNot* bool_not) {
5192 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005193 new (GetGraph()->GetAllocator()) LocationSummary(bool_not, LocationSummary::kNoCall);
Scott Wakelingc34dba72016-10-03 10:14:44 +01005194 locations->SetInAt(0, Location::RequiresRegister());
5195 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5196}
5197
5198void InstructionCodeGeneratorARMVIXL::VisitBooleanNot(HBooleanNot* bool_not) {
5199 __ Eor(OutputRegister(bool_not), InputRegister(bool_not), 1);
5200}
5201
Artem Serov02d37832016-10-25 15:25:33 +01005202void LocationsBuilderARMVIXL::VisitCompare(HCompare* compare) {
5203 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005204 new (GetGraph()->GetAllocator()) LocationSummary(compare, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01005205 switch (compare->InputAt(0)->GetType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005206 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005207 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005208 case DataType::Type::kInt8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005209 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005210 case DataType::Type::kInt16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005211 case DataType::Type::kInt32:
5212 case DataType::Type::kInt64: {
Artem Serov02d37832016-10-25 15:25:33 +01005213 locations->SetInAt(0, Location::RequiresRegister());
5214 locations->SetInAt(1, Location::RequiresRegister());
5215 // Output overlaps because it is written before doing the low comparison.
5216 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
5217 break;
5218 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005219 case DataType::Type::kFloat32:
5220 case DataType::Type::kFloat64: {
Artem Serov02d37832016-10-25 15:25:33 +01005221 locations->SetInAt(0, Location::RequiresFpuRegister());
5222 locations->SetInAt(1, ArithmeticZeroOrFpuRegister(compare->InputAt(1)));
5223 locations->SetOut(Location::RequiresRegister());
5224 break;
5225 }
5226 default:
5227 LOG(FATAL) << "Unexpected type for compare operation " << compare->InputAt(0)->GetType();
5228 }
5229}
5230
5231void InstructionCodeGeneratorARMVIXL::VisitCompare(HCompare* compare) {
5232 LocationSummary* locations = compare->GetLocations();
5233 vixl32::Register out = OutputRegister(compare);
5234 Location left = locations->InAt(0);
5235 Location right = locations->InAt(1);
5236
5237 vixl32::Label less, greater, done;
Anton Kirilov6f644202017-02-27 18:29:45 +00005238 vixl32::Label* final_label = codegen_->GetFinalLabel(compare, &done);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005239 DataType::Type type = compare->InputAt(0)->GetType();
Vladimir Marko33bff252017-11-01 14:35:42 +00005240 vixl32::Condition less_cond = vixl32::Condition::None();
Artem Serov02d37832016-10-25 15:25:33 +01005241 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005242 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005243 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005244 case DataType::Type::kInt8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005245 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005246 case DataType::Type::kInt16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005247 case DataType::Type::kInt32: {
Artem Serov02d37832016-10-25 15:25:33 +01005248 // Emit move to `out` before the `Cmp`, as `Mov` might affect the status flags.
5249 __ Mov(out, 0);
5250 __ Cmp(RegisterFrom(left), RegisterFrom(right)); // Signed compare.
5251 less_cond = lt;
5252 break;
5253 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005254 case DataType::Type::kInt64: {
Artem Serov02d37832016-10-25 15:25:33 +01005255 __ Cmp(HighRegisterFrom(left), HighRegisterFrom(right)); // Signed compare.
Andreas Gampe3db70682018-12-26 15:12:03 -08005256 __ B(lt, &less, /* is_far_target= */ false);
5257 __ B(gt, &greater, /* is_far_target= */ false);
Artem Serov02d37832016-10-25 15:25:33 +01005258 // Emit move to `out` before the last `Cmp`, as `Mov` might affect the status flags.
5259 __ Mov(out, 0);
5260 __ Cmp(LowRegisterFrom(left), LowRegisterFrom(right)); // Unsigned compare.
5261 less_cond = lo;
5262 break;
5263 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005264 case DataType::Type::kFloat32:
5265 case DataType::Type::kFloat64: {
Artem Serov02d37832016-10-25 15:25:33 +01005266 __ Mov(out, 0);
Donghui Bai426b49c2016-11-08 14:55:38 +08005267 GenerateVcmp(compare, codegen_);
Artem Serov02d37832016-10-25 15:25:33 +01005268 // To branch on the FP compare result we transfer FPSCR to APSR (encoded as PC in VMRS).
5269 __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
5270 less_cond = ARMFPCondition(kCondLT, compare->IsGtBias());
5271 break;
5272 }
5273 default:
5274 LOG(FATAL) << "Unexpected compare type " << type;
5275 UNREACHABLE();
5276 }
5277
Andreas Gampe3db70682018-12-26 15:12:03 -08005278 __ B(eq, final_label, /* is_far_target= */ false);
5279 __ B(less_cond, &less, /* is_far_target= */ false);
Artem Serov02d37832016-10-25 15:25:33 +01005280
5281 __ Bind(&greater);
5282 __ Mov(out, 1);
Anton Kirilov6f644202017-02-27 18:29:45 +00005283 __ B(final_label);
Artem Serov02d37832016-10-25 15:25:33 +01005284
5285 __ Bind(&less);
5286 __ Mov(out, -1);
5287
Anton Kirilov6f644202017-02-27 18:29:45 +00005288 if (done.IsReferenced()) {
5289 __ Bind(&done);
5290 }
Artem Serov02d37832016-10-25 15:25:33 +01005291}
5292
5293void LocationsBuilderARMVIXL::VisitPhi(HPhi* instruction) {
5294 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005295 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01005296 for (size_t i = 0, e = locations->GetInputCount(); i < e; ++i) {
5297 locations->SetInAt(i, Location::Any());
5298 }
5299 locations->SetOut(Location::Any());
5300}
5301
5302void InstructionCodeGeneratorARMVIXL::VisitPhi(HPhi* instruction ATTRIBUTE_UNUSED) {
5303 LOG(FATAL) << "Unreachable";
5304}
5305
5306void CodeGeneratorARMVIXL::GenerateMemoryBarrier(MemBarrierKind kind) {
5307 // TODO (ported from quick): revisit ARM barrier kinds.
5308 DmbOptions flavor = DmbOptions::ISH; // Quiet C++ warnings.
5309 switch (kind) {
5310 case MemBarrierKind::kAnyStore:
5311 case MemBarrierKind::kLoadAny:
5312 case MemBarrierKind::kAnyAny: {
5313 flavor = DmbOptions::ISH;
5314 break;
5315 }
5316 case MemBarrierKind::kStoreStore: {
5317 flavor = DmbOptions::ISHST;
5318 break;
5319 }
5320 default:
5321 LOG(FATAL) << "Unexpected memory barrier " << kind;
5322 }
5323 __ Dmb(flavor);
5324}
5325
5326void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicLoad(vixl32::Register addr,
5327 uint32_t offset,
5328 vixl32::Register out_lo,
5329 vixl32::Register out_hi) {
5330 UseScratchRegisterScope temps(GetVIXLAssembler());
5331 if (offset != 0) {
5332 vixl32::Register temp = temps.Acquire();
5333 __ Add(temp, addr, offset);
5334 addr = temp;
5335 }
Scott Wakelingb77051e2016-11-21 19:46:00 +00005336 __ Ldrexd(out_lo, out_hi, MemOperand(addr));
Artem Serov02d37832016-10-25 15:25:33 +01005337}
5338
5339void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicStore(vixl32::Register addr,
5340 uint32_t offset,
5341 vixl32::Register value_lo,
5342 vixl32::Register value_hi,
5343 vixl32::Register temp1,
5344 vixl32::Register temp2,
5345 HInstruction* instruction) {
5346 UseScratchRegisterScope temps(GetVIXLAssembler());
5347 vixl32::Label fail;
5348 if (offset != 0) {
5349 vixl32::Register temp = temps.Acquire();
5350 __ Add(temp, addr, offset);
5351 addr = temp;
5352 }
5353 __ Bind(&fail);
Alexandre Rames374ddf32016-11-04 10:40:49 +00005354 {
5355 // Ensure the pc position is recorded immediately after the `ldrexd` instruction.
Artem Serov0fb37192016-12-06 18:13:40 +00005356 ExactAssemblyScope aas(GetVIXLAssembler(),
5357 vixl32::kMaxInstructionSizeInBytes,
5358 CodeBufferCheckScope::kMaximumSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00005359 // We need a load followed by store. (The address used in a STREX instruction must
5360 // be the same as the address in the most recently executed LDREX instruction.)
5361 __ ldrexd(temp1, temp2, MemOperand(addr));
5362 codegen_->MaybeRecordImplicitNullCheck(instruction);
5363 }
Scott Wakelingb77051e2016-11-21 19:46:00 +00005364 __ Strexd(temp1, value_lo, value_hi, MemOperand(addr));
xueliang.zhongf51bc622016-11-04 09:23:32 +00005365 __ CompareAndBranchIfNonZero(temp1, &fail);
Artem Serov02d37832016-10-25 15:25:33 +01005366}
Artem Serov02109dd2016-09-23 17:17:54 +01005367
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005368void LocationsBuilderARMVIXL::HandleFieldSet(
5369 HInstruction* instruction, const FieldInfo& field_info) {
5370 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
5371
5372 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005373 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005374 locations->SetInAt(0, Location::RequiresRegister());
5375
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005376 DataType::Type field_type = field_info.GetFieldType();
5377 if (DataType::IsFloatingPointType(field_type)) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005378 locations->SetInAt(1, Location::RequiresFpuRegister());
5379 } else {
5380 locations->SetInAt(1, Location::RequiresRegister());
5381 }
5382
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005383 bool is_wide = field_type == DataType::Type::kInt64 || field_type == DataType::Type::kFloat64;
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005384 bool generate_volatile = field_info.IsVolatile()
5385 && is_wide
5386 && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
5387 bool needs_write_barrier =
5388 CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1));
5389 // Temporary registers for the write barrier.
5390 // TODO: consider renaming StoreNeedsWriteBarrier to StoreNeedsGCMark.
5391 if (needs_write_barrier) {
5392 locations->AddTemp(Location::RequiresRegister()); // Possibly used for reference poisoning too.
5393 locations->AddTemp(Location::RequiresRegister());
5394 } else if (generate_volatile) {
5395 // ARM encoding have some additional constraints for ldrexd/strexd:
5396 // - registers need to be consecutive
5397 // - the first register should be even but not R14.
5398 // We don't test for ARM yet, and the assertion makes sure that we
5399 // revisit this if we ever enable ARM encoding.
5400 DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet());
5401
5402 locations->AddTemp(Location::RequiresRegister());
5403 locations->AddTemp(Location::RequiresRegister());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005404 if (field_type == DataType::Type::kFloat64) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005405 // For doubles we need two more registers to copy the value.
5406 locations->AddTemp(LocationFrom(r2));
5407 locations->AddTemp(LocationFrom(r3));
5408 }
5409 }
5410}
5411
5412void InstructionCodeGeneratorARMVIXL::HandleFieldSet(HInstruction* instruction,
5413 const FieldInfo& field_info,
5414 bool value_can_be_null) {
5415 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
5416
5417 LocationSummary* locations = instruction->GetLocations();
5418 vixl32::Register base = InputRegisterAt(instruction, 0);
5419 Location value = locations->InAt(1);
5420
5421 bool is_volatile = field_info.IsVolatile();
5422 bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005423 DataType::Type field_type = field_info.GetFieldType();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005424 uint32_t offset = field_info.GetFieldOffset().Uint32Value();
5425 bool needs_write_barrier =
5426 CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1));
5427
5428 if (is_volatile) {
5429 codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
5430 }
5431
5432 switch (field_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005433 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005434 case DataType::Type::kUint8:
5435 case DataType::Type::kInt8:
5436 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005437 case DataType::Type::kInt16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005438 case DataType::Type::kInt32: {
5439 StoreOperandType operand_type = GetStoreOperandType(field_type);
5440 GetAssembler()->StoreToOffset(operand_type, RegisterFrom(value), base, offset);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005441 break;
5442 }
5443
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005444 case DataType::Type::kReference: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005445 if (kPoisonHeapReferences && needs_write_barrier) {
5446 // Note that in the case where `value` is a null reference,
5447 // we do not enter this block, as a null reference does not
5448 // need poisoning.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005449 DCHECK_EQ(field_type, DataType::Type::kReference);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005450 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
5451 __ Mov(temp, RegisterFrom(value));
5452 GetAssembler()->PoisonHeapReference(temp);
5453 GetAssembler()->StoreToOffset(kStoreWord, temp, base, offset);
5454 } else {
5455 GetAssembler()->StoreToOffset(kStoreWord, RegisterFrom(value), base, offset);
5456 }
5457 break;
5458 }
5459
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005460 case DataType::Type::kInt64: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005461 if (is_volatile && !atomic_ldrd_strd) {
5462 GenerateWideAtomicStore(base,
5463 offset,
5464 LowRegisterFrom(value),
5465 HighRegisterFrom(value),
5466 RegisterFrom(locations->GetTemp(0)),
5467 RegisterFrom(locations->GetTemp(1)),
5468 instruction);
5469 } else {
5470 GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), base, offset);
5471 codegen_->MaybeRecordImplicitNullCheck(instruction);
5472 }
5473 break;
5474 }
5475
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005476 case DataType::Type::kFloat32: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005477 GetAssembler()->StoreSToOffset(SRegisterFrom(value), base, offset);
5478 break;
5479 }
5480
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005481 case DataType::Type::kFloat64: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01005482 vixl32::DRegister value_reg = DRegisterFrom(value);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005483 if (is_volatile && !atomic_ldrd_strd) {
5484 vixl32::Register value_reg_lo = RegisterFrom(locations->GetTemp(0));
5485 vixl32::Register value_reg_hi = RegisterFrom(locations->GetTemp(1));
5486
5487 __ Vmov(value_reg_lo, value_reg_hi, value_reg);
5488
5489 GenerateWideAtomicStore(base,
5490 offset,
5491 value_reg_lo,
5492 value_reg_hi,
5493 RegisterFrom(locations->GetTemp(2)),
5494 RegisterFrom(locations->GetTemp(3)),
5495 instruction);
5496 } else {
5497 GetAssembler()->StoreDToOffset(value_reg, base, offset);
5498 codegen_->MaybeRecordImplicitNullCheck(instruction);
5499 }
5500 break;
5501 }
5502
Aart Bik66c158e2018-01-31 12:55:04 -08005503 case DataType::Type::kUint32:
5504 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005505 case DataType::Type::kVoid:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005506 LOG(FATAL) << "Unreachable type " << field_type;
5507 UNREACHABLE();
5508 }
5509
5510 // Longs and doubles are handled in the switch.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005511 if (field_type != DataType::Type::kInt64 && field_type != DataType::Type::kFloat64) {
Alexandre Rames374ddf32016-11-04 10:40:49 +00005512 // TODO(VIXL): Here and for other calls to `MaybeRecordImplicitNullCheck` in this method, we
5513 // should use a scope and the assembler to emit the store instruction to guarantee that we
5514 // record the pc at the correct position. But the `Assembler` does not automatically handle
5515 // unencodable offsets. Practically, everything is fine because the helper and VIXL, at the time
5516 // of writing, do generate the store instruction last.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005517 codegen_->MaybeRecordImplicitNullCheck(instruction);
5518 }
5519
5520 if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
5521 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
5522 vixl32::Register card = RegisterFrom(locations->GetTemp(1));
5523 codegen_->MarkGCCard(temp, card, base, RegisterFrom(value), value_can_be_null);
5524 }
5525
5526 if (is_volatile) {
5527 codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
5528 }
5529}
5530
Artem Serov02d37832016-10-25 15:25:33 +01005531void LocationsBuilderARMVIXL::HandleFieldGet(HInstruction* instruction,
5532 const FieldInfo& field_info) {
5533 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
5534
5535 bool object_field_get_with_read_barrier =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005536 kEmitCompilerReadBarrier && (field_info.GetFieldType() == DataType::Type::kReference);
Artem Serov02d37832016-10-25 15:25:33 +01005537 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005538 new (GetGraph()->GetAllocator()) LocationSummary(instruction,
5539 object_field_get_with_read_barrier
5540 ? LocationSummary::kCallOnSlowPath
5541 : LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01005542 if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
5543 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
5544 }
5545 locations->SetInAt(0, Location::RequiresRegister());
5546
5547 bool volatile_for_double = field_info.IsVolatile()
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005548 && (field_info.GetFieldType() == DataType::Type::kFloat64)
Artem Serov02d37832016-10-25 15:25:33 +01005549 && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
5550 // The output overlaps in case of volatile long: we don't want the
5551 // code generated by GenerateWideAtomicLoad to overwrite the
5552 // object's location. Likewise, in the case of an object field get
5553 // with read barriers enabled, we do not want the load to overwrite
5554 // the object's location, as we need it to emit the read barrier.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005555 bool overlap =
5556 (field_info.IsVolatile() && (field_info.GetFieldType() == DataType::Type::kInt64)) ||
Artem Serov02d37832016-10-25 15:25:33 +01005557 object_field_get_with_read_barrier;
5558
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005559 if (DataType::IsFloatingPointType(instruction->GetType())) {
Artem Serov02d37832016-10-25 15:25:33 +01005560 locations->SetOut(Location::RequiresFpuRegister());
5561 } else {
5562 locations->SetOut(Location::RequiresRegister(),
5563 (overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap));
5564 }
5565 if (volatile_for_double) {
5566 // ARM encoding have some additional constraints for ldrexd/strexd:
5567 // - registers need to be consecutive
5568 // - the first register should be even but not R14.
5569 // We don't test for ARM yet, and the assertion makes sure that we
5570 // revisit this if we ever enable ARM encoding.
5571 DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet());
5572 locations->AddTemp(Location::RequiresRegister());
5573 locations->AddTemp(Location::RequiresRegister());
5574 } else if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
Vladimir Marko008e09f32018-08-06 15:42:43 +01005575 // We need a temporary register for the read barrier load in
5576 // CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier()
5577 // only if the offset is too big.
5578 if (field_info.GetFieldOffset().Uint32Value() >= kReferenceLoadMinFarOffset) {
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01005579 locations->AddTemp(Location::RequiresRegister());
5580 }
Artem Serov02d37832016-10-25 15:25:33 +01005581 }
5582}
5583
5584Location LocationsBuilderARMVIXL::ArithmeticZeroOrFpuRegister(HInstruction* input) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005585 DCHECK(DataType::IsFloatingPointType(input->GetType())) << input->GetType();
Artem Serov02d37832016-10-25 15:25:33 +01005586 if ((input->IsFloatConstant() && (input->AsFloatConstant()->IsArithmeticZero())) ||
5587 (input->IsDoubleConstant() && (input->AsDoubleConstant()->IsArithmeticZero()))) {
5588 return Location::ConstantLocation(input->AsConstant());
5589 } else {
5590 return Location::RequiresFpuRegister();
5591 }
5592}
5593
Artem Serov02109dd2016-09-23 17:17:54 +01005594Location LocationsBuilderARMVIXL::ArmEncodableConstantOrRegister(HInstruction* constant,
5595 Opcode opcode) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005596 DCHECK(!DataType::IsFloatingPointType(constant->GetType()));
Artem Serov02109dd2016-09-23 17:17:54 +01005597 if (constant->IsConstant() &&
5598 CanEncodeConstantAsImmediate(constant->AsConstant(), opcode)) {
5599 return Location::ConstantLocation(constant->AsConstant());
5600 }
5601 return Location::RequiresRegister();
5602}
5603
Vladimir Markof0a6a1d2018-01-08 14:23:56 +00005604static bool CanEncode32BitConstantAsImmediate(
5605 CodeGeneratorARMVIXL* codegen,
5606 uint32_t value,
5607 Opcode opcode,
5608 vixl32::FlagsUpdate flags_update = vixl32::FlagsUpdate::DontCare) {
5609 ArmVIXLAssembler* assembler = codegen->GetAssembler();
5610 if (assembler->ShifterOperandCanHold(opcode, value, flags_update)) {
Artem Serov02109dd2016-09-23 17:17:54 +01005611 return true;
5612 }
5613 Opcode neg_opcode = kNoOperand;
Anton Kiriloveffd5bf2017-02-28 16:59:15 +00005614 uint32_t neg_value = 0;
Artem Serov02109dd2016-09-23 17:17:54 +01005615 switch (opcode) {
Anton Kiriloveffd5bf2017-02-28 16:59:15 +00005616 case AND: neg_opcode = BIC; neg_value = ~value; break;
5617 case ORR: neg_opcode = ORN; neg_value = ~value; break;
5618 case ADD: neg_opcode = SUB; neg_value = -value; break;
5619 case ADC: neg_opcode = SBC; neg_value = ~value; break;
5620 case SUB: neg_opcode = ADD; neg_value = -value; break;
5621 case SBC: neg_opcode = ADC; neg_value = ~value; break;
5622 case MOV: neg_opcode = MVN; neg_value = ~value; break;
Artem Serov02109dd2016-09-23 17:17:54 +01005623 default:
5624 return false;
5625 }
Anton Kiriloveffd5bf2017-02-28 16:59:15 +00005626
Vladimir Markof0a6a1d2018-01-08 14:23:56 +00005627 if (assembler->ShifterOperandCanHold(neg_opcode, neg_value, flags_update)) {
Anton Kiriloveffd5bf2017-02-28 16:59:15 +00005628 return true;
5629 }
5630
5631 return opcode == AND && IsPowerOfTwo(value + 1);
Artem Serov02109dd2016-09-23 17:17:54 +01005632}
5633
Vladimir Markof0a6a1d2018-01-08 14:23:56 +00005634bool LocationsBuilderARMVIXL::CanEncodeConstantAsImmediate(HConstant* input_cst, Opcode opcode) {
5635 uint64_t value = static_cast<uint64_t>(Int64FromConstant(input_cst));
5636 if (DataType::Is64BitType(input_cst->GetType())) {
5637 Opcode high_opcode = opcode;
5638 vixl32::FlagsUpdate low_flags_update = vixl32::FlagsUpdate::DontCare;
5639 switch (opcode) {
5640 case SUB:
5641 // Flip the operation to an ADD.
5642 value = -value;
5643 opcode = ADD;
5644 FALLTHROUGH_INTENDED;
5645 case ADD:
5646 if (Low32Bits(value) == 0u) {
5647 return CanEncode32BitConstantAsImmediate(codegen_, High32Bits(value), opcode);
5648 }
5649 high_opcode = ADC;
5650 low_flags_update = vixl32::FlagsUpdate::SetFlags;
5651 break;
5652 default:
5653 break;
5654 }
5655 return CanEncode32BitConstantAsImmediate(codegen_, High32Bits(value), high_opcode) &&
5656 CanEncode32BitConstantAsImmediate(codegen_, Low32Bits(value), opcode, low_flags_update);
5657 } else {
5658 return CanEncode32BitConstantAsImmediate(codegen_, Low32Bits(value), opcode);
5659 }
5660}
5661
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005662void InstructionCodeGeneratorARMVIXL::HandleFieldGet(HInstruction* instruction,
5663 const FieldInfo& field_info) {
5664 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
5665
5666 LocationSummary* locations = instruction->GetLocations();
5667 vixl32::Register base = InputRegisterAt(instruction, 0);
5668 Location out = locations->Out();
5669 bool is_volatile = field_info.IsVolatile();
5670 bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
Vladimir Marko61b92282017-10-11 13:23:17 +01005671 DCHECK_EQ(DataType::Size(field_info.GetFieldType()), DataType::Size(instruction->GetType()));
5672 DataType::Type load_type = instruction->GetType();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005673 uint32_t offset = field_info.GetFieldOffset().Uint32Value();
5674
Vladimir Marko61b92282017-10-11 13:23:17 +01005675 switch (load_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005676 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005677 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005678 case DataType::Type::kInt8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005679 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005680 case DataType::Type::kInt16:
5681 case DataType::Type::kInt32: {
Vladimir Marko61b92282017-10-11 13:23:17 +01005682 LoadOperandType operand_type = GetLoadOperandType(load_type);
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005683 GetAssembler()->LoadFromOffset(operand_type, RegisterFrom(out), base, offset);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005684 break;
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005685 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005686
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005687 case DataType::Type::kReference: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005688 // /* HeapReference<Object> */ out = *(base + offset)
5689 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
Vladimir Markodcd117e2018-04-19 11:54:00 +01005690 Location maybe_temp = (locations->GetTempCount() != 0) ? locations->GetTemp(0) : Location();
Anton Kirilovedb2ac32016-11-30 15:14:10 +00005691 // Note that a potential implicit null check is handled in this
5692 // CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier call.
5693 codegen_->GenerateFieldLoadWithBakerReadBarrier(
Andreas Gampe3db70682018-12-26 15:12:03 -08005694 instruction, out, base, offset, maybe_temp, /* needs_null_check= */ true);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00005695 if (is_volatile) {
5696 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
5697 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005698 } else {
5699 GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(out), base, offset);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005700 codegen_->MaybeRecordImplicitNullCheck(instruction);
5701 if (is_volatile) {
5702 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
5703 }
5704 // If read barriers are enabled, emit read barriers other than
5705 // Baker's using a slow path (and also unpoison the loaded
5706 // reference, if heap poisoning is enabled).
5707 codegen_->MaybeGenerateReadBarrierSlow(instruction, out, out, locations->InAt(0), offset);
5708 }
5709 break;
5710 }
5711
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005712 case DataType::Type::kInt64:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005713 if (is_volatile && !atomic_ldrd_strd) {
5714 GenerateWideAtomicLoad(base, offset, LowRegisterFrom(out), HighRegisterFrom(out));
5715 } else {
5716 GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out), base, offset);
5717 }
5718 break;
5719
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005720 case DataType::Type::kFloat32:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005721 GetAssembler()->LoadSFromOffset(SRegisterFrom(out), base, offset);
5722 break;
5723
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005724 case DataType::Type::kFloat64: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01005725 vixl32::DRegister out_dreg = DRegisterFrom(out);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005726 if (is_volatile && !atomic_ldrd_strd) {
5727 vixl32::Register lo = RegisterFrom(locations->GetTemp(0));
5728 vixl32::Register hi = RegisterFrom(locations->GetTemp(1));
5729 GenerateWideAtomicLoad(base, offset, lo, hi);
5730 // TODO(VIXL): Do we need to be immediately after the ldrexd instruction? If so we need a
5731 // scope.
5732 codegen_->MaybeRecordImplicitNullCheck(instruction);
5733 __ Vmov(out_dreg, lo, hi);
5734 } else {
5735 GetAssembler()->LoadDFromOffset(out_dreg, base, offset);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005736 codegen_->MaybeRecordImplicitNullCheck(instruction);
5737 }
5738 break;
5739 }
5740
Aart Bik66c158e2018-01-31 12:55:04 -08005741 case DataType::Type::kUint32:
5742 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005743 case DataType::Type::kVoid:
Vladimir Marko61b92282017-10-11 13:23:17 +01005744 LOG(FATAL) << "Unreachable type " << load_type;
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005745 UNREACHABLE();
5746 }
5747
Vladimir Marko61b92282017-10-11 13:23:17 +01005748 if (load_type == DataType::Type::kReference || load_type == DataType::Type::kFloat64) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005749 // Potential implicit null checks, in the case of reference or
5750 // double fields, are handled in the previous switch statement.
5751 } else {
5752 // Address cases other than reference and double that may require an implicit null check.
Alexandre Rames374ddf32016-11-04 10:40:49 +00005753 // TODO(VIXL): Here and for other calls to `MaybeRecordImplicitNullCheck` in this method, we
5754 // should use a scope and the assembler to emit the load instruction to guarantee that we
5755 // record the pc at the correct position. But the `Assembler` does not automatically handle
5756 // unencodable offsets. Practically, everything is fine because the helper and VIXL, at the time
5757 // of writing, do generate the store instruction last.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005758 codegen_->MaybeRecordImplicitNullCheck(instruction);
5759 }
5760
5761 if (is_volatile) {
Vladimir Marko61b92282017-10-11 13:23:17 +01005762 if (load_type == DataType::Type::kReference) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005763 // Memory barriers, in the case of references, are also handled
5764 // in the previous switch statement.
5765 } else {
5766 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
5767 }
5768 }
5769}
5770
5771void LocationsBuilderARMVIXL::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
5772 HandleFieldSet(instruction, instruction->GetFieldInfo());
5773}
5774
5775void InstructionCodeGeneratorARMVIXL::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
5776 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
5777}
5778
5779void LocationsBuilderARMVIXL::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
5780 HandleFieldGet(instruction, instruction->GetFieldInfo());
5781}
5782
5783void InstructionCodeGeneratorARMVIXL::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
5784 HandleFieldGet(instruction, instruction->GetFieldInfo());
5785}
5786
5787void LocationsBuilderARMVIXL::VisitStaticFieldGet(HStaticFieldGet* instruction) {
5788 HandleFieldGet(instruction, instruction->GetFieldInfo());
5789}
5790
5791void InstructionCodeGeneratorARMVIXL::VisitStaticFieldGet(HStaticFieldGet* instruction) {
5792 HandleFieldGet(instruction, instruction->GetFieldInfo());
5793}
5794
Scott Wakelingc34dba72016-10-03 10:14:44 +01005795void LocationsBuilderARMVIXL::VisitStaticFieldSet(HStaticFieldSet* instruction) {
5796 HandleFieldSet(instruction, instruction->GetFieldInfo());
5797}
5798
5799void InstructionCodeGeneratorARMVIXL::VisitStaticFieldSet(HStaticFieldSet* instruction) {
5800 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
5801}
5802
Vladimir Marko552a1342017-10-31 10:56:47 +00005803void LocationsBuilderARMVIXL::VisitStringBuilderAppend(HStringBuilderAppend* instruction) {
5804 codegen_->CreateStringBuilderAppendLocations(instruction, LocationFrom(r0));
5805}
5806
5807void InstructionCodeGeneratorARMVIXL::VisitStringBuilderAppend(HStringBuilderAppend* instruction) {
5808 __ Mov(r0, instruction->GetFormat()->GetValue());
5809 codegen_->InvokeRuntime(kQuickStringBuilderAppend, instruction, instruction->GetDexPc());
5810}
5811
Artem Serovcfbe9132016-10-14 15:58:56 +01005812void LocationsBuilderARMVIXL::VisitUnresolvedInstanceFieldGet(
5813 HUnresolvedInstanceFieldGet* instruction) {
5814 FieldAccessCallingConventionARMVIXL calling_convention;
5815 codegen_->CreateUnresolvedFieldLocationSummary(
5816 instruction, instruction->GetFieldType(), calling_convention);
5817}
5818
5819void InstructionCodeGeneratorARMVIXL::VisitUnresolvedInstanceFieldGet(
5820 HUnresolvedInstanceFieldGet* instruction) {
5821 FieldAccessCallingConventionARMVIXL calling_convention;
5822 codegen_->GenerateUnresolvedFieldAccess(instruction,
5823 instruction->GetFieldType(),
5824 instruction->GetFieldIndex(),
5825 instruction->GetDexPc(),
5826 calling_convention);
5827}
5828
5829void LocationsBuilderARMVIXL::VisitUnresolvedInstanceFieldSet(
5830 HUnresolvedInstanceFieldSet* instruction) {
5831 FieldAccessCallingConventionARMVIXL calling_convention;
5832 codegen_->CreateUnresolvedFieldLocationSummary(
5833 instruction, instruction->GetFieldType(), calling_convention);
5834}
5835
5836void InstructionCodeGeneratorARMVIXL::VisitUnresolvedInstanceFieldSet(
5837 HUnresolvedInstanceFieldSet* instruction) {
5838 FieldAccessCallingConventionARMVIXL calling_convention;
5839 codegen_->GenerateUnresolvedFieldAccess(instruction,
5840 instruction->GetFieldType(),
5841 instruction->GetFieldIndex(),
5842 instruction->GetDexPc(),
5843 calling_convention);
5844}
5845
5846void LocationsBuilderARMVIXL::VisitUnresolvedStaticFieldGet(
5847 HUnresolvedStaticFieldGet* instruction) {
5848 FieldAccessCallingConventionARMVIXL calling_convention;
5849 codegen_->CreateUnresolvedFieldLocationSummary(
5850 instruction, instruction->GetFieldType(), calling_convention);
5851}
5852
5853void InstructionCodeGeneratorARMVIXL::VisitUnresolvedStaticFieldGet(
5854 HUnresolvedStaticFieldGet* instruction) {
5855 FieldAccessCallingConventionARMVIXL calling_convention;
5856 codegen_->GenerateUnresolvedFieldAccess(instruction,
5857 instruction->GetFieldType(),
5858 instruction->GetFieldIndex(),
5859 instruction->GetDexPc(),
5860 calling_convention);
5861}
5862
5863void LocationsBuilderARMVIXL::VisitUnresolvedStaticFieldSet(
5864 HUnresolvedStaticFieldSet* instruction) {
5865 FieldAccessCallingConventionARMVIXL calling_convention;
5866 codegen_->CreateUnresolvedFieldLocationSummary(
5867 instruction, instruction->GetFieldType(), calling_convention);
5868}
5869
5870void InstructionCodeGeneratorARMVIXL::VisitUnresolvedStaticFieldSet(
5871 HUnresolvedStaticFieldSet* instruction) {
5872 FieldAccessCallingConventionARMVIXL calling_convention;
5873 codegen_->GenerateUnresolvedFieldAccess(instruction,
5874 instruction->GetFieldType(),
5875 instruction->GetFieldIndex(),
5876 instruction->GetDexPc(),
5877 calling_convention);
5878}
5879
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005880void LocationsBuilderARMVIXL::VisitNullCheck(HNullCheck* instruction) {
Artem Serov657022c2016-11-23 14:19:38 +00005881 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005882 locations->SetInAt(0, Location::RequiresRegister());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005883}
5884
5885void CodeGeneratorARMVIXL::GenerateImplicitNullCheck(HNullCheck* instruction) {
5886 if (CanMoveNullCheckToUser(instruction)) {
5887 return;
5888 }
5889
5890 UseScratchRegisterScope temps(GetVIXLAssembler());
Alexandre Rames374ddf32016-11-04 10:40:49 +00005891 // Ensure the pc position is recorded immediately after the `ldr` instruction.
Artem Serov0fb37192016-12-06 18:13:40 +00005892 ExactAssemblyScope aas(GetVIXLAssembler(),
5893 vixl32::kMaxInstructionSizeInBytes,
5894 CodeBufferCheckScope::kMaximumSize);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005895 __ ldr(temps.Acquire(), MemOperand(InputRegisterAt(instruction, 0)));
5896 RecordPcInfo(instruction, instruction->GetDexPc());
5897}
5898
5899void CodeGeneratorARMVIXL::GenerateExplicitNullCheck(HNullCheck* instruction) {
5900 NullCheckSlowPathARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01005901 new (GetScopedAllocator()) NullCheckSlowPathARMVIXL(instruction);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005902 AddSlowPath(slow_path);
xueliang.zhongf51bc622016-11-04 09:23:32 +00005903 __ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005904}
5905
5906void InstructionCodeGeneratorARMVIXL::VisitNullCheck(HNullCheck* instruction) {
5907 codegen_->GenerateNullCheck(instruction);
5908}
5909
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005910void CodeGeneratorARMVIXL::LoadFromShiftedRegOffset(DataType::Type type,
Scott Wakelingc34dba72016-10-03 10:14:44 +01005911 Location out_loc,
5912 vixl32::Register base,
5913 vixl32::Register reg_index,
5914 vixl32::Condition cond) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005915 uint32_t shift_count = DataType::SizeShift(type);
Scott Wakelingc34dba72016-10-03 10:14:44 +01005916 MemOperand mem_address(base, reg_index, vixl32::LSL, shift_count);
5917
5918 switch (type) {
Vladimir Marko61b92282017-10-11 13:23:17 +01005919 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005920 case DataType::Type::kUint8:
Vladimir Marko61b92282017-10-11 13:23:17 +01005921 __ Ldrb(cond, RegisterFrom(out_loc), mem_address);
5922 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005923 case DataType::Type::kInt8:
Scott Wakelingc34dba72016-10-03 10:14:44 +01005924 __ Ldrsb(cond, RegisterFrom(out_loc), mem_address);
5925 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005926 case DataType::Type::kUint16:
Scott Wakelingc34dba72016-10-03 10:14:44 +01005927 __ Ldrh(cond, RegisterFrom(out_loc), mem_address);
5928 break;
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005929 case DataType::Type::kInt16:
5930 __ Ldrsh(cond, RegisterFrom(out_loc), mem_address);
5931 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005932 case DataType::Type::kReference:
5933 case DataType::Type::kInt32:
Scott Wakelingc34dba72016-10-03 10:14:44 +01005934 __ Ldr(cond, RegisterFrom(out_loc), mem_address);
5935 break;
5936 // T32 doesn't support LoadFromShiftedRegOffset mem address mode for these types.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005937 case DataType::Type::kInt64:
5938 case DataType::Type::kFloat32:
5939 case DataType::Type::kFloat64:
Scott Wakelingc34dba72016-10-03 10:14:44 +01005940 default:
5941 LOG(FATAL) << "Unreachable type " << type;
5942 UNREACHABLE();
5943 }
5944}
5945
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005946void CodeGeneratorARMVIXL::StoreToShiftedRegOffset(DataType::Type type,
Scott Wakelingc34dba72016-10-03 10:14:44 +01005947 Location loc,
5948 vixl32::Register base,
5949 vixl32::Register reg_index,
5950 vixl32::Condition cond) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005951 uint32_t shift_count = DataType::SizeShift(type);
Scott Wakelingc34dba72016-10-03 10:14:44 +01005952 MemOperand mem_address(base, reg_index, vixl32::LSL, shift_count);
5953
5954 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005955 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005956 case DataType::Type::kUint8:
5957 case DataType::Type::kInt8:
Scott Wakelingc34dba72016-10-03 10:14:44 +01005958 __ Strb(cond, RegisterFrom(loc), mem_address);
5959 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005960 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005961 case DataType::Type::kInt16:
Scott Wakelingc34dba72016-10-03 10:14:44 +01005962 __ Strh(cond, RegisterFrom(loc), mem_address);
5963 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005964 case DataType::Type::kReference:
5965 case DataType::Type::kInt32:
Scott Wakelingc34dba72016-10-03 10:14:44 +01005966 __ Str(cond, RegisterFrom(loc), mem_address);
5967 break;
5968 // T32 doesn't support StoreToShiftedRegOffset mem address mode for these types.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005969 case DataType::Type::kInt64:
5970 case DataType::Type::kFloat32:
5971 case DataType::Type::kFloat64:
Scott Wakelingc34dba72016-10-03 10:14:44 +01005972 default:
5973 LOG(FATAL) << "Unreachable type " << type;
5974 UNREACHABLE();
5975 }
5976}
5977
5978void LocationsBuilderARMVIXL::VisitArrayGet(HArrayGet* instruction) {
5979 bool object_array_get_with_read_barrier =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005980 kEmitCompilerReadBarrier && (instruction->GetType() == DataType::Type::kReference);
Scott Wakelingc34dba72016-10-03 10:14:44 +01005981 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005982 new (GetGraph()->GetAllocator()) LocationSummary(instruction,
5983 object_array_get_with_read_barrier
5984 ? LocationSummary::kCallOnSlowPath
5985 : LocationSummary::kNoCall);
Scott Wakelingc34dba72016-10-03 10:14:44 +01005986 if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00005987 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Scott Wakelingc34dba72016-10-03 10:14:44 +01005988 }
5989 locations->SetInAt(0, Location::RequiresRegister());
5990 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005991 if (DataType::IsFloatingPointType(instruction->GetType())) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01005992 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
5993 } else {
5994 // The output overlaps in the case of an object array get with
5995 // read barriers enabled: we do not want the move to overwrite the
5996 // array's location, as we need it to emit the read barrier.
5997 locations->SetOut(
5998 Location::RequiresRegister(),
5999 object_array_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap);
6000 }
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006001 if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
Vladimir Marko008e09f32018-08-06 15:42:43 +01006002 if (instruction->GetIndex()->IsConstant()) {
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006003 // Array loads with constant index are treated as field loads.
Vladimir Marko008e09f32018-08-06 15:42:43 +01006004 // We need a temporary register for the read barrier load in
6005 // CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier()
6006 // only if the offset is too big.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006007 uint32_t offset = CodeGenerator::GetArrayDataOffset(instruction);
6008 uint32_t index = instruction->GetIndex()->AsIntConstant()->GetValue();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006009 offset += index << DataType::SizeShift(DataType::Type::kReference);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006010 if (offset >= kReferenceLoadMinFarOffset) {
6011 locations->AddTemp(Location::RequiresRegister());
6012 }
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006013 } else {
Vladimir Marko008e09f32018-08-06 15:42:43 +01006014 // We need a non-scratch temporary for the array data pointer in
6015 // CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier().
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006016 locations->AddTemp(Location::RequiresRegister());
6017 }
6018 } else if (mirror::kUseStringCompression && instruction->IsStringCharAt()) {
6019 // Also need a temporary for String compression feature.
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006020 locations->AddTemp(Location::RequiresRegister());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006021 }
6022}
6023
6024void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006025 LocationSummary* locations = instruction->GetLocations();
6026 Location obj_loc = locations->InAt(0);
6027 vixl32::Register obj = InputRegisterAt(instruction, 0);
6028 Location index = locations->InAt(1);
6029 Location out_loc = locations->Out();
6030 uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006031 DataType::Type type = instruction->GetType();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006032 const bool maybe_compressed_char_at = mirror::kUseStringCompression &&
6033 instruction->IsStringCharAt();
6034 HInstruction* array_instr = instruction->GetArray();
6035 bool has_intermediate_address = array_instr->IsIntermediateAddress();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006036
6037 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006038 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006039 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006040 case DataType::Type::kInt8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006041 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006042 case DataType::Type::kInt16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006043 case DataType::Type::kInt32: {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01006044 vixl32::Register length;
6045 if (maybe_compressed_char_at) {
6046 length = RegisterFrom(locations->GetTemp(0));
6047 uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
6048 GetAssembler()->LoadFromOffset(kLoadWord, length, obj, count_offset);
6049 codegen_->MaybeRecordImplicitNullCheck(instruction);
6050 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006051 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006052 int32_t const_index = Int32ConstantFrom(index);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006053 if (maybe_compressed_char_at) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006054 vixl32::Label uncompressed_load, done;
Anton Kirilov6f644202017-02-27 18:29:45 +00006055 vixl32::Label* final_label = codegen_->GetFinalLabel(instruction, &done);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01006056 __ Lsrs(length, length, 1u); // LSRS has a 16-bit encoding, TST (immediate) does not.
6057 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
6058 "Expecting 0=compressed, 1=uncompressed");
Andreas Gampe3db70682018-12-26 15:12:03 -08006059 __ B(cs, &uncompressed_load, /* is_far_target= */ false);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006060 GetAssembler()->LoadFromOffset(kLoadUnsignedByte,
6061 RegisterFrom(out_loc),
6062 obj,
6063 data_offset + const_index);
Anton Kirilov6f644202017-02-27 18:29:45 +00006064 __ B(final_label);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006065 __ Bind(&uncompressed_load);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006066 GetAssembler()->LoadFromOffset(GetLoadOperandType(DataType::Type::kUint16),
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006067 RegisterFrom(out_loc),
6068 obj,
6069 data_offset + (const_index << 1));
Anton Kirilov6f644202017-02-27 18:29:45 +00006070 if (done.IsReferenced()) {
6071 __ Bind(&done);
6072 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006073 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006074 uint32_t full_offset = data_offset + (const_index << DataType::SizeShift(type));
Scott Wakelingc34dba72016-10-03 10:14:44 +01006075
6076 LoadOperandType load_type = GetLoadOperandType(type);
6077 GetAssembler()->LoadFromOffset(load_type, RegisterFrom(out_loc), obj, full_offset);
6078 }
6079 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006080 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006081 vixl32::Register temp = temps.Acquire();
6082
6083 if (has_intermediate_address) {
Artem Serov2bbc9532016-10-21 11:51:50 +01006084 // We do not need to compute the intermediate address from the array: the
6085 // input instruction has done it already. See the comment in
6086 // `TryExtractArrayAccessAddress()`.
6087 if (kIsDebugBuild) {
6088 HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
Anton Kirilov644032c2016-12-06 17:51:43 +00006089 DCHECK_EQ(Uint64ConstantFrom(tmp->GetOffset()), data_offset);
Artem Serov2bbc9532016-10-21 11:51:50 +01006090 }
6091 temp = obj;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006092 } else {
6093 __ Add(temp, obj, data_offset);
6094 }
6095 if (maybe_compressed_char_at) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006096 vixl32::Label uncompressed_load, done;
Anton Kirilov6f644202017-02-27 18:29:45 +00006097 vixl32::Label* final_label = codegen_->GetFinalLabel(instruction, &done);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01006098 __ Lsrs(length, length, 1u); // LSRS has a 16-bit encoding, TST (immediate) does not.
6099 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
6100 "Expecting 0=compressed, 1=uncompressed");
Andreas Gampe3db70682018-12-26 15:12:03 -08006101 __ B(cs, &uncompressed_load, /* is_far_target= */ false);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006102 __ Ldrb(RegisterFrom(out_loc), MemOperand(temp, RegisterFrom(index), vixl32::LSL, 0));
Anton Kirilov6f644202017-02-27 18:29:45 +00006103 __ B(final_label);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006104 __ Bind(&uncompressed_load);
6105 __ Ldrh(RegisterFrom(out_loc), MemOperand(temp, RegisterFrom(index), vixl32::LSL, 1));
Anton Kirilov6f644202017-02-27 18:29:45 +00006106 if (done.IsReferenced()) {
6107 __ Bind(&done);
6108 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006109 } else {
6110 codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index));
6111 }
6112 }
6113 break;
6114 }
6115
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006116 case DataType::Type::kReference: {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006117 // The read barrier instrumentation of object ArrayGet
6118 // instructions does not support the HIntermediateAddress
6119 // instruction.
6120 DCHECK(!(has_intermediate_address && kEmitCompilerReadBarrier));
6121
Scott Wakelingc34dba72016-10-03 10:14:44 +01006122 static_assert(
6123 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
6124 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
6125 // /* HeapReference<Object> */ out =
6126 // *(obj + data_offset + index * sizeof(HeapReference<Object>))
6127 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006128 // Note that a potential implicit null check is handled in this
6129 // CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier call.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006130 DCHECK(!instruction->CanDoImplicitNullCheckOn(instruction->InputAt(0)));
6131 if (index.IsConstant()) {
6132 // Array load with a constant index can be treated as a field load.
Vladimir Markodcd117e2018-04-19 11:54:00 +01006133 Location maybe_temp =
6134 (locations->GetTempCount() != 0) ? locations->GetTemp(0) : Location();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006135 data_offset += Int32ConstantFrom(index) << DataType::SizeShift(type);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006136 codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction,
6137 out_loc,
6138 obj,
6139 data_offset,
Vladimir Markodcd117e2018-04-19 11:54:00 +01006140 maybe_temp,
Andreas Gampe3db70682018-12-26 15:12:03 -08006141 /* needs_null_check= */ false);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006142 } else {
Vladimir Markodcd117e2018-04-19 11:54:00 +01006143 Location temp = locations->GetTemp(0);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006144 codegen_->GenerateArrayLoadWithBakerReadBarrier(
Andreas Gampe3db70682018-12-26 15:12:03 -08006145 out_loc, obj, data_offset, index, temp, /* needs_null_check= */ false);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006146 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006147 } else {
6148 vixl32::Register out = OutputRegister(instruction);
6149 if (index.IsConstant()) {
6150 size_t offset =
Anton Kirilov644032c2016-12-06 17:51:43 +00006151 (Int32ConstantFrom(index) << TIMES_4) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006152 GetAssembler()->LoadFromOffset(kLoadWord, out, obj, offset);
Alexandre Rames374ddf32016-11-04 10:40:49 +00006153 // TODO(VIXL): Here and for other calls to `MaybeRecordImplicitNullCheck` in this method,
6154 // we should use a scope and the assembler to emit the load instruction to guarantee that
6155 // we record the pc at the correct position. But the `Assembler` does not automatically
6156 // handle unencodable offsets. Practically, everything is fine because the helper and
6157 // VIXL, at the time of writing, do generate the store instruction last.
Scott Wakelingc34dba72016-10-03 10:14:44 +01006158 codegen_->MaybeRecordImplicitNullCheck(instruction);
6159 // If read barriers are enabled, emit read barriers other than
6160 // Baker's using a slow path (and also unpoison the loaded
6161 // reference, if heap poisoning is enabled).
6162 codegen_->MaybeGenerateReadBarrierSlow(instruction, out_loc, out_loc, obj_loc, offset);
6163 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006164 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006165 vixl32::Register temp = temps.Acquire();
6166
6167 if (has_intermediate_address) {
Artem Serov2bbc9532016-10-21 11:51:50 +01006168 // We do not need to compute the intermediate address from the array: the
6169 // input instruction has done it already. See the comment in
6170 // `TryExtractArrayAccessAddress()`.
6171 if (kIsDebugBuild) {
6172 HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
Anton Kirilov644032c2016-12-06 17:51:43 +00006173 DCHECK_EQ(Uint64ConstantFrom(tmp->GetOffset()), data_offset);
Artem Serov2bbc9532016-10-21 11:51:50 +01006174 }
6175 temp = obj;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006176 } else {
6177 __ Add(temp, obj, data_offset);
6178 }
6179 codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index));
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006180 temps.Close();
Alexandre Rames374ddf32016-11-04 10:40:49 +00006181 // TODO(VIXL): Use a scope to ensure that we record the pc position immediately after the
6182 // load instruction. Practically, everything is fine because the helper and VIXL, at the
6183 // time of writing, do generate the store instruction last.
Scott Wakelingc34dba72016-10-03 10:14:44 +01006184 codegen_->MaybeRecordImplicitNullCheck(instruction);
6185 // If read barriers are enabled, emit read barriers other than
6186 // Baker's using a slow path (and also unpoison the loaded
6187 // reference, if heap poisoning is enabled).
6188 codegen_->MaybeGenerateReadBarrierSlow(
6189 instruction, out_loc, out_loc, obj_loc, data_offset, index);
6190 }
6191 }
6192 break;
6193 }
6194
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006195 case DataType::Type::kInt64: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006196 if (index.IsConstant()) {
6197 size_t offset =
Anton Kirilov644032c2016-12-06 17:51:43 +00006198 (Int32ConstantFrom(index) << TIMES_8) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006199 GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out_loc), obj, offset);
6200 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006201 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006202 vixl32::Register temp = temps.Acquire();
6203 __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
6204 GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out_loc), temp, data_offset);
6205 }
6206 break;
6207 }
6208
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006209 case DataType::Type::kFloat32: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006210 vixl32::SRegister out = SRegisterFrom(out_loc);
6211 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006212 size_t offset = (Int32ConstantFrom(index) << TIMES_4) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006213 GetAssembler()->LoadSFromOffset(out, obj, offset);
6214 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006215 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006216 vixl32::Register temp = temps.Acquire();
6217 __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_4));
6218 GetAssembler()->LoadSFromOffset(out, temp, data_offset);
6219 }
6220 break;
6221 }
6222
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006223 case DataType::Type::kFloat64: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006224 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006225 size_t offset = (Int32ConstantFrom(index) << TIMES_8) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006226 GetAssembler()->LoadDFromOffset(DRegisterFrom(out_loc), obj, offset);
6227 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006228 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006229 vixl32::Register temp = temps.Acquire();
6230 __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
6231 GetAssembler()->LoadDFromOffset(DRegisterFrom(out_loc), temp, data_offset);
6232 }
6233 break;
6234 }
6235
Aart Bik66c158e2018-01-31 12:55:04 -08006236 case DataType::Type::kUint32:
6237 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006238 case DataType::Type::kVoid:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006239 LOG(FATAL) << "Unreachable type " << type;
6240 UNREACHABLE();
6241 }
6242
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006243 if (type == DataType::Type::kReference) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006244 // Potential implicit null checks, in the case of reference
6245 // arrays, are handled in the previous switch statement.
6246 } else if (!maybe_compressed_char_at) {
Alexandre Rames374ddf32016-11-04 10:40:49 +00006247 // TODO(VIXL): Use a scope to ensure we record the pc info immediately after
6248 // the preceding load instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01006249 codegen_->MaybeRecordImplicitNullCheck(instruction);
6250 }
6251}
6252
6253void LocationsBuilderARMVIXL::VisitArraySet(HArraySet* instruction) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006254 DataType::Type value_type = instruction->GetComponentType();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006255
6256 bool needs_write_barrier =
6257 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
Vladimir Marko8fa839c2019-05-16 12:50:47 +00006258 bool needs_type_check = instruction->NeedsTypeCheck();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006259
Vladimir Markoca6fff82017-10-03 14:49:14 +01006260 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
Scott Wakelingc34dba72016-10-03 10:14:44 +01006261 instruction,
Vladimir Marko8fa839c2019-05-16 12:50:47 +00006262 needs_type_check ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006263
6264 locations->SetInAt(0, Location::RequiresRegister());
6265 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006266 if (DataType::IsFloatingPointType(value_type)) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006267 locations->SetInAt(2, Location::RequiresFpuRegister());
6268 } else {
6269 locations->SetInAt(2, Location::RequiresRegister());
6270 }
6271 if (needs_write_barrier) {
6272 // Temporary registers for the write barrier.
6273 locations->AddTemp(Location::RequiresRegister()); // Possibly used for ref. poisoning too.
6274 locations->AddTemp(Location::RequiresRegister());
6275 }
6276}
6277
6278void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006279 LocationSummary* locations = instruction->GetLocations();
6280 vixl32::Register array = InputRegisterAt(instruction, 0);
6281 Location index = locations->InAt(1);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006282 DataType::Type value_type = instruction->GetComponentType();
Vladimir Marko8fa839c2019-05-16 12:50:47 +00006283 bool needs_type_check = instruction->NeedsTypeCheck();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006284 bool needs_write_barrier =
6285 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
6286 uint32_t data_offset =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006287 mirror::Array::DataOffset(DataType::Size(value_type)).Uint32Value();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006288 Location value_loc = locations->InAt(2);
6289 HInstruction* array_instr = instruction->GetArray();
6290 bool has_intermediate_address = array_instr->IsIntermediateAddress();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006291
6292 switch (value_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006293 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006294 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006295 case DataType::Type::kInt8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006296 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006297 case DataType::Type::kInt16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006298 case DataType::Type::kInt32: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006299 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006300 int32_t const_index = Int32ConstantFrom(index);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006301 uint32_t full_offset =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006302 data_offset + (const_index << DataType::SizeShift(value_type));
Scott Wakelingc34dba72016-10-03 10:14:44 +01006303 StoreOperandType store_type = GetStoreOperandType(value_type);
6304 GetAssembler()->StoreToOffset(store_type, RegisterFrom(value_loc), array, full_offset);
6305 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006306 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006307 vixl32::Register temp = temps.Acquire();
6308
6309 if (has_intermediate_address) {
Artem Serov2bbc9532016-10-21 11:51:50 +01006310 // We do not need to compute the intermediate address from the array: the
6311 // input instruction has done it already. See the comment in
6312 // `TryExtractArrayAccessAddress()`.
6313 if (kIsDebugBuild) {
6314 HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
Anton Kirilov644032c2016-12-06 17:51:43 +00006315 DCHECK_EQ(Uint64ConstantFrom(tmp->GetOffset()), data_offset);
Artem Serov2bbc9532016-10-21 11:51:50 +01006316 }
6317 temp = array;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006318 } else {
6319 __ Add(temp, array, data_offset);
6320 }
6321 codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
6322 }
6323 break;
6324 }
6325
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006326 case DataType::Type::kReference: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006327 vixl32::Register value = RegisterFrom(value_loc);
6328 // TryExtractArrayAccessAddress optimization is never applied for non-primitive ArraySet.
6329 // See the comment in instruction_simplifier_shared.cc.
6330 DCHECK(!has_intermediate_address);
6331
6332 if (instruction->InputAt(2)->IsNullConstant()) {
6333 // Just setting null.
6334 if (index.IsConstant()) {
Vladimir Marko8fa839c2019-05-16 12:50:47 +00006335 size_t offset = (Int32ConstantFrom(index) << TIMES_4) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006336 GetAssembler()->StoreToOffset(kStoreWord, value, array, offset);
6337 } else {
6338 DCHECK(index.IsRegister()) << index;
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006339 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006340 vixl32::Register temp = temps.Acquire();
6341 __ Add(temp, array, data_offset);
6342 codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
6343 }
Alexandre Rames374ddf32016-11-04 10:40:49 +00006344 // TODO(VIXL): Use a scope to ensure we record the pc info immediately after the preceding
6345 // store instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01006346 codegen_->MaybeRecordImplicitNullCheck(instruction);
6347 DCHECK(!needs_write_barrier);
Vladimir Marko8fa839c2019-05-16 12:50:47 +00006348 DCHECK(!needs_type_check);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006349 break;
6350 }
6351
6352 DCHECK(needs_write_barrier);
6353 Location temp1_loc = locations->GetTemp(0);
6354 vixl32::Register temp1 = RegisterFrom(temp1_loc);
6355 Location temp2_loc = locations->GetTemp(1);
6356 vixl32::Register temp2 = RegisterFrom(temp2_loc);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006357
Vladimir Marko8fa839c2019-05-16 12:50:47 +00006358 bool can_value_be_null = instruction->GetValueCanBeNull();
6359 vixl32::Label do_store;
6360 if (can_value_be_null) {
6361 __ CompareAndBranchIfZero(value, &do_store, /* is_far_target= */ false);
6362 }
6363
6364 SlowPathCodeARMVIXL* slow_path = nullptr;
6365 if (needs_type_check) {
Vladimir Marko0dda8c82019-05-16 12:47:40 +00006366 slow_path = new (codegen_->GetScopedAllocator()) ArraySetSlowPathARMVIXL(instruction);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006367 codegen_->AddSlowPath(slow_path);
Vladimir Marko8fa839c2019-05-16 12:50:47 +00006368
6369 const uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
6370 const uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
6371 const uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006372
6373 // Note that when read barriers are enabled, the type checks
6374 // are performed without read barriers. This is fine, even in
6375 // the case where a class object is in the from-space after
6376 // the flip, as a comparison involving such a type would not
6377 // produce a false positive; it may of course produce a false
6378 // negative, in which case we would take the ArraySet slow
6379 // path.
6380
Alexandre Rames374ddf32016-11-04 10:40:49 +00006381 {
6382 // Ensure we record the pc position immediately after the `ldr` instruction.
Artem Serov0fb37192016-12-06 18:13:40 +00006383 ExactAssemblyScope aas(GetVIXLAssembler(),
6384 vixl32::kMaxInstructionSizeInBytes,
6385 CodeBufferCheckScope::kMaximumSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00006386 // /* HeapReference<Class> */ temp1 = array->klass_
6387 __ ldr(temp1, MemOperand(array, class_offset));
6388 codegen_->MaybeRecordImplicitNullCheck(instruction);
6389 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006390 GetAssembler()->MaybeUnpoisonHeapReference(temp1);
6391
6392 // /* HeapReference<Class> */ temp1 = temp1->component_type_
6393 GetAssembler()->LoadFromOffset(kLoadWord, temp1, temp1, component_offset);
6394 // /* HeapReference<Class> */ temp2 = value->klass_
6395 GetAssembler()->LoadFromOffset(kLoadWord, temp2, value, class_offset);
6396 // If heap poisoning is enabled, no need to unpoison `temp1`
6397 // nor `temp2`, as we are comparing two poisoned references.
6398 __ Cmp(temp1, temp2);
6399
6400 if (instruction->StaticTypeOfArrayIsObjectArray()) {
Vladimir Marko0dda8c82019-05-16 12:47:40 +00006401 vixl32::Label do_put;
6402 __ B(eq, &do_put, /* is_far_target= */ false);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006403 // If heap poisoning is enabled, the `temp1` reference has
6404 // not been unpoisoned yet; unpoison it now.
6405 GetAssembler()->MaybeUnpoisonHeapReference(temp1);
6406
6407 // /* HeapReference<Class> */ temp1 = temp1->super_class_
6408 GetAssembler()->LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
6409 // If heap poisoning is enabled, no need to unpoison
6410 // `temp1`, as we are comparing against null below.
xueliang.zhongf51bc622016-11-04 09:23:32 +00006411 __ CompareAndBranchIfNonZero(temp1, slow_path->GetEntryLabel());
Vladimir Marko0dda8c82019-05-16 12:47:40 +00006412 __ Bind(&do_put);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006413 } else {
6414 __ B(ne, slow_path->GetEntryLabel());
6415 }
6416 }
6417
Vladimir Marko8fa839c2019-05-16 12:50:47 +00006418 codegen_->MarkGCCard(temp1, temp2, array, value, /* can_be_null= */ false);
6419
6420 if (can_value_be_null) {
6421 DCHECK(do_store.IsReferenced());
6422 __ Bind(&do_store);
6423 }
6424
Scott Wakelingc34dba72016-10-03 10:14:44 +01006425 vixl32::Register source = value;
6426 if (kPoisonHeapReferences) {
6427 // Note that in the case where `value` is a null reference,
6428 // we do not enter this block, as a null reference does not
6429 // need poisoning.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006430 DCHECK_EQ(value_type, DataType::Type::kReference);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006431 __ Mov(temp1, value);
6432 GetAssembler()->PoisonHeapReference(temp1);
6433 source = temp1;
6434 }
6435
6436 if (index.IsConstant()) {
Vladimir Marko8fa839c2019-05-16 12:50:47 +00006437 size_t offset = (Int32ConstantFrom(index) << TIMES_4) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006438 GetAssembler()->StoreToOffset(kStoreWord, source, array, offset);
6439 } else {
6440 DCHECK(index.IsRegister()) << index;
6441
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006442 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006443 vixl32::Register temp = temps.Acquire();
6444 __ Add(temp, array, data_offset);
6445 codegen_->StoreToShiftedRegOffset(value_type,
6446 LocationFrom(source),
6447 temp,
6448 RegisterFrom(index));
6449 }
6450
Vladimir Marko8fa839c2019-05-16 12:50:47 +00006451 if (can_value_be_null || !needs_type_check) {
Alexandre Rames374ddf32016-11-04 10:40:49 +00006452 // TODO(VIXL): Ensure we record the pc position immediately after the preceding store
6453 // instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01006454 codegen_->MaybeRecordImplicitNullCheck(instruction);
6455 }
6456
Vladimir Marko0dda8c82019-05-16 12:47:40 +00006457 if (slow_path != nullptr) {
6458 __ Bind(slow_path->GetExitLabel());
6459 }
6460
Scott Wakelingc34dba72016-10-03 10:14:44 +01006461 break;
6462 }
6463
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006464 case DataType::Type::kInt64: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006465 Location value = locations->InAt(2);
6466 if (index.IsConstant()) {
6467 size_t offset =
Anton Kirilov644032c2016-12-06 17:51:43 +00006468 (Int32ConstantFrom(index) << TIMES_8) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006469 GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), array, offset);
6470 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006471 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006472 vixl32::Register temp = temps.Acquire();
6473 __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
6474 GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), temp, data_offset);
6475 }
6476 break;
6477 }
6478
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006479 case DataType::Type::kFloat32: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006480 Location value = locations->InAt(2);
6481 DCHECK(value.IsFpuRegister());
6482 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006483 size_t offset = (Int32ConstantFrom(index) << TIMES_4) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006484 GetAssembler()->StoreSToOffset(SRegisterFrom(value), array, offset);
6485 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006486 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006487 vixl32::Register temp = temps.Acquire();
6488 __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_4));
6489 GetAssembler()->StoreSToOffset(SRegisterFrom(value), temp, data_offset);
6490 }
6491 break;
6492 }
6493
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006494 case DataType::Type::kFloat64: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006495 Location value = locations->InAt(2);
6496 DCHECK(value.IsFpuRegisterPair());
6497 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006498 size_t offset = (Int32ConstantFrom(index) << TIMES_8) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006499 GetAssembler()->StoreDToOffset(DRegisterFrom(value), array, offset);
6500 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006501 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006502 vixl32::Register temp = temps.Acquire();
6503 __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
6504 GetAssembler()->StoreDToOffset(DRegisterFrom(value), temp, data_offset);
6505 }
6506 break;
6507 }
6508
Aart Bik66c158e2018-01-31 12:55:04 -08006509 case DataType::Type::kUint32:
6510 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006511 case DataType::Type::kVoid:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006512 LOG(FATAL) << "Unreachable type " << value_type;
6513 UNREACHABLE();
6514 }
6515
6516 // Objects are handled in the switch.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006517 if (value_type != DataType::Type::kReference) {
Alexandre Rames374ddf32016-11-04 10:40:49 +00006518 // TODO(VIXL): Ensure we record the pc position immediately after the preceding store
6519 // instruction.
Scott Wakelingc34dba72016-10-03 10:14:44 +01006520 codegen_->MaybeRecordImplicitNullCheck(instruction);
6521 }
6522}
6523
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006524void LocationsBuilderARMVIXL::VisitArrayLength(HArrayLength* instruction) {
6525 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01006526 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006527 locations->SetInAt(0, Location::RequiresRegister());
6528 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
6529}
6530
6531void InstructionCodeGeneratorARMVIXL::VisitArrayLength(HArrayLength* instruction) {
6532 uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction);
6533 vixl32::Register obj = InputRegisterAt(instruction, 0);
6534 vixl32::Register out = OutputRegister(instruction);
Alexandre Rames374ddf32016-11-04 10:40:49 +00006535 {
Artem Serov0fb37192016-12-06 18:13:40 +00006536 ExactAssemblyScope aas(GetVIXLAssembler(),
6537 vixl32::kMaxInstructionSizeInBytes,
6538 CodeBufferCheckScope::kMaximumSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00006539 __ ldr(out, MemOperand(obj, offset));
6540 codegen_->MaybeRecordImplicitNullCheck(instruction);
6541 }
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006542 // Mask out compression flag from String's array length.
6543 if (mirror::kUseStringCompression && instruction->IsStringLength()) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01006544 __ Lsr(out, out, 1u);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006545 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006546}
6547
Artem Serov2bbc9532016-10-21 11:51:50 +01006548void LocationsBuilderARMVIXL::VisitIntermediateAddress(HIntermediateAddress* instruction) {
Artem Serov2bbc9532016-10-21 11:51:50 +01006549 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01006550 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Artem Serov2bbc9532016-10-21 11:51:50 +01006551
6552 locations->SetInAt(0, Location::RequiresRegister());
6553 locations->SetInAt(1, Location::RegisterOrConstant(instruction->GetOffset()));
6554 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
6555}
6556
6557void InstructionCodeGeneratorARMVIXL::VisitIntermediateAddress(HIntermediateAddress* instruction) {
6558 vixl32::Register out = OutputRegister(instruction);
6559 vixl32::Register first = InputRegisterAt(instruction, 0);
6560 Location second = instruction->GetLocations()->InAt(1);
6561
Artem Serov2bbc9532016-10-21 11:51:50 +01006562 if (second.IsRegister()) {
6563 __ Add(out, first, RegisterFrom(second));
6564 } else {
Anton Kirilov644032c2016-12-06 17:51:43 +00006565 __ Add(out, first, Int32ConstantFrom(second));
Artem Serov2bbc9532016-10-21 11:51:50 +01006566 }
6567}
6568
Artem Serove1811ed2017-04-27 16:50:47 +01006569void LocationsBuilderARMVIXL::VisitIntermediateAddressIndex(
6570 HIntermediateAddressIndex* instruction) {
6571 LOG(FATAL) << "Unreachable " << instruction->GetId();
6572}
6573
6574void InstructionCodeGeneratorARMVIXL::VisitIntermediateAddressIndex(
6575 HIntermediateAddressIndex* instruction) {
6576 LOG(FATAL) << "Unreachable " << instruction->GetId();
6577}
6578
Scott Wakelingc34dba72016-10-03 10:14:44 +01006579void LocationsBuilderARMVIXL::VisitBoundsCheck(HBoundsCheck* instruction) {
6580 RegisterSet caller_saves = RegisterSet::Empty();
6581 InvokeRuntimeCallingConventionARMVIXL calling_convention;
6582 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0)));
6583 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(1)));
6584 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction, caller_saves);
Artem Serov2dd053d2017-03-08 14:54:06 +00006585
6586 HInstruction* index = instruction->InputAt(0);
6587 HInstruction* length = instruction->InputAt(1);
6588 // If both index and length are constants we can statically check the bounds. But if at least one
6589 // of them is not encodable ArmEncodableConstantOrRegister will create
6590 // Location::RequiresRegister() which is not desired to happen. Instead we create constant
6591 // locations.
6592 bool both_const = index->IsConstant() && length->IsConstant();
6593 locations->SetInAt(0, both_const
6594 ? Location::ConstantLocation(index->AsConstant())
6595 : ArmEncodableConstantOrRegister(index, CMP));
6596 locations->SetInAt(1, both_const
6597 ? Location::ConstantLocation(length->AsConstant())
6598 : ArmEncodableConstantOrRegister(length, CMP));
Scott Wakelingc34dba72016-10-03 10:14:44 +01006599}
6600
6601void InstructionCodeGeneratorARMVIXL::VisitBoundsCheck(HBoundsCheck* instruction) {
Artem Serov2dd053d2017-03-08 14:54:06 +00006602 LocationSummary* locations = instruction->GetLocations();
6603 Location index_loc = locations->InAt(0);
6604 Location length_loc = locations->InAt(1);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006605
Artem Serov2dd053d2017-03-08 14:54:06 +00006606 if (length_loc.IsConstant()) {
6607 int32_t length = Int32ConstantFrom(length_loc);
6608 if (index_loc.IsConstant()) {
6609 // BCE will remove the bounds check if we are guaranteed to pass.
6610 int32_t index = Int32ConstantFrom(index_loc);
6611 if (index < 0 || index >= length) {
6612 SlowPathCodeARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01006613 new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathARMVIXL(instruction);
Artem Serov2dd053d2017-03-08 14:54:06 +00006614 codegen_->AddSlowPath(slow_path);
6615 __ B(slow_path->GetEntryLabel());
6616 } else {
6617 // Some optimization after BCE may have generated this, and we should not
6618 // generate a bounds check if it is a valid range.
6619 }
6620 return;
6621 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006622
Artem Serov2dd053d2017-03-08 14:54:06 +00006623 SlowPathCodeARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01006624 new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathARMVIXL(instruction);
Artem Serov2dd053d2017-03-08 14:54:06 +00006625 __ Cmp(RegisterFrom(index_loc), length);
6626 codegen_->AddSlowPath(slow_path);
6627 __ B(hs, slow_path->GetEntryLabel());
6628 } else {
6629 SlowPathCodeARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01006630 new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathARMVIXL(instruction);
Artem Serov2dd053d2017-03-08 14:54:06 +00006631 __ Cmp(RegisterFrom(length_loc), InputOperandAt(instruction, 0));
6632 codegen_->AddSlowPath(slow_path);
6633 __ B(ls, slow_path->GetEntryLabel());
6634 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006635}
6636
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006637void CodeGeneratorARMVIXL::MarkGCCard(vixl32::Register temp,
6638 vixl32::Register card,
6639 vixl32::Register object,
6640 vixl32::Register value,
6641 bool can_be_null) {
6642 vixl32::Label is_null;
6643 if (can_be_null) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00006644 __ CompareAndBranchIfZero(value, &is_null);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006645 }
Roland Levillainc73f0522018-08-14 15:16:50 +01006646 // Load the address of the card table into `card`.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006647 GetAssembler()->LoadFromOffset(
6648 kLoadWord, card, tr, Thread::CardTableOffset<kArmPointerSize>().Int32Value());
Roland Levillainc73f0522018-08-14 15:16:50 +01006649 // Calculate the offset (in the card table) of the card corresponding to
6650 // `object`.
Scott Wakelingb77051e2016-11-21 19:46:00 +00006651 __ Lsr(temp, object, Operand::From(gc::accounting::CardTable::kCardShift));
Roland Levillainc73f0522018-08-14 15:16:50 +01006652 // Write the `art::gc::accounting::CardTable::kCardDirty` value into the
6653 // `object`'s card.
6654 //
6655 // Register `card` contains the address of the card table. Note that the card
6656 // table's base is biased during its creation so that it always starts at an
6657 // address whose least-significant byte is equal to `kCardDirty` (see
6658 // art::gc::accounting::CardTable::Create). Therefore the STRB instruction
6659 // below writes the `kCardDirty` (byte) value into the `object`'s card
6660 // (located at `card + object >> kCardShift`).
6661 //
6662 // This dual use of the value in register `card` (1. to calculate the location
6663 // of the card to mark; and 2. to load the `kCardDirty` value) saves a load
6664 // (no need to explicitly load `kCardDirty` as an immediate value).
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006665 __ Strb(card, MemOperand(card, temp));
6666 if (can_be_null) {
6667 __ Bind(&is_null);
6668 }
6669}
6670
Scott Wakelingfe885462016-09-22 10:24:38 +01006671void LocationsBuilderARMVIXL::VisitParallelMove(HParallelMove* instruction ATTRIBUTE_UNUSED) {
6672 LOG(FATAL) << "Unreachable";
6673}
6674
6675void InstructionCodeGeneratorARMVIXL::VisitParallelMove(HParallelMove* instruction) {
Vladimir Markobea75ff2017-10-11 20:39:54 +01006676 if (instruction->GetNext()->IsSuspendCheck() &&
6677 instruction->GetBlock()->GetLoopInformation() != nullptr) {
6678 HSuspendCheck* suspend_check = instruction->GetNext()->AsSuspendCheck();
6679 // The back edge will generate the suspend check.
6680 codegen_->ClearSpillSlotsFromLoopPhisInStackMap(suspend_check, instruction);
6681 }
6682
Scott Wakelingfe885462016-09-22 10:24:38 +01006683 codegen_->GetMoveResolver()->EmitNativeCode(instruction);
6684}
6685
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006686void LocationsBuilderARMVIXL::VisitSuspendCheck(HSuspendCheck* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01006687 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
6688 instruction, LocationSummary::kCallOnSlowPath);
Artem Serov657022c2016-11-23 14:19:38 +00006689 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006690}
6691
6692void InstructionCodeGeneratorARMVIXL::VisitSuspendCheck(HSuspendCheck* instruction) {
6693 HBasicBlock* block = instruction->GetBlock();
6694 if (block->GetLoopInformation() != nullptr) {
6695 DCHECK(block->GetLoopInformation()->GetSuspendCheck() == instruction);
6696 // The back edge will generate the suspend check.
6697 return;
6698 }
6699 if (block->IsEntryBlock() && instruction->GetNext()->IsGoto()) {
6700 // The goto will generate the suspend check.
6701 return;
6702 }
6703 GenerateSuspendCheck(instruction, nullptr);
Andreas Gampe3db70682018-12-26 15:12:03 -08006704 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 13);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006705}
6706
6707void InstructionCodeGeneratorARMVIXL::GenerateSuspendCheck(HSuspendCheck* instruction,
6708 HBasicBlock* successor) {
6709 SuspendCheckSlowPathARMVIXL* slow_path =
6710 down_cast<SuspendCheckSlowPathARMVIXL*>(instruction->GetSlowPath());
6711 if (slow_path == nullptr) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01006712 slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01006713 new (codegen_->GetScopedAllocator()) SuspendCheckSlowPathARMVIXL(instruction, successor);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006714 instruction->SetSlowPath(slow_path);
6715 codegen_->AddSlowPath(slow_path);
6716 if (successor != nullptr) {
6717 DCHECK(successor->IsLoopHeader());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006718 }
6719 } else {
6720 DCHECK_EQ(slow_path->GetSuccessor(), successor);
6721 }
6722
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006723 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006724 vixl32::Register temp = temps.Acquire();
6725 GetAssembler()->LoadFromOffset(
6726 kLoadUnsignedHalfword, temp, tr, Thread::ThreadFlagsOffset<kArmPointerSize>().Int32Value());
6727 if (successor == nullptr) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00006728 __ CompareAndBranchIfNonZero(temp, slow_path->GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006729 __ Bind(slow_path->GetReturnLabel());
6730 } else {
xueliang.zhongf51bc622016-11-04 09:23:32 +00006731 __ CompareAndBranchIfZero(temp, codegen_->GetLabelOf(successor));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006732 __ B(slow_path->GetEntryLabel());
6733 }
6734}
6735
Scott Wakelingfe885462016-09-22 10:24:38 +01006736ArmVIXLAssembler* ParallelMoveResolverARMVIXL::GetAssembler() const {
6737 return codegen_->GetAssembler();
6738}
6739
6740void ParallelMoveResolverARMVIXL::EmitMove(size_t index) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006741 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
Scott Wakelingfe885462016-09-22 10:24:38 +01006742 MoveOperands* move = moves_[index];
6743 Location source = move->GetSource();
6744 Location destination = move->GetDestination();
6745
6746 if (source.IsRegister()) {
6747 if (destination.IsRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006748 __ Mov(RegisterFrom(destination), RegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01006749 } else if (destination.IsFpuRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006750 __ Vmov(SRegisterFrom(destination), RegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01006751 } else {
6752 DCHECK(destination.IsStackSlot());
6753 GetAssembler()->StoreToOffset(kStoreWord,
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006754 RegisterFrom(source),
Scott Wakelingfe885462016-09-22 10:24:38 +01006755 sp,
6756 destination.GetStackIndex());
6757 }
6758 } else if (source.IsStackSlot()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006759 if (destination.IsRegister()) {
6760 GetAssembler()->LoadFromOffset(kLoadWord,
6761 RegisterFrom(destination),
6762 sp,
6763 source.GetStackIndex());
6764 } else if (destination.IsFpuRegister()) {
6765 GetAssembler()->LoadSFromOffset(SRegisterFrom(destination), sp, source.GetStackIndex());
6766 } else {
6767 DCHECK(destination.IsStackSlot());
6768 vixl32::Register temp = temps.Acquire();
6769 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, source.GetStackIndex());
6770 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
6771 }
Scott Wakelingfe885462016-09-22 10:24:38 +01006772 } else if (source.IsFpuRegister()) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01006773 if (destination.IsRegister()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006774 __ Vmov(RegisterFrom(destination), SRegisterFrom(source));
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01006775 } else if (destination.IsFpuRegister()) {
6776 __ Vmov(SRegisterFrom(destination), SRegisterFrom(source));
6777 } else {
6778 DCHECK(destination.IsStackSlot());
6779 GetAssembler()->StoreSToOffset(SRegisterFrom(source), sp, destination.GetStackIndex());
6780 }
Scott Wakelingfe885462016-09-22 10:24:38 +01006781 } else if (source.IsDoubleStackSlot()) {
Alexandre Rames9c19bd62016-10-24 11:50:32 +01006782 if (destination.IsDoubleStackSlot()) {
6783 vixl32::DRegister temp = temps.AcquireD();
6784 GetAssembler()->LoadDFromOffset(temp, sp, source.GetStackIndex());
6785 GetAssembler()->StoreDToOffset(temp, sp, destination.GetStackIndex());
6786 } else if (destination.IsRegisterPair()) {
6787 DCHECK(ExpectedPairLayout(destination));
6788 GetAssembler()->LoadFromOffset(
6789 kLoadWordPair, LowRegisterFrom(destination), sp, source.GetStackIndex());
6790 } else {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01006791 DCHECK(destination.IsFpuRegisterPair()) << destination;
6792 GetAssembler()->LoadDFromOffset(DRegisterFrom(destination), sp, source.GetStackIndex());
Alexandre Rames9c19bd62016-10-24 11:50:32 +01006793 }
Scott Wakelingfe885462016-09-22 10:24:38 +01006794 } else if (source.IsRegisterPair()) {
6795 if (destination.IsRegisterPair()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006796 __ Mov(LowRegisterFrom(destination), LowRegisterFrom(source));
6797 __ Mov(HighRegisterFrom(destination), HighRegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01006798 } else if (destination.IsFpuRegisterPair()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006799 __ Vmov(DRegisterFrom(destination), LowRegisterFrom(source), HighRegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01006800 } else {
6801 DCHECK(destination.IsDoubleStackSlot()) << destination;
6802 DCHECK(ExpectedPairLayout(source));
6803 GetAssembler()->StoreToOffset(kStoreWordPair,
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006804 LowRegisterFrom(source),
Scott Wakelingfe885462016-09-22 10:24:38 +01006805 sp,
6806 destination.GetStackIndex());
6807 }
6808 } else if (source.IsFpuRegisterPair()) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01006809 if (destination.IsRegisterPair()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006810 __ Vmov(LowRegisterFrom(destination), HighRegisterFrom(destination), DRegisterFrom(source));
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01006811 } else if (destination.IsFpuRegisterPair()) {
6812 __ Vmov(DRegisterFrom(destination), DRegisterFrom(source));
6813 } else {
6814 DCHECK(destination.IsDoubleStackSlot()) << destination;
6815 GetAssembler()->StoreDToOffset(DRegisterFrom(source), sp, destination.GetStackIndex());
6816 }
Scott Wakelingfe885462016-09-22 10:24:38 +01006817 } else {
6818 DCHECK(source.IsConstant()) << source;
6819 HConstant* constant = source.GetConstant();
6820 if (constant->IsIntConstant() || constant->IsNullConstant()) {
6821 int32_t value = CodeGenerator::GetInt32ValueOf(constant);
6822 if (destination.IsRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006823 __ Mov(RegisterFrom(destination), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01006824 } else {
6825 DCHECK(destination.IsStackSlot());
Scott Wakelingfe885462016-09-22 10:24:38 +01006826 vixl32::Register temp = temps.Acquire();
6827 __ Mov(temp, value);
6828 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
6829 }
6830 } else if (constant->IsLongConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006831 int64_t value = Int64ConstantFrom(source);
Scott Wakelingfe885462016-09-22 10:24:38 +01006832 if (destination.IsRegisterPair()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006833 __ Mov(LowRegisterFrom(destination), Low32Bits(value));
6834 __ Mov(HighRegisterFrom(destination), High32Bits(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01006835 } else {
6836 DCHECK(destination.IsDoubleStackSlot()) << destination;
Scott Wakelingfe885462016-09-22 10:24:38 +01006837 vixl32::Register temp = temps.Acquire();
6838 __ Mov(temp, Low32Bits(value));
6839 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
6840 __ Mov(temp, High32Bits(value));
6841 GetAssembler()->StoreToOffset(kStoreWord,
6842 temp,
6843 sp,
6844 destination.GetHighStackIndex(kArmWordSize));
6845 }
6846 } else if (constant->IsDoubleConstant()) {
6847 double value = constant->AsDoubleConstant()->GetValue();
6848 if (destination.IsFpuRegisterPair()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006849 __ Vmov(DRegisterFrom(destination), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01006850 } else {
6851 DCHECK(destination.IsDoubleStackSlot()) << destination;
6852 uint64_t int_value = bit_cast<uint64_t, double>(value);
Scott Wakelingfe885462016-09-22 10:24:38 +01006853 vixl32::Register temp = temps.Acquire();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006854 __ Mov(temp, Low32Bits(int_value));
Scott Wakelingfe885462016-09-22 10:24:38 +01006855 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006856 __ Mov(temp, High32Bits(int_value));
Scott Wakelingfe885462016-09-22 10:24:38 +01006857 GetAssembler()->StoreToOffset(kStoreWord,
6858 temp,
6859 sp,
6860 destination.GetHighStackIndex(kArmWordSize));
6861 }
6862 } else {
6863 DCHECK(constant->IsFloatConstant()) << constant->DebugName();
6864 float value = constant->AsFloatConstant()->GetValue();
6865 if (destination.IsFpuRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006866 __ Vmov(SRegisterFrom(destination), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01006867 } else {
6868 DCHECK(destination.IsStackSlot());
Scott Wakelingfe885462016-09-22 10:24:38 +01006869 vixl32::Register temp = temps.Acquire();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006870 __ Mov(temp, bit_cast<int32_t, float>(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01006871 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
6872 }
6873 }
6874 }
6875}
6876
Alexandre Rames9c19bd62016-10-24 11:50:32 +01006877void ParallelMoveResolverARMVIXL::Exchange(vixl32::Register reg, int mem) {
6878 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
6879 vixl32::Register temp = temps.Acquire();
6880 __ Mov(temp, reg);
6881 GetAssembler()->LoadFromOffset(kLoadWord, reg, sp, mem);
6882 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, mem);
Scott Wakelingfe885462016-09-22 10:24:38 +01006883}
6884
Alexandre Rames9c19bd62016-10-24 11:50:32 +01006885void ParallelMoveResolverARMVIXL::Exchange(int mem1, int mem2) {
6886 // TODO(VIXL32): Double check the performance of this implementation.
6887 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
Nicolas Geoffray13a797b2017-03-15 16:41:31 +00006888 vixl32::Register temp1 = temps.Acquire();
6889 ScratchRegisterScope ensure_scratch(
6890 this, temp1.GetCode(), r0.GetCode(), codegen_->GetNumberOfCoreRegisters());
6891 vixl32::Register temp2(ensure_scratch.GetRegister());
Alexandre Rames9c19bd62016-10-24 11:50:32 +01006892
Nicolas Geoffray13a797b2017-03-15 16:41:31 +00006893 int stack_offset = ensure_scratch.IsSpilled() ? kArmWordSize : 0;
6894 GetAssembler()->LoadFromOffset(kLoadWord, temp1, sp, mem1 + stack_offset);
6895 GetAssembler()->LoadFromOffset(kLoadWord, temp2, sp, mem2 + stack_offset);
6896 GetAssembler()->StoreToOffset(kStoreWord, temp1, sp, mem2 + stack_offset);
6897 GetAssembler()->StoreToOffset(kStoreWord, temp2, sp, mem1 + stack_offset);
Scott Wakelingfe885462016-09-22 10:24:38 +01006898}
6899
Alexandre Rames9c19bd62016-10-24 11:50:32 +01006900void ParallelMoveResolverARMVIXL::EmitSwap(size_t index) {
6901 MoveOperands* move = moves_[index];
6902 Location source = move->GetSource();
6903 Location destination = move->GetDestination();
6904 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
6905
6906 if (source.IsRegister() && destination.IsRegister()) {
6907 vixl32::Register temp = temps.Acquire();
6908 DCHECK(!RegisterFrom(source).Is(temp));
6909 DCHECK(!RegisterFrom(destination).Is(temp));
6910 __ Mov(temp, RegisterFrom(destination));
6911 __ Mov(RegisterFrom(destination), RegisterFrom(source));
6912 __ Mov(RegisterFrom(source), temp);
6913 } else if (source.IsRegister() && destination.IsStackSlot()) {
6914 Exchange(RegisterFrom(source), destination.GetStackIndex());
6915 } else if (source.IsStackSlot() && destination.IsRegister()) {
6916 Exchange(RegisterFrom(destination), source.GetStackIndex());
6917 } else if (source.IsStackSlot() && destination.IsStackSlot()) {
Anton Kirilovdda43962016-11-21 19:55:20 +00006918 Exchange(source.GetStackIndex(), destination.GetStackIndex());
Alexandre Rames9c19bd62016-10-24 11:50:32 +01006919 } else if (source.IsFpuRegister() && destination.IsFpuRegister()) {
Nicolas Geoffray13a797b2017-03-15 16:41:31 +00006920 vixl32::Register temp = temps.Acquire();
Anton Kirilovdda43962016-11-21 19:55:20 +00006921 __ Vmov(temp, SRegisterFrom(source));
6922 __ Vmov(SRegisterFrom(source), SRegisterFrom(destination));
6923 __ Vmov(SRegisterFrom(destination), temp);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01006924 } else if (source.IsRegisterPair() && destination.IsRegisterPair()) {
6925 vixl32::DRegister temp = temps.AcquireD();
6926 __ Vmov(temp, LowRegisterFrom(source), HighRegisterFrom(source));
6927 __ Mov(LowRegisterFrom(source), LowRegisterFrom(destination));
6928 __ Mov(HighRegisterFrom(source), HighRegisterFrom(destination));
6929 __ Vmov(LowRegisterFrom(destination), HighRegisterFrom(destination), temp);
6930 } else if (source.IsRegisterPair() || destination.IsRegisterPair()) {
6931 vixl32::Register low_reg = LowRegisterFrom(source.IsRegisterPair() ? source : destination);
6932 int mem = source.IsRegisterPair() ? destination.GetStackIndex() : source.GetStackIndex();
6933 DCHECK(ExpectedPairLayout(source.IsRegisterPair() ? source : destination));
6934 vixl32::DRegister temp = temps.AcquireD();
6935 __ Vmov(temp, low_reg, vixl32::Register(low_reg.GetCode() + 1));
6936 GetAssembler()->LoadFromOffset(kLoadWordPair, low_reg, sp, mem);
6937 GetAssembler()->StoreDToOffset(temp, sp, mem);
6938 } else if (source.IsFpuRegisterPair() && destination.IsFpuRegisterPair()) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006939 vixl32::DRegister first = DRegisterFrom(source);
6940 vixl32::DRegister second = DRegisterFrom(destination);
6941 vixl32::DRegister temp = temps.AcquireD();
6942 __ Vmov(temp, first);
6943 __ Vmov(first, second);
6944 __ Vmov(second, temp);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01006945 } else if (source.IsFpuRegisterPair() || destination.IsFpuRegisterPair()) {
Anton Kirilovdda43962016-11-21 19:55:20 +00006946 vixl32::DRegister reg = source.IsFpuRegisterPair()
6947 ? DRegisterFrom(source)
6948 : DRegisterFrom(destination);
6949 int mem = source.IsFpuRegisterPair()
6950 ? destination.GetStackIndex()
6951 : source.GetStackIndex();
6952 vixl32::DRegister temp = temps.AcquireD();
6953 __ Vmov(temp, reg);
6954 GetAssembler()->LoadDFromOffset(reg, sp, mem);
6955 GetAssembler()->StoreDToOffset(temp, sp, mem);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01006956 } else if (source.IsFpuRegister() || destination.IsFpuRegister()) {
Anton Kirilovdda43962016-11-21 19:55:20 +00006957 vixl32::SRegister reg = source.IsFpuRegister()
6958 ? SRegisterFrom(source)
6959 : SRegisterFrom(destination);
6960 int mem = source.IsFpuRegister()
6961 ? destination.GetStackIndex()
6962 : source.GetStackIndex();
6963 vixl32::Register temp = temps.Acquire();
6964 __ Vmov(temp, reg);
6965 GetAssembler()->LoadSFromOffset(reg, sp, mem);
6966 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, mem);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01006967 } else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) {
6968 vixl32::DRegister temp1 = temps.AcquireD();
6969 vixl32::DRegister temp2 = temps.AcquireD();
6970 __ Vldr(temp1, MemOperand(sp, source.GetStackIndex()));
6971 __ Vldr(temp2, MemOperand(sp, destination.GetStackIndex()));
6972 __ Vstr(temp1, MemOperand(sp, destination.GetStackIndex()));
6973 __ Vstr(temp2, MemOperand(sp, source.GetStackIndex()));
6974 } else {
6975 LOG(FATAL) << "Unimplemented" << source << " <-> " << destination;
6976 }
Scott Wakelingfe885462016-09-22 10:24:38 +01006977}
6978
Nicolas Geoffray13a797b2017-03-15 16:41:31 +00006979void ParallelMoveResolverARMVIXL::SpillScratch(int reg) {
6980 __ Push(vixl32::Register(reg));
Scott Wakelingfe885462016-09-22 10:24:38 +01006981}
6982
Nicolas Geoffray13a797b2017-03-15 16:41:31 +00006983void ParallelMoveResolverARMVIXL::RestoreScratch(int reg) {
6984 __ Pop(vixl32::Register(reg));
Scott Wakelingfe885462016-09-22 10:24:38 +01006985}
6986
Artem Serov02d37832016-10-25 15:25:33 +01006987HLoadClass::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadClassKind(
Artem Serovd4cc5b22016-11-04 11:19:09 +00006988 HLoadClass::LoadKind desired_class_load_kind) {
6989 switch (desired_class_load_kind) {
Nicolas Geoffray83c8e272017-01-31 14:36:37 +00006990 case HLoadClass::LoadKind::kInvalid:
6991 LOG(FATAL) << "UNREACHABLE";
6992 UNREACHABLE();
Artem Serovd4cc5b22016-11-04 11:19:09 +00006993 case HLoadClass::LoadKind::kReferrersClass:
6994 break;
Artem Serovd4cc5b22016-11-04 11:19:09 +00006995 case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
Vladimir Markoe47f60c2018-02-21 13:43:28 +00006996 case HLoadClass::LoadKind::kBootImageRelRo:
Vladimir Marko6bec91c2017-01-09 15:03:12 +00006997 case HLoadClass::LoadKind::kBssEntry:
6998 DCHECK(!Runtime::Current()->UseJitCompilation());
6999 break;
Vladimir Marko8e524ad2018-07-13 10:27:43 +01007000 case HLoadClass::LoadKind::kJitBootImageAddress:
Nicolas Geoffray22384ae2016-12-12 22:33:36 +00007001 case HLoadClass::LoadKind::kJitTableAddress:
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007002 DCHECK(Runtime::Current()->UseJitCompilation());
Artem Serovc5fcb442016-12-02 19:19:58 +00007003 break;
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007004 case HLoadClass::LoadKind::kRuntimeCall:
Artem Serovd4cc5b22016-11-04 11:19:09 +00007005 break;
7006 }
7007 return desired_class_load_kind;
Artem Serov02d37832016-10-25 15:25:33 +01007008}
7009
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007010void LocationsBuilderARMVIXL::VisitLoadClass(HLoadClass* cls) {
Vladimir Marko41559982017-01-06 14:04:23 +00007011 HLoadClass::LoadKind load_kind = cls->GetLoadKind();
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007012 if (load_kind == HLoadClass::LoadKind::kRuntimeCall) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007013 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Vladimir Marko41559982017-01-06 14:04:23 +00007014 CodeGenerator::CreateLoadClassRuntimeCallLocationSummary(
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007015 cls,
7016 LocationFrom(calling_convention.GetRegisterAt(0)),
Vladimir Marko41559982017-01-06 14:04:23 +00007017 LocationFrom(r0));
Vladimir Markoea4c1262017-02-06 19:59:33 +00007018 DCHECK(calling_convention.GetRegisterAt(0).Is(r0));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007019 return;
7020 }
Vladimir Marko41559982017-01-06 14:04:23 +00007021 DCHECK(!cls->NeedsAccessCheck());
Scott Wakelingfe885462016-09-22 10:24:38 +01007022
Artem Serovd4cc5b22016-11-04 11:19:09 +00007023 const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
7024 LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier)
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007025 ? LocationSummary::kCallOnSlowPath
7026 : LocationSummary::kNoCall;
Vladimir Markoca6fff82017-10-03 14:49:14 +01007027 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(cls, call_kind);
Artem Serovd4cc5b22016-11-04 11:19:09 +00007028 if (kUseBakerReadBarrier && requires_read_barrier && !cls->NeedsEnvironment()) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00007029 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Artem Serovd4cc5b22016-11-04 11:19:09 +00007030 }
7031
Vladimir Marko41559982017-01-06 14:04:23 +00007032 if (load_kind == HLoadClass::LoadKind::kReferrersClass) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007033 locations->SetInAt(0, Location::RequiresRegister());
7034 }
7035 locations->SetOut(Location::RequiresRegister());
Vladimir Markoea4c1262017-02-06 19:59:33 +00007036 if (load_kind == HLoadClass::LoadKind::kBssEntry) {
7037 if (!kUseReadBarrier || kUseBakerReadBarrier) {
7038 // Rely on the type resolution or initialization and marking to save everything we need.
Vladimir Marko3232dbb2018-07-25 15:42:46 +01007039 locations->SetCustomSlowPathCallerSaves(OneRegInReferenceOutSaveEverythingCallerSaves());
Vladimir Markoea4c1262017-02-06 19:59:33 +00007040 } else {
7041 // For non-Baker read barrier we have a temp-clobbering call.
7042 }
7043 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007044}
7045
Nicolas Geoffray5247c082017-01-13 14:17:29 +00007046// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
7047// move.
7048void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFETY_ANALYSIS {
Vladimir Marko41559982017-01-06 14:04:23 +00007049 HLoadClass::LoadKind load_kind = cls->GetLoadKind();
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007050 if (load_kind == HLoadClass::LoadKind::kRuntimeCall) {
Vladimir Marko41559982017-01-06 14:04:23 +00007051 codegen_->GenerateLoadClassRuntimeCall(cls);
Andreas Gampe3db70682018-12-26 15:12:03 -08007052 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 14);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007053 return;
7054 }
Vladimir Marko41559982017-01-06 14:04:23 +00007055 DCHECK(!cls->NeedsAccessCheck());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007056
Vladimir Marko41559982017-01-06 14:04:23 +00007057 LocationSummary* locations = cls->GetLocations();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007058 Location out_loc = locations->Out();
7059 vixl32::Register out = OutputRegister(cls);
7060
Artem Serovd4cc5b22016-11-04 11:19:09 +00007061 const ReadBarrierOption read_barrier_option = cls->IsInBootImage()
7062 ? kWithoutReadBarrier
7063 : kCompilerReadBarrierOption;
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007064 bool generate_null_check = false;
Vladimir Marko41559982017-01-06 14:04:23 +00007065 switch (load_kind) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007066 case HLoadClass::LoadKind::kReferrersClass: {
7067 DCHECK(!cls->CanCallRuntime());
7068 DCHECK(!cls->MustGenerateClinitCheck());
7069 // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
7070 vixl32::Register current_method = InputRegisterAt(cls, 0);
Vladimir Markoca1e0382018-04-11 09:58:41 +00007071 codegen_->GenerateGcRootFieldLoad(cls,
7072 out_loc,
7073 current_method,
7074 ArtMethod::DeclaringClassOffset().Int32Value(),
7075 read_barrier_option);
Artem Serovd4cc5b22016-11-04 11:19:09 +00007076 break;
7077 }
Artem Serovd4cc5b22016-11-04 11:19:09 +00007078 case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: {
Vladimir Marko44ca0752019-07-29 10:18:25 +01007079 DCHECK(codegen_->GetCompilerOptions().IsBootImage() ||
7080 codegen_->GetCompilerOptions().IsBootImageExtension());
Artem Serovd4cc5b22016-11-04 11:19:09 +00007081 DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
7082 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
Vladimir Marko59eb30f2018-02-20 11:52:34 +00007083 codegen_->NewBootImageTypePatch(cls->GetDexFile(), cls->GetTypeIndex());
Artem Serovd4cc5b22016-11-04 11:19:09 +00007084 codegen_->EmitMovwMovtPlaceholder(labels, out);
7085 break;
7086 }
Vladimir Markoe47f60c2018-02-21 13:43:28 +00007087 case HLoadClass::LoadKind::kBootImageRelRo: {
Vladimir Marko94ec2db2017-09-06 17:21:03 +01007088 DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
7089 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
Vladimir Markoe47f60c2018-02-21 13:43:28 +00007090 codegen_->NewBootImageRelRoPatch(codegen_->GetBootImageOffset(cls));
Vladimir Marko94ec2db2017-09-06 17:21:03 +01007091 codegen_->EmitMovwMovtPlaceholder(labels, out);
Andreas Gampe3db70682018-12-26 15:12:03 -08007092 __ Ldr(out, MemOperand(out, /* offset= */ 0));
Vladimir Marko94ec2db2017-09-06 17:21:03 +01007093 break;
7094 }
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007095 case HLoadClass::LoadKind::kBssEntry: {
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007096 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
Vladimir Marko1998cd02017-01-13 13:02:58 +00007097 codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex());
Vladimir Markof3c52b42017-11-17 17:32:12 +00007098 codegen_->EmitMovwMovtPlaceholder(labels, out);
Vladimir Markod5fd5c32019-07-02 14:46:32 +01007099 // All aligned loads are implicitly atomic consume operations on ARM.
Andreas Gampe3db70682018-12-26 15:12:03 -08007100 codegen_->GenerateGcRootFieldLoad(cls, out_loc, out, /* offset= */ 0, read_barrier_option);
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007101 generate_null_check = true;
7102 break;
7103 }
Vladimir Marko8e524ad2018-07-13 10:27:43 +01007104 case HLoadClass::LoadKind::kJitBootImageAddress: {
7105 DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
7106 uint32_t address = reinterpret_cast32<uint32_t>(cls->GetClass().Get());
7107 DCHECK_NE(address, 0u);
7108 __ Ldr(out, codegen_->DeduplicateBootImageAddressLiteral(address));
7109 break;
7110 }
Nicolas Geoffray22384ae2016-12-12 22:33:36 +00007111 case HLoadClass::LoadKind::kJitTableAddress: {
Artem Serovc5fcb442016-12-02 19:19:58 +00007112 __ Ldr(out, codegen_->DeduplicateJitClassLiteral(cls->GetDexFile(),
7113 cls->GetTypeIndex(),
Nicolas Geoffray5247c082017-01-13 14:17:29 +00007114 cls->GetClass()));
Artem Serovc5fcb442016-12-02 19:19:58 +00007115 // /* GcRoot<mirror::Class> */ out = *out
Andreas Gampe3db70682018-12-26 15:12:03 -08007116 codegen_->GenerateGcRootFieldLoad(cls, out_loc, out, /* offset= */ 0, read_barrier_option);
Artem Serovd4cc5b22016-11-04 11:19:09 +00007117 break;
7118 }
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007119 case HLoadClass::LoadKind::kRuntimeCall:
Nicolas Geoffray83c8e272017-01-31 14:36:37 +00007120 case HLoadClass::LoadKind::kInvalid:
Vladimir Marko41559982017-01-06 14:04:23 +00007121 LOG(FATAL) << "UNREACHABLE";
7122 UNREACHABLE();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007123 }
7124
7125 if (generate_null_check || cls->MustGenerateClinitCheck()) {
7126 DCHECK(cls->CanCallRuntime());
Vladimir Marko174b2e22017-10-12 13:34:49 +01007127 LoadClassSlowPathARMVIXL* slow_path =
Vladimir Markoa9f303c2018-07-20 16:43:56 +01007128 new (codegen_->GetScopedAllocator()) LoadClassSlowPathARMVIXL(cls, cls);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007129 codegen_->AddSlowPath(slow_path);
7130 if (generate_null_check) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00007131 __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007132 }
7133 if (cls->MustGenerateClinitCheck()) {
7134 GenerateClassInitializationCheck(slow_path, out);
7135 } else {
7136 __ Bind(slow_path->GetExitLabel());
7137 }
Andreas Gampe3db70682018-12-26 15:12:03 -08007138 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 15);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007139 }
7140}
7141
Orion Hodsondbaa5c72018-05-10 08:22:46 +01007142void LocationsBuilderARMVIXL::VisitLoadMethodHandle(HLoadMethodHandle* load) {
7143 InvokeRuntimeCallingConventionARMVIXL calling_convention;
7144 Location location = LocationFrom(calling_convention.GetRegisterAt(0));
7145 CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(load, location, location);
7146}
7147
7148void InstructionCodeGeneratorARMVIXL::VisitLoadMethodHandle(HLoadMethodHandle* load) {
7149 codegen_->GenerateLoadMethodHandleRuntimeCall(load);
7150}
7151
Orion Hodson18259d72018-04-12 11:18:23 +01007152void LocationsBuilderARMVIXL::VisitLoadMethodType(HLoadMethodType* load) {
7153 InvokeRuntimeCallingConventionARMVIXL calling_convention;
7154 Location location = LocationFrom(calling_convention.GetRegisterAt(0));
7155 CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, location, location);
7156}
7157
7158void InstructionCodeGeneratorARMVIXL::VisitLoadMethodType(HLoadMethodType* load) {
7159 codegen_->GenerateLoadMethodTypeRuntimeCall(load);
7160}
7161
Artem Serov02d37832016-10-25 15:25:33 +01007162void LocationsBuilderARMVIXL::VisitClinitCheck(HClinitCheck* check) {
7163 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01007164 new (GetGraph()->GetAllocator()) LocationSummary(check, LocationSummary::kCallOnSlowPath);
Artem Serov02d37832016-10-25 15:25:33 +01007165 locations->SetInAt(0, Location::RequiresRegister());
7166 if (check->HasUses()) {
7167 locations->SetOut(Location::SameAsFirstInput());
7168 }
Vladimir Marko3232dbb2018-07-25 15:42:46 +01007169 // Rely on the type initialization to save everything we need.
7170 locations->SetCustomSlowPathCallerSaves(OneRegInReferenceOutSaveEverythingCallerSaves());
Artem Serov02d37832016-10-25 15:25:33 +01007171}
7172
7173void InstructionCodeGeneratorARMVIXL::VisitClinitCheck(HClinitCheck* check) {
7174 // We assume the class is not null.
7175 LoadClassSlowPathARMVIXL* slow_path =
Vladimir Markoa9f303c2018-07-20 16:43:56 +01007176 new (codegen_->GetScopedAllocator()) LoadClassSlowPathARMVIXL(check->GetLoadClass(), check);
Artem Serov02d37832016-10-25 15:25:33 +01007177 codegen_->AddSlowPath(slow_path);
7178 GenerateClassInitializationCheck(slow_path, InputRegisterAt(check, 0));
7179}
7180
7181void InstructionCodeGeneratorARMVIXL::GenerateClassInitializationCheck(
7182 LoadClassSlowPathARMVIXL* slow_path, vixl32::Register class_reg) {
7183 UseScratchRegisterScope temps(GetVIXLAssembler());
7184 vixl32::Register temp = temps.Acquire();
Vladimir Markodc682aa2018-01-04 18:42:57 +00007185 constexpr size_t status_lsb_position = SubtypeCheckBits::BitStructSizeOf();
Vladimir Markobf121912019-06-04 13:49:05 +01007186 constexpr uint32_t shifted_visibly_initialized_value =
7187 enum_cast<uint32_t>(ClassStatus::kVisiblyInitialized) << status_lsb_position;
Vladimir Markodc682aa2018-01-04 18:42:57 +00007188
Vladimir Markobf121912019-06-04 13:49:05 +01007189 const size_t status_offset = mirror::Class::StatusOffset().SizeValue();
7190 GetAssembler()->LoadFromOffset(kLoadWord, temp, class_reg, status_offset);
7191 __ Cmp(temp, shifted_visibly_initialized_value);
Vladimir Marko2c64a832018-01-04 11:31:56 +00007192 __ B(lo, slow_path->GetEntryLabel());
Artem Serov02d37832016-10-25 15:25:33 +01007193 __ Bind(slow_path->GetExitLabel());
7194}
7195
Vladimir Marko175e7862018-03-27 09:03:13 +00007196void InstructionCodeGeneratorARMVIXL::GenerateBitstringTypeCheckCompare(
7197 HTypeCheckInstruction* check,
7198 vixl32::Register temp,
7199 vixl32::FlagsUpdate flags_update) {
7200 uint32_t path_to_root = check->GetBitstringPathToRoot();
7201 uint32_t mask = check->GetBitstringMask();
7202 DCHECK(IsPowerOfTwo(mask + 1));
7203 size_t mask_bits = WhichPowerOf2(mask + 1);
7204
7205 // Note that HInstanceOf shall check for zero value in `temp` but HCheckCast needs
7206 // the Z flag for BNE. This is indicated by the `flags_update` parameter.
7207 if (mask_bits == 16u) {
7208 // Load only the bitstring part of the status word.
7209 __ Ldrh(temp, MemOperand(temp, mirror::Class::StatusOffset().Int32Value()));
7210 // Check if the bitstring bits are equal to `path_to_root`.
7211 if (flags_update == SetFlags) {
7212 __ Cmp(temp, path_to_root);
7213 } else {
7214 __ Sub(temp, temp, path_to_root);
7215 }
7216 } else {
7217 // /* uint32_t */ temp = temp->status_
7218 __ Ldr(temp, MemOperand(temp, mirror::Class::StatusOffset().Int32Value()));
7219 if (GetAssembler()->ShifterOperandCanHold(SUB, path_to_root)) {
7220 // Compare the bitstring bits using SUB.
7221 __ Sub(temp, temp, path_to_root);
7222 // Shift out bits that do not contribute to the comparison.
7223 __ Lsl(flags_update, temp, temp, dchecked_integral_cast<uint32_t>(32u - mask_bits));
7224 } else if (IsUint<16>(path_to_root)) {
7225 if (temp.IsLow()) {
7226 // Note: Optimized for size but contains one more dependent instruction than necessary.
7227 // MOVW+SUB(register) would be 8 bytes unless we find a low-reg temporary but the
7228 // macro assembler would use the high reg IP for the constant by default.
7229 // Compare the bitstring bits using SUB.
7230 __ Sub(temp, temp, path_to_root & 0x00ffu); // 16-bit SUB (immediate) T2
7231 __ Sub(temp, temp, path_to_root & 0xff00u); // 32-bit SUB (immediate) T3
7232 // Shift out bits that do not contribute to the comparison.
7233 __ Lsl(flags_update, temp, temp, dchecked_integral_cast<uint32_t>(32u - mask_bits));
7234 } else {
7235 // Extract the bitstring bits.
7236 __ Ubfx(temp, temp, 0, mask_bits);
7237 // Check if the bitstring bits are equal to `path_to_root`.
7238 if (flags_update == SetFlags) {
7239 __ Cmp(temp, path_to_root);
7240 } else {
7241 __ Sub(temp, temp, path_to_root);
7242 }
7243 }
7244 } else {
7245 // Shift out bits that do not contribute to the comparison.
7246 __ Lsl(temp, temp, dchecked_integral_cast<uint32_t>(32u - mask_bits));
7247 // Check if the shifted bitstring bits are equal to `path_to_root << (32u - mask_bits)`.
7248 if (flags_update == SetFlags) {
7249 __ Cmp(temp, path_to_root << (32u - mask_bits));
7250 } else {
7251 __ Sub(temp, temp, path_to_root << (32u - mask_bits));
7252 }
7253 }
7254 }
7255}
7256
Artem Serov02d37832016-10-25 15:25:33 +01007257HLoadString::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadStringKind(
Artem Serovd4cc5b22016-11-04 11:19:09 +00007258 HLoadString::LoadKind desired_string_load_kind) {
7259 switch (desired_string_load_kind) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00007260 case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
Vladimir Markoe47f60c2018-02-21 13:43:28 +00007261 case HLoadString::LoadKind::kBootImageRelRo:
Artem Serovd4cc5b22016-11-04 11:19:09 +00007262 case HLoadString::LoadKind::kBssEntry:
7263 DCHECK(!Runtime::Current()->UseJitCompilation());
7264 break;
Vladimir Marko8e524ad2018-07-13 10:27:43 +01007265 case HLoadString::LoadKind::kJitBootImageAddress:
Artem Serovd4cc5b22016-11-04 11:19:09 +00007266 case HLoadString::LoadKind::kJitTableAddress:
7267 DCHECK(Runtime::Current()->UseJitCompilation());
Artem Serovc5fcb442016-12-02 19:19:58 +00007268 break;
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007269 case HLoadString::LoadKind::kRuntimeCall:
Artem Serovd4cc5b22016-11-04 11:19:09 +00007270 break;
7271 }
7272 return desired_string_load_kind;
Artem Serov02d37832016-10-25 15:25:33 +01007273}
7274
7275void LocationsBuilderARMVIXL::VisitLoadString(HLoadString* load) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00007276 LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load);
Vladimir Markoca6fff82017-10-03 14:49:14 +01007277 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(load, call_kind);
Artem Serov02d37832016-10-25 15:25:33 +01007278 HLoadString::LoadKind load_kind = load->GetLoadKind();
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007279 if (load_kind == HLoadString::LoadKind::kRuntimeCall) {
Artem Serov02d37832016-10-25 15:25:33 +01007280 locations->SetOut(LocationFrom(r0));
7281 } else {
7282 locations->SetOut(Location::RequiresRegister());
Artem Serovd4cc5b22016-11-04 11:19:09 +00007283 if (load_kind == HLoadString::LoadKind::kBssEntry) {
7284 if (!kUseReadBarrier || kUseBakerReadBarrier) {
Vladimir Markoea4c1262017-02-06 19:59:33 +00007285 // Rely on the pResolveString and marking to save everything we need, including temps.
Vladimir Marko3232dbb2018-07-25 15:42:46 +01007286 locations->SetCustomSlowPathCallerSaves(OneRegInReferenceOutSaveEverythingCallerSaves());
Artem Serovd4cc5b22016-11-04 11:19:09 +00007287 } else {
7288 // For non-Baker read barrier we have a temp-clobbering call.
7289 }
7290 }
Artem Serov02d37832016-10-25 15:25:33 +01007291 }
7292}
7293
Nicolas Geoffrayf0acfe72017-01-09 20:54:52 +00007294// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
7295// move.
7296void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) NO_THREAD_SAFETY_ANALYSIS {
Artem Serovd4cc5b22016-11-04 11:19:09 +00007297 LocationSummary* locations = load->GetLocations();
7298 Location out_loc = locations->Out();
7299 vixl32::Register out = OutputRegister(load);
7300 HLoadString::LoadKind load_kind = load->GetLoadKind();
7301
7302 switch (load_kind) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00007303 case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
Vladimir Marko44ca0752019-07-29 10:18:25 +01007304 DCHECK(codegen_->GetCompilerOptions().IsBootImage() ||
7305 codegen_->GetCompilerOptions().IsBootImageExtension());
Artem Serovd4cc5b22016-11-04 11:19:09 +00007306 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
Vladimir Marko59eb30f2018-02-20 11:52:34 +00007307 codegen_->NewBootImageStringPatch(load->GetDexFile(), load->GetStringIndex());
Artem Serovd4cc5b22016-11-04 11:19:09 +00007308 codegen_->EmitMovwMovtPlaceholder(labels, out);
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01007309 return;
Artem Serovd4cc5b22016-11-04 11:19:09 +00007310 }
Vladimir Markoe47f60c2018-02-21 13:43:28 +00007311 case HLoadString::LoadKind::kBootImageRelRo: {
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01007312 DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
7313 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
Vladimir Markoe47f60c2018-02-21 13:43:28 +00007314 codegen_->NewBootImageRelRoPatch(codegen_->GetBootImageOffset(load));
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01007315 codegen_->EmitMovwMovtPlaceholder(labels, out);
Andreas Gampe3db70682018-12-26 15:12:03 -08007316 __ Ldr(out, MemOperand(out, /* offset= */ 0));
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01007317 return;
Artem Serovd4cc5b22016-11-04 11:19:09 +00007318 }
7319 case HLoadString::LoadKind::kBssEntry: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00007320 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01007321 codegen_->NewStringBssEntryPatch(load->GetDexFile(), load->GetStringIndex());
Vladimir Markof3c52b42017-11-17 17:32:12 +00007322 codegen_->EmitMovwMovtPlaceholder(labels, out);
Vladimir Markod5fd5c32019-07-02 14:46:32 +01007323 // All aligned loads are implicitly atomic consume operations on ARM.
Vladimir Markoca1e0382018-04-11 09:58:41 +00007324 codegen_->GenerateGcRootFieldLoad(
Andreas Gampe3db70682018-12-26 15:12:03 -08007325 load, out_loc, out, /* offset= */ 0, kCompilerReadBarrierOption);
Artem Serovd4cc5b22016-11-04 11:19:09 +00007326 LoadStringSlowPathARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01007327 new (codegen_->GetScopedAllocator()) LoadStringSlowPathARMVIXL(load);
Artem Serovd4cc5b22016-11-04 11:19:09 +00007328 codegen_->AddSlowPath(slow_path);
7329 __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
7330 __ Bind(slow_path->GetExitLabel());
Andreas Gampe3db70682018-12-26 15:12:03 -08007331 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 16);
Artem Serovd4cc5b22016-11-04 11:19:09 +00007332 return;
7333 }
Vladimir Marko8e524ad2018-07-13 10:27:43 +01007334 case HLoadString::LoadKind::kJitBootImageAddress: {
7335 uint32_t address = reinterpret_cast32<uint32_t>(load->GetString().Get());
7336 DCHECK_NE(address, 0u);
7337 __ Ldr(out, codegen_->DeduplicateBootImageAddressLiteral(address));
7338 return;
7339 }
Artem Serovd4cc5b22016-11-04 11:19:09 +00007340 case HLoadString::LoadKind::kJitTableAddress: {
Artem Serovc5fcb442016-12-02 19:19:58 +00007341 __ Ldr(out, codegen_->DeduplicateJitStringLiteral(load->GetDexFile(),
Nicolas Geoffrayf0acfe72017-01-09 20:54:52 +00007342 load->GetStringIndex(),
7343 load->GetString()));
Artem Serovc5fcb442016-12-02 19:19:58 +00007344 // /* GcRoot<mirror::String> */ out = *out
Vladimir Markoca1e0382018-04-11 09:58:41 +00007345 codegen_->GenerateGcRootFieldLoad(
Andreas Gampe3db70682018-12-26 15:12:03 -08007346 load, out_loc, out, /* offset= */ 0, kCompilerReadBarrierOption);
Artem Serovc5fcb442016-12-02 19:19:58 +00007347 return;
Artem Serovd4cc5b22016-11-04 11:19:09 +00007348 }
7349 default:
7350 break;
7351 }
Artem Serov02d37832016-10-25 15:25:33 +01007352
7353 // TODO: Re-add the compiler code to do string dex cache lookup again.
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007354 DCHECK_EQ(load->GetLoadKind(), HLoadString::LoadKind::kRuntimeCall);
Artem Serov02d37832016-10-25 15:25:33 +01007355 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Andreas Gampe8a0128a2016-11-28 07:38:35 -08007356 __ Mov(calling_convention.GetRegisterAt(0), load->GetStringIndex().index_);
Artem Serov02d37832016-10-25 15:25:33 +01007357 codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc());
7358 CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
Andreas Gampe3db70682018-12-26 15:12:03 -08007359 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 17);
Artem Serov02d37832016-10-25 15:25:33 +01007360}
7361
7362static int32_t GetExceptionTlsOffset() {
7363 return Thread::ExceptionOffset<kArmPointerSize>().Int32Value();
7364}
7365
7366void LocationsBuilderARMVIXL::VisitLoadException(HLoadException* load) {
7367 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01007368 new (GetGraph()->GetAllocator()) LocationSummary(load, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01007369 locations->SetOut(Location::RequiresRegister());
7370}
7371
7372void InstructionCodeGeneratorARMVIXL::VisitLoadException(HLoadException* load) {
7373 vixl32::Register out = OutputRegister(load);
7374 GetAssembler()->LoadFromOffset(kLoadWord, out, tr, GetExceptionTlsOffset());
7375}
7376
7377
7378void LocationsBuilderARMVIXL::VisitClearException(HClearException* clear) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01007379 new (GetGraph()->GetAllocator()) LocationSummary(clear, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01007380}
7381
7382void InstructionCodeGeneratorARMVIXL::VisitClearException(HClearException* clear ATTRIBUTE_UNUSED) {
7383 UseScratchRegisterScope temps(GetVIXLAssembler());
7384 vixl32::Register temp = temps.Acquire();
7385 __ Mov(temp, 0);
7386 GetAssembler()->StoreToOffset(kStoreWord, temp, tr, GetExceptionTlsOffset());
7387}
7388
7389void LocationsBuilderARMVIXL::VisitThrow(HThrow* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01007390 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
7391 instruction, LocationSummary::kCallOnMainOnly);
Artem Serov02d37832016-10-25 15:25:33 +01007392 InvokeRuntimeCallingConventionARMVIXL calling_convention;
7393 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
7394}
7395
7396void InstructionCodeGeneratorARMVIXL::VisitThrow(HThrow* instruction) {
7397 codegen_->InvokeRuntime(kQuickDeliverException, instruction, instruction->GetDexPc());
7398 CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
7399}
7400
Artem Serov657022c2016-11-23 14:19:38 +00007401// Temp is used for read barrier.
7402static size_t NumberOfInstanceOfTemps(TypeCheckKind type_check_kind) {
7403 if (kEmitCompilerReadBarrier &&
7404 (kUseBakerReadBarrier ||
7405 type_check_kind == TypeCheckKind::kAbstractClassCheck ||
7406 type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
7407 type_check_kind == TypeCheckKind::kArrayObjectCheck)) {
7408 return 1;
7409 }
7410 return 0;
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007411}
7412
Artem Serov657022c2016-11-23 14:19:38 +00007413// Interface case has 3 temps, one for holding the number of interfaces, one for the current
7414// interface pointer, one for loading the current interface.
7415// The other checks have one temp for loading the object's class.
7416static size_t NumberOfCheckCastTemps(TypeCheckKind type_check_kind) {
7417 if (type_check_kind == TypeCheckKind::kInterfaceCheck) {
7418 return 3;
7419 }
7420 return 1 + NumberOfInstanceOfTemps(type_check_kind);
7421}
Artem Serovcfbe9132016-10-14 15:58:56 +01007422
7423void LocationsBuilderARMVIXL::VisitInstanceOf(HInstanceOf* instruction) {
7424 LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
7425 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
7426 bool baker_read_barrier_slow_path = false;
7427 switch (type_check_kind) {
7428 case TypeCheckKind::kExactCheck:
7429 case TypeCheckKind::kAbstractClassCheck:
7430 case TypeCheckKind::kClassHierarchyCheck:
Vladimir Marko87584542017-12-12 17:47:52 +00007431 case TypeCheckKind::kArrayObjectCheck: {
7432 bool needs_read_barrier = CodeGenerator::InstanceOfNeedsReadBarrier(instruction);
7433 call_kind = needs_read_barrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall;
7434 baker_read_barrier_slow_path = kUseBakerReadBarrier && needs_read_barrier;
Artem Serovcfbe9132016-10-14 15:58:56 +01007435 break;
Vladimir Marko87584542017-12-12 17:47:52 +00007436 }
Artem Serovcfbe9132016-10-14 15:58:56 +01007437 case TypeCheckKind::kArrayCheck:
7438 case TypeCheckKind::kUnresolvedCheck:
7439 case TypeCheckKind::kInterfaceCheck:
7440 call_kind = LocationSummary::kCallOnSlowPath;
7441 break;
Vladimir Marko175e7862018-03-27 09:03:13 +00007442 case TypeCheckKind::kBitstringCheck:
7443 break;
Artem Serovcfbe9132016-10-14 15:58:56 +01007444 }
7445
Vladimir Markoca6fff82017-10-03 14:49:14 +01007446 LocationSummary* locations =
7447 new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
Artem Serovcfbe9132016-10-14 15:58:56 +01007448 if (baker_read_barrier_slow_path) {
7449 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
7450 }
7451 locations->SetInAt(0, Location::RequiresRegister());
Vladimir Marko175e7862018-03-27 09:03:13 +00007452 if (type_check_kind == TypeCheckKind::kBitstringCheck) {
7453 locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
7454 locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
7455 locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
7456 } else {
7457 locations->SetInAt(1, Location::RequiresRegister());
7458 }
Artem Serovcfbe9132016-10-14 15:58:56 +01007459 // The "out" register is used as a temporary, so it overlaps with the inputs.
7460 // Note that TypeCheckSlowPathARM uses this register too.
7461 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
Artem Serov657022c2016-11-23 14:19:38 +00007462 locations->AddRegisterTemps(NumberOfInstanceOfTemps(type_check_kind));
Artem Serovcfbe9132016-10-14 15:58:56 +01007463}
7464
7465void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) {
7466 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
7467 LocationSummary* locations = instruction->GetLocations();
7468 Location obj_loc = locations->InAt(0);
7469 vixl32::Register obj = InputRegisterAt(instruction, 0);
Vladimir Marko175e7862018-03-27 09:03:13 +00007470 vixl32::Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck)
7471 ? vixl32::Register()
7472 : InputRegisterAt(instruction, 1);
Artem Serovcfbe9132016-10-14 15:58:56 +01007473 Location out_loc = locations->Out();
7474 vixl32::Register out = OutputRegister(instruction);
Artem Serov657022c2016-11-23 14:19:38 +00007475 const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind);
7476 DCHECK_LE(num_temps, 1u);
7477 Location maybe_temp_loc = (num_temps >= 1) ? locations->GetTemp(0) : Location::NoLocation();
Artem Serovcfbe9132016-10-14 15:58:56 +01007478 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
7479 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
7480 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
7481 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007482 vixl32::Label done;
7483 vixl32::Label* const final_label = codegen_->GetFinalLabel(instruction, &done);
Artem Serovcfbe9132016-10-14 15:58:56 +01007484 SlowPathCodeARMVIXL* slow_path = nullptr;
7485
7486 // Return 0 if `obj` is null.
7487 // avoid null check if we know obj is not null.
7488 if (instruction->MustDoNullCheck()) {
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007489 DCHECK(!out.Is(obj));
7490 __ Mov(out, 0);
Andreas Gampe3db70682018-12-26 15:12:03 -08007491 __ CompareAndBranchIfZero(obj, final_label, /* is_far_target= */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007492 }
7493
Artem Serovcfbe9132016-10-14 15:58:56 +01007494 switch (type_check_kind) {
7495 case TypeCheckKind::kExactCheck: {
Vladimir Marko87584542017-12-12 17:47:52 +00007496 ReadBarrierOption read_barrier_option =
7497 CodeGenerator::ReadBarrierOptionForInstanceOf(instruction);
Mathieu Chartier6beced42016-11-15 15:51:31 -08007498 // /* HeapReference<Class> */ out = obj->klass_
7499 GenerateReferenceLoadTwoRegisters(instruction,
7500 out_loc,
7501 obj_loc,
7502 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00007503 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00007504 read_barrier_option);
Artem Serovcfbe9132016-10-14 15:58:56 +01007505 // Classes must be equal for the instanceof to succeed.
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007506 __ Cmp(out, cls);
7507 // We speculatively set the result to false without changing the condition
7508 // flags, which allows us to avoid some branching later.
7509 __ Mov(LeaveFlags, out, 0);
7510
7511 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
7512 // we check that the output is in a low register, so that a 16-bit MOV
7513 // encoding can be used.
7514 if (out.IsLow()) {
7515 // We use the scope because of the IT block that follows.
7516 ExactAssemblyScope guard(GetVIXLAssembler(),
7517 2 * vixl32::k16BitT32InstructionSizeInBytes,
7518 CodeBufferCheckScope::kExactSize);
7519
7520 __ it(eq);
7521 __ mov(eq, out, 1);
7522 } else {
Andreas Gampe3db70682018-12-26 15:12:03 -08007523 __ B(ne, final_label, /* is_far_target= */ false);
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007524 __ Mov(out, 1);
7525 }
7526
Artem Serovcfbe9132016-10-14 15:58:56 +01007527 break;
7528 }
7529
7530 case TypeCheckKind::kAbstractClassCheck: {
Vladimir Marko87584542017-12-12 17:47:52 +00007531 ReadBarrierOption read_barrier_option =
7532 CodeGenerator::ReadBarrierOptionForInstanceOf(instruction);
Mathieu Chartier6beced42016-11-15 15:51:31 -08007533 // /* HeapReference<Class> */ out = obj->klass_
7534 GenerateReferenceLoadTwoRegisters(instruction,
7535 out_loc,
7536 obj_loc,
7537 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00007538 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00007539 read_barrier_option);
Artem Serovcfbe9132016-10-14 15:58:56 +01007540 // If the class is abstract, we eagerly fetch the super class of the
7541 // object to avoid doing a comparison we know will fail.
7542 vixl32::Label loop;
7543 __ Bind(&loop);
7544 // /* HeapReference<Class> */ out = out->super_class_
Artem Serov657022c2016-11-23 14:19:38 +00007545 GenerateReferenceLoadOneRegister(instruction,
7546 out_loc,
7547 super_offset,
7548 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00007549 read_barrier_option);
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007550 // If `out` is null, we use it for the result, and jump to the final label.
Andreas Gampe3db70682018-12-26 15:12:03 -08007551 __ CompareAndBranchIfZero(out, final_label, /* is_far_target= */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007552 __ Cmp(out, cls);
Andreas Gampe3db70682018-12-26 15:12:03 -08007553 __ B(ne, &loop, /* is_far_target= */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007554 __ Mov(out, 1);
Artem Serovcfbe9132016-10-14 15:58:56 +01007555 break;
7556 }
7557
7558 case TypeCheckKind::kClassHierarchyCheck: {
Vladimir Marko87584542017-12-12 17:47:52 +00007559 ReadBarrierOption read_barrier_option =
7560 CodeGenerator::ReadBarrierOptionForInstanceOf(instruction);
Mathieu Chartier6beced42016-11-15 15:51:31 -08007561 // /* HeapReference<Class> */ out = obj->klass_
7562 GenerateReferenceLoadTwoRegisters(instruction,
7563 out_loc,
7564 obj_loc,
7565 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00007566 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00007567 read_barrier_option);
Artem Serovcfbe9132016-10-14 15:58:56 +01007568 // Walk over the class hierarchy to find a match.
7569 vixl32::Label loop, success;
7570 __ Bind(&loop);
7571 __ Cmp(out, cls);
Andreas Gampe3db70682018-12-26 15:12:03 -08007572 __ B(eq, &success, /* is_far_target= */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007573 // /* HeapReference<Class> */ out = out->super_class_
Artem Serov657022c2016-11-23 14:19:38 +00007574 GenerateReferenceLoadOneRegister(instruction,
7575 out_loc,
7576 super_offset,
7577 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00007578 read_barrier_option);
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007579 // This is essentially a null check, but it sets the condition flags to the
7580 // proper value for the code that follows the loop, i.e. not `eq`.
7581 __ Cmp(out, 1);
Andreas Gampe3db70682018-12-26 15:12:03 -08007582 __ B(hs, &loop, /* is_far_target= */ false);
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007583
7584 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
7585 // we check that the output is in a low register, so that a 16-bit MOV
7586 // encoding can be used.
7587 if (out.IsLow()) {
7588 // If `out` is null, we use it for the result, and the condition flags
7589 // have already been set to `ne`, so the IT block that comes afterwards
7590 // (and which handles the successful case) turns into a NOP (instead of
7591 // overwriting `out`).
7592 __ Bind(&success);
7593
7594 // We use the scope because of the IT block that follows.
7595 ExactAssemblyScope guard(GetVIXLAssembler(),
7596 2 * vixl32::k16BitT32InstructionSizeInBytes,
7597 CodeBufferCheckScope::kExactSize);
7598
7599 // There is only one branch to the `success` label (which is bound to this
7600 // IT block), and it has the same condition, `eq`, so in that case the MOV
7601 // is executed.
7602 __ it(eq);
7603 __ mov(eq, out, 1);
7604 } else {
7605 // If `out` is null, we use it for the result, and jump to the final label.
Anton Kirilov6f644202017-02-27 18:29:45 +00007606 __ B(final_label);
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007607 __ Bind(&success);
7608 __ Mov(out, 1);
Artem Serovcfbe9132016-10-14 15:58:56 +01007609 }
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007610
Artem Serovcfbe9132016-10-14 15:58:56 +01007611 break;
7612 }
7613
7614 case TypeCheckKind::kArrayObjectCheck: {
Vladimir Marko87584542017-12-12 17:47:52 +00007615 ReadBarrierOption read_barrier_option =
7616 CodeGenerator::ReadBarrierOptionForInstanceOf(instruction);
Mathieu Chartier6beced42016-11-15 15:51:31 -08007617 // /* HeapReference<Class> */ out = obj->klass_
7618 GenerateReferenceLoadTwoRegisters(instruction,
7619 out_loc,
7620 obj_loc,
7621 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00007622 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00007623 read_barrier_option);
Artem Serovcfbe9132016-10-14 15:58:56 +01007624 // Do an exact check.
7625 vixl32::Label exact_check;
7626 __ Cmp(out, cls);
Andreas Gampe3db70682018-12-26 15:12:03 -08007627 __ B(eq, &exact_check, /* is_far_target= */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007628 // Otherwise, we need to check that the object's class is a non-primitive array.
7629 // /* HeapReference<Class> */ out = out->component_type_
Artem Serov657022c2016-11-23 14:19:38 +00007630 GenerateReferenceLoadOneRegister(instruction,
7631 out_loc,
7632 component_offset,
7633 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00007634 read_barrier_option);
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007635 // If `out` is null, we use it for the result, and jump to the final label.
Andreas Gampe3db70682018-12-26 15:12:03 -08007636 __ CompareAndBranchIfZero(out, final_label, /* is_far_target= */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007637 GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset);
7638 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007639 __ Cmp(out, 0);
7640 // We speculatively set the result to false without changing the condition
7641 // flags, which allows us to avoid some branching later.
7642 __ Mov(LeaveFlags, out, 0);
7643
7644 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
7645 // we check that the output is in a low register, so that a 16-bit MOV
7646 // encoding can be used.
7647 if (out.IsLow()) {
7648 __ Bind(&exact_check);
7649
7650 // We use the scope because of the IT block that follows.
7651 ExactAssemblyScope guard(GetVIXLAssembler(),
7652 2 * vixl32::k16BitT32InstructionSizeInBytes,
7653 CodeBufferCheckScope::kExactSize);
7654
7655 __ it(eq);
7656 __ mov(eq, out, 1);
7657 } else {
Andreas Gampe3db70682018-12-26 15:12:03 -08007658 __ B(ne, final_label, /* is_far_target= */ false);
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007659 __ Bind(&exact_check);
7660 __ Mov(out, 1);
7661 }
7662
Artem Serovcfbe9132016-10-14 15:58:56 +01007663 break;
7664 }
7665
7666 case TypeCheckKind::kArrayCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00007667 // No read barrier since the slow path will retry upon failure.
Mathieu Chartier6beced42016-11-15 15:51:31 -08007668 // /* HeapReference<Class> */ out = obj->klass_
7669 GenerateReferenceLoadTwoRegisters(instruction,
7670 out_loc,
7671 obj_loc,
7672 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00007673 maybe_temp_loc,
7674 kWithoutReadBarrier);
Artem Serovcfbe9132016-10-14 15:58:56 +01007675 __ Cmp(out, cls);
7676 DCHECK(locations->OnlyCallsOnSlowPath());
Vladimir Marko174b2e22017-10-12 13:34:49 +01007677 slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathARMVIXL(
Andreas Gampe3db70682018-12-26 15:12:03 -08007678 instruction, /* is_fatal= */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007679 codegen_->AddSlowPath(slow_path);
7680 __ B(ne, slow_path->GetEntryLabel());
7681 __ Mov(out, 1);
Artem Serovcfbe9132016-10-14 15:58:56 +01007682 break;
7683 }
7684
7685 case TypeCheckKind::kUnresolvedCheck:
7686 case TypeCheckKind::kInterfaceCheck: {
7687 // Note that we indeed only call on slow path, but we always go
7688 // into the slow path for the unresolved and interface check
7689 // cases.
7690 //
7691 // We cannot directly call the InstanceofNonTrivial runtime
7692 // entry point without resorting to a type checking slow path
7693 // here (i.e. by calling InvokeRuntime directly), as it would
7694 // require to assign fixed registers for the inputs of this
7695 // HInstanceOf instruction (following the runtime calling
7696 // convention), which might be cluttered by the potential first
7697 // read barrier emission at the beginning of this method.
7698 //
7699 // TODO: Introduce a new runtime entry point taking the object
7700 // to test (instead of its class) as argument, and let it deal
7701 // with the read barrier issues. This will let us refactor this
7702 // case of the `switch` code as it was previously (with a direct
7703 // call to the runtime not using a type checking slow path).
7704 // This should also be beneficial for the other cases above.
7705 DCHECK(locations->OnlyCallsOnSlowPath());
Vladimir Marko174b2e22017-10-12 13:34:49 +01007706 slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathARMVIXL(
Andreas Gampe3db70682018-12-26 15:12:03 -08007707 instruction, /* is_fatal= */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007708 codegen_->AddSlowPath(slow_path);
7709 __ B(slow_path->GetEntryLabel());
Artem Serovcfbe9132016-10-14 15:58:56 +01007710 break;
7711 }
Vladimir Marko175e7862018-03-27 09:03:13 +00007712
7713 case TypeCheckKind::kBitstringCheck: {
7714 // /* HeapReference<Class> */ temp = obj->klass_
7715 GenerateReferenceLoadTwoRegisters(instruction,
7716 out_loc,
7717 obj_loc,
7718 class_offset,
7719 maybe_temp_loc,
7720 kWithoutReadBarrier);
7721
7722 GenerateBitstringTypeCheckCompare(instruction, out, DontCare);
7723 // If `out` is a low reg and we would have another low reg temp, we could
7724 // optimize this as RSBS+ADC, see GenerateConditionWithZero().
7725 //
7726 // Also, in some cases when `out` is a low reg and we're loading a constant to IP
7727 // it would make sense to use CMP+MOV+IT+MOV instead of SUB+CLZ+LSR as the code size
7728 // would be the same and we would have fewer direct data dependencies.
7729 codegen_->GenerateConditionWithZero(kCondEQ, out, out); // CLZ+LSR
7730 break;
7731 }
Artem Serovcfbe9132016-10-14 15:58:56 +01007732 }
7733
Artem Serovcfbe9132016-10-14 15:58:56 +01007734 if (done.IsReferenced()) {
7735 __ Bind(&done);
7736 }
7737
7738 if (slow_path != nullptr) {
7739 __ Bind(slow_path->GetExitLabel());
7740 }
7741}
7742
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007743void LocationsBuilderARMVIXL::VisitCheckCast(HCheckCast* instruction) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007744 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
Vladimir Marko87584542017-12-12 17:47:52 +00007745 LocationSummary::CallKind call_kind = CodeGenerator::GetCheckCastCallKind(instruction);
Vladimir Markoca6fff82017-10-03 14:49:14 +01007746 LocationSummary* locations =
7747 new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007748 locations->SetInAt(0, Location::RequiresRegister());
Vladimir Marko175e7862018-03-27 09:03:13 +00007749 if (type_check_kind == TypeCheckKind::kBitstringCheck) {
7750 locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
7751 locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
7752 locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
7753 } else {
7754 locations->SetInAt(1, Location::RequiresRegister());
7755 }
Artem Serov657022c2016-11-23 14:19:38 +00007756 locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind));
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007757}
7758
7759void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) {
7760 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
7761 LocationSummary* locations = instruction->GetLocations();
7762 Location obj_loc = locations->InAt(0);
7763 vixl32::Register obj = InputRegisterAt(instruction, 0);
Vladimir Marko175e7862018-03-27 09:03:13 +00007764 vixl32::Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck)
7765 ? vixl32::Register()
7766 : InputRegisterAt(instruction, 1);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007767 Location temp_loc = locations->GetTemp(0);
7768 vixl32::Register temp = RegisterFrom(temp_loc);
Artem Serov657022c2016-11-23 14:19:38 +00007769 const size_t num_temps = NumberOfCheckCastTemps(type_check_kind);
7770 DCHECK_LE(num_temps, 3u);
7771 Location maybe_temp2_loc = (num_temps >= 2) ? locations->GetTemp(1) : Location::NoLocation();
7772 Location maybe_temp3_loc = (num_temps >= 3) ? locations->GetTemp(2) : Location::NoLocation();
7773 const uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
7774 const uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
7775 const uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
7776 const uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
7777 const uint32_t iftable_offset = mirror::Class::IfTableOffset().Uint32Value();
7778 const uint32_t array_length_offset = mirror::Array::LengthOffset().Uint32Value();
7779 const uint32_t object_array_data_offset =
7780 mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value();
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007781
Vladimir Marko87584542017-12-12 17:47:52 +00007782 bool is_type_check_slow_path_fatal = CodeGenerator::IsTypeCheckSlowPathFatal(instruction);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007783 SlowPathCodeARMVIXL* type_check_slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01007784 new (codegen_->GetScopedAllocator()) TypeCheckSlowPathARMVIXL(
7785 instruction, is_type_check_slow_path_fatal);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007786 codegen_->AddSlowPath(type_check_slow_path);
7787
7788 vixl32::Label done;
Anton Kirilov6f644202017-02-27 18:29:45 +00007789 vixl32::Label* final_label = codegen_->GetFinalLabel(instruction, &done);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007790 // Avoid null check if we know obj is not null.
7791 if (instruction->MustDoNullCheck()) {
Andreas Gampe3db70682018-12-26 15:12:03 -08007792 __ CompareAndBranchIfZero(obj, final_label, /* is_far_target= */ false);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007793 }
7794
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007795 switch (type_check_kind) {
7796 case TypeCheckKind::kExactCheck:
7797 case TypeCheckKind::kArrayCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00007798 // /* HeapReference<Class> */ temp = obj->klass_
7799 GenerateReferenceLoadTwoRegisters(instruction,
7800 temp_loc,
7801 obj_loc,
7802 class_offset,
7803 maybe_temp2_loc,
7804 kWithoutReadBarrier);
7805
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007806 __ Cmp(temp, cls);
7807 // Jump to slow path for throwing the exception or doing a
7808 // more involved array check.
7809 __ B(ne, type_check_slow_path->GetEntryLabel());
7810 break;
7811 }
7812
7813 case TypeCheckKind::kAbstractClassCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00007814 // /* HeapReference<Class> */ temp = obj->klass_
7815 GenerateReferenceLoadTwoRegisters(instruction,
7816 temp_loc,
7817 obj_loc,
7818 class_offset,
7819 maybe_temp2_loc,
7820 kWithoutReadBarrier);
7821
Artem Serovcfbe9132016-10-14 15:58:56 +01007822 // If the class is abstract, we eagerly fetch the super class of the
7823 // object to avoid doing a comparison we know will fail.
7824 vixl32::Label loop;
7825 __ Bind(&loop);
7826 // /* HeapReference<Class> */ temp = temp->super_class_
Artem Serov657022c2016-11-23 14:19:38 +00007827 GenerateReferenceLoadOneRegister(instruction,
7828 temp_loc,
7829 super_offset,
7830 maybe_temp2_loc,
7831 kWithoutReadBarrier);
Artem Serovcfbe9132016-10-14 15:58:56 +01007832
7833 // If the class reference currently in `temp` is null, jump to the slow path to throw the
7834 // exception.
xueliang.zhongf51bc622016-11-04 09:23:32 +00007835 __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
Artem Serovcfbe9132016-10-14 15:58:56 +01007836
7837 // Otherwise, compare the classes.
7838 __ Cmp(temp, cls);
Andreas Gampe3db70682018-12-26 15:12:03 -08007839 __ B(ne, &loop, /* is_far_target= */ false);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007840 break;
7841 }
7842
7843 case TypeCheckKind::kClassHierarchyCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00007844 // /* HeapReference<Class> */ temp = obj->klass_
7845 GenerateReferenceLoadTwoRegisters(instruction,
7846 temp_loc,
7847 obj_loc,
7848 class_offset,
7849 maybe_temp2_loc,
7850 kWithoutReadBarrier);
7851
Artem Serovcfbe9132016-10-14 15:58:56 +01007852 // Walk over the class hierarchy to find a match.
7853 vixl32::Label loop;
7854 __ Bind(&loop);
7855 __ Cmp(temp, cls);
Andreas Gampe3db70682018-12-26 15:12:03 -08007856 __ B(eq, final_label, /* is_far_target= */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007857
7858 // /* HeapReference<Class> */ temp = temp->super_class_
Artem Serov657022c2016-11-23 14:19:38 +00007859 GenerateReferenceLoadOneRegister(instruction,
7860 temp_loc,
7861 super_offset,
7862 maybe_temp2_loc,
7863 kWithoutReadBarrier);
Artem Serovcfbe9132016-10-14 15:58:56 +01007864
7865 // If the class reference currently in `temp` is null, jump to the slow path to throw the
7866 // exception.
xueliang.zhongf51bc622016-11-04 09:23:32 +00007867 __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
Artem Serovcfbe9132016-10-14 15:58:56 +01007868 // Otherwise, jump to the beginning of the loop.
7869 __ B(&loop);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007870 break;
7871 }
7872
Artem Serovcfbe9132016-10-14 15:58:56 +01007873 case TypeCheckKind::kArrayObjectCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00007874 // /* HeapReference<Class> */ temp = obj->klass_
7875 GenerateReferenceLoadTwoRegisters(instruction,
7876 temp_loc,
7877 obj_loc,
7878 class_offset,
7879 maybe_temp2_loc,
7880 kWithoutReadBarrier);
7881
Artem Serovcfbe9132016-10-14 15:58:56 +01007882 // Do an exact check.
7883 __ Cmp(temp, cls);
Andreas Gampe3db70682018-12-26 15:12:03 -08007884 __ B(eq, final_label, /* is_far_target= */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007885
7886 // Otherwise, we need to check that the object's class is a non-primitive array.
7887 // /* HeapReference<Class> */ temp = temp->component_type_
Artem Serov657022c2016-11-23 14:19:38 +00007888 GenerateReferenceLoadOneRegister(instruction,
7889 temp_loc,
7890 component_offset,
7891 maybe_temp2_loc,
7892 kWithoutReadBarrier);
Artem Serovcfbe9132016-10-14 15:58:56 +01007893 // If the component type is null, jump to the slow path to throw the exception.
xueliang.zhongf51bc622016-11-04 09:23:32 +00007894 __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
Artem Serovcfbe9132016-10-14 15:58:56 +01007895 // Otherwise,the object is indeed an array, jump to label `check_non_primitive_component_type`
7896 // to further check that this component type is not a primitive type.
7897 GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, temp, temp, primitive_offset);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01007898 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
xueliang.zhongf51bc622016-11-04 09:23:32 +00007899 __ CompareAndBranchIfNonZero(temp, type_check_slow_path->GetEntryLabel());
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007900 break;
7901 }
7902
7903 case TypeCheckKind::kUnresolvedCheck:
Artem Serov657022c2016-11-23 14:19:38 +00007904 // We always go into the type check slow path for the unresolved check case.
Artem Serovcfbe9132016-10-14 15:58:56 +01007905 // We cannot directly call the CheckCast runtime entry point
7906 // without resorting to a type checking slow path here (i.e. by
7907 // calling InvokeRuntime directly), as it would require to
7908 // assign fixed registers for the inputs of this HInstanceOf
7909 // instruction (following the runtime calling convention), which
7910 // might be cluttered by the potential first read barrier
7911 // emission at the beginning of this method.
Artem Serov657022c2016-11-23 14:19:38 +00007912
Artem Serovcfbe9132016-10-14 15:58:56 +01007913 __ B(type_check_slow_path->GetEntryLabel());
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007914 break;
Artem Serov657022c2016-11-23 14:19:38 +00007915
7916 case TypeCheckKind::kInterfaceCheck: {
7917 // Avoid read barriers to improve performance of the fast path. We can not get false
7918 // positives by doing this.
7919 // /* HeapReference<Class> */ temp = obj->klass_
7920 GenerateReferenceLoadTwoRegisters(instruction,
7921 temp_loc,
7922 obj_loc,
7923 class_offset,
7924 maybe_temp2_loc,
7925 kWithoutReadBarrier);
7926
7927 // /* HeapReference<Class> */ temp = temp->iftable_
7928 GenerateReferenceLoadTwoRegisters(instruction,
7929 temp_loc,
7930 temp_loc,
7931 iftable_offset,
7932 maybe_temp2_loc,
7933 kWithoutReadBarrier);
7934 // Iftable is never null.
7935 __ Ldr(RegisterFrom(maybe_temp2_loc), MemOperand(temp, array_length_offset));
7936 // Loop through the iftable and check if any class matches.
7937 vixl32::Label start_loop;
7938 __ Bind(&start_loop);
7939 __ CompareAndBranchIfZero(RegisterFrom(maybe_temp2_loc),
7940 type_check_slow_path->GetEntryLabel());
7941 __ Ldr(RegisterFrom(maybe_temp3_loc), MemOperand(temp, object_array_data_offset));
7942 GetAssembler()->MaybeUnpoisonHeapReference(RegisterFrom(maybe_temp3_loc));
7943 // Go to next interface.
7944 __ Add(temp, temp, Operand::From(2 * kHeapReferenceSize));
7945 __ Sub(RegisterFrom(maybe_temp2_loc), RegisterFrom(maybe_temp2_loc), 2);
7946 // Compare the classes and continue the loop if they do not match.
7947 __ Cmp(cls, RegisterFrom(maybe_temp3_loc));
Andreas Gampe3db70682018-12-26 15:12:03 -08007948 __ B(ne, &start_loop, /* is_far_target= */ false);
Artem Serov657022c2016-11-23 14:19:38 +00007949 break;
7950 }
Vladimir Marko175e7862018-03-27 09:03:13 +00007951
7952 case TypeCheckKind::kBitstringCheck: {
7953 // /* HeapReference<Class> */ temp = obj->klass_
7954 GenerateReferenceLoadTwoRegisters(instruction,
7955 temp_loc,
7956 obj_loc,
7957 class_offset,
7958 maybe_temp2_loc,
7959 kWithoutReadBarrier);
7960
7961 GenerateBitstringTypeCheckCompare(instruction, temp, SetFlags);
7962 __ B(ne, type_check_slow_path->GetEntryLabel());
7963 break;
7964 }
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007965 }
Anton Kirilov6f644202017-02-27 18:29:45 +00007966 if (done.IsReferenced()) {
7967 __ Bind(&done);
7968 }
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007969
7970 __ Bind(type_check_slow_path->GetExitLabel());
7971}
7972
Artem Serov551b28f2016-10-18 19:11:30 +01007973void LocationsBuilderARMVIXL::VisitMonitorOperation(HMonitorOperation* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01007974 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
7975 instruction, LocationSummary::kCallOnMainOnly);
Artem Serov551b28f2016-10-18 19:11:30 +01007976 InvokeRuntimeCallingConventionARMVIXL calling_convention;
7977 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
7978}
7979
7980void InstructionCodeGeneratorARMVIXL::VisitMonitorOperation(HMonitorOperation* instruction) {
7981 codegen_->InvokeRuntime(instruction->IsEnter() ? kQuickLockObject : kQuickUnlockObject,
7982 instruction,
7983 instruction->GetDexPc());
7984 if (instruction->IsEnter()) {
7985 CheckEntrypointTypes<kQuickLockObject, void, mirror::Object*>();
7986 } else {
7987 CheckEntrypointTypes<kQuickUnlockObject, void, mirror::Object*>();
7988 }
Andreas Gampe3db70682018-12-26 15:12:03 -08007989 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 18);
Artem Serov551b28f2016-10-18 19:11:30 +01007990}
7991
Artem Serov02109dd2016-09-23 17:17:54 +01007992void LocationsBuilderARMVIXL::VisitAnd(HAnd* instruction) {
7993 HandleBitwiseOperation(instruction, AND);
7994}
7995
7996void LocationsBuilderARMVIXL::VisitOr(HOr* instruction) {
7997 HandleBitwiseOperation(instruction, ORR);
7998}
7999
8000void LocationsBuilderARMVIXL::VisitXor(HXor* instruction) {
8001 HandleBitwiseOperation(instruction, EOR);
8002}
8003
8004void LocationsBuilderARMVIXL::HandleBitwiseOperation(HBinaryOperation* instruction, Opcode opcode) {
8005 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01008006 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008007 DCHECK(instruction->GetResultType() == DataType::Type::kInt32
8008 || instruction->GetResultType() == DataType::Type::kInt64);
Artem Serov02109dd2016-09-23 17:17:54 +01008009 // Note: GVN reorders commutative operations to have the constant on the right hand side.
8010 locations->SetInAt(0, Location::RequiresRegister());
8011 locations->SetInAt(1, ArmEncodableConstantOrRegister(instruction->InputAt(1), opcode));
8012 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
8013}
8014
8015void InstructionCodeGeneratorARMVIXL::VisitAnd(HAnd* instruction) {
8016 HandleBitwiseOperation(instruction);
8017}
8018
8019void InstructionCodeGeneratorARMVIXL::VisitOr(HOr* instruction) {
8020 HandleBitwiseOperation(instruction);
8021}
8022
8023void InstructionCodeGeneratorARMVIXL::VisitXor(HXor* instruction) {
8024 HandleBitwiseOperation(instruction);
8025}
8026
Artem Serov2bbc9532016-10-21 11:51:50 +01008027void LocationsBuilderARMVIXL::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) {
8028 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01008029 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008030 DCHECK(instruction->GetResultType() == DataType::Type::kInt32
8031 || instruction->GetResultType() == DataType::Type::kInt64);
Artem Serov2bbc9532016-10-21 11:51:50 +01008032
8033 locations->SetInAt(0, Location::RequiresRegister());
8034 locations->SetInAt(1, Location::RequiresRegister());
8035 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
8036}
8037
8038void InstructionCodeGeneratorARMVIXL::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) {
8039 LocationSummary* locations = instruction->GetLocations();
8040 Location first = locations->InAt(0);
8041 Location second = locations->InAt(1);
8042 Location out = locations->Out();
8043
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008044 if (instruction->GetResultType() == DataType::Type::kInt32) {
Artem Serov2bbc9532016-10-21 11:51:50 +01008045 vixl32::Register first_reg = RegisterFrom(first);
8046 vixl32::Register second_reg = RegisterFrom(second);
8047 vixl32::Register out_reg = RegisterFrom(out);
8048
8049 switch (instruction->GetOpKind()) {
8050 case HInstruction::kAnd:
8051 __ Bic(out_reg, first_reg, second_reg);
8052 break;
8053 case HInstruction::kOr:
8054 __ Orn(out_reg, first_reg, second_reg);
8055 break;
8056 // There is no EON on arm.
8057 case HInstruction::kXor:
8058 default:
8059 LOG(FATAL) << "Unexpected instruction " << instruction->DebugName();
8060 UNREACHABLE();
8061 }
8062 return;
8063
8064 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008065 DCHECK_EQ(instruction->GetResultType(), DataType::Type::kInt64);
Artem Serov2bbc9532016-10-21 11:51:50 +01008066 vixl32::Register first_low = LowRegisterFrom(first);
8067 vixl32::Register first_high = HighRegisterFrom(first);
8068 vixl32::Register second_low = LowRegisterFrom(second);
8069 vixl32::Register second_high = HighRegisterFrom(second);
8070 vixl32::Register out_low = LowRegisterFrom(out);
8071 vixl32::Register out_high = HighRegisterFrom(out);
8072
8073 switch (instruction->GetOpKind()) {
8074 case HInstruction::kAnd:
8075 __ Bic(out_low, first_low, second_low);
8076 __ Bic(out_high, first_high, second_high);
8077 break;
8078 case HInstruction::kOr:
8079 __ Orn(out_low, first_low, second_low);
8080 __ Orn(out_high, first_high, second_high);
8081 break;
8082 // There is no EON on arm.
8083 case HInstruction::kXor:
8084 default:
8085 LOG(FATAL) << "Unexpected instruction " << instruction->DebugName();
8086 UNREACHABLE();
8087 }
8088 }
8089}
8090
Anton Kirilov74234da2017-01-13 14:42:47 +00008091void LocationsBuilderARMVIXL::VisitDataProcWithShifterOp(
8092 HDataProcWithShifterOp* instruction) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008093 DCHECK(instruction->GetType() == DataType::Type::kInt32 ||
8094 instruction->GetType() == DataType::Type::kInt64);
Anton Kirilov74234da2017-01-13 14:42:47 +00008095 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01008096 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008097 const bool overlap = instruction->GetType() == DataType::Type::kInt64 &&
Anton Kirilov74234da2017-01-13 14:42:47 +00008098 HDataProcWithShifterOp::IsExtensionOp(instruction->GetOpKind());
8099
8100 locations->SetInAt(0, Location::RequiresRegister());
8101 locations->SetInAt(1, Location::RequiresRegister());
8102 locations->SetOut(Location::RequiresRegister(),
8103 overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap);
8104}
8105
8106void InstructionCodeGeneratorARMVIXL::VisitDataProcWithShifterOp(
8107 HDataProcWithShifterOp* instruction) {
8108 const LocationSummary* const locations = instruction->GetLocations();
8109 const HInstruction::InstructionKind kind = instruction->GetInstrKind();
8110 const HDataProcWithShifterOp::OpKind op_kind = instruction->GetOpKind();
8111
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008112 if (instruction->GetType() == DataType::Type::kInt32) {
Anton Kirilov420ee302017-02-21 18:10:26 +00008113 const vixl32::Register first = InputRegisterAt(instruction, 0);
8114 const vixl32::Register output = OutputRegister(instruction);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008115 const vixl32::Register second = instruction->InputAt(1)->GetType() == DataType::Type::kInt64
Anton Kirilov74234da2017-01-13 14:42:47 +00008116 ? LowRegisterFrom(locations->InAt(1))
8117 : InputRegisterAt(instruction, 1);
8118
Anton Kirilov420ee302017-02-21 18:10:26 +00008119 if (HDataProcWithShifterOp::IsExtensionOp(op_kind)) {
8120 DCHECK_EQ(kind, HInstruction::kAdd);
8121
8122 switch (op_kind) {
8123 case HDataProcWithShifterOp::kUXTB:
8124 __ Uxtab(output, first, second);
8125 break;
8126 case HDataProcWithShifterOp::kUXTH:
8127 __ Uxtah(output, first, second);
8128 break;
8129 case HDataProcWithShifterOp::kSXTB:
8130 __ Sxtab(output, first, second);
8131 break;
8132 case HDataProcWithShifterOp::kSXTH:
8133 __ Sxtah(output, first, second);
8134 break;
8135 default:
8136 LOG(FATAL) << "Unexpected operation kind: " << op_kind;
8137 UNREACHABLE();
8138 }
8139 } else {
8140 GenerateDataProcInstruction(kind,
8141 output,
8142 first,
8143 Operand(second,
8144 ShiftFromOpKind(op_kind),
8145 instruction->GetShiftAmount()),
8146 codegen_);
8147 }
Anton Kirilov74234da2017-01-13 14:42:47 +00008148 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008149 DCHECK_EQ(instruction->GetType(), DataType::Type::kInt64);
Anton Kirilov74234da2017-01-13 14:42:47 +00008150
8151 if (HDataProcWithShifterOp::IsExtensionOp(op_kind)) {
8152 const vixl32::Register second = InputRegisterAt(instruction, 1);
8153
8154 DCHECK(!LowRegisterFrom(locations->Out()).Is(second));
8155 GenerateDataProc(kind,
8156 locations->Out(),
8157 locations->InAt(0),
8158 second,
8159 Operand(second, ShiftType::ASR, 31),
8160 codegen_);
8161 } else {
8162 GenerateLongDataProc(instruction, codegen_);
8163 }
8164 }
8165}
8166
Artem Serov02109dd2016-09-23 17:17:54 +01008167// TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
8168void InstructionCodeGeneratorARMVIXL::GenerateAndConst(vixl32::Register out,
8169 vixl32::Register first,
8170 uint32_t value) {
8171 // Optimize special cases for individual halfs of `and-long` (`and` is simplified earlier).
8172 if (value == 0xffffffffu) {
8173 if (!out.Is(first)) {
8174 __ Mov(out, first);
8175 }
8176 return;
8177 }
8178 if (value == 0u) {
8179 __ Mov(out, 0);
8180 return;
8181 }
8182 if (GetAssembler()->ShifterOperandCanHold(AND, value)) {
Anton Kiriloveffd5bf2017-02-28 16:59:15 +00008183 __ And(out, first, value);
8184 } else if (GetAssembler()->ShifterOperandCanHold(BIC, ~value)) {
8185 __ Bic(out, first, ~value);
Artem Serov02109dd2016-09-23 17:17:54 +01008186 } else {
Anton Kiriloveffd5bf2017-02-28 16:59:15 +00008187 DCHECK(IsPowerOfTwo(value + 1));
8188 __ Ubfx(out, first, 0, WhichPowerOf2(value + 1));
Artem Serov02109dd2016-09-23 17:17:54 +01008189 }
8190}
8191
8192// TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
8193void InstructionCodeGeneratorARMVIXL::GenerateOrrConst(vixl32::Register out,
8194 vixl32::Register first,
8195 uint32_t value) {
8196 // Optimize special cases for individual halfs of `or-long` (`or` is simplified earlier).
8197 if (value == 0u) {
8198 if (!out.Is(first)) {
8199 __ Mov(out, first);
8200 }
8201 return;
8202 }
8203 if (value == 0xffffffffu) {
8204 __ Mvn(out, 0);
8205 return;
8206 }
8207 if (GetAssembler()->ShifterOperandCanHold(ORR, value)) {
8208 __ Orr(out, first, value);
8209 } else {
8210 DCHECK(GetAssembler()->ShifterOperandCanHold(ORN, ~value));
8211 __ Orn(out, first, ~value);
8212 }
8213}
8214
8215// TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
8216void InstructionCodeGeneratorARMVIXL::GenerateEorConst(vixl32::Register out,
8217 vixl32::Register first,
8218 uint32_t value) {
8219 // Optimize special case for individual halfs of `xor-long` (`xor` is simplified earlier).
8220 if (value == 0u) {
8221 if (!out.Is(first)) {
8222 __ Mov(out, first);
8223 }
8224 return;
8225 }
8226 __ Eor(out, first, value);
8227}
8228
Anton Kirilovdda43962016-11-21 19:55:20 +00008229void InstructionCodeGeneratorARMVIXL::GenerateAddLongConst(Location out,
8230 Location first,
8231 uint64_t value) {
8232 vixl32::Register out_low = LowRegisterFrom(out);
8233 vixl32::Register out_high = HighRegisterFrom(out);
8234 vixl32::Register first_low = LowRegisterFrom(first);
8235 vixl32::Register first_high = HighRegisterFrom(first);
8236 uint32_t value_low = Low32Bits(value);
8237 uint32_t value_high = High32Bits(value);
8238 if (value_low == 0u) {
8239 if (!out_low.Is(first_low)) {
8240 __ Mov(out_low, first_low);
8241 }
8242 __ Add(out_high, first_high, value_high);
8243 return;
8244 }
8245 __ Adds(out_low, first_low, value_low);
Vladimir Markof0a6a1d2018-01-08 14:23:56 +00008246 if (GetAssembler()->ShifterOperandCanHold(ADC, value_high)) {
Anton Kirilovdda43962016-11-21 19:55:20 +00008247 __ Adc(out_high, first_high, value_high);
Anton Kirilovdda43962016-11-21 19:55:20 +00008248 } else {
Vladimir Markof0a6a1d2018-01-08 14:23:56 +00008249 DCHECK(GetAssembler()->ShifterOperandCanHold(SBC, ~value_high));
8250 __ Sbc(out_high, first_high, ~value_high);
Anton Kirilovdda43962016-11-21 19:55:20 +00008251 }
8252}
8253
Artem Serov02109dd2016-09-23 17:17:54 +01008254void InstructionCodeGeneratorARMVIXL::HandleBitwiseOperation(HBinaryOperation* instruction) {
8255 LocationSummary* locations = instruction->GetLocations();
8256 Location first = locations->InAt(0);
8257 Location second = locations->InAt(1);
8258 Location out = locations->Out();
8259
8260 if (second.IsConstant()) {
8261 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
8262 uint32_t value_low = Low32Bits(value);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008263 if (instruction->GetResultType() == DataType::Type::kInt32) {
Artem Serov02109dd2016-09-23 17:17:54 +01008264 vixl32::Register first_reg = InputRegisterAt(instruction, 0);
8265 vixl32::Register out_reg = OutputRegister(instruction);
8266 if (instruction->IsAnd()) {
8267 GenerateAndConst(out_reg, first_reg, value_low);
8268 } else if (instruction->IsOr()) {
8269 GenerateOrrConst(out_reg, first_reg, value_low);
8270 } else {
8271 DCHECK(instruction->IsXor());
8272 GenerateEorConst(out_reg, first_reg, value_low);
8273 }
8274 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008275 DCHECK_EQ(instruction->GetResultType(), DataType::Type::kInt64);
Artem Serov02109dd2016-09-23 17:17:54 +01008276 uint32_t value_high = High32Bits(value);
8277 vixl32::Register first_low = LowRegisterFrom(first);
8278 vixl32::Register first_high = HighRegisterFrom(first);
8279 vixl32::Register out_low = LowRegisterFrom(out);
8280 vixl32::Register out_high = HighRegisterFrom(out);
8281 if (instruction->IsAnd()) {
8282 GenerateAndConst(out_low, first_low, value_low);
8283 GenerateAndConst(out_high, first_high, value_high);
8284 } else if (instruction->IsOr()) {
8285 GenerateOrrConst(out_low, first_low, value_low);
8286 GenerateOrrConst(out_high, first_high, value_high);
8287 } else {
8288 DCHECK(instruction->IsXor());
8289 GenerateEorConst(out_low, first_low, value_low);
8290 GenerateEorConst(out_high, first_high, value_high);
8291 }
8292 }
8293 return;
8294 }
8295
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008296 if (instruction->GetResultType() == DataType::Type::kInt32) {
Artem Serov02109dd2016-09-23 17:17:54 +01008297 vixl32::Register first_reg = InputRegisterAt(instruction, 0);
8298 vixl32::Register second_reg = InputRegisterAt(instruction, 1);
8299 vixl32::Register out_reg = OutputRegister(instruction);
8300 if (instruction->IsAnd()) {
8301 __ And(out_reg, first_reg, second_reg);
8302 } else if (instruction->IsOr()) {
8303 __ Orr(out_reg, first_reg, second_reg);
8304 } else {
8305 DCHECK(instruction->IsXor());
8306 __ Eor(out_reg, first_reg, second_reg);
8307 }
8308 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008309 DCHECK_EQ(instruction->GetResultType(), DataType::Type::kInt64);
Artem Serov02109dd2016-09-23 17:17:54 +01008310 vixl32::Register first_low = LowRegisterFrom(first);
8311 vixl32::Register first_high = HighRegisterFrom(first);
8312 vixl32::Register second_low = LowRegisterFrom(second);
8313 vixl32::Register second_high = HighRegisterFrom(second);
8314 vixl32::Register out_low = LowRegisterFrom(out);
8315 vixl32::Register out_high = HighRegisterFrom(out);
8316 if (instruction->IsAnd()) {
8317 __ And(out_low, first_low, second_low);
8318 __ And(out_high, first_high, second_high);
8319 } else if (instruction->IsOr()) {
8320 __ Orr(out_low, first_low, second_low);
8321 __ Orr(out_high, first_high, second_high);
8322 } else {
8323 DCHECK(instruction->IsXor());
8324 __ Eor(out_low, first_low, second_low);
8325 __ Eor(out_high, first_high, second_high);
8326 }
8327 }
8328}
8329
Artem Serovcfbe9132016-10-14 15:58:56 +01008330void InstructionCodeGeneratorARMVIXL::GenerateReferenceLoadOneRegister(
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008331 HInstruction* instruction,
Artem Serovcfbe9132016-10-14 15:58:56 +01008332 Location out,
8333 uint32_t offset,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008334 Location maybe_temp,
8335 ReadBarrierOption read_barrier_option) {
Artem Serovcfbe9132016-10-14 15:58:56 +01008336 vixl32::Register out_reg = RegisterFrom(out);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008337 if (read_barrier_option == kWithReadBarrier) {
8338 CHECK(kEmitCompilerReadBarrier);
8339 DCHECK(maybe_temp.IsRegister()) << maybe_temp;
8340 if (kUseBakerReadBarrier) {
8341 // Load with fast path based Baker's read barrier.
8342 // /* HeapReference<Object> */ out = *(out + offset)
8343 codegen_->GenerateFieldLoadWithBakerReadBarrier(
Andreas Gampe3db70682018-12-26 15:12:03 -08008344 instruction, out, out_reg, offset, maybe_temp, /* needs_null_check= */ false);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008345 } else {
8346 // Load with slow path based read barrier.
8347 // Save the value of `out` into `maybe_temp` before overwriting it
8348 // in the following move operation, as we will need it for the
8349 // read barrier below.
8350 __ Mov(RegisterFrom(maybe_temp), out_reg);
8351 // /* HeapReference<Object> */ out = *(out + offset)
8352 GetAssembler()->LoadFromOffset(kLoadWord, out_reg, out_reg, offset);
8353 codegen_->GenerateReadBarrierSlow(instruction, out, out, maybe_temp, offset);
8354 }
Artem Serovcfbe9132016-10-14 15:58:56 +01008355 } else {
8356 // Plain load with no read barrier.
8357 // /* HeapReference<Object> */ out = *(out + offset)
8358 GetAssembler()->LoadFromOffset(kLoadWord, out_reg, out_reg, offset);
8359 GetAssembler()->MaybeUnpoisonHeapReference(out_reg);
8360 }
8361}
8362
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008363void InstructionCodeGeneratorARMVIXL::GenerateReferenceLoadTwoRegisters(
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008364 HInstruction* instruction,
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008365 Location out,
8366 Location obj,
8367 uint32_t offset,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008368 Location maybe_temp,
8369 ReadBarrierOption read_barrier_option) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008370 vixl32::Register out_reg = RegisterFrom(out);
8371 vixl32::Register obj_reg = RegisterFrom(obj);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008372 if (read_barrier_option == kWithReadBarrier) {
8373 CHECK(kEmitCompilerReadBarrier);
8374 if (kUseBakerReadBarrier) {
8375 DCHECK(maybe_temp.IsRegister()) << maybe_temp;
8376 // Load with fast path based Baker's read barrier.
8377 // /* HeapReference<Object> */ out = *(obj + offset)
8378 codegen_->GenerateFieldLoadWithBakerReadBarrier(
Andreas Gampe3db70682018-12-26 15:12:03 -08008379 instruction, out, obj_reg, offset, maybe_temp, /* needs_null_check= */ false);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008380 } else {
8381 // Load with slow path based read barrier.
8382 // /* HeapReference<Object> */ out = *(obj + offset)
8383 GetAssembler()->LoadFromOffset(kLoadWord, out_reg, obj_reg, offset);
8384 codegen_->GenerateReadBarrierSlow(instruction, out, out, obj, offset);
8385 }
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008386 } else {
8387 // Plain load with no read barrier.
8388 // /* HeapReference<Object> */ out = *(obj + offset)
8389 GetAssembler()->LoadFromOffset(kLoadWord, out_reg, obj_reg, offset);
8390 GetAssembler()->MaybeUnpoisonHeapReference(out_reg);
8391 }
8392}
8393
Vladimir Markoca1e0382018-04-11 09:58:41 +00008394void CodeGeneratorARMVIXL::GenerateGcRootFieldLoad(
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008395 HInstruction* instruction,
Scott Wakelinga7812ae2016-10-17 10:03:36 +01008396 Location root,
8397 vixl32::Register obj,
8398 uint32_t offset,
Artem Serovd4cc5b22016-11-04 11:19:09 +00008399 ReadBarrierOption read_barrier_option) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01008400 vixl32::Register root_reg = RegisterFrom(root);
Artem Serovd4cc5b22016-11-04 11:19:09 +00008401 if (read_barrier_option == kWithReadBarrier) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008402 DCHECK(kEmitCompilerReadBarrier);
8403 if (kUseBakerReadBarrier) {
8404 // Fast path implementation of art::ReadBarrier::BarrierForRoot when
Roland Levillainba650a42017-03-06 13:52:32 +00008405 // Baker's read barrier are used.
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008406
Vladimir Marko008e09f32018-08-06 15:42:43 +01008407 // Query `art::Thread::Current()->GetIsGcMarking()` (stored in
8408 // the Marking Register) to decide whether we need to enter
8409 // the slow path to mark the GC root.
8410 //
8411 // We use shared thunks for the slow path; shared within the method
8412 // for JIT, across methods for AOT. That thunk checks the reference
8413 // and jumps to the entrypoint if needed.
8414 //
8415 // lr = &return_address;
8416 // GcRoot<mirror::Object> root = *(obj+offset); // Original reference load.
8417 // if (mr) { // Thread::Current()->GetIsGcMarking()
8418 // goto gc_root_thunk<root_reg>(lr)
8419 // }
8420 // return_address:
Roland Levillainba650a42017-03-06 13:52:32 +00008421
Vladimir Marko008e09f32018-08-06 15:42:43 +01008422 UseScratchRegisterScope temps(GetVIXLAssembler());
8423 temps.Exclude(ip);
8424 bool narrow = CanEmitNarrowLdr(root_reg, obj, offset);
8425 uint32_t custom_data = EncodeBakerReadBarrierGcRootData(root_reg.GetCode(), narrow);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008426
Vladimir Markod887ed82018-08-14 13:52:12 +00008427 size_t narrow_instructions = /* CMP */ (mr.IsLow() ? 1u : 0u) + /* LDR */ (narrow ? 1u : 0u);
8428 size_t wide_instructions = /* ADR+CMP+LDR+BNE */ 4u - narrow_instructions;
8429 size_t exact_size = wide_instructions * vixl32::k32BitT32InstructionSizeInBytes +
8430 narrow_instructions * vixl32::k16BitT32InstructionSizeInBytes;
8431 ExactAssemblyScope guard(GetVIXLAssembler(), exact_size);
Vladimir Marko008e09f32018-08-06 15:42:43 +01008432 vixl32::Label return_address;
8433 EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
8434 __ cmp(mr, Operand(0));
8435 // Currently the offset is always within range. If that changes,
8436 // we shall have to split the load the same way as for fields.
8437 DCHECK_LT(offset, kReferenceLoadMinFarOffset);
8438 ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
8439 __ ldr(EncodingSize(narrow ? Narrow : Wide), root_reg, MemOperand(obj, offset));
8440 EmitBakerReadBarrierBne(custom_data);
Vladimir Markod887ed82018-08-14 13:52:12 +00008441 __ bind(&return_address);
Vladimir Marko008e09f32018-08-06 15:42:43 +01008442 DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
8443 narrow ? BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_OFFSET
8444 : BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_OFFSET);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008445 } else {
8446 // GC root loaded through a slow path for read barriers other
8447 // than Baker's.
8448 // /* GcRoot<mirror::Object>* */ root = obj + offset
8449 __ Add(root_reg, obj, offset);
8450 // /* mirror::Object* */ root = root->Read()
Vladimir Markoca1e0382018-04-11 09:58:41 +00008451 GenerateReadBarrierForRootSlow(instruction, root, root);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008452 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01008453 } else {
8454 // Plain GC root load with no read barrier.
8455 // /* GcRoot<mirror::Object> */ root = *(obj + offset)
8456 GetAssembler()->LoadFromOffset(kLoadWord, root_reg, obj, offset);
8457 // Note that GC roots are not affected by heap poisoning, thus we
8458 // do not have to unpoison `root_reg` here.
8459 }
Andreas Gampe3db70682018-12-26 15:12:03 -08008460 MaybeGenerateMarkingRegisterCheck(/* code= */ 19);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01008461}
8462
Vladimir Markod887ed82018-08-14 13:52:12 +00008463void CodeGeneratorARMVIXL::GenerateUnsafeCasOldValueAddWithBakerReadBarrier(
8464 vixl::aarch32::Register old_value,
8465 vixl::aarch32::Register adjusted_old_value,
8466 vixl::aarch32::Register expected) {
8467 DCHECK(kEmitCompilerReadBarrier);
8468 DCHECK(kUseBakerReadBarrier);
8469
8470 // Similar to the Baker RB path in GenerateGcRootFieldLoad(), with an ADD instead of LDR.
8471 uint32_t custom_data = EncodeBakerReadBarrierUnsafeCasData(old_value.GetCode());
8472
8473 size_t narrow_instructions = /* CMP */ (mr.IsLow() ? 1u : 0u);
8474 size_t wide_instructions = /* ADR+CMP+ADD+BNE */ 4u - narrow_instructions;
8475 size_t exact_size = wide_instructions * vixl32::k32BitT32InstructionSizeInBytes +
8476 narrow_instructions * vixl32::k16BitT32InstructionSizeInBytes;
8477 ExactAssemblyScope guard(GetVIXLAssembler(), exact_size);
8478 vixl32::Label return_address;
8479 EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
8480 __ cmp(mr, Operand(0));
8481 ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
8482 __ add(EncodingSize(Wide), old_value, adjusted_old_value, Operand(expected)); // Preserves flags.
8483 EmitBakerReadBarrierBne(custom_data);
8484 __ bind(&return_address);
8485 DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
8486 BAKER_MARK_INTROSPECTION_UNSAFE_CAS_ADD_OFFSET);
8487}
8488
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008489void CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
8490 Location ref,
8491 vixl32::Register obj,
Vladimir Marko248141f2018-08-10 10:40:07 +01008492 const vixl32::MemOperand& src,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008493 bool needs_null_check) {
8494 DCHECK(kEmitCompilerReadBarrier);
8495 DCHECK(kUseBakerReadBarrier);
8496
Vladimir Marko008e09f32018-08-06 15:42:43 +01008497 // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
8498 // Marking Register) to decide whether we need to enter the slow
8499 // path to mark the reference. Then, in the slow path, check the
8500 // gray bit in the lock word of the reference's holder (`obj`) to
8501 // decide whether to mark `ref` or not.
8502 //
8503 // We use shared thunks for the slow path; shared within the method
8504 // for JIT, across methods for AOT. That thunk checks the holder
8505 // and jumps to the entrypoint if needed. If the holder is not gray,
8506 // it creates a fake dependency and returns to the LDR instruction.
8507 //
8508 // lr = &gray_return_address;
8509 // if (mr) { // Thread::Current()->GetIsGcMarking()
8510 // goto field_thunk<holder_reg, base_reg>(lr)
8511 // }
8512 // not_gray_return_address:
8513 // // Original reference load. If the offset is too large to fit
8514 // // into LDR, we use an adjusted base register here.
8515 // HeapReference<mirror::Object> reference = *(obj+offset);
8516 // gray_return_address:
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008517
Vladimir Marko248141f2018-08-10 10:40:07 +01008518 DCHECK(src.GetAddrMode() == vixl32::Offset);
8519 DCHECK_ALIGNED(src.GetOffsetImmediate(), sizeof(mirror::HeapReference<mirror::Object>));
Vladimir Marko008e09f32018-08-06 15:42:43 +01008520 vixl32::Register ref_reg = RegisterFrom(ref, DataType::Type::kReference);
Vladimir Marko248141f2018-08-10 10:40:07 +01008521 bool narrow = CanEmitNarrowLdr(ref_reg, src.GetBaseRegister(), src.GetOffsetImmediate());
8522
Vladimir Marko008e09f32018-08-06 15:42:43 +01008523 UseScratchRegisterScope temps(GetVIXLAssembler());
8524 temps.Exclude(ip);
Vladimir Marko248141f2018-08-10 10:40:07 +01008525 uint32_t custom_data =
8526 EncodeBakerReadBarrierFieldData(src.GetBaseRegister().GetCode(), obj.GetCode(), narrow);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008527
Vladimir Marko008e09f32018-08-06 15:42:43 +01008528 {
Vladimir Markod887ed82018-08-14 13:52:12 +00008529 size_t narrow_instructions =
8530 /* CMP */ (mr.IsLow() ? 1u : 0u) +
8531 /* LDR+unpoison? */ (narrow ? (kPoisonHeapReferences ? 2u : 1u) : 0u);
8532 size_t wide_instructions =
8533 /* ADR+CMP+LDR+BNE+unpoison? */ (kPoisonHeapReferences ? 5u : 4u) - narrow_instructions;
8534 size_t exact_size = wide_instructions * vixl32::k32BitT32InstructionSizeInBytes +
8535 narrow_instructions * vixl32::k16BitT32InstructionSizeInBytes;
8536 ExactAssemblyScope guard(GetVIXLAssembler(), exact_size);
Vladimir Marko008e09f32018-08-06 15:42:43 +01008537 vixl32::Label return_address;
8538 EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
8539 __ cmp(mr, Operand(0));
8540 EmitBakerReadBarrierBne(custom_data);
8541 ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
Vladimir Marko248141f2018-08-10 10:40:07 +01008542 __ ldr(EncodingSize(narrow ? Narrow : Wide), ref_reg, src);
Vladimir Marko008e09f32018-08-06 15:42:43 +01008543 if (needs_null_check) {
8544 MaybeRecordImplicitNullCheck(instruction);
8545 }
8546 // Note: We need a specific width for the unpoisoning NEG.
8547 if (kPoisonHeapReferences) {
8548 if (narrow) {
8549 // The only 16-bit encoding is T1 which sets flags outside IT block (i.e. RSBS, not RSB).
8550 __ rsbs(EncodingSize(Narrow), ref_reg, ref_reg, Operand(0));
8551 } else {
8552 __ rsb(EncodingSize(Wide), ref_reg, ref_reg, Operand(0));
8553 }
8554 }
Vladimir Markod887ed82018-08-14 13:52:12 +00008555 __ bind(&return_address);
Vladimir Marko008e09f32018-08-06 15:42:43 +01008556 DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
8557 narrow ? BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET
8558 : BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET);
8559 }
Andreas Gampe3db70682018-12-26 15:12:03 -08008560 MaybeGenerateMarkingRegisterCheck(/* code= */ 20, /* temp_loc= */ LocationFrom(ip));
Roland Levillain6070e882016-11-03 17:51:58 +00008561}
8562
Vladimir Marko248141f2018-08-10 10:40:07 +01008563void CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
8564 Location ref,
8565 vixl32::Register obj,
8566 uint32_t offset,
8567 Location temp,
8568 bool needs_null_check) {
8569 DCHECK_ALIGNED(offset, sizeof(mirror::HeapReference<mirror::Object>));
8570 vixl32::Register base = obj;
8571 if (offset >= kReferenceLoadMinFarOffset) {
8572 base = RegisterFrom(temp);
8573 static_assert(IsPowerOfTwo(kReferenceLoadMinFarOffset), "Expecting a power of 2.");
8574 __ Add(base, obj, Operand(offset & ~(kReferenceLoadMinFarOffset - 1u)));
8575 offset &= (kReferenceLoadMinFarOffset - 1u);
8576 }
8577 GenerateFieldLoadWithBakerReadBarrier(
8578 instruction, ref, obj, MemOperand(base, offset), needs_null_check);
8579}
8580
Vladimir Marko008e09f32018-08-06 15:42:43 +01008581void CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier(Location ref,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008582 vixl32::Register obj,
8583 uint32_t data_offset,
8584 Location index,
8585 Location temp,
8586 bool needs_null_check) {
8587 DCHECK(kEmitCompilerReadBarrier);
8588 DCHECK(kUseBakerReadBarrier);
8589
8590 static_assert(
8591 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
8592 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008593 ScaleFactor scale_factor = TIMES_4;
8594
Vladimir Marko008e09f32018-08-06 15:42:43 +01008595 // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
8596 // Marking Register) to decide whether we need to enter the slow
8597 // path to mark the reference. Then, in the slow path, check the
8598 // gray bit in the lock word of the reference's holder (`obj`) to
8599 // decide whether to mark `ref` or not.
8600 //
8601 // We use shared thunks for the slow path; shared within the method
8602 // for JIT, across methods for AOT. That thunk checks the holder
8603 // and jumps to the entrypoint if needed. If the holder is not gray,
8604 // it creates a fake dependency and returns to the LDR instruction.
8605 //
8606 // lr = &gray_return_address;
8607 // if (mr) { // Thread::Current()->GetIsGcMarking()
8608 // goto array_thunk<base_reg>(lr)
8609 // }
8610 // not_gray_return_address:
8611 // // Original reference load. If the offset is too large to fit
8612 // // into LDR, we use an adjusted base register here.
8613 // HeapReference<mirror::Object> reference = data[index];
8614 // gray_return_address:
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008615
Vladimir Marko008e09f32018-08-06 15:42:43 +01008616 DCHECK(index.IsValid());
8617 vixl32::Register index_reg = RegisterFrom(index, DataType::Type::kInt32);
8618 vixl32::Register ref_reg = RegisterFrom(ref, DataType::Type::kReference);
8619 vixl32::Register data_reg = RegisterFrom(temp, DataType::Type::kInt32); // Raw pointer.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008620
Vladimir Marko008e09f32018-08-06 15:42:43 +01008621 UseScratchRegisterScope temps(GetVIXLAssembler());
8622 temps.Exclude(ip);
8623 uint32_t custom_data = EncodeBakerReadBarrierArrayData(data_reg.GetCode());
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008624
Vladimir Marko008e09f32018-08-06 15:42:43 +01008625 __ Add(data_reg, obj, Operand(data_offset));
8626 {
Vladimir Markod887ed82018-08-14 13:52:12 +00008627 size_t narrow_instructions = /* CMP */ (mr.IsLow() ? 1u : 0u);
8628 size_t wide_instructions =
8629 /* ADR+CMP+BNE+LDR+unpoison? */ (kPoisonHeapReferences ? 5u : 4u) - narrow_instructions;
8630 size_t exact_size = wide_instructions * vixl32::k32BitT32InstructionSizeInBytes +
8631 narrow_instructions * vixl32::k16BitT32InstructionSizeInBytes;
8632 ExactAssemblyScope guard(GetVIXLAssembler(), exact_size);
Vladimir Marko008e09f32018-08-06 15:42:43 +01008633 vixl32::Label return_address;
8634 EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
8635 __ cmp(mr, Operand(0));
8636 EmitBakerReadBarrierBne(custom_data);
8637 ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
8638 __ ldr(ref_reg, MemOperand(data_reg, index_reg, vixl32::LSL, scale_factor));
8639 DCHECK(!needs_null_check); // The thunk cannot handle the null check.
8640 // Note: We need a Wide NEG for the unpoisoning.
8641 if (kPoisonHeapReferences) {
8642 __ rsb(EncodingSize(Wide), ref_reg, ref_reg, Operand(0));
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008643 }
Vladimir Markod887ed82018-08-14 13:52:12 +00008644 __ bind(&return_address);
Vladimir Marko008e09f32018-08-06 15:42:43 +01008645 DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
8646 BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008647 }
Andreas Gampe3db70682018-12-26 15:12:03 -08008648 MaybeGenerateMarkingRegisterCheck(/* code= */ 21, /* temp_loc= */ LocationFrom(ip));
Roland Levillain6070e882016-11-03 17:51:58 +00008649}
8650
Roland Levillain5daa4952017-07-03 17:23:56 +01008651void CodeGeneratorARMVIXL::MaybeGenerateMarkingRegisterCheck(int code, Location temp_loc) {
8652 // The following condition is a compile-time one, so it does not have a run-time cost.
8653 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier && kIsDebugBuild) {
8654 // The following condition is a run-time one; it is executed after the
8655 // previous compile-time test, to avoid penalizing non-debug builds.
8656 if (GetCompilerOptions().EmitRunTimeChecksInDebugMode()) {
8657 UseScratchRegisterScope temps(GetVIXLAssembler());
8658 vixl32::Register temp = temp_loc.IsValid() ? RegisterFrom(temp_loc) : temps.Acquire();
8659 GetAssembler()->GenerateMarkingRegisterCheck(temp,
8660 kMarkingRegisterCheckBreakCodeBaseCode + code);
8661 }
8662 }
8663}
8664
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008665void CodeGeneratorARMVIXL::GenerateReadBarrierSlow(HInstruction* instruction,
8666 Location out,
8667 Location ref,
8668 Location obj,
8669 uint32_t offset,
8670 Location index) {
8671 DCHECK(kEmitCompilerReadBarrier);
8672
8673 // Insert a slow path based read barrier *after* the reference load.
8674 //
8675 // If heap poisoning is enabled, the unpoisoning of the loaded
8676 // reference will be carried out by the runtime within the slow
8677 // path.
8678 //
8679 // Note that `ref` currently does not get unpoisoned (when heap
8680 // poisoning is enabled), which is alright as the `ref` argument is
8681 // not used by the artReadBarrierSlow entry point.
8682 //
8683 // TODO: Unpoison `ref` when it is used by artReadBarrierSlow.
Vladimir Marko174b2e22017-10-12 13:34:49 +01008684 SlowPathCodeARMVIXL* slow_path = new (GetScopedAllocator())
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008685 ReadBarrierForHeapReferenceSlowPathARMVIXL(instruction, out, ref, obj, offset, index);
8686 AddSlowPath(slow_path);
8687
8688 __ B(slow_path->GetEntryLabel());
8689 __ Bind(slow_path->GetExitLabel());
8690}
8691
8692void CodeGeneratorARMVIXL::MaybeGenerateReadBarrierSlow(HInstruction* instruction,
Artem Serov02d37832016-10-25 15:25:33 +01008693 Location out,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008694 Location ref,
8695 Location obj,
8696 uint32_t offset,
8697 Location index) {
Artem Serov02d37832016-10-25 15:25:33 +01008698 if (kEmitCompilerReadBarrier) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008699 // Baker's read barriers shall be handled by the fast path
Roland Levillain9983e302017-07-14 14:34:22 +01008700 // (CodeGeneratorARMVIXL::GenerateReferenceLoadWithBakerReadBarrier).
Artem Serov02d37832016-10-25 15:25:33 +01008701 DCHECK(!kUseBakerReadBarrier);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008702 // If heap poisoning is enabled, unpoisoning will be taken care of
8703 // by the runtime within the slow path.
8704 GenerateReadBarrierSlow(instruction, out, ref, obj, offset, index);
Artem Serov02d37832016-10-25 15:25:33 +01008705 } else if (kPoisonHeapReferences) {
8706 GetAssembler()->UnpoisonHeapReference(RegisterFrom(out));
8707 }
8708}
8709
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008710void CodeGeneratorARMVIXL::GenerateReadBarrierForRootSlow(HInstruction* instruction,
8711 Location out,
8712 Location root) {
8713 DCHECK(kEmitCompilerReadBarrier);
8714
8715 // Insert a slow path based read barrier *after* the GC root load.
8716 //
8717 // Note that GC roots are not affected by heap poisoning, so we do
8718 // not need to do anything special for this here.
8719 SlowPathCodeARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01008720 new (GetScopedAllocator()) ReadBarrierForRootSlowPathARMVIXL(instruction, out, root);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008721 AddSlowPath(slow_path);
8722
8723 __ B(slow_path->GetEntryLabel());
8724 __ Bind(slow_path->GetExitLabel());
8725}
8726
Artem Serov02d37832016-10-25 15:25:33 +01008727// Check if the desired_dispatch_info is supported. If it is, return it,
8728// otherwise return a fall-back info that should be used instead.
8729HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARMVIXL::GetSupportedInvokeStaticOrDirectDispatch(
Artem Serovd4cc5b22016-11-04 11:19:09 +00008730 const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
Nicolas Geoffraybdb2ecc2018-09-18 14:33:55 +01008731 ArtMethod* method ATTRIBUTE_UNUSED) {
Nicolas Geoffraye807ff72017-01-23 09:03:12 +00008732 return desired_dispatch_info;
Artem Serov02d37832016-10-25 15:25:33 +01008733}
8734
Scott Wakelinga7812ae2016-10-17 10:03:36 +01008735vixl32::Register CodeGeneratorARMVIXL::GetInvokeStaticOrDirectExtraParameter(
8736 HInvokeStaticOrDirect* invoke, vixl32::Register temp) {
8737 DCHECK_EQ(invoke->InputCount(), invoke->GetNumberOfArguments() + 1u);
8738 Location location = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
8739 if (!invoke->GetLocations()->Intrinsified()) {
8740 return RegisterFrom(location);
8741 }
8742 // For intrinsics we allow any location, so it may be on the stack.
8743 if (!location.IsRegister()) {
8744 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, location.GetStackIndex());
8745 return temp;
8746 }
8747 // For register locations, check if the register was saved. If so, get it from the stack.
8748 // Note: There is a chance that the register was saved but not overwritten, so we could
8749 // save one load. However, since this is just an intrinsic slow path we prefer this
8750 // simple and more robust approach rather that trying to determine if that's the case.
8751 SlowPathCode* slow_path = GetCurrentSlowPath();
Scott Wakelingd5cd4972017-02-03 11:38:35 +00008752 if (slow_path != nullptr && slow_path->IsCoreRegisterSaved(RegisterFrom(location).GetCode())) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01008753 int stack_offset = slow_path->GetStackOffsetOfCoreRegister(RegisterFrom(location).GetCode());
8754 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, stack_offset);
8755 return temp;
8756 }
8757 return RegisterFrom(location);
8758}
8759
Vladimir Markod254f5c2017-06-02 15:18:36 +00008760void CodeGeneratorARMVIXL::GenerateStaticOrDirectCall(
Vladimir Markoe7197bf2017-06-02 17:00:23 +01008761 HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00008762 Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01008763 switch (invoke->GetMethodLoadKind()) {
8764 case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: {
8765 uint32_t offset =
8766 GetThreadOffset<kArmPointerSize>(invoke->GetStringInitEntryPoint()).Int32Value();
8767 // temp = thread->string_init_entrypoint
Artem Serovd4cc5b22016-11-04 11:19:09 +00008768 GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(temp), tr, offset);
8769 break;
8770 }
8771 case HInvokeStaticOrDirect::MethodLoadKind::kRecursive:
8772 callee_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
8773 break;
Vladimir Marko65979462017-05-19 17:25:12 +01008774 case HInvokeStaticOrDirect::MethodLoadKind::kBootImageLinkTimePcRelative: {
Vladimir Marko44ca0752019-07-29 10:18:25 +01008775 DCHECK(GetCompilerOptions().IsBootImage() || GetCompilerOptions().IsBootImageExtension());
Vladimir Marko59eb30f2018-02-20 11:52:34 +00008776 PcRelativePatchInfo* labels = NewBootImageMethodPatch(invoke->GetTargetMethod());
Vladimir Marko65979462017-05-19 17:25:12 +01008777 vixl32::Register temp_reg = RegisterFrom(temp);
8778 EmitMovwMovtPlaceholder(labels, temp_reg);
8779 break;
8780 }
Vladimir Markob066d432018-01-03 13:14:37 +00008781 case HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo: {
Vladimir Markoe47f60c2018-02-21 13:43:28 +00008782 uint32_t boot_image_offset = GetBootImageOffset(invoke);
Vladimir Markob066d432018-01-03 13:14:37 +00008783 PcRelativePatchInfo* labels = NewBootImageRelRoPatch(boot_image_offset);
8784 vixl32::Register temp_reg = RegisterFrom(temp);
8785 EmitMovwMovtPlaceholder(labels, temp_reg);
8786 GetAssembler()->LoadFromOffset(kLoadWord, temp_reg, temp_reg, /* offset*/ 0);
8787 break;
8788 }
Vladimir Marko0eb882b2017-05-15 13:39:18 +01008789 case HInvokeStaticOrDirect::MethodLoadKind::kBssEntry: {
8790 PcRelativePatchInfo* labels = NewMethodBssEntryPatch(
8791 MethodReference(&GetGraph()->GetDexFile(), invoke->GetDexMethodIndex()));
8792 vixl32::Register temp_reg = RegisterFrom(temp);
8793 EmitMovwMovtPlaceholder(labels, temp_reg);
Vladimir Markod5fd5c32019-07-02 14:46:32 +01008794 // All aligned loads are implicitly atomic consume operations on ARM.
Vladimir Marko0eb882b2017-05-15 13:39:18 +01008795 GetAssembler()->LoadFromOffset(kLoadWord, temp_reg, temp_reg, /* offset*/ 0);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01008796 break;
8797 }
Vladimir Marko8e524ad2018-07-13 10:27:43 +01008798 case HInvokeStaticOrDirect::MethodLoadKind::kJitDirectAddress:
8799 __ Mov(RegisterFrom(temp), Operand::From(invoke->GetMethodAddress()));
8800 break;
Vladimir Markoe7197bf2017-06-02 17:00:23 +01008801 case HInvokeStaticOrDirect::MethodLoadKind::kRuntimeCall: {
8802 GenerateInvokeStaticOrDirectRuntimeCall(invoke, temp, slow_path);
8803 return; // No code pointer retrieval; the runtime performs the call directly.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01008804 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01008805 }
8806
Artem Serovd4cc5b22016-11-04 11:19:09 +00008807 switch (invoke->GetCodePtrLocation()) {
8808 case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf:
Vladimir Markoe7197bf2017-06-02 17:00:23 +01008809 {
8810 // Use a scope to help guarantee that `RecordPcInfo()` records the correct pc.
8811 ExactAssemblyScope aas(GetVIXLAssembler(),
8812 vixl32::k32BitT32InstructionSizeInBytes,
8813 CodeBufferCheckScope::kMaximumSize);
8814 __ bl(GetFrameEntryLabel());
8815 RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
8816 }
Artem Serovd4cc5b22016-11-04 11:19:09 +00008817 break;
Artem Serovd4cc5b22016-11-04 11:19:09 +00008818 case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod:
8819 // LR = callee_method->entry_point_from_quick_compiled_code_
8820 GetAssembler()->LoadFromOffset(
8821 kLoadWord,
8822 lr,
8823 RegisterFrom(callee_method),
8824 ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value());
Alexandre Rames374ddf32016-11-04 10:40:49 +00008825 {
Vladimir Markoe7197bf2017-06-02 17:00:23 +01008826 // Use a scope to help guarantee that `RecordPcInfo()` records the correct pc.
Alexandre Rames374ddf32016-11-04 10:40:49 +00008827 // 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 +00008828 ExactAssemblyScope aas(GetVIXLAssembler(),
8829 vixl32::k16BitT32InstructionSizeInBytes,
8830 CodeBufferCheckScope::kExactSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00008831 // LR()
8832 __ blx(lr);
Vladimir Markoe7197bf2017-06-02 17:00:23 +01008833 RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
Alexandre Rames374ddf32016-11-04 10:40:49 +00008834 }
Artem Serovd4cc5b22016-11-04 11:19:09 +00008835 break;
Scott Wakelinga7812ae2016-10-17 10:03:36 +01008836 }
8837
Scott Wakelinga7812ae2016-10-17 10:03:36 +01008838 DCHECK(!IsLeafMethod());
8839}
8840
Vladimir Markoe7197bf2017-06-02 17:00:23 +01008841void CodeGeneratorARMVIXL::GenerateVirtualCall(
8842 HInvokeVirtual* invoke, Location temp_location, SlowPathCode* slow_path) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01008843 vixl32::Register temp = RegisterFrom(temp_location);
8844 uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
8845 invoke->GetVTableIndex(), kArmPointerSize).Uint32Value();
8846
8847 // Use the calling convention instead of the location of the receiver, as
8848 // intrinsics may have put the receiver in a different register. In the intrinsics
8849 // slow path, the arguments have been moved to the right place, so here we are
8850 // guaranteed that the receiver is the first register of the calling convention.
8851 InvokeDexCallingConventionARMVIXL calling_convention;
8852 vixl32::Register receiver = calling_convention.GetRegisterAt(0);
8853 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
Alexandre Rames374ddf32016-11-04 10:40:49 +00008854 {
8855 // Make sure the pc is recorded immediately after the `ldr` instruction.
Artem Serov0fb37192016-12-06 18:13:40 +00008856 ExactAssemblyScope aas(GetVIXLAssembler(),
8857 vixl32::kMaxInstructionSizeInBytes,
8858 CodeBufferCheckScope::kMaximumSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00008859 // /* HeapReference<Class> */ temp = receiver->klass_
8860 __ ldr(temp, MemOperand(receiver, class_offset));
8861 MaybeRecordImplicitNullCheck(invoke);
8862 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01008863 // Instead of simply (possibly) unpoisoning `temp` here, we should
8864 // emit a read barrier for the previous class reference load.
8865 // However this is not required in practice, as this is an
8866 // intermediate/temporary reference and because the current
8867 // concurrent copying collector keeps the from-space memory
8868 // intact/accessible until the end of the marking phase (the
8869 // concurrent copying collector may not in the future).
8870 GetAssembler()->MaybeUnpoisonHeapReference(temp);
8871
8872 // temp = temp->GetMethodAt(method_offset);
8873 uint32_t entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(
8874 kArmPointerSize).Int32Value();
8875 GetAssembler()->LoadFromOffset(kLoadWord, temp, temp, method_offset);
8876 // LR = temp->GetEntryPoint();
8877 GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, entry_point);
Vladimir Markoe7197bf2017-06-02 17:00:23 +01008878 {
8879 // Use a scope to help guarantee that `RecordPcInfo()` records the correct pc.
8880 // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
8881 ExactAssemblyScope aas(GetVIXLAssembler(),
8882 vixl32::k16BitT32InstructionSizeInBytes,
8883 CodeBufferCheckScope::kExactSize);
8884 // LR();
8885 __ blx(lr);
8886 RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
8887 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01008888}
8889
Vladimir Marko6fd16062018-06-26 11:02:04 +01008890CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewBootImageIntrinsicPatch(
8891 uint32_t intrinsic_data) {
Vladimir Marko2d06e022019-07-08 15:45:19 +01008892 return NewPcRelativePatch(/* dex_file= */ nullptr, intrinsic_data, &boot_image_other_patches_);
Vladimir Marko6fd16062018-06-26 11:02:04 +01008893}
8894
Vladimir Markob066d432018-01-03 13:14:37 +00008895CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewBootImageRelRoPatch(
8896 uint32_t boot_image_offset) {
Andreas Gampe3db70682018-12-26 15:12:03 -08008897 return NewPcRelativePatch(/* dex_file= */ nullptr,
Vladimir Markob066d432018-01-03 13:14:37 +00008898 boot_image_offset,
Vladimir Marko2d06e022019-07-08 15:45:19 +01008899 &boot_image_other_patches_);
Vladimir Markob066d432018-01-03 13:14:37 +00008900}
8901
Vladimir Marko59eb30f2018-02-20 11:52:34 +00008902CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewBootImageMethodPatch(
Vladimir Marko65979462017-05-19 17:25:12 +01008903 MethodReference target_method) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00008904 return NewPcRelativePatch(
8905 target_method.dex_file, target_method.index, &boot_image_method_patches_);
Artem Serovd4cc5b22016-11-04 11:19:09 +00008906}
8907
Vladimir Marko0eb882b2017-05-15 13:39:18 +01008908CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewMethodBssEntryPatch(
8909 MethodReference target_method) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00008910 return NewPcRelativePatch(
8911 target_method.dex_file, target_method.index, &method_bss_entry_patches_);
Vladimir Marko0eb882b2017-05-15 13:39:18 +01008912}
8913
Vladimir Marko59eb30f2018-02-20 11:52:34 +00008914CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewBootImageTypePatch(
Artem Serovd4cc5b22016-11-04 11:19:09 +00008915 const DexFile& dex_file, dex::TypeIndex type_index) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00008916 return NewPcRelativePatch(&dex_file, type_index.index_, &boot_image_type_patches_);
Artem Serovd4cc5b22016-11-04 11:19:09 +00008917}
8918
Vladimir Marko1998cd02017-01-13 13:02:58 +00008919CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewTypeBssEntryPatch(
8920 const DexFile& dex_file, dex::TypeIndex type_index) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00008921 return NewPcRelativePatch(&dex_file, type_index.index_, &type_bss_entry_patches_);
Vladimir Marko1998cd02017-01-13 13:02:58 +00008922}
8923
Vladimir Marko59eb30f2018-02-20 11:52:34 +00008924CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewBootImageStringPatch(
Vladimir Marko65979462017-05-19 17:25:12 +01008925 const DexFile& dex_file, dex::StringIndex string_index) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00008926 return NewPcRelativePatch(&dex_file, string_index.index_, &boot_image_string_patches_);
Vladimir Marko65979462017-05-19 17:25:12 +01008927}
8928
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01008929CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewStringBssEntryPatch(
8930 const DexFile& dex_file, dex::StringIndex string_index) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00008931 return NewPcRelativePatch(&dex_file, string_index.index_, &string_bss_entry_patches_);
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01008932}
8933
Artem Serovd4cc5b22016-11-04 11:19:09 +00008934CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewPcRelativePatch(
Vladimir Marko59eb30f2018-02-20 11:52:34 +00008935 const DexFile* dex_file, uint32_t offset_or_index, ArenaDeque<PcRelativePatchInfo>* patches) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00008936 patches->emplace_back(dex_file, offset_or_index);
8937 return &patches->back();
8938}
8939
Vladimir Markof6675082019-05-17 12:05:28 +01008940void CodeGeneratorARMVIXL::EmitEntrypointThunkCall(ThreadOffset32 entrypoint_offset) {
8941 DCHECK(!__ AllowMacroInstructions()); // In ExactAssemblyScope.
8942 DCHECK(!Runtime::Current()->UseJitCompilation());
8943 call_entrypoint_patches_.emplace_back(/*dex_file*/ nullptr, entrypoint_offset.Uint32Value());
8944 vixl::aarch32::Label* bl_label = &call_entrypoint_patches_.back().label;
8945 __ bind(bl_label);
8946 vixl32::Label placeholder_label;
8947 __ bl(&placeholder_label); // Placeholder, patched at link-time.
8948 __ bind(&placeholder_label);
8949}
8950
Vladimir Marko966b46f2018-08-03 10:20:19 +00008951void CodeGeneratorARMVIXL::EmitBakerReadBarrierBne(uint32_t custom_data) {
Vladimir Markod887ed82018-08-14 13:52:12 +00008952 DCHECK(!__ AllowMacroInstructions()); // In ExactAssemblyScope.
Vladimir Marko966b46f2018-08-03 10:20:19 +00008953 if (Runtime::Current()->UseJitCompilation()) {
8954 auto it = jit_baker_read_barrier_slow_paths_.FindOrAdd(custom_data);
8955 vixl::aarch32::Label* slow_path_entry = &it->second.label;
8956 __ b(ne, EncodingSize(Wide), slow_path_entry);
8957 } else {
8958 baker_read_barrier_patches_.emplace_back(custom_data);
8959 vixl::aarch32::Label* patch_label = &baker_read_barrier_patches_.back().label;
8960 __ bind(patch_label);
8961 vixl32::Label placeholder_label;
8962 __ b(ne, EncodingSize(Wide), &placeholder_label); // Placeholder, patched at link-time.
8963 __ bind(&placeholder_label);
8964 }
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008965}
8966
Artem Serovc5fcb442016-12-02 19:19:58 +00008967VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateBootImageAddressLiteral(uint32_t address) {
Vladimir Marko8e524ad2018-07-13 10:27:43 +01008968 return DeduplicateUint32Literal(address, &uint32_literals_);
Artem Serovc5fcb442016-12-02 19:19:58 +00008969}
8970
Nicolas Geoffrayf0acfe72017-01-09 20:54:52 +00008971VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateJitStringLiteral(
8972 const DexFile& dex_file,
8973 dex::StringIndex string_index,
8974 Handle<mirror::String> handle) {
Vladimir Marko174b2e22017-10-12 13:34:49 +01008975 ReserveJitStringRoot(StringReference(&dex_file, string_index), handle);
Artem Serovc5fcb442016-12-02 19:19:58 +00008976 return jit_string_patches_.GetOrCreate(
8977 StringReference(&dex_file, string_index),
8978 [this]() {
Andreas Gampe3db70682018-12-26 15:12:03 -08008979 return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* value= */ 0u);
Artem Serovc5fcb442016-12-02 19:19:58 +00008980 });
8981}
8982
8983VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateJitClassLiteral(const DexFile& dex_file,
8984 dex::TypeIndex type_index,
Nicolas Geoffray5247c082017-01-13 14:17:29 +00008985 Handle<mirror::Class> handle) {
Vladimir Marko174b2e22017-10-12 13:34:49 +01008986 ReserveJitClassRoot(TypeReference(&dex_file, type_index), handle);
Artem Serovc5fcb442016-12-02 19:19:58 +00008987 return jit_class_patches_.GetOrCreate(
8988 TypeReference(&dex_file, type_index),
8989 [this]() {
Andreas Gampe3db70682018-12-26 15:12:03 -08008990 return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* value= */ 0u);
Artem Serovc5fcb442016-12-02 19:19:58 +00008991 });
8992}
8993
Vladimir Marko6fd16062018-06-26 11:02:04 +01008994void CodeGeneratorARMVIXL::LoadBootImageAddress(vixl32::Register reg,
8995 uint32_t boot_image_reference) {
8996 if (GetCompilerOptions().IsBootImage()) {
8997 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
8998 NewBootImageIntrinsicPatch(boot_image_reference);
8999 EmitMovwMovtPlaceholder(labels, reg);
Vladimir Markoa2da9b92018-10-10 14:21:55 +01009000 } else if (GetCompilerOptions().GetCompilePic()) {
Vladimir Marko6fd16062018-06-26 11:02:04 +01009001 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
9002 NewBootImageRelRoPatch(boot_image_reference);
Vladimir Markoeebb8212018-06-05 14:57:24 +01009003 EmitMovwMovtPlaceholder(labels, reg);
Andreas Gampe3db70682018-12-26 15:12:03 -08009004 __ Ldr(reg, MemOperand(reg, /* offset= */ 0));
Vladimir Markoeebb8212018-06-05 14:57:24 +01009005 } else {
Vladimir Marko8e524ad2018-07-13 10:27:43 +01009006 DCHECK(Runtime::Current()->UseJitCompilation());
Vladimir Markoeebb8212018-06-05 14:57:24 +01009007 gc::Heap* heap = Runtime::Current()->GetHeap();
9008 DCHECK(!heap->GetBootImageSpaces().empty());
9009 uintptr_t address =
Vladimir Marko6fd16062018-06-26 11:02:04 +01009010 reinterpret_cast<uintptr_t>(heap->GetBootImageSpaces()[0]->Begin() + boot_image_reference);
Vladimir Markoeebb8212018-06-05 14:57:24 +01009011 __ Ldr(reg, DeduplicateBootImageAddressLiteral(dchecked_integral_cast<uint32_t>(address)));
9012 }
9013}
9014
Vladimir Marko6fd16062018-06-26 11:02:04 +01009015void CodeGeneratorARMVIXL::AllocateInstanceForIntrinsic(HInvokeStaticOrDirect* invoke,
9016 uint32_t boot_image_offset) {
9017 DCHECK(invoke->IsStatic());
9018 InvokeRuntimeCallingConventionARMVIXL calling_convention;
9019 vixl32::Register argument = calling_convention.GetRegisterAt(0);
9020 if (GetCompilerOptions().IsBootImage()) {
9021 DCHECK_EQ(boot_image_offset, IntrinsicVisitor::IntegerValueOfInfo::kInvalidReference);
9022 // Load the class the same way as for HLoadClass::LoadKind::kBootImageLinkTimePcRelative.
9023 MethodReference target_method = invoke->GetTargetMethod();
9024 dex::TypeIndex type_idx = target_method.dex_file->GetMethodId(target_method.index).class_idx_;
9025 PcRelativePatchInfo* labels = NewBootImageTypePatch(*target_method.dex_file, type_idx);
9026 EmitMovwMovtPlaceholder(labels, argument);
9027 } else {
9028 LoadBootImageAddress(argument, boot_image_offset);
9029 }
9030 InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc());
9031 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
9032}
9033
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009034template <linker::LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)>
Artem Serovd4cc5b22016-11-04 11:19:09 +00009035inline void CodeGeneratorARMVIXL::EmitPcRelativeLinkerPatches(
9036 const ArenaDeque<PcRelativePatchInfo>& infos,
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009037 ArenaVector<linker::LinkerPatch>* linker_patches) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00009038 for (const PcRelativePatchInfo& info : infos) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009039 const DexFile* dex_file = info.target_dex_file;
Artem Serovd4cc5b22016-11-04 11:19:09 +00009040 size_t offset_or_index = info.offset_or_index;
9041 DCHECK(info.add_pc_label.IsBound());
9042 uint32_t add_pc_offset = dchecked_integral_cast<uint32_t>(info.add_pc_label.GetLocation());
9043 // Add MOVW patch.
9044 DCHECK(info.movw_label.IsBound());
9045 uint32_t movw_offset = dchecked_integral_cast<uint32_t>(info.movw_label.GetLocation());
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009046 linker_patches->push_back(Factory(movw_offset, dex_file, add_pc_offset, offset_or_index));
Artem Serovd4cc5b22016-11-04 11:19:09 +00009047 // Add MOVT patch.
9048 DCHECK(info.movt_label.IsBound());
9049 uint32_t movt_offset = dchecked_integral_cast<uint32_t>(info.movt_label.GetLocation());
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009050 linker_patches->push_back(Factory(movt_offset, dex_file, add_pc_offset, offset_or_index));
Artem Serovd4cc5b22016-11-04 11:19:09 +00009051 }
9052}
9053
Vladimir Marko6fd16062018-06-26 11:02:04 +01009054template <linker::LinkerPatch (*Factory)(size_t, uint32_t, uint32_t)>
9055linker::LinkerPatch NoDexFileAdapter(size_t literal_offset,
9056 const DexFile* target_dex_file,
9057 uint32_t pc_insn_offset,
9058 uint32_t boot_image_offset) {
9059 DCHECK(target_dex_file == nullptr); // Unused for these patches, should be null.
9060 return Factory(literal_offset, pc_insn_offset, boot_image_offset);
Vladimir Markob066d432018-01-03 13:14:37 +00009061}
9062
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009063void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* linker_patches) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00009064 DCHECK(linker_patches->empty());
9065 size_t size =
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009066 /* MOVW+MOVT for each entry */ 2u * boot_image_method_patches_.size() +
Vladimir Marko0eb882b2017-05-15 13:39:18 +01009067 /* MOVW+MOVT for each entry */ 2u * method_bss_entry_patches_.size() +
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009068 /* MOVW+MOVT for each entry */ 2u * boot_image_type_patches_.size() +
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009069 /* MOVW+MOVT for each entry */ 2u * type_bss_entry_patches_.size() +
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009070 /* MOVW+MOVT for each entry */ 2u * boot_image_string_patches_.size() +
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01009071 /* MOVW+MOVT for each entry */ 2u * string_bss_entry_patches_.size() +
Vladimir Marko2d06e022019-07-08 15:45:19 +01009072 /* MOVW+MOVT for each entry */ 2u * boot_image_other_patches_.size() +
Vladimir Markof6675082019-05-17 12:05:28 +01009073 call_entrypoint_patches_.size() +
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009074 baker_read_barrier_patches_.size();
Artem Serovd4cc5b22016-11-04 11:19:09 +00009075 linker_patches->reserve(size);
Vladimir Marko44ca0752019-07-29 10:18:25 +01009076 if (GetCompilerOptions().IsBootImage() || GetCompilerOptions().IsBootImageExtension()) {
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009077 EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeMethodPatch>(
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009078 boot_image_method_patches_, linker_patches);
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009079 EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeTypePatch>(
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009080 boot_image_type_patches_, linker_patches);
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009081 EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeStringPatch>(
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009082 boot_image_string_patches_, linker_patches);
Vladimir Marko65979462017-05-19 17:25:12 +01009083 } else {
Vladimir Marko2d06e022019-07-08 15:45:19 +01009084 DCHECK(boot_image_method_patches_.empty());
Vladimir Markoe47f60c2018-02-21 13:43:28 +00009085 DCHECK(boot_image_type_patches_.empty());
9086 DCHECK(boot_image_string_patches_.empty());
Vladimir Marko2d06e022019-07-08 15:45:19 +01009087 }
9088 if (GetCompilerOptions().IsBootImage()) {
9089 EmitPcRelativeLinkerPatches<NoDexFileAdapter<linker::LinkerPatch::IntrinsicReferencePatch>>(
9090 boot_image_other_patches_, linker_patches);
9091 } else {
9092 EmitPcRelativeLinkerPatches<NoDexFileAdapter<linker::LinkerPatch::DataBimgRelRoPatch>>(
9093 boot_image_other_patches_, linker_patches);
Artem Serovd4cc5b22016-11-04 11:19:09 +00009094 }
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009095 EmitPcRelativeLinkerPatches<linker::LinkerPatch::MethodBssEntryPatch>(
9096 method_bss_entry_patches_, linker_patches);
9097 EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeBssEntryPatch>(
9098 type_bss_entry_patches_, linker_patches);
9099 EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringBssEntryPatch>(
9100 string_bss_entry_patches_, linker_patches);
Vladimir Markof6675082019-05-17 12:05:28 +01009101 for (const PatchInfo<vixl32::Label>& info : call_entrypoint_patches_) {
9102 DCHECK(info.target_dex_file == nullptr);
9103 linker_patches->push_back(linker::LinkerPatch::CallEntrypointPatch(
9104 info.label.GetLocation(), info.offset_or_index));
9105 }
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009106 for (const BakerReadBarrierPatchInfo& info : baker_read_barrier_patches_) {
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009107 linker_patches->push_back(linker::LinkerPatch::BakerReadBarrierBranchPatch(
9108 info.label.GetLocation(), info.custom_data));
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009109 }
Vladimir Marko1998cd02017-01-13 13:02:58 +00009110 DCHECK_EQ(size, linker_patches->size());
Artem Serovc5fcb442016-12-02 19:19:58 +00009111}
9112
Vladimir Markoca1e0382018-04-11 09:58:41 +00009113bool CodeGeneratorARMVIXL::NeedsThunkCode(const linker::LinkerPatch& patch) const {
Vladimir Markof6675082019-05-17 12:05:28 +01009114 return patch.GetType() == linker::LinkerPatch::Type::kCallEntrypoint ||
9115 patch.GetType() == linker::LinkerPatch::Type::kBakerReadBarrierBranch ||
Vladimir Markoca1e0382018-04-11 09:58:41 +00009116 patch.GetType() == linker::LinkerPatch::Type::kCallRelative;
9117}
9118
9119void CodeGeneratorARMVIXL::EmitThunkCode(const linker::LinkerPatch& patch,
9120 /*out*/ ArenaVector<uint8_t>* code,
9121 /*out*/ std::string* debug_name) {
9122 arm::ArmVIXLAssembler assembler(GetGraph()->GetAllocator());
9123 switch (patch.GetType()) {
Vladimir Markof6675082019-05-17 12:05:28 +01009124 case linker::LinkerPatch::Type::kCallRelative: {
Vladimir Markoca1e0382018-04-11 09:58:41 +00009125 // The thunk just uses the entry point in the ArtMethod. This works even for calls
9126 // to the generic JNI and interpreter trampolines.
Vladimir Markof6675082019-05-17 12:05:28 +01009127 MemberOffset offset = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize);
9128 assembler.LoadFromOffset(arm::kLoadWord, vixl32::pc, vixl32::r0, offset.Int32Value());
Vladimir Markoca1e0382018-04-11 09:58:41 +00009129 assembler.GetVIXLAssembler()->Bkpt(0);
9130 if (GetCompilerOptions().GenerateAnyDebugInfo()) {
9131 *debug_name = "MethodCallThunk";
9132 }
9133 break;
Vladimir Markof6675082019-05-17 12:05:28 +01009134 }
9135 case linker::LinkerPatch::Type::kCallEntrypoint: {
9136 assembler.LoadFromOffset(arm::kLoadWord, vixl32::pc, tr, patch.EntrypointOffset());
9137 assembler.GetVIXLAssembler()->Bkpt(0);
9138 if (GetCompilerOptions().GenerateAnyDebugInfo()) {
9139 *debug_name = "EntrypointCallThunk_" + std::to_string(patch.EntrypointOffset());
9140 }
9141 break;
9142 }
9143 case linker::LinkerPatch::Type::kBakerReadBarrierBranch: {
Vladimir Markoca1e0382018-04-11 09:58:41 +00009144 DCHECK_EQ(patch.GetBakerCustomValue2(), 0u);
9145 CompileBakerReadBarrierThunk(assembler, patch.GetBakerCustomValue1(), debug_name);
9146 break;
Vladimir Markof6675082019-05-17 12:05:28 +01009147 }
Vladimir Markoca1e0382018-04-11 09:58:41 +00009148 default:
9149 LOG(FATAL) << "Unexpected patch type " << patch.GetType();
9150 UNREACHABLE();
9151 }
9152
9153 // Ensure we emit the literal pool if any.
9154 assembler.FinalizeCode();
9155 code->resize(assembler.CodeSize());
9156 MemoryRegion code_region(code->data(), code->size());
9157 assembler.FinalizeInstructions(code_region);
9158}
9159
Artem Serovc5fcb442016-12-02 19:19:58 +00009160VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateUint32Literal(
9161 uint32_t value,
9162 Uint32ToLiteralMap* map) {
9163 return map->GetOrCreate(
9164 value,
9165 [this, value]() {
Andreas Gampe3db70682018-12-26 15:12:03 -08009166 return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* value= */ value);
Artem Serovc5fcb442016-12-02 19:19:58 +00009167 });
9168}
9169
Artem Serov2bbc9532016-10-21 11:51:50 +01009170void LocationsBuilderARMVIXL::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
9171 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01009172 new (GetGraph()->GetAllocator()) LocationSummary(instr, LocationSummary::kNoCall);
Artem Serov2bbc9532016-10-21 11:51:50 +01009173 locations->SetInAt(HMultiplyAccumulate::kInputAccumulatorIndex,
9174 Location::RequiresRegister());
9175 locations->SetInAt(HMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister());
9176 locations->SetInAt(HMultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister());
9177 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
9178}
9179
9180void InstructionCodeGeneratorARMVIXL::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
9181 vixl32::Register res = OutputRegister(instr);
9182 vixl32::Register accumulator =
9183 InputRegisterAt(instr, HMultiplyAccumulate::kInputAccumulatorIndex);
9184 vixl32::Register mul_left =
9185 InputRegisterAt(instr, HMultiplyAccumulate::kInputMulLeftIndex);
9186 vixl32::Register mul_right =
9187 InputRegisterAt(instr, HMultiplyAccumulate::kInputMulRightIndex);
9188
9189 if (instr->GetOpKind() == HInstruction::kAdd) {
9190 __ Mla(res, mul_left, mul_right, accumulator);
9191 } else {
9192 __ Mls(res, mul_left, mul_right, accumulator);
9193 }
9194}
9195
Artem Serov551b28f2016-10-18 19:11:30 +01009196void LocationsBuilderARMVIXL::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
9197 // Nothing to do, this should be removed during prepare for register allocator.
9198 LOG(FATAL) << "Unreachable";
9199}
9200
9201void InstructionCodeGeneratorARMVIXL::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
9202 // Nothing to do, this should be removed during prepare for register allocator.
9203 LOG(FATAL) << "Unreachable";
9204}
9205
9206// Simple implementation of packed switch - generate cascaded compare/jumps.
9207void LocationsBuilderARMVIXL::VisitPackedSwitch(HPackedSwitch* switch_instr) {
9208 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01009209 new (GetGraph()->GetAllocator()) LocationSummary(switch_instr, LocationSummary::kNoCall);
Artem Serov551b28f2016-10-18 19:11:30 +01009210 locations->SetInAt(0, Location::RequiresRegister());
9211 if (switch_instr->GetNumEntries() > kPackedSwitchCompareJumpThreshold &&
9212 codegen_->GetAssembler()->GetVIXLAssembler()->IsUsingT32()) {
9213 locations->AddTemp(Location::RequiresRegister()); // We need a temp for the table base.
9214 if (switch_instr->GetStartValue() != 0) {
9215 locations->AddTemp(Location::RequiresRegister()); // We need a temp for the bias.
9216 }
9217 }
9218}
9219
9220// TODO(VIXL): Investigate and reach the parity with old arm codegen.
9221void InstructionCodeGeneratorARMVIXL::VisitPackedSwitch(HPackedSwitch* switch_instr) {
9222 int32_t lower_bound = switch_instr->GetStartValue();
9223 uint32_t num_entries = switch_instr->GetNumEntries();
9224 LocationSummary* locations = switch_instr->GetLocations();
9225 vixl32::Register value_reg = InputRegisterAt(switch_instr, 0);
9226 HBasicBlock* default_block = switch_instr->GetDefaultBlock();
9227
9228 if (num_entries <= kPackedSwitchCompareJumpThreshold ||
9229 !codegen_->GetAssembler()->GetVIXLAssembler()->IsUsingT32()) {
9230 // Create a series of compare/jumps.
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009231 UseScratchRegisterScope temps(GetVIXLAssembler());
Artem Serov551b28f2016-10-18 19:11:30 +01009232 vixl32::Register temp_reg = temps.Acquire();
9233 // Note: It is fine for the below AddConstantSetFlags() using IP register to temporarily store
9234 // the immediate, because IP is used as the destination register. For the other
9235 // AddConstantSetFlags() and GenerateCompareWithImmediate(), the immediate values are constant,
9236 // and they can be encoded in the instruction without making use of IP register.
9237 __ Adds(temp_reg, value_reg, -lower_bound);
9238
9239 const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors();
9240 // Jump to successors[0] if value == lower_bound.
9241 __ B(eq, codegen_->GetLabelOf(successors[0]));
9242 int32_t last_index = 0;
9243 for (; num_entries - last_index > 2; last_index += 2) {
9244 __ Adds(temp_reg, temp_reg, -2);
9245 // Jump to successors[last_index + 1] if value < case_value[last_index + 2].
9246 __ B(lo, codegen_->GetLabelOf(successors[last_index + 1]));
9247 // Jump to successors[last_index + 2] if value == case_value[last_index + 2].
9248 __ B(eq, codegen_->GetLabelOf(successors[last_index + 2]));
9249 }
9250 if (num_entries - last_index == 2) {
9251 // The last missing case_value.
9252 __ Cmp(temp_reg, 1);
9253 __ B(eq, codegen_->GetLabelOf(successors[last_index + 1]));
9254 }
9255
9256 // And the default for any other value.
9257 if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) {
9258 __ B(codegen_->GetLabelOf(default_block));
9259 }
9260 } else {
9261 // Create a table lookup.
9262 vixl32::Register table_base = RegisterFrom(locations->GetTemp(0));
9263
9264 JumpTableARMVIXL* jump_table = codegen_->CreateJumpTable(switch_instr);
9265
9266 // Remove the bias.
9267 vixl32::Register key_reg;
9268 if (lower_bound != 0) {
9269 key_reg = RegisterFrom(locations->GetTemp(1));
9270 __ Sub(key_reg, value_reg, lower_bound);
9271 } else {
9272 key_reg = value_reg;
9273 }
9274
9275 // Check whether the value is in the table, jump to default block if not.
9276 __ Cmp(key_reg, num_entries - 1);
9277 __ B(hi, codegen_->GetLabelOf(default_block));
9278
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009279 UseScratchRegisterScope temps(GetVIXLAssembler());
Artem Serov551b28f2016-10-18 19:11:30 +01009280 vixl32::Register jump_offset = temps.Acquire();
9281
9282 // Load jump offset from the table.
Scott Wakeling86e9d262017-01-18 15:59:24 +00009283 {
9284 const size_t jump_size = switch_instr->GetNumEntries() * sizeof(int32_t);
9285 ExactAssemblyScope aas(GetVIXLAssembler(),
9286 (vixl32::kMaxInstructionSizeInBytes * 4) + jump_size,
9287 CodeBufferCheckScope::kMaximumSize);
9288 __ adr(table_base, jump_table->GetTableStartLabel());
9289 __ ldr(jump_offset, MemOperand(table_base, key_reg, vixl32::LSL, 2));
Artem Serov551b28f2016-10-18 19:11:30 +01009290
Scott Wakeling86e9d262017-01-18 15:59:24 +00009291 // Jump to target block by branching to table_base(pc related) + offset.
9292 vixl32::Register target_address = table_base;
9293 __ add(target_address, table_base, jump_offset);
9294 __ bx(target_address);
Artem Serov09a940d2016-11-11 16:15:11 +00009295
Scott Wakeling86e9d262017-01-18 15:59:24 +00009296 jump_table->EmitTable(codegen_);
9297 }
Artem Serov551b28f2016-10-18 19:11:30 +01009298 }
9299}
9300
Artem Serov02d37832016-10-25 15:25:33 +01009301// Copy the result of a call into the given target.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01009302void CodeGeneratorARMVIXL::MoveFromReturnRegister(Location trg, DataType::Type type) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009303 if (!trg.IsValid()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01009304 DCHECK_EQ(type, DataType::Type::kVoid);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009305 return;
9306 }
9307
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01009308 DCHECK_NE(type, DataType::Type::kVoid);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009309
Artem Serovd4cc5b22016-11-04 11:19:09 +00009310 Location return_loc = InvokeDexCallingConventionVisitorARMVIXL().GetReturnLocation(type);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009311 if (return_loc.Equals(trg)) {
9312 return;
9313 }
9314
9315 // TODO: Consider pairs in the parallel move resolver, then this could be nicely merged
9316 // with the last branch.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01009317 if (type == DataType::Type::kInt64) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009318 TODO_VIXL32(FATAL);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01009319 } else if (type == DataType::Type::kFloat64) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009320 TODO_VIXL32(FATAL);
9321 } else {
9322 // Let the parallel move resolver take care of all of this.
Vladimir Markoca6fff82017-10-03 14:49:14 +01009323 HParallelMove parallel_move(GetGraph()->GetAllocator());
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009324 parallel_move.AddMove(return_loc, trg, type, nullptr);
9325 GetMoveResolver()->EmitNativeCode(&parallel_move);
9326 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01009327}
Scott Wakelingfe885462016-09-22 10:24:38 +01009328
xueliang.zhong8d2c4592016-11-23 17:05:25 +00009329void LocationsBuilderARMVIXL::VisitClassTableGet(HClassTableGet* instruction) {
9330 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01009331 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
xueliang.zhong8d2c4592016-11-23 17:05:25 +00009332 locations->SetInAt(0, Location::RequiresRegister());
9333 locations->SetOut(Location::RequiresRegister());
Artem Serov551b28f2016-10-18 19:11:30 +01009334}
9335
xueliang.zhong8d2c4592016-11-23 17:05:25 +00009336void InstructionCodeGeneratorARMVIXL::VisitClassTableGet(HClassTableGet* instruction) {
9337 if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) {
9338 uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
9339 instruction->GetIndex(), kArmPointerSize).SizeValue();
9340 GetAssembler()->LoadFromOffset(kLoadWord,
9341 OutputRegister(instruction),
9342 InputRegisterAt(instruction, 0),
9343 method_offset);
9344 } else {
9345 uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement(
9346 instruction->GetIndex(), kArmPointerSize));
9347 GetAssembler()->LoadFromOffset(kLoadWord,
9348 OutputRegister(instruction),
9349 InputRegisterAt(instruction, 0),
9350 mirror::Class::ImtPtrOffset(kArmPointerSize).Uint32Value());
9351 GetAssembler()->LoadFromOffset(kLoadWord,
9352 OutputRegister(instruction),
9353 OutputRegister(instruction),
9354 method_offset);
9355 }
Artem Serov551b28f2016-10-18 19:11:30 +01009356}
9357
Artem Serovc5fcb442016-12-02 19:19:58 +00009358static void PatchJitRootUse(uint8_t* code,
9359 const uint8_t* roots_data,
9360 VIXLUInt32Literal* literal,
9361 uint64_t index_in_table) {
9362 DCHECK(literal->IsBound());
9363 uint32_t literal_offset = literal->GetLocation();
9364 uintptr_t address =
9365 reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>);
9366 uint8_t* data = code + literal_offset;
9367 reinterpret_cast<uint32_t*>(data)[0] = dchecked_integral_cast<uint32_t>(address);
9368}
9369
9370void CodeGeneratorARMVIXL::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) {
9371 for (const auto& entry : jit_string_patches_) {
Vladimir Marko7d157fc2017-05-10 16:29:23 +01009372 const StringReference& string_reference = entry.first;
9373 VIXLUInt32Literal* table_entry_literal = entry.second;
Vladimir Marko174b2e22017-10-12 13:34:49 +01009374 uint64_t index_in_table = GetJitStringRootIndex(string_reference);
Vladimir Marko7d157fc2017-05-10 16:29:23 +01009375 PatchJitRootUse(code, roots_data, table_entry_literal, index_in_table);
Artem Serovc5fcb442016-12-02 19:19:58 +00009376 }
9377 for (const auto& entry : jit_class_patches_) {
Vladimir Marko7d157fc2017-05-10 16:29:23 +01009378 const TypeReference& type_reference = entry.first;
9379 VIXLUInt32Literal* table_entry_literal = entry.second;
Vladimir Marko174b2e22017-10-12 13:34:49 +01009380 uint64_t index_in_table = GetJitClassRootIndex(type_reference);
Vladimir Marko7d157fc2017-05-10 16:29:23 +01009381 PatchJitRootUse(code, roots_data, table_entry_literal, index_in_table);
Artem Serovc5fcb442016-12-02 19:19:58 +00009382 }
9383}
9384
Artem Serovd4cc5b22016-11-04 11:19:09 +00009385void CodeGeneratorARMVIXL::EmitMovwMovtPlaceholder(
9386 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels,
9387 vixl32::Register out) {
Artem Serov0fb37192016-12-06 18:13:40 +00009388 ExactAssemblyScope aas(GetVIXLAssembler(),
9389 3 * vixl32::kMaxInstructionSizeInBytes,
9390 CodeBufferCheckScope::kMaximumSize);
Artem Serovd4cc5b22016-11-04 11:19:09 +00009391 // TODO(VIXL): Think about using mov instead of movw.
9392 __ bind(&labels->movw_label);
Andreas Gampe3db70682018-12-26 15:12:03 -08009393 __ movw(out, /* operand= */ 0u);
Artem Serovd4cc5b22016-11-04 11:19:09 +00009394 __ bind(&labels->movt_label);
Andreas Gampe3db70682018-12-26 15:12:03 -08009395 __ movt(out, /* operand= */ 0u);
Artem Serovd4cc5b22016-11-04 11:19:09 +00009396 __ bind(&labels->add_pc_label);
9397 __ add(out, out, pc);
9398}
9399
Scott Wakelingfe885462016-09-22 10:24:38 +01009400#undef __
9401#undef QUICK_ENTRY_POINT
9402#undef TODO_VIXL32
9403
Vladimir Markoca1e0382018-04-11 09:58:41 +00009404#define __ assembler.GetVIXLAssembler()->
9405
9406static void EmitGrayCheckAndFastPath(ArmVIXLAssembler& assembler,
9407 vixl32::Register base_reg,
9408 vixl32::MemOperand& lock_word,
9409 vixl32::Label* slow_path,
Vladimir Marko7a695052018-04-12 10:26:50 +01009410 int32_t raw_ldr_offset,
9411 vixl32::Label* throw_npe = nullptr) {
Vladimir Markoca1e0382018-04-11 09:58:41 +00009412 // Load the lock word containing the rb_state.
9413 __ Ldr(ip, lock_word);
9414 // Given the numeric representation, it's enough to check the low bit of the rb_state.
Roland Levillain14e5a292018-06-28 12:00:56 +01009415 static_assert(ReadBarrier::NonGrayState() == 0, "Expecting non-gray to have value 0");
Vladimir Markoca1e0382018-04-11 09:58:41 +00009416 static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
9417 __ Tst(ip, Operand(LockWord::kReadBarrierStateMaskShifted));
Andreas Gampe3db70682018-12-26 15:12:03 -08009418 __ B(ne, slow_path, /* is_far_target= */ false);
Vladimir Marko7a695052018-04-12 10:26:50 +01009419 // To throw NPE, we return to the fast path; the artificial dependence below does not matter.
9420 if (throw_npe != nullptr) {
9421 __ Bind(throw_npe);
9422 }
Vladimir Markoca1e0382018-04-11 09:58:41 +00009423 __ Add(lr, lr, raw_ldr_offset);
9424 // Introduce a dependency on the lock_word including rb_state,
9425 // to prevent load-load reordering, and without using
9426 // a memory barrier (which would be more expensive).
9427 __ Add(base_reg, base_reg, Operand(ip, LSR, 32));
9428 __ Bx(lr); // And return back to the function.
9429 // Note: The fake dependency is unnecessary for the slow path.
9430}
9431
9432// Load the read barrier introspection entrypoint in register `entrypoint`
Vladimir Markodcd117e2018-04-19 11:54:00 +01009433static vixl32::Register LoadReadBarrierMarkIntrospectionEntrypoint(ArmVIXLAssembler& assembler) {
Vladimir Markoca1e0382018-04-11 09:58:41 +00009434 // The register where the read barrier introspection entrypoint is loaded
Vladimir Markodcd117e2018-04-19 11:54:00 +01009435 // is the marking register. We clobber it here and the entrypoint restores it to 1.
9436 vixl32::Register entrypoint = mr;
Vladimir Markoca1e0382018-04-11 09:58:41 +00009437 // entrypoint = Thread::Current()->pReadBarrierMarkReg12, i.e. pReadBarrierMarkIntrospection.
9438 DCHECK_EQ(ip.GetCode(), 12u);
9439 const int32_t entry_point_offset =
9440 Thread::ReadBarrierMarkEntryPointsOffset<kArmPointerSize>(ip.GetCode());
9441 __ Ldr(entrypoint, MemOperand(tr, entry_point_offset));
Vladimir Markodcd117e2018-04-19 11:54:00 +01009442 return entrypoint;
Vladimir Markoca1e0382018-04-11 09:58:41 +00009443}
9444
9445void CodeGeneratorARMVIXL::CompileBakerReadBarrierThunk(ArmVIXLAssembler& assembler,
9446 uint32_t encoded_data,
9447 /*out*/ std::string* debug_name) {
9448 BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data);
9449 switch (kind) {
9450 case BakerReadBarrierKind::kField: {
Vladimir Markoca1e0382018-04-11 09:58:41 +00009451 vixl32::Register base_reg(BakerReadBarrierFirstRegField::Decode(encoded_data));
9452 CheckValidReg(base_reg.GetCode());
9453 vixl32::Register holder_reg(BakerReadBarrierSecondRegField::Decode(encoded_data));
9454 CheckValidReg(holder_reg.GetCode());
9455 BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data);
9456 UseScratchRegisterScope temps(assembler.GetVIXLAssembler());
9457 temps.Exclude(ip);
Vladimir Marko7a695052018-04-12 10:26:50 +01009458 // If base_reg differs from holder_reg, the offset was too large and we must have emitted
9459 // an explicit null check before the load. Otherwise, for implicit null checks, we need to
9460 // null-check the holder as we do not necessarily do that check before going to the thunk.
9461 vixl32::Label throw_npe_label;
9462 vixl32::Label* throw_npe = nullptr;
9463 if (GetCompilerOptions().GetImplicitNullChecks() && holder_reg.Is(base_reg)) {
9464 throw_npe = &throw_npe_label;
Andreas Gampe3db70682018-12-26 15:12:03 -08009465 __ CompareAndBranchIfZero(holder_reg, throw_npe, /* is_far_target= */ false);
Vladimir Markoca1e0382018-04-11 09:58:41 +00009466 }
Vladimir Marko7a695052018-04-12 10:26:50 +01009467 // Check if the holder is gray and, if not, add fake dependency to the base register
9468 // and return to the LDR instruction to load the reference. Otherwise, use introspection
9469 // to load the reference and call the entrypoint that performs further checks on the
9470 // reference and marks it if needed.
Vladimir Markoca1e0382018-04-11 09:58:41 +00009471 vixl32::Label slow_path;
9472 MemOperand lock_word(holder_reg, mirror::Object::MonitorOffset().Int32Value());
9473 const int32_t raw_ldr_offset = (width == BakerReadBarrierWidth::kWide)
9474 ? BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET
9475 : BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET;
Vladimir Marko7a695052018-04-12 10:26:50 +01009476 EmitGrayCheckAndFastPath(
9477 assembler, base_reg, lock_word, &slow_path, raw_ldr_offset, throw_npe);
Vladimir Markoca1e0382018-04-11 09:58:41 +00009478 __ Bind(&slow_path);
9479 const int32_t ldr_offset = /* Thumb state adjustment (LR contains Thumb state). */ -1 +
9480 raw_ldr_offset;
Vladimir Markodcd117e2018-04-19 11:54:00 +01009481 vixl32::Register ep_reg = LoadReadBarrierMarkIntrospectionEntrypoint(assembler);
Vladimir Markoca1e0382018-04-11 09:58:41 +00009482 if (width == BakerReadBarrierWidth::kWide) {
9483 MemOperand ldr_half_address(lr, ldr_offset + 2);
9484 __ Ldrh(ip, ldr_half_address); // Load the LDR immediate half-word with "Rt | imm12".
9485 __ Ubfx(ip, ip, 0, 12); // Extract the offset imm12.
9486 __ Ldr(ip, MemOperand(base_reg, ip)); // Load the reference.
9487 } else {
9488 MemOperand ldr_address(lr, ldr_offset);
9489 __ Ldrh(ip, ldr_address); // Load the LDR immediate, encoding T1.
9490 __ Add(ep_reg, // Adjust the entrypoint address to the entrypoint
9491 ep_reg, // for narrow LDR.
9492 Operand(BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_ENTRYPOINT_OFFSET));
9493 __ Ubfx(ip, ip, 6, 5); // Extract the imm5, i.e. offset / 4.
9494 __ Ldr(ip, MemOperand(base_reg, ip, LSL, 2)); // Load the reference.
9495 }
9496 // Do not unpoison. With heap poisoning enabled, the entrypoint expects a poisoned reference.
9497 __ Bx(ep_reg); // Jump to the entrypoint.
Vladimir Markoca1e0382018-04-11 09:58:41 +00009498 break;
9499 }
9500 case BakerReadBarrierKind::kArray: {
9501 vixl32::Register base_reg(BakerReadBarrierFirstRegField::Decode(encoded_data));
9502 CheckValidReg(base_reg.GetCode());
9503 DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg,
9504 BakerReadBarrierSecondRegField::Decode(encoded_data));
9505 DCHECK(BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide);
9506 UseScratchRegisterScope temps(assembler.GetVIXLAssembler());
9507 temps.Exclude(ip);
9508 vixl32::Label slow_path;
9509 int32_t data_offset =
9510 mirror::Array::DataOffset(Primitive::ComponentSize(Primitive::kPrimNot)).Int32Value();
9511 MemOperand lock_word(base_reg, mirror::Object::MonitorOffset().Int32Value() - data_offset);
9512 DCHECK_LT(lock_word.GetOffsetImmediate(), 0);
9513 const int32_t raw_ldr_offset = BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET;
9514 EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path, raw_ldr_offset);
9515 __ Bind(&slow_path);
9516 const int32_t ldr_offset = /* Thumb state adjustment (LR contains Thumb state). */ -1 +
9517 raw_ldr_offset;
9518 MemOperand ldr_address(lr, ldr_offset + 2);
9519 __ Ldrb(ip, ldr_address); // Load the LDR (register) byte with "00 | imm2 | Rm",
9520 // i.e. Rm+32 because the scale in imm2 is 2.
Vladimir Markodcd117e2018-04-19 11:54:00 +01009521 vixl32::Register ep_reg = LoadReadBarrierMarkIntrospectionEntrypoint(assembler);
Vladimir Markoca1e0382018-04-11 09:58:41 +00009522 __ Bfi(ep_reg, ip, 3, 6); // Insert ip to the entrypoint address to create
9523 // a switch case target based on the index register.
9524 __ Mov(ip, base_reg); // Move the base register to ip0.
9525 __ Bx(ep_reg); // Jump to the entrypoint's array switch case.
9526 break;
9527 }
Vladimir Markod887ed82018-08-14 13:52:12 +00009528 case BakerReadBarrierKind::kGcRoot:
9529 case BakerReadBarrierKind::kUnsafeCas: {
Vladimir Markoca1e0382018-04-11 09:58:41 +00009530 // Check if the reference needs to be marked and if so (i.e. not null, not marked yet
9531 // and it does not have a forwarding address), call the correct introspection entrypoint;
9532 // otherwise return the reference (or the extracted forwarding address).
9533 // There is no gray bit check for GC roots.
9534 vixl32::Register root_reg(BakerReadBarrierFirstRegField::Decode(encoded_data));
9535 CheckValidReg(root_reg.GetCode());
9536 DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg,
9537 BakerReadBarrierSecondRegField::Decode(encoded_data));
9538 BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data);
9539 UseScratchRegisterScope temps(assembler.GetVIXLAssembler());
9540 temps.Exclude(ip);
9541 vixl32::Label return_label, not_marked, forwarding_address;
Andreas Gampe3db70682018-12-26 15:12:03 -08009542 __ CompareAndBranchIfZero(root_reg, &return_label, /* is_far_target= */ false);
Vladimir Markoca1e0382018-04-11 09:58:41 +00009543 MemOperand lock_word(root_reg, mirror::Object::MonitorOffset().Int32Value());
9544 __ Ldr(ip, lock_word);
9545 __ Tst(ip, LockWord::kMarkBitStateMaskShifted);
9546 __ B(eq, &not_marked);
9547 __ Bind(&return_label);
9548 __ Bx(lr);
9549 __ Bind(&not_marked);
9550 static_assert(LockWord::kStateShift == 30 && LockWord::kStateForwardingAddress == 3,
9551 "To use 'CMP ip, #modified-immediate; BHS', we need the lock word state in "
9552 " the highest bits and the 'forwarding address' state to have all bits set");
9553 __ Cmp(ip, Operand(0xc0000000));
9554 __ B(hs, &forwarding_address);
Vladimir Markodcd117e2018-04-19 11:54:00 +01009555 vixl32::Register ep_reg = LoadReadBarrierMarkIntrospectionEntrypoint(assembler);
Vladimir Markoca1e0382018-04-11 09:58:41 +00009556 // Adjust the art_quick_read_barrier_mark_introspection address in kBakerCcEntrypointRegister
Vladimir Markod887ed82018-08-14 13:52:12 +00009557 // to one of art_quick_read_barrier_mark_introspection_{gc_roots_{wide,narrow},unsafe_cas}.
9558 DCHECK(kind != BakerReadBarrierKind::kUnsafeCas || width == BakerReadBarrierWidth::kWide);
9559 int32_t entrypoint_offset =
9560 (kind == BakerReadBarrierKind::kGcRoot)
9561 ? (width == BakerReadBarrierWidth::kWide)
9562 ? BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_ENTRYPOINT_OFFSET
9563 : BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_ENTRYPOINT_OFFSET
9564 : BAKER_MARK_INTROSPECTION_UNSAFE_CAS_ENTRYPOINT_OFFSET;
Vladimir Markoca1e0382018-04-11 09:58:41 +00009565 __ Add(ep_reg, ep_reg, Operand(entrypoint_offset));
9566 __ Mov(ip, root_reg);
9567 __ Bx(ep_reg);
9568 __ Bind(&forwarding_address);
9569 __ Lsl(root_reg, ip, LockWord::kForwardingAddressShift);
9570 __ Bx(lr);
9571 break;
9572 }
9573 default:
9574 LOG(FATAL) << "Unexpected kind: " << static_cast<uint32_t>(kind);
9575 UNREACHABLE();
9576 }
9577
Vladimir Marko966b46f2018-08-03 10:20:19 +00009578 // For JIT, the slow path is considered part of the compiled method,
9579 // so JIT should pass null as `debug_name`. Tests may not have a runtime.
9580 DCHECK(Runtime::Current() == nullptr ||
9581 !Runtime::Current()->UseJitCompilation() ||
9582 debug_name == nullptr);
9583 if (debug_name != nullptr && GetCompilerOptions().GenerateAnyDebugInfo()) {
Vladimir Markoca1e0382018-04-11 09:58:41 +00009584 std::ostringstream oss;
9585 oss << "BakerReadBarrierThunk";
9586 switch (kind) {
9587 case BakerReadBarrierKind::kField:
9588 oss << "Field";
9589 if (BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide) {
9590 oss << "Wide";
9591 }
9592 oss << "_r" << BakerReadBarrierFirstRegField::Decode(encoded_data)
9593 << "_r" << BakerReadBarrierSecondRegField::Decode(encoded_data);
9594 break;
9595 case BakerReadBarrierKind::kArray:
9596 oss << "Array_r" << BakerReadBarrierFirstRegField::Decode(encoded_data);
9597 DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg,
9598 BakerReadBarrierSecondRegField::Decode(encoded_data));
9599 DCHECK(BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide);
9600 break;
9601 case BakerReadBarrierKind::kGcRoot:
9602 oss << "GcRoot";
9603 if (BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide) {
9604 oss << "Wide";
9605 }
9606 oss << "_r" << BakerReadBarrierFirstRegField::Decode(encoded_data);
9607 DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg,
9608 BakerReadBarrierSecondRegField::Decode(encoded_data));
9609 break;
Vladimir Markod887ed82018-08-14 13:52:12 +00009610 case BakerReadBarrierKind::kUnsafeCas:
9611 oss << "UnsafeCas_r" << BakerReadBarrierFirstRegField::Decode(encoded_data);
9612 DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg,
9613 BakerReadBarrierSecondRegField::Decode(encoded_data));
9614 DCHECK(BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide);
9615 break;
Vladimir Markoca1e0382018-04-11 09:58:41 +00009616 }
9617 *debug_name = oss.str();
9618 }
9619}
9620
9621#undef __
9622
Scott Wakelingfe885462016-09-22 10:24:38 +01009623} // namespace arm
9624} // namespace art