blob: cdd5d223ae0c5f5d0d303e640d0bd7a2791944cf [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"
Vladimir Marko86c87522020-05-11 16:55:55 +010021#include "arch/arm/jni_frame_arm.h"
Nicolas Geoffraye2a3aa92019-11-25 17:52:58 +000022#include "art_method-inl.h"
Andreas Gampe5678db52017-06-08 14:11:18 -070023#include "base/bit_utils.h"
24#include "base/bit_utils_iterator.h"
Vladimir Marko94ec2db2017-09-06 17:21:03 +010025#include "class_table.h"
Scott Wakelingfe885462016-09-22 10:24:38 +010026#include "code_generator_utils.h"
27#include "common_arm.h"
28#include "compiled_method.h"
29#include "entrypoints/quick/quick_entrypoints.h"
30#include "gc/accounting/card_table.h"
Vladimir Markoeebb8212018-06-05 14:57:24 +010031#include "gc/space/image_space.h"
Andreas Gampe09659c22017-09-18 18:23:32 -070032#include "heap_poisoning.h"
Nicolas Geoffray8b8d93d2020-09-17 14:30:01 +010033#include "interpreter/mterp/nterp.h"
Vladimir Marko6fd16062018-06-26 11:02:04 +010034#include "intrinsics.h"
Anton Kirilov5ec62182016-10-13 20:16:02 +010035#include "intrinsics_arm_vixl.h"
Vladimir Markod8dbc8d2017-09-20 13:37:47 +010036#include "linker/linker_patch.h"
Scott Wakelingfe885462016-09-22 10:24:38 +010037#include "mirror/array-inl.h"
38#include "mirror/class-inl.h"
Vladimir Markoac3fcff2020-11-17 12:17:58 +000039#include "mirror/var_handle.h"
Nicolas Geoffraye2a3aa92019-11-25 17:52:58 +000040#include "scoped_thread_state_change-inl.h"
Scott Wakelingfe885462016-09-22 10:24:38 +010041#include "thread.h"
42#include "utils/arm/assembler_arm_vixl.h"
43#include "utils/arm/managed_register_arm.h"
44#include "utils/assembler.h"
45#include "utils/stack_checks.h"
46
Vladimir Marko0a516052019-10-14 13:00:44 +000047namespace art {
Scott Wakelingfe885462016-09-22 10:24:38 +010048namespace arm {
49
50namespace vixl32 = vixl::aarch32;
51using namespace vixl32; // NOLINT(build/namespaces)
52
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +010053using helpers::DRegisterFrom;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010054using helpers::HighRegisterFrom;
Donghui Bai426b49c2016-11-08 14:55:38 +080055using helpers::InputDRegisterAt;
Scott Wakelingfe885462016-09-22 10:24:38 +010056using helpers::InputOperandAt;
Scott Wakelingc34dba72016-10-03 10:14:44 +010057using helpers::InputRegister;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010058using helpers::InputRegisterAt;
Scott Wakelingfe885462016-09-22 10:24:38 +010059using helpers::InputSRegisterAt;
Anton Kirilov644032c2016-12-06 17:51:43 +000060using helpers::InputVRegister;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010061using helpers::InputVRegisterAt;
Scott Wakelingb77051e2016-11-21 19:46:00 +000062using helpers::Int32ConstantFrom;
Anton Kirilov644032c2016-12-06 17:51:43 +000063using helpers::Int64ConstantFrom;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010064using helpers::LocationFrom;
65using helpers::LowRegisterFrom;
66using helpers::LowSRegisterFrom;
Donghui Bai426b49c2016-11-08 14:55:38 +080067using helpers::OperandFrom;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010068using helpers::OutputRegister;
69using helpers::OutputSRegister;
70using helpers::OutputVRegister;
71using helpers::RegisterFrom;
72using helpers::SRegisterFrom;
Anton Kirilov644032c2016-12-06 17:51:43 +000073using helpers::Uint64ConstantFrom;
Scott Wakelingfe885462016-09-22 10:24:38 +010074
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +010075using vixl::EmissionCheckScope;
Artem Serov0fb37192016-12-06 18:13:40 +000076using vixl::ExactAssemblyScope;
77using vixl::CodeBufferCheckScope;
78
Scott Wakelingfe885462016-09-22 10:24:38 +010079using RegisterList = vixl32::RegisterList;
80
81static bool ExpectedPairLayout(Location location) {
82 // We expected this for both core and fpu register pairs.
83 return ((location.low() & 1) == 0) && (location.low() + 1 == location.high());
84}
Artem Serovd4cc5b22016-11-04 11:19:09 +000085// Use a local definition to prevent copying mistakes.
86static constexpr size_t kArmWordSize = static_cast<size_t>(kArmPointerSize);
87static constexpr size_t kArmBitsPerWord = kArmWordSize * kBitsPerByte;
Artem Serov551b28f2016-10-18 19:11:30 +010088static constexpr uint32_t kPackedSwitchCompareJumpThreshold = 7;
Scott Wakelingfe885462016-09-22 10:24:38 +010089
Vladimir Markoeee1c0e2017-04-21 17:58:41 +010090// Reference load (except object array loads) is using LDR Rt, [Rn, #offset] which can handle
91// offset < 4KiB. For offsets >= 4KiB, the load shall be emitted as two or more instructions.
Vladimir Marko008e09f32018-08-06 15:42:43 +010092// For the Baker read barrier implementation using link-time generated thunks we need to split
Vladimir Markoeee1c0e2017-04-21 17:58:41 +010093// the offset explicitly.
94constexpr uint32_t kReferenceLoadMinFarOffset = 4 * KB;
95
Roland Levillain5daa4952017-07-03 17:23:56 +010096// Using a base helps identify when we hit Marking Register check breakpoints.
97constexpr int kMarkingRegisterCheckBreakCodeBaseCode = 0x10;
98
Scott Wakelingfe885462016-09-22 10:24:38 +010099#ifdef __
100#error "ARM Codegen VIXL macro-assembler macro already defined."
101#endif
102
Scott Wakelingfe885462016-09-22 10:24:38 +0100103// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
104#define __ down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler()-> // NOLINT
105#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArmPointerSize, x).Int32Value()
106
107// Marker that code is yet to be, and must, be implemented.
108#define TODO_VIXL32(level) LOG(level) << __PRETTY_FUNCTION__ << " unimplemented "
109
Vladimir Marko88abba22017-05-03 17:09:25 +0100110static inline bool CanEmitNarrowLdr(vixl32::Register rt, vixl32::Register rn, uint32_t offset) {
111 return rt.IsLow() && rn.IsLow() && offset < 32u;
112}
113
Vladimir Markoeee1c0e2017-04-21 17:58:41 +0100114class EmitAdrCode {
115 public:
116 EmitAdrCode(ArmVIXLMacroAssembler* assembler, vixl32::Register rd, vixl32::Label* label)
117 : assembler_(assembler), rd_(rd), label_(label) {
Vladimir Markod887ed82018-08-14 13:52:12 +0000118 DCHECK(!assembler->AllowMacroInstructions()); // In ExactAssemblyScope.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +0100119 adr_location_ = assembler->GetCursorOffset();
120 assembler->adr(EncodingSize(Wide), rd, label);
121 }
122
123 ~EmitAdrCode() {
124 DCHECK(label_->IsBound());
125 // The ADR emitted by the assembler does not set the Thumb mode bit we need.
126 // TODO: Maybe extend VIXL to allow ADR for return address?
127 uint8_t* raw_adr = assembler_->GetBuffer()->GetOffsetAddress<uint8_t*>(adr_location_);
128 // Expecting ADR encoding T3 with `(offset & 1) == 0`.
129 DCHECK_EQ(raw_adr[1] & 0xfbu, 0xf2u); // Check bits 24-31, except 26.
130 DCHECK_EQ(raw_adr[0] & 0xffu, 0x0fu); // Check bits 16-23.
131 DCHECK_EQ(raw_adr[3] & 0x8fu, rd_.GetCode()); // Check bits 8-11 and 15.
132 DCHECK_EQ(raw_adr[2] & 0x01u, 0x00u); // Check bit 0, i.e. the `offset & 1`.
133 // Add the Thumb mode bit.
134 raw_adr[2] |= 0x01u;
135 }
136
137 private:
138 ArmVIXLMacroAssembler* const assembler_;
139 vixl32::Register rd_;
140 vixl32::Label* const label_;
141 int32_t adr_location_;
142};
143
Vladimir Marko3232dbb2018-07-25 15:42:46 +0100144static RegisterSet OneRegInReferenceOutSaveEverythingCallerSaves() {
145 InvokeRuntimeCallingConventionARMVIXL calling_convention;
146 RegisterSet caller_saves = RegisterSet::Empty();
147 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0)));
148 // TODO: Add GetReturnLocation() to the calling convention so that we can DCHECK()
149 // that the the kPrimNot result register is the same as the first argument register.
150 return caller_saves;
151}
152
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100153// SaveLiveRegisters and RestoreLiveRegisters from SlowPathCodeARM operate on sets of S registers,
154// for each live D registers they treat two corresponding S registers as live ones.
155//
156// Two following functions (SaveContiguousSRegisterList, RestoreContiguousSRegisterList) build
157// from a list of contiguous S registers a list of contiguous D registers (processing first/last
158// S registers corner cases) and save/restore this new list treating them as D registers.
159// - decreasing code size
160// - avoiding hazards on Cortex-A57, when a pair of S registers for an actual live D register is
161// restored and then used in regular non SlowPath code as D register.
162//
163// For the following example (v means the S register is live):
164// D names: | D0 | D1 | D2 | D4 | ...
165// S names: | S0 | S1 | S2 | S3 | S4 | S5 | S6 | S7 | ...
166// Live? | | v | v | v | v | v | v | | ...
167//
168// S1 and S6 will be saved/restored independently; D registers list (D1, D2) will be processed
169// as D registers.
170//
171// TODO(VIXL): All this code should be unnecessary once the VIXL AArch32 backend provides helpers
172// for lists of floating-point registers.
173static size_t SaveContiguousSRegisterList(size_t first,
174 size_t last,
175 CodeGenerator* codegen,
176 size_t stack_offset) {
177 static_assert(kSRegSizeInBytes == kArmWordSize, "Broken assumption on reg/word sizes.");
178 static_assert(kDRegSizeInBytes == 2 * kArmWordSize, "Broken assumption on reg/word sizes.");
179 DCHECK_LE(first, last);
180 if ((first == last) && (first == 0)) {
181 __ Vstr(vixl32::SRegister(first), MemOperand(sp, stack_offset));
182 return stack_offset + kSRegSizeInBytes;
183 }
184 if (first % 2 == 1) {
185 __ Vstr(vixl32::SRegister(first++), MemOperand(sp, stack_offset));
186 stack_offset += kSRegSizeInBytes;
187 }
188
189 bool save_last = false;
190 if (last % 2 == 0) {
191 save_last = true;
192 --last;
193 }
194
195 if (first < last) {
196 vixl32::DRegister d_reg = vixl32::DRegister(first / 2);
197 DCHECK_EQ((last - first + 1) % 2, 0u);
198 size_t number_of_d_regs = (last - first + 1) / 2;
199
200 if (number_of_d_regs == 1) {
201 __ Vstr(d_reg, MemOperand(sp, stack_offset));
202 } else if (number_of_d_regs > 1) {
203 UseScratchRegisterScope temps(down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler());
204 vixl32::Register base = sp;
205 if (stack_offset != 0) {
206 base = temps.Acquire();
Scott Wakelingb77051e2016-11-21 19:46:00 +0000207 __ Add(base, sp, Operand::From(stack_offset));
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100208 }
209 __ Vstm(F64, base, NO_WRITE_BACK, DRegisterList(d_reg, number_of_d_regs));
210 }
211 stack_offset += number_of_d_regs * kDRegSizeInBytes;
212 }
213
214 if (save_last) {
215 __ Vstr(vixl32::SRegister(last + 1), MemOperand(sp, stack_offset));
216 stack_offset += kSRegSizeInBytes;
217 }
218
219 return stack_offset;
220}
221
222static size_t RestoreContiguousSRegisterList(size_t first,
223 size_t last,
224 CodeGenerator* codegen,
225 size_t stack_offset) {
226 static_assert(kSRegSizeInBytes == kArmWordSize, "Broken assumption on reg/word sizes.");
227 static_assert(kDRegSizeInBytes == 2 * kArmWordSize, "Broken assumption on reg/word sizes.");
228 DCHECK_LE(first, last);
229 if ((first == last) && (first == 0)) {
230 __ Vldr(vixl32::SRegister(first), MemOperand(sp, stack_offset));
231 return stack_offset + kSRegSizeInBytes;
232 }
233 if (first % 2 == 1) {
234 __ Vldr(vixl32::SRegister(first++), MemOperand(sp, stack_offset));
235 stack_offset += kSRegSizeInBytes;
236 }
237
238 bool restore_last = false;
239 if (last % 2 == 0) {
240 restore_last = true;
241 --last;
242 }
243
244 if (first < last) {
245 vixl32::DRegister d_reg = vixl32::DRegister(first / 2);
246 DCHECK_EQ((last - first + 1) % 2, 0u);
247 size_t number_of_d_regs = (last - first + 1) / 2;
248 if (number_of_d_regs == 1) {
249 __ Vldr(d_reg, MemOperand(sp, stack_offset));
250 } else if (number_of_d_regs > 1) {
251 UseScratchRegisterScope temps(down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler());
252 vixl32::Register base = sp;
253 if (stack_offset != 0) {
254 base = temps.Acquire();
Scott Wakelingb77051e2016-11-21 19:46:00 +0000255 __ Add(base, sp, Operand::From(stack_offset));
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100256 }
257 __ Vldm(F64, base, NO_WRITE_BACK, DRegisterList(d_reg, number_of_d_regs));
258 }
259 stack_offset += number_of_d_regs * kDRegSizeInBytes;
260 }
261
262 if (restore_last) {
263 __ Vldr(vixl32::SRegister(last + 1), MemOperand(sp, stack_offset));
264 stack_offset += kSRegSizeInBytes;
265 }
266
267 return stack_offset;
268}
269
Vladimir Markod5d2f2c2017-09-26 12:37:26 +0100270static LoadOperandType GetLoadOperandType(DataType::Type type) {
271 switch (type) {
272 case DataType::Type::kReference:
273 return kLoadWord;
274 case DataType::Type::kBool:
275 case DataType::Type::kUint8:
276 return kLoadUnsignedByte;
277 case DataType::Type::kInt8:
278 return kLoadSignedByte;
279 case DataType::Type::kUint16:
280 return kLoadUnsignedHalfword;
281 case DataType::Type::kInt16:
282 return kLoadSignedHalfword;
283 case DataType::Type::kInt32:
284 return kLoadWord;
285 case DataType::Type::kInt64:
286 return kLoadWordPair;
287 case DataType::Type::kFloat32:
288 return kLoadSWord;
289 case DataType::Type::kFloat64:
290 return kLoadDWord;
291 default:
292 LOG(FATAL) << "Unreachable type " << type;
293 UNREACHABLE();
294 }
295}
296
297static StoreOperandType GetStoreOperandType(DataType::Type type) {
298 switch (type) {
299 case DataType::Type::kReference:
300 return kStoreWord;
301 case DataType::Type::kBool:
302 case DataType::Type::kUint8:
303 case DataType::Type::kInt8:
304 return kStoreByte;
305 case DataType::Type::kUint16:
306 case DataType::Type::kInt16:
307 return kStoreHalfword;
308 case DataType::Type::kInt32:
309 return kStoreWord;
310 case DataType::Type::kInt64:
311 return kStoreWordPair;
312 case DataType::Type::kFloat32:
313 return kStoreSWord;
314 case DataType::Type::kFloat64:
315 return kStoreDWord;
316 default:
317 LOG(FATAL) << "Unreachable type " << type;
318 UNREACHABLE();
319 }
320}
321
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100322void SlowPathCodeARMVIXL::SaveLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
323 size_t stack_offset = codegen->GetFirstRegisterSlotInSlowPath();
324 size_t orig_offset = stack_offset;
325
Andreas Gampe3db70682018-12-26 15:12:03 -0800326 const uint32_t core_spills = codegen->GetSlowPathSpills(locations, /* core_registers= */ true);
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100327 for (uint32_t i : LowToHighBits(core_spills)) {
328 // If the register holds an object, update the stack mask.
329 if (locations->RegisterContainsObject(i)) {
330 locations->SetStackBit(stack_offset / kVRegSize);
331 }
332 DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
333 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
334 saved_core_stack_offsets_[i] = stack_offset;
335 stack_offset += kArmWordSize;
336 }
337
338 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
339 arm_codegen->GetAssembler()->StoreRegisterList(core_spills, orig_offset);
340
Andreas Gampe3db70682018-12-26 15:12:03 -0800341 uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers= */ false);
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100342 orig_offset = stack_offset;
343 for (uint32_t i : LowToHighBits(fp_spills)) {
344 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
345 saved_fpu_stack_offsets_[i] = stack_offset;
346 stack_offset += kArmWordSize;
347 }
348
349 stack_offset = orig_offset;
350 while (fp_spills != 0u) {
351 uint32_t begin = CTZ(fp_spills);
352 uint32_t tmp = fp_spills + (1u << begin);
353 fp_spills &= tmp; // Clear the contiguous range of 1s.
354 uint32_t end = (tmp == 0u) ? 32u : CTZ(tmp); // CTZ(0) is undefined.
355 stack_offset = SaveContiguousSRegisterList(begin, end - 1, codegen, stack_offset);
356 }
357 DCHECK_LE(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
358}
359
360void SlowPathCodeARMVIXL::RestoreLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
361 size_t stack_offset = codegen->GetFirstRegisterSlotInSlowPath();
362 size_t orig_offset = stack_offset;
363
Andreas Gampe3db70682018-12-26 15:12:03 -0800364 const uint32_t core_spills = codegen->GetSlowPathSpills(locations, /* core_registers= */ true);
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100365 for (uint32_t i : LowToHighBits(core_spills)) {
366 DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
367 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
368 stack_offset += kArmWordSize;
369 }
370
371 // TODO(VIXL): Check the coherency of stack_offset after this with a test.
372 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
373 arm_codegen->GetAssembler()->LoadRegisterList(core_spills, orig_offset);
374
Andreas Gampe3db70682018-12-26 15:12:03 -0800375 uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers= */ false);
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100376 while (fp_spills != 0u) {
377 uint32_t begin = CTZ(fp_spills);
378 uint32_t tmp = fp_spills + (1u << begin);
379 fp_spills &= tmp; // Clear the contiguous range of 1s.
380 uint32_t end = (tmp == 0u) ? 32u : CTZ(tmp); // CTZ(0) is undefined.
381 stack_offset = RestoreContiguousSRegisterList(begin, end - 1, codegen, stack_offset);
382 }
383 DCHECK_LE(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
384}
385
386class NullCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
387 public:
388 explicit NullCheckSlowPathARMVIXL(HNullCheck* instruction) : SlowPathCodeARMVIXL(instruction) {}
389
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100390 void EmitNativeCode(CodeGenerator* codegen) override {
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100391 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
392 __ Bind(GetEntryLabel());
393 if (instruction_->CanThrowIntoCatchBlock()) {
394 // Live registers will be restored in the catch block if caught.
395 SaveLiveRegisters(codegen, instruction_->GetLocations());
396 }
397 arm_codegen->InvokeRuntime(kQuickThrowNullPointer,
398 instruction_,
399 instruction_->GetDexPc(),
400 this);
401 CheckEntrypointTypes<kQuickThrowNullPointer, void, void>();
402 }
403
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100404 bool IsFatal() const override { return true; }
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100405
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100406 const char* GetDescription() const override { return "NullCheckSlowPathARMVIXL"; }
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100407
408 private:
409 DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARMVIXL);
410};
411
Scott Wakelingfe885462016-09-22 10:24:38 +0100412class DivZeroCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
413 public:
414 explicit DivZeroCheckSlowPathARMVIXL(HDivZeroCheck* instruction)
415 : SlowPathCodeARMVIXL(instruction) {}
416
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100417 void EmitNativeCode(CodeGenerator* codegen) override {
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100418 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
Scott Wakelingfe885462016-09-22 10:24:38 +0100419 __ Bind(GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100420 arm_codegen->InvokeRuntime(kQuickThrowDivZero, instruction_, instruction_->GetDexPc(), this);
Scott Wakelingfe885462016-09-22 10:24:38 +0100421 CheckEntrypointTypes<kQuickThrowDivZero, void, void>();
422 }
423
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100424 bool IsFatal() const override { return true; }
Scott Wakelingfe885462016-09-22 10:24:38 +0100425
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100426 const char* GetDescription() const override { return "DivZeroCheckSlowPathARMVIXL"; }
Scott Wakelingfe885462016-09-22 10:24:38 +0100427
428 private:
429 DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathARMVIXL);
430};
431
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100432class SuspendCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
433 public:
434 SuspendCheckSlowPathARMVIXL(HSuspendCheck* instruction, HBasicBlock* successor)
435 : SlowPathCodeARMVIXL(instruction), successor_(successor) {}
436
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100437 void EmitNativeCode(CodeGenerator* codegen) override {
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100438 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
439 __ Bind(GetEntryLabel());
440 arm_codegen->InvokeRuntime(kQuickTestSuspend, instruction_, instruction_->GetDexPc(), this);
441 CheckEntrypointTypes<kQuickTestSuspend, void, void>();
442 if (successor_ == nullptr) {
443 __ B(GetReturnLabel());
444 } else {
445 __ B(arm_codegen->GetLabelOf(successor_));
446 }
447 }
448
449 vixl32::Label* GetReturnLabel() {
450 DCHECK(successor_ == nullptr);
451 return &return_label_;
452 }
453
454 HBasicBlock* GetSuccessor() const {
455 return successor_;
456 }
457
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100458 const char* GetDescription() const override { return "SuspendCheckSlowPathARMVIXL"; }
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100459
460 private:
461 // If not null, the block to branch to after the suspend check.
462 HBasicBlock* const successor_;
463
464 // If `successor_` is null, the label to branch to after the suspend check.
465 vixl32::Label return_label_;
466
467 DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathARMVIXL);
468};
469
Scott Wakelingc34dba72016-10-03 10:14:44 +0100470class BoundsCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
471 public:
472 explicit BoundsCheckSlowPathARMVIXL(HBoundsCheck* instruction)
473 : SlowPathCodeARMVIXL(instruction) {}
474
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100475 void EmitNativeCode(CodeGenerator* codegen) override {
Scott Wakelingc34dba72016-10-03 10:14:44 +0100476 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
477 LocationSummary* locations = instruction_->GetLocations();
478
479 __ Bind(GetEntryLabel());
480 if (instruction_->CanThrowIntoCatchBlock()) {
481 // Live registers will be restored in the catch block if caught.
482 SaveLiveRegisters(codegen, instruction_->GetLocations());
483 }
484 // We're moving two locations to locations that could overlap, so we need a parallel
485 // move resolver.
486 InvokeRuntimeCallingConventionARMVIXL calling_convention;
487 codegen->EmitParallelMoves(
488 locations->InAt(0),
489 LocationFrom(calling_convention.GetRegisterAt(0)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100490 DataType::Type::kInt32,
Scott Wakelingc34dba72016-10-03 10:14:44 +0100491 locations->InAt(1),
492 LocationFrom(calling_convention.GetRegisterAt(1)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100493 DataType::Type::kInt32);
Scott Wakelingc34dba72016-10-03 10:14:44 +0100494 QuickEntrypointEnum entrypoint = instruction_->AsBoundsCheck()->IsStringCharAt()
495 ? kQuickThrowStringBounds
496 : kQuickThrowArrayBounds;
497 arm_codegen->InvokeRuntime(entrypoint, instruction_, instruction_->GetDexPc(), this);
498 CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>();
499 CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>();
500 }
501
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100502 bool IsFatal() const override { return true; }
Scott Wakelingc34dba72016-10-03 10:14:44 +0100503
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100504 const char* GetDescription() const override { return "BoundsCheckSlowPathARMVIXL"; }
Scott Wakelingc34dba72016-10-03 10:14:44 +0100505
506 private:
507 DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARMVIXL);
508};
509
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100510class LoadClassSlowPathARMVIXL : public SlowPathCodeARMVIXL {
511 public:
Vladimir Markoa9f303c2018-07-20 16:43:56 +0100512 LoadClassSlowPathARMVIXL(HLoadClass* cls, HInstruction* at)
513 : SlowPathCodeARMVIXL(at), cls_(cls) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100514 DCHECK(at->IsLoadClass() || at->IsClinitCheck());
Vladimir Markoa9f303c2018-07-20 16:43:56 +0100515 DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_);
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100516 }
517
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100518 void EmitNativeCode(CodeGenerator* codegen) override {
Vladimir Marko6bec91c2017-01-09 15:03:12 +0000519 LocationSummary* locations = instruction_->GetLocations();
Vladimir Markoea4c1262017-02-06 19:59:33 +0000520 Location out = locations->Out();
Vladimir Markoa9f303c2018-07-20 16:43:56 +0100521 const uint32_t dex_pc = instruction_->GetDexPc();
522 bool must_resolve_type = instruction_->IsLoadClass() && cls_->MustResolveTypeOnSlowPath();
523 bool must_do_clinit = instruction_->IsClinitCheck() || cls_->MustGenerateClinitCheck();
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100524
525 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
526 __ Bind(GetEntryLabel());
527 SaveLiveRegisters(codegen, locations);
528
529 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Vladimir Markoa9f303c2018-07-20 16:43:56 +0100530 if (must_resolve_type) {
531 DCHECK(IsSameDexFile(cls_->GetDexFile(), arm_codegen->GetGraph()->GetDexFile()));
532 dex::TypeIndex type_index = cls_->GetTypeIndex();
533 __ Mov(calling_convention.GetRegisterAt(0), type_index.index_);
Vladimir Marko8f63f102020-09-28 12:10:28 +0100534 if (cls_->NeedsAccessCheck()) {
535 CheckEntrypointTypes<kQuickResolveTypeAndVerifyAccess, void*, uint32_t>();
536 arm_codegen->InvokeRuntime(kQuickResolveTypeAndVerifyAccess, instruction_, dex_pc, this);
537 } else {
538 CheckEntrypointTypes<kQuickResolveType, void*, uint32_t>();
539 arm_codegen->InvokeRuntime(kQuickResolveType, instruction_, dex_pc, this);
540 }
Vladimir Markoa9f303c2018-07-20 16:43:56 +0100541 // If we also must_do_clinit, the resolved type is now in the correct register.
542 } else {
543 DCHECK(must_do_clinit);
544 Location source = instruction_->IsLoadClass() ? out : locations->InAt(0);
545 arm_codegen->Move32(LocationFrom(calling_convention.GetRegisterAt(0)), source);
546 }
547 if (must_do_clinit) {
548 arm_codegen->InvokeRuntime(kQuickInitializeStaticStorage, instruction_, dex_pc, this);
549 CheckEntrypointTypes<kQuickInitializeStaticStorage, void*, mirror::Class*>();
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100550 }
551
552 // Move the class to the desired location.
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100553 if (out.IsValid()) {
554 DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
555 arm_codegen->Move32(locations->Out(), LocationFrom(r0));
556 }
557 RestoreLiveRegisters(codegen, locations);
558 __ B(GetExitLabel());
559 }
560
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100561 const char* GetDescription() const override { return "LoadClassSlowPathARMVIXL"; }
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100562
563 private:
564 // The class this slow path will load.
565 HLoadClass* const cls_;
566
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100567 DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathARMVIXL);
568};
569
Artem Serovd4cc5b22016-11-04 11:19:09 +0000570class LoadStringSlowPathARMVIXL : public SlowPathCodeARMVIXL {
571 public:
572 explicit LoadStringSlowPathARMVIXL(HLoadString* instruction)
573 : SlowPathCodeARMVIXL(instruction) {}
574
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100575 void EmitNativeCode(CodeGenerator* codegen) override {
Vladimir Markoea4c1262017-02-06 19:59:33 +0000576 DCHECK(instruction_->IsLoadString());
577 DCHECK_EQ(instruction_->AsLoadString()->GetLoadKind(), HLoadString::LoadKind::kBssEntry);
Artem Serovd4cc5b22016-11-04 11:19:09 +0000578 LocationSummary* locations = instruction_->GetLocations();
579 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
Vladimir Markof3c52b42017-11-17 17:32:12 +0000580 const dex::StringIndex string_index = instruction_->AsLoadString()->GetStringIndex();
Artem Serovd4cc5b22016-11-04 11:19:09 +0000581
582 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
583 __ Bind(GetEntryLabel());
584 SaveLiveRegisters(codegen, locations);
585
586 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Vladimir Marko6bec91c2017-01-09 15:03:12 +0000587 __ Mov(calling_convention.GetRegisterAt(0), string_index.index_);
Artem Serovd4cc5b22016-11-04 11:19:09 +0000588 arm_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), this);
589 CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
590
Artem Serovd4cc5b22016-11-04 11:19:09 +0000591 arm_codegen->Move32(locations->Out(), LocationFrom(r0));
592 RestoreLiveRegisters(codegen, locations);
593
594 __ B(GetExitLabel());
595 }
596
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100597 const char* GetDescription() const override { return "LoadStringSlowPathARMVIXL"; }
Artem Serovd4cc5b22016-11-04 11:19:09 +0000598
599 private:
600 DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathARMVIXL);
601};
602
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100603class TypeCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
604 public:
605 TypeCheckSlowPathARMVIXL(HInstruction* instruction, bool is_fatal)
606 : SlowPathCodeARMVIXL(instruction), is_fatal_(is_fatal) {}
607
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100608 void EmitNativeCode(CodeGenerator* codegen) override {
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100609 LocationSummary* locations = instruction_->GetLocations();
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100610 DCHECK(instruction_->IsCheckCast()
611 || !locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
612
613 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
614 __ Bind(GetEntryLabel());
615
Vladimir Marko87584542017-12-12 17:47:52 +0000616 if (!is_fatal_ || instruction_->CanThrowIntoCatchBlock()) {
Artem Serovcfbe9132016-10-14 15:58:56 +0100617 SaveLiveRegisters(codegen, locations);
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100618 }
619
620 // We're moving two locations to locations that could overlap, so we need a parallel
621 // move resolver.
622 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100623
Mathieu Chartier9fd8c602016-11-14 14:38:53 -0800624 codegen->EmitParallelMoves(locations->InAt(0),
Mathieu Chartierb99f4d62016-11-07 16:17:26 -0800625 LocationFrom(calling_convention.GetRegisterAt(0)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100626 DataType::Type::kReference,
Mathieu Chartier9fd8c602016-11-14 14:38:53 -0800627 locations->InAt(1),
Mathieu Chartierb99f4d62016-11-07 16:17:26 -0800628 LocationFrom(calling_convention.GetRegisterAt(1)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100629 DataType::Type::kReference);
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100630 if (instruction_->IsInstanceOf()) {
Artem Serovcfbe9132016-10-14 15:58:56 +0100631 arm_codegen->InvokeRuntime(kQuickInstanceofNonTrivial,
632 instruction_,
633 instruction_->GetDexPc(),
634 this);
Mathieu Chartier9fd8c602016-11-14 14:38:53 -0800635 CheckEntrypointTypes<kQuickInstanceofNonTrivial, size_t, mirror::Object*, mirror::Class*>();
Artem Serovcfbe9132016-10-14 15:58:56 +0100636 arm_codegen->Move32(locations->Out(), LocationFrom(r0));
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100637 } else {
638 DCHECK(instruction_->IsCheckCast());
Mathieu Chartierb99f4d62016-11-07 16:17:26 -0800639 arm_codegen->InvokeRuntime(kQuickCheckInstanceOf,
640 instruction_,
641 instruction_->GetDexPc(),
642 this);
643 CheckEntrypointTypes<kQuickCheckInstanceOf, void, mirror::Object*, mirror::Class*>();
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100644 }
645
646 if (!is_fatal_) {
Artem Serovcfbe9132016-10-14 15:58:56 +0100647 RestoreLiveRegisters(codegen, locations);
648 __ B(GetExitLabel());
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100649 }
650 }
651
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100652 const char* GetDescription() const override { return "TypeCheckSlowPathARMVIXL"; }
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100653
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100654 bool IsFatal() const override { return is_fatal_; }
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100655
656 private:
657 const bool is_fatal_;
658
659 DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathARMVIXL);
660};
661
Scott Wakelingc34dba72016-10-03 10:14:44 +0100662class DeoptimizationSlowPathARMVIXL : public SlowPathCodeARMVIXL {
663 public:
664 explicit DeoptimizationSlowPathARMVIXL(HDeoptimize* instruction)
665 : SlowPathCodeARMVIXL(instruction) {}
666
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100667 void EmitNativeCode(CodeGenerator* codegen) override {
Scott Wakelingc34dba72016-10-03 10:14:44 +0100668 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
669 __ Bind(GetEntryLabel());
Nicolas Geoffray4e92c3c2017-05-08 09:34:26 +0100670 LocationSummary* locations = instruction_->GetLocations();
671 SaveLiveRegisters(codegen, locations);
672 InvokeRuntimeCallingConventionARMVIXL calling_convention;
673 __ Mov(calling_convention.GetRegisterAt(0),
674 static_cast<uint32_t>(instruction_->AsDeoptimize()->GetDeoptimizationKind()));
675
Scott Wakelingc34dba72016-10-03 10:14:44 +0100676 arm_codegen->InvokeRuntime(kQuickDeoptimize, instruction_, instruction_->GetDexPc(), this);
Nicolas Geoffray4e92c3c2017-05-08 09:34:26 +0100677 CheckEntrypointTypes<kQuickDeoptimize, void, DeoptimizationKind>();
Scott Wakelingc34dba72016-10-03 10:14:44 +0100678 }
679
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100680 const char* GetDescription() const override { return "DeoptimizationSlowPathARMVIXL"; }
Scott Wakelingc34dba72016-10-03 10:14:44 +0100681
682 private:
683 DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathARMVIXL);
684};
685
686class ArraySetSlowPathARMVIXL : public SlowPathCodeARMVIXL {
687 public:
688 explicit ArraySetSlowPathARMVIXL(HInstruction* instruction) : SlowPathCodeARMVIXL(instruction) {}
689
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100690 void EmitNativeCode(CodeGenerator* codegen) override {
Scott Wakelingc34dba72016-10-03 10:14:44 +0100691 LocationSummary* locations = instruction_->GetLocations();
692 __ Bind(GetEntryLabel());
693 SaveLiveRegisters(codegen, locations);
694
695 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Vladimir Markoca6fff82017-10-03 14:49:14 +0100696 HParallelMove parallel_move(codegen->GetGraph()->GetAllocator());
Scott Wakelingc34dba72016-10-03 10:14:44 +0100697 parallel_move.AddMove(
698 locations->InAt(0),
699 LocationFrom(calling_convention.GetRegisterAt(0)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100700 DataType::Type::kReference,
Scott Wakelingc34dba72016-10-03 10:14:44 +0100701 nullptr);
702 parallel_move.AddMove(
703 locations->InAt(1),
704 LocationFrom(calling_convention.GetRegisterAt(1)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100705 DataType::Type::kInt32,
Scott Wakelingc34dba72016-10-03 10:14:44 +0100706 nullptr);
707 parallel_move.AddMove(
708 locations->InAt(2),
709 LocationFrom(calling_convention.GetRegisterAt(2)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100710 DataType::Type::kReference,
Scott Wakelingc34dba72016-10-03 10:14:44 +0100711 nullptr);
712 codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
713
714 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
715 arm_codegen->InvokeRuntime(kQuickAputObject, instruction_, instruction_->GetDexPc(), this);
716 CheckEntrypointTypes<kQuickAputObject, void, mirror::Array*, int32_t, mirror::Object*>();
717 RestoreLiveRegisters(codegen, locations);
718 __ B(GetExitLabel());
719 }
720
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100721 const char* GetDescription() const override { return "ArraySetSlowPathARMVIXL"; }
Scott Wakelingc34dba72016-10-03 10:14:44 +0100722
723 private:
724 DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathARMVIXL);
725};
726
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000727// Slow path generating a read barrier for a heap reference.
728class ReadBarrierForHeapReferenceSlowPathARMVIXL : public SlowPathCodeARMVIXL {
729 public:
730 ReadBarrierForHeapReferenceSlowPathARMVIXL(HInstruction* instruction,
731 Location out,
732 Location ref,
733 Location obj,
734 uint32_t offset,
735 Location index)
736 : SlowPathCodeARMVIXL(instruction),
737 out_(out),
738 ref_(ref),
739 obj_(obj),
740 offset_(offset),
741 index_(index) {
742 DCHECK(kEmitCompilerReadBarrier);
743 // If `obj` is equal to `out` or `ref`, it means the initial object
744 // has been overwritten by (or after) the heap object reference load
745 // to be instrumented, e.g.:
746 //
747 // __ LoadFromOffset(kLoadWord, out, out, offset);
748 // codegen_->GenerateReadBarrierSlow(instruction, out_loc, out_loc, out_loc, offset);
749 //
750 // In that case, we have lost the information about the original
751 // object, and the emitted read barrier cannot work properly.
752 DCHECK(!obj.Equals(out)) << "obj=" << obj << " out=" << out;
753 DCHECK(!obj.Equals(ref)) << "obj=" << obj << " ref=" << ref;
754 }
755
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100756 void EmitNativeCode(CodeGenerator* codegen) override {
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000757 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
758 LocationSummary* locations = instruction_->GetLocations();
759 vixl32::Register reg_out = RegisterFrom(out_);
760 DCHECK(locations->CanCall());
761 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out.GetCode()));
762 DCHECK(instruction_->IsInstanceFieldGet() ||
763 instruction_->IsStaticFieldGet() ||
764 instruction_->IsArrayGet() ||
765 instruction_->IsInstanceOf() ||
766 instruction_->IsCheckCast() ||
Vladimir Marko94d2c812020-11-05 10:04:45 +0000767 (instruction_->IsInvoke() && instruction_->GetLocations()->Intrinsified()))
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000768 << "Unexpected instruction in read barrier for heap reference slow path: "
769 << instruction_->DebugName();
770 // The read barrier instrumentation of object ArrayGet
771 // instructions does not support the HIntermediateAddress
772 // instruction.
773 DCHECK(!(instruction_->IsArrayGet() &&
774 instruction_->AsArrayGet()->GetArray()->IsIntermediateAddress()));
775
776 __ Bind(GetEntryLabel());
777 SaveLiveRegisters(codegen, locations);
778
779 // We may have to change the index's value, but as `index_` is a
780 // constant member (like other "inputs" of this slow path),
781 // introduce a copy of it, `index`.
782 Location index = index_;
783 if (index_.IsValid()) {
784 // Handle `index_` for HArrayGet and UnsafeGetObject/UnsafeGetObjectVolatile intrinsics.
785 if (instruction_->IsArrayGet()) {
786 // Compute the actual memory offset and store it in `index`.
787 vixl32::Register index_reg = RegisterFrom(index_);
788 DCHECK(locations->GetLiveRegisters()->ContainsCoreRegister(index_reg.GetCode()));
789 if (codegen->IsCoreCalleeSaveRegister(index_reg.GetCode())) {
790 // We are about to change the value of `index_reg` (see the
Roland Levillain9983e302017-07-14 14:34:22 +0100791 // calls to art::arm::ArmVIXLMacroAssembler::Lsl and
792 // art::arm::ArmVIXLMacroAssembler::Add below), but it has
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000793 // not been saved by the previous call to
794 // art::SlowPathCode::SaveLiveRegisters, as it is a
795 // callee-save register --
796 // art::SlowPathCode::SaveLiveRegisters does not consider
797 // callee-save registers, as it has been designed with the
798 // assumption that callee-save registers are supposed to be
799 // handled by the called function. So, as a callee-save
800 // register, `index_reg` _would_ eventually be saved onto
801 // the stack, but it would be too late: we would have
802 // changed its value earlier. Therefore, we manually save
803 // it here into another freely available register,
804 // `free_reg`, chosen of course among the caller-save
805 // registers (as a callee-save `free_reg` register would
806 // exhibit the same problem).
807 //
808 // Note we could have requested a temporary register from
809 // the register allocator instead; but we prefer not to, as
810 // this is a slow path, and we know we can find a
811 // caller-save register that is available.
812 vixl32::Register free_reg = FindAvailableCallerSaveRegister(codegen);
813 __ Mov(free_reg, index_reg);
814 index_reg = free_reg;
815 index = LocationFrom(index_reg);
816 } else {
817 // The initial register stored in `index_` has already been
818 // saved in the call to art::SlowPathCode::SaveLiveRegisters
819 // (as it is not a callee-save register), so we can freely
820 // use it.
821 }
822 // Shifting the index value contained in `index_reg` by the scale
823 // factor (2) cannot overflow in practice, as the runtime is
824 // unable to allocate object arrays with a size larger than
825 // 2^26 - 1 (that is, 2^28 - 4 bytes).
826 __ Lsl(index_reg, index_reg, TIMES_4);
827 static_assert(
828 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
829 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
830 __ Add(index_reg, index_reg, offset_);
831 } else {
832 // In the case of the UnsafeGetObject/UnsafeGetObjectVolatile
833 // intrinsics, `index_` is not shifted by a scale factor of 2
834 // (as in the case of ArrayGet), as it is actually an offset
835 // to an object field within an object.
836 DCHECK(instruction_->IsInvoke()) << instruction_->DebugName();
837 DCHECK(instruction_->GetLocations()->Intrinsified());
Vladimir Markoac3fcff2020-11-17 12:17:58 +0000838 Intrinsics intrinsic = instruction_->AsInvoke()->GetIntrinsic();
839 DCHECK(intrinsic == Intrinsics::kUnsafeGetObject ||
840 intrinsic == Intrinsics::kUnsafeGetObjectVolatile ||
841 mirror::VarHandle::GetAccessModeTemplateByIntrinsic(intrinsic) ==
842 mirror::VarHandle::AccessModeTemplate::kGet)
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000843 << instruction_->AsInvoke()->GetIntrinsic();
844 DCHECK_EQ(offset_, 0U);
Vladimir Markoac3fcff2020-11-17 12:17:58 +0000845 // Though UnsafeGet's offset location is a register pair, we only pass the low
846 // part (high part is irrelevant for 32-bit addresses) to the slow path.
847 // For VarHandle intrinsics, the index is always just a register.
848 DCHECK(index_.IsRegister());
849 index = index_;
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000850 }
851 }
852
853 // We're moving two or three locations to locations that could
854 // overlap, so we need a parallel move resolver.
855 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Vladimir Markoca6fff82017-10-03 14:49:14 +0100856 HParallelMove parallel_move(codegen->GetGraph()->GetAllocator());
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000857 parallel_move.AddMove(ref_,
858 LocationFrom(calling_convention.GetRegisterAt(0)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100859 DataType::Type::kReference,
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000860 nullptr);
861 parallel_move.AddMove(obj_,
862 LocationFrom(calling_convention.GetRegisterAt(1)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100863 DataType::Type::kReference,
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000864 nullptr);
865 if (index.IsValid()) {
866 parallel_move.AddMove(index,
867 LocationFrom(calling_convention.GetRegisterAt(2)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100868 DataType::Type::kInt32,
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000869 nullptr);
870 codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
871 } else {
872 codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
873 __ Mov(calling_convention.GetRegisterAt(2), offset_);
874 }
875 arm_codegen->InvokeRuntime(kQuickReadBarrierSlow, instruction_, instruction_->GetDexPc(), this);
876 CheckEntrypointTypes<
877 kQuickReadBarrierSlow, mirror::Object*, mirror::Object*, mirror::Object*, uint32_t>();
878 arm_codegen->Move32(out_, LocationFrom(r0));
879
880 RestoreLiveRegisters(codegen, locations);
881 __ B(GetExitLabel());
882 }
883
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100884 const char* GetDescription() const override {
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000885 return "ReadBarrierForHeapReferenceSlowPathARMVIXL";
886 }
887
888 private:
889 vixl32::Register FindAvailableCallerSaveRegister(CodeGenerator* codegen) {
890 uint32_t ref = RegisterFrom(ref_).GetCode();
891 uint32_t obj = RegisterFrom(obj_).GetCode();
892 for (uint32_t i = 0, e = codegen->GetNumberOfCoreRegisters(); i < e; ++i) {
893 if (i != ref && i != obj && !codegen->IsCoreCalleeSaveRegister(i)) {
894 return vixl32::Register(i);
895 }
896 }
897 // We shall never fail to find a free caller-save register, as
898 // there are more than two core caller-save registers on ARM
899 // (meaning it is possible to find one which is different from
900 // `ref` and `obj`).
901 DCHECK_GT(codegen->GetNumberOfCoreCallerSaveRegisters(), 2u);
902 LOG(FATAL) << "Could not find a free caller-save register";
903 UNREACHABLE();
904 }
905
906 const Location out_;
907 const Location ref_;
908 const Location obj_;
909 const uint32_t offset_;
910 // An additional location containing an index to an array.
911 // Only used for HArrayGet and the UnsafeGetObject &
912 // UnsafeGetObjectVolatile intrinsics.
913 const Location index_;
914
915 DISALLOW_COPY_AND_ASSIGN(ReadBarrierForHeapReferenceSlowPathARMVIXL);
916};
917
918// Slow path generating a read barrier for a GC root.
919class ReadBarrierForRootSlowPathARMVIXL : public SlowPathCodeARMVIXL {
920 public:
921 ReadBarrierForRootSlowPathARMVIXL(HInstruction* instruction, Location out, Location root)
922 : SlowPathCodeARMVIXL(instruction), out_(out), root_(root) {
923 DCHECK(kEmitCompilerReadBarrier);
924 }
925
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100926 void EmitNativeCode(CodeGenerator* codegen) override {
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000927 LocationSummary* locations = instruction_->GetLocations();
928 vixl32::Register reg_out = RegisterFrom(out_);
929 DCHECK(locations->CanCall());
930 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out.GetCode()));
Vladimir Markoac3fcff2020-11-17 12:17:58 +0000931 DCHECK(instruction_->IsLoadClass() ||
932 instruction_->IsLoadString() ||
933 (instruction_->IsInvoke() && instruction_->GetLocations()->Intrinsified()))
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000934 << "Unexpected instruction in read barrier for GC root slow path: "
935 << instruction_->DebugName();
936
937 __ Bind(GetEntryLabel());
938 SaveLiveRegisters(codegen, locations);
939
940 InvokeRuntimeCallingConventionARMVIXL calling_convention;
941 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
942 arm_codegen->Move32(LocationFrom(calling_convention.GetRegisterAt(0)), root_);
943 arm_codegen->InvokeRuntime(kQuickReadBarrierForRootSlow,
944 instruction_,
945 instruction_->GetDexPc(),
946 this);
947 CheckEntrypointTypes<kQuickReadBarrierForRootSlow, mirror::Object*, GcRoot<mirror::Object>*>();
948 arm_codegen->Move32(out_, LocationFrom(r0));
949
950 RestoreLiveRegisters(codegen, locations);
951 __ B(GetExitLabel());
952 }
953
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100954 const char* GetDescription() const override { return "ReadBarrierForRootSlowPathARMVIXL"; }
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000955
956 private:
957 const Location out_;
958 const Location root_;
959
960 DISALLOW_COPY_AND_ASSIGN(ReadBarrierForRootSlowPathARMVIXL);
961};
Scott Wakelingc34dba72016-10-03 10:14:44 +0100962
Scott Wakelingfe885462016-09-22 10:24:38 +0100963inline vixl32::Condition ARMCondition(IfCondition cond) {
964 switch (cond) {
965 case kCondEQ: return eq;
966 case kCondNE: return ne;
967 case kCondLT: return lt;
968 case kCondLE: return le;
969 case kCondGT: return gt;
970 case kCondGE: return ge;
971 case kCondB: return lo;
972 case kCondBE: return ls;
973 case kCondA: return hi;
974 case kCondAE: return hs;
975 }
976 LOG(FATAL) << "Unreachable";
977 UNREACHABLE();
978}
979
980// Maps signed condition to unsigned condition.
981inline vixl32::Condition ARMUnsignedCondition(IfCondition cond) {
982 switch (cond) {
983 case kCondEQ: return eq;
984 case kCondNE: return ne;
985 // Signed to unsigned.
986 case kCondLT: return lo;
987 case kCondLE: return ls;
988 case kCondGT: return hi;
989 case kCondGE: return hs;
990 // Unsigned remain unchanged.
991 case kCondB: return lo;
992 case kCondBE: return ls;
993 case kCondA: return hi;
994 case kCondAE: return hs;
995 }
996 LOG(FATAL) << "Unreachable";
997 UNREACHABLE();
998}
999
1000inline vixl32::Condition ARMFPCondition(IfCondition cond, bool gt_bias) {
1001 // The ARM condition codes can express all the necessary branches, see the
1002 // "Meaning (floating-point)" column in the table A8-1 of the ARMv7 reference manual.
1003 // There is no dex instruction or HIR that would need the missing conditions
1004 // "equal or unordered" or "not equal".
1005 switch (cond) {
1006 case kCondEQ: return eq;
1007 case kCondNE: return ne /* unordered */;
1008 case kCondLT: return gt_bias ? cc : lt /* unordered */;
1009 case kCondLE: return gt_bias ? ls : le /* unordered */;
1010 case kCondGT: return gt_bias ? hi /* unordered */ : gt;
1011 case kCondGE: return gt_bias ? cs /* unordered */ : ge;
1012 default:
1013 LOG(FATAL) << "UNREACHABLE";
1014 UNREACHABLE();
1015 }
1016}
1017
Anton Kirilov74234da2017-01-13 14:42:47 +00001018inline ShiftType ShiftFromOpKind(HDataProcWithShifterOp::OpKind op_kind) {
1019 switch (op_kind) {
1020 case HDataProcWithShifterOp::kASR: return ShiftType::ASR;
1021 case HDataProcWithShifterOp::kLSL: return ShiftType::LSL;
1022 case HDataProcWithShifterOp::kLSR: return ShiftType::LSR;
1023 default:
1024 LOG(FATAL) << "Unexpected op kind " << op_kind;
1025 UNREACHABLE();
1026 }
1027}
1028
Scott Wakelingfe885462016-09-22 10:24:38 +01001029void CodeGeneratorARMVIXL::DumpCoreRegister(std::ostream& stream, int reg) const {
1030 stream << vixl32::Register(reg);
1031}
1032
1033void CodeGeneratorARMVIXL::DumpFloatingPointRegister(std::ostream& stream, int reg) const {
1034 stream << vixl32::SRegister(reg);
1035}
1036
Vladimir Markoa0431112018-06-25 09:32:54 +01001037const ArmInstructionSetFeatures& CodeGeneratorARMVIXL::GetInstructionSetFeatures() const {
1038 return *GetCompilerOptions().GetInstructionSetFeatures()->AsArmInstructionSetFeatures();
1039}
1040
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001041static uint32_t ComputeSRegisterListMask(const SRegisterList& regs) {
Scott Wakelingfe885462016-09-22 10:24:38 +01001042 uint32_t mask = 0;
1043 for (uint32_t i = regs.GetFirstSRegister().GetCode();
1044 i <= regs.GetLastSRegister().GetCode();
1045 ++i) {
1046 mask |= (1 << i);
1047 }
1048 return mask;
1049}
1050
Artem Serovd4cc5b22016-11-04 11:19:09 +00001051// Saves the register in the stack. Returns the size taken on stack.
1052size_t CodeGeneratorARMVIXL::SaveCoreRegister(size_t stack_index ATTRIBUTE_UNUSED,
1053 uint32_t reg_id ATTRIBUTE_UNUSED) {
1054 TODO_VIXL32(FATAL);
Elliott Hughesc1896c92018-11-29 11:33:18 -08001055 UNREACHABLE();
Artem Serovd4cc5b22016-11-04 11:19:09 +00001056}
1057
1058// Restores the register from the stack. Returns the size taken on stack.
1059size_t CodeGeneratorARMVIXL::RestoreCoreRegister(size_t stack_index ATTRIBUTE_UNUSED,
1060 uint32_t reg_id ATTRIBUTE_UNUSED) {
1061 TODO_VIXL32(FATAL);
Elliott Hughesc1896c92018-11-29 11:33:18 -08001062 UNREACHABLE();
Artem Serovd4cc5b22016-11-04 11:19:09 +00001063}
1064
1065size_t CodeGeneratorARMVIXL::SaveFloatingPointRegister(size_t stack_index ATTRIBUTE_UNUSED,
1066 uint32_t reg_id ATTRIBUTE_UNUSED) {
1067 TODO_VIXL32(FATAL);
Elliott Hughesc1896c92018-11-29 11:33:18 -08001068 UNREACHABLE();
Artem Serovd4cc5b22016-11-04 11:19:09 +00001069}
1070
1071size_t CodeGeneratorARMVIXL::RestoreFloatingPointRegister(size_t stack_index ATTRIBUTE_UNUSED,
1072 uint32_t reg_id ATTRIBUTE_UNUSED) {
1073 TODO_VIXL32(FATAL);
Elliott Hughesc1896c92018-11-29 11:33:18 -08001074 UNREACHABLE();
Anton Kirilove28d9ae2016-10-25 18:17:23 +01001075}
1076
Anton Kirilov74234da2017-01-13 14:42:47 +00001077static void GenerateDataProcInstruction(HInstruction::InstructionKind kind,
1078 vixl32::Register out,
1079 vixl32::Register first,
1080 const Operand& second,
1081 CodeGeneratorARMVIXL* codegen) {
1082 if (second.IsImmediate() && second.GetImmediate() == 0) {
1083 const Operand in = kind == HInstruction::kAnd
1084 ? Operand(0)
1085 : Operand(first);
1086
1087 __ Mov(out, in);
1088 } else {
1089 switch (kind) {
1090 case HInstruction::kAdd:
1091 __ Add(out, first, second);
1092 break;
1093 case HInstruction::kAnd:
1094 __ And(out, first, second);
1095 break;
1096 case HInstruction::kOr:
1097 __ Orr(out, first, second);
1098 break;
1099 case HInstruction::kSub:
1100 __ Sub(out, first, second);
1101 break;
1102 case HInstruction::kXor:
1103 __ Eor(out, first, second);
1104 break;
1105 default:
1106 LOG(FATAL) << "Unexpected instruction kind: " << kind;
1107 UNREACHABLE();
1108 }
1109 }
1110}
1111
1112static void GenerateDataProc(HInstruction::InstructionKind kind,
1113 const Location& out,
1114 const Location& first,
1115 const Operand& second_lo,
1116 const Operand& second_hi,
1117 CodeGeneratorARMVIXL* codegen) {
1118 const vixl32::Register first_hi = HighRegisterFrom(first);
1119 const vixl32::Register first_lo = LowRegisterFrom(first);
1120 const vixl32::Register out_hi = HighRegisterFrom(out);
1121 const vixl32::Register out_lo = LowRegisterFrom(out);
1122
1123 if (kind == HInstruction::kAdd) {
1124 __ Adds(out_lo, first_lo, second_lo);
1125 __ Adc(out_hi, first_hi, second_hi);
1126 } else if (kind == HInstruction::kSub) {
1127 __ Subs(out_lo, first_lo, second_lo);
1128 __ Sbc(out_hi, first_hi, second_hi);
1129 } else {
1130 GenerateDataProcInstruction(kind, out_lo, first_lo, second_lo, codegen);
1131 GenerateDataProcInstruction(kind, out_hi, first_hi, second_hi, codegen);
1132 }
1133}
1134
1135static Operand GetShifterOperand(vixl32::Register rm, ShiftType shift, uint32_t shift_imm) {
1136 return shift_imm == 0 ? Operand(rm) : Operand(rm, shift, shift_imm);
1137}
1138
1139static void GenerateLongDataProc(HDataProcWithShifterOp* instruction,
1140 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001141 DCHECK_EQ(instruction->GetType(), DataType::Type::kInt64);
Anton Kirilov74234da2017-01-13 14:42:47 +00001142 DCHECK(HDataProcWithShifterOp::IsShiftOp(instruction->GetOpKind()));
1143
1144 const LocationSummary* const locations = instruction->GetLocations();
1145 const uint32_t shift_value = instruction->GetShiftAmount();
1146 const HInstruction::InstructionKind kind = instruction->GetInstrKind();
1147 const Location first = locations->InAt(0);
1148 const Location second = locations->InAt(1);
1149 const Location out = locations->Out();
1150 const vixl32::Register first_hi = HighRegisterFrom(first);
1151 const vixl32::Register first_lo = LowRegisterFrom(first);
1152 const vixl32::Register out_hi = HighRegisterFrom(out);
1153 const vixl32::Register out_lo = LowRegisterFrom(out);
1154 const vixl32::Register second_hi = HighRegisterFrom(second);
1155 const vixl32::Register second_lo = LowRegisterFrom(second);
1156 const ShiftType shift = ShiftFromOpKind(instruction->GetOpKind());
1157
1158 if (shift_value >= 32) {
1159 if (shift == ShiftType::LSL) {
1160 GenerateDataProcInstruction(kind,
1161 out_hi,
1162 first_hi,
1163 Operand(second_lo, ShiftType::LSL, shift_value - 32),
1164 codegen);
1165 GenerateDataProcInstruction(kind, out_lo, first_lo, 0, codegen);
1166 } else if (shift == ShiftType::ASR) {
1167 GenerateDataProc(kind,
1168 out,
1169 first,
1170 GetShifterOperand(second_hi, ShiftType::ASR, shift_value - 32),
1171 Operand(second_hi, ShiftType::ASR, 31),
1172 codegen);
1173 } else {
1174 DCHECK_EQ(shift, ShiftType::LSR);
1175 GenerateDataProc(kind,
1176 out,
1177 first,
1178 GetShifterOperand(second_hi, ShiftType::LSR, shift_value - 32),
1179 0,
1180 codegen);
1181 }
1182 } else {
1183 DCHECK_GT(shift_value, 1U);
1184 DCHECK_LT(shift_value, 32U);
1185
1186 UseScratchRegisterScope temps(codegen->GetVIXLAssembler());
1187
1188 if (shift == ShiftType::LSL) {
1189 // We are not doing this for HInstruction::kAdd because the output will require
1190 // Location::kOutputOverlap; not applicable to other cases.
1191 if (kind == HInstruction::kOr || kind == HInstruction::kXor) {
1192 GenerateDataProcInstruction(kind,
1193 out_hi,
1194 first_hi,
1195 Operand(second_hi, ShiftType::LSL, shift_value),
1196 codegen);
1197 GenerateDataProcInstruction(kind,
1198 out_hi,
1199 out_hi,
1200 Operand(second_lo, ShiftType::LSR, 32 - shift_value),
1201 codegen);
1202 GenerateDataProcInstruction(kind,
1203 out_lo,
1204 first_lo,
1205 Operand(second_lo, ShiftType::LSL, shift_value),
1206 codegen);
1207 } else {
1208 const vixl32::Register temp = temps.Acquire();
1209
1210 __ Lsl(temp, second_hi, shift_value);
1211 __ Orr(temp, temp, Operand(second_lo, ShiftType::LSR, 32 - shift_value));
1212 GenerateDataProc(kind,
1213 out,
1214 first,
1215 Operand(second_lo, ShiftType::LSL, shift_value),
1216 temp,
1217 codegen);
1218 }
1219 } else {
1220 DCHECK(shift == ShiftType::ASR || shift == ShiftType::LSR);
1221
1222 // We are not doing this for HInstruction::kAdd because the output will require
1223 // Location::kOutputOverlap; not applicable to other cases.
1224 if (kind == HInstruction::kOr || kind == HInstruction::kXor) {
1225 GenerateDataProcInstruction(kind,
1226 out_lo,
1227 first_lo,
1228 Operand(second_lo, ShiftType::LSR, shift_value),
1229 codegen);
1230 GenerateDataProcInstruction(kind,
1231 out_lo,
1232 out_lo,
1233 Operand(second_hi, ShiftType::LSL, 32 - shift_value),
1234 codegen);
1235 GenerateDataProcInstruction(kind,
1236 out_hi,
1237 first_hi,
1238 Operand(second_hi, shift, shift_value),
1239 codegen);
1240 } else {
1241 const vixl32::Register temp = temps.Acquire();
1242
1243 __ Lsr(temp, second_lo, shift_value);
1244 __ Orr(temp, temp, Operand(second_hi, ShiftType::LSL, 32 - shift_value));
1245 GenerateDataProc(kind,
1246 out,
1247 first,
1248 temp,
1249 Operand(second_hi, shift, shift_value),
1250 codegen);
1251 }
1252 }
1253 }
1254}
1255
Donghui Bai426b49c2016-11-08 14:55:38 +08001256static void GenerateVcmp(HInstruction* instruction, CodeGeneratorARMVIXL* codegen) {
1257 const Location rhs_loc = instruction->GetLocations()->InAt(1);
1258 if (rhs_loc.IsConstant()) {
1259 // 0.0 is the only immediate that can be encoded directly in
1260 // a VCMP instruction.
1261 //
1262 // Both the JLS (section 15.20.1) and the JVMS (section 6.5)
1263 // specify that in a floating-point comparison, positive zero
1264 // and negative zero are considered equal, so we can use the
1265 // literal 0.0 for both cases here.
1266 //
1267 // Note however that some methods (Float.equal, Float.compare,
1268 // Float.compareTo, Double.equal, Double.compare,
1269 // Double.compareTo, Math.max, Math.min, StrictMath.max,
1270 // StrictMath.min) consider 0.0 to be (strictly) greater than
1271 // -0.0. So if we ever translate calls to these methods into a
1272 // HCompare instruction, we must handle the -0.0 case with
1273 // care here.
1274 DCHECK(rhs_loc.GetConstant()->IsArithmeticZero());
1275
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001276 const DataType::Type type = instruction->InputAt(0)->GetType();
Donghui Bai426b49c2016-11-08 14:55:38 +08001277
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001278 if (type == DataType::Type::kFloat32) {
Donghui Bai426b49c2016-11-08 14:55:38 +08001279 __ Vcmp(F32, InputSRegisterAt(instruction, 0), 0.0);
1280 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001281 DCHECK_EQ(type, DataType::Type::kFloat64);
Donghui Bai426b49c2016-11-08 14:55:38 +08001282 __ Vcmp(F64, InputDRegisterAt(instruction, 0), 0.0);
1283 }
1284 } else {
1285 __ Vcmp(InputVRegisterAt(instruction, 0), InputVRegisterAt(instruction, 1));
1286 }
1287}
1288
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001289static int64_t AdjustConstantForCondition(int64_t value,
1290 IfCondition* condition,
1291 IfCondition* opposite) {
1292 if (value == 1) {
1293 if (*condition == kCondB) {
1294 value = 0;
1295 *condition = kCondEQ;
1296 *opposite = kCondNE;
1297 } else if (*condition == kCondAE) {
1298 value = 0;
1299 *condition = kCondNE;
1300 *opposite = kCondEQ;
1301 }
1302 } else if (value == -1) {
1303 if (*condition == kCondGT) {
1304 value = 0;
1305 *condition = kCondGE;
1306 *opposite = kCondLT;
1307 } else if (*condition == kCondLE) {
1308 value = 0;
1309 *condition = kCondLT;
1310 *opposite = kCondGE;
1311 }
1312 }
1313
1314 return value;
1315}
1316
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001317static std::pair<vixl32::Condition, vixl32::Condition> GenerateLongTestConstant(
1318 HCondition* condition,
1319 bool invert,
1320 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001321 DCHECK_EQ(condition->GetLeft()->GetType(), DataType::Type::kInt64);
Donghui Bai426b49c2016-11-08 14:55:38 +08001322
1323 const LocationSummary* const locations = condition->GetLocations();
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001324 IfCondition cond = condition->GetCondition();
1325 IfCondition opposite = condition->GetOppositeCondition();
1326
1327 if (invert) {
1328 std::swap(cond, opposite);
1329 }
1330
1331 std::pair<vixl32::Condition, vixl32::Condition> ret(eq, ne);
Donghui Bai426b49c2016-11-08 14:55:38 +08001332 const Location left = locations->InAt(0);
1333 const Location right = locations->InAt(1);
1334
1335 DCHECK(right.IsConstant());
1336
1337 const vixl32::Register left_high = HighRegisterFrom(left);
1338 const vixl32::Register left_low = LowRegisterFrom(left);
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001339 int64_t value = AdjustConstantForCondition(Int64ConstantFrom(right), &cond, &opposite);
1340 UseScratchRegisterScope temps(codegen->GetVIXLAssembler());
1341
1342 // Comparisons against 0 are common enough to deserve special attention.
1343 if (value == 0) {
1344 switch (cond) {
1345 case kCondNE:
1346 // x > 0 iff x != 0 when the comparison is unsigned.
1347 case kCondA:
1348 ret = std::make_pair(ne, eq);
1349 FALLTHROUGH_INTENDED;
1350 case kCondEQ:
1351 // x <= 0 iff x == 0 when the comparison is unsigned.
1352 case kCondBE:
1353 __ Orrs(temps.Acquire(), left_low, left_high);
1354 return ret;
1355 case kCondLT:
1356 case kCondGE:
1357 __ Cmp(left_high, 0);
1358 return std::make_pair(ARMCondition(cond), ARMCondition(opposite));
1359 // Trivially true or false.
1360 case kCondB:
1361 ret = std::make_pair(ne, eq);
1362 FALLTHROUGH_INTENDED;
1363 case kCondAE:
1364 __ Cmp(left_low, left_low);
1365 return ret;
1366 default:
1367 break;
1368 }
1369 }
Donghui Bai426b49c2016-11-08 14:55:38 +08001370
1371 switch (cond) {
1372 case kCondEQ:
1373 case kCondNE:
1374 case kCondB:
1375 case kCondBE:
1376 case kCondA:
1377 case kCondAE: {
Anton Kirilov23b752b2017-07-20 14:40:44 +01001378 const uint32_t value_low = Low32Bits(value);
1379 Operand operand_low(value_low);
1380
Donghui Bai426b49c2016-11-08 14:55:38 +08001381 __ Cmp(left_high, High32Bits(value));
1382
Anton Kirilov23b752b2017-07-20 14:40:44 +01001383 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
1384 // we must ensure that the operands corresponding to the least significant
1385 // halves of the inputs fit into a 16-bit CMP encoding.
1386 if (!left_low.IsLow() || !IsUint<8>(value_low)) {
1387 operand_low = Operand(temps.Acquire());
1388 __ Mov(LeaveFlags, operand_low.GetBaseRegister(), value_low);
1389 }
1390
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001391 // We use the scope because of the IT block that follows.
Donghui Bai426b49c2016-11-08 14:55:38 +08001392 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
1393 2 * vixl32::k16BitT32InstructionSizeInBytes,
1394 CodeBufferCheckScope::kExactSize);
1395
1396 __ it(eq);
Anton Kirilov23b752b2017-07-20 14:40:44 +01001397 __ cmp(eq, left_low, operand_low);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001398 ret = std::make_pair(ARMUnsignedCondition(cond), ARMUnsignedCondition(opposite));
Donghui Bai426b49c2016-11-08 14:55:38 +08001399 break;
1400 }
1401 case kCondLE:
1402 case kCondGT:
1403 // Trivially true or false.
1404 if (value == std::numeric_limits<int64_t>::max()) {
1405 __ Cmp(left_low, left_low);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001406 ret = cond == kCondLE ? std::make_pair(eq, ne) : std::make_pair(ne, eq);
Donghui Bai426b49c2016-11-08 14:55:38 +08001407 break;
1408 }
1409
1410 if (cond == kCondLE) {
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001411 DCHECK_EQ(opposite, kCondGT);
Donghui Bai426b49c2016-11-08 14:55:38 +08001412 cond = kCondLT;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001413 opposite = kCondGE;
Donghui Bai426b49c2016-11-08 14:55:38 +08001414 } else {
1415 DCHECK_EQ(cond, kCondGT);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001416 DCHECK_EQ(opposite, kCondLE);
Donghui Bai426b49c2016-11-08 14:55:38 +08001417 cond = kCondGE;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001418 opposite = kCondLT;
Donghui Bai426b49c2016-11-08 14:55:38 +08001419 }
1420
1421 value++;
1422 FALLTHROUGH_INTENDED;
1423 case kCondGE:
1424 case kCondLT: {
Donghui Bai426b49c2016-11-08 14:55:38 +08001425 __ Cmp(left_low, Low32Bits(value));
1426 __ Sbcs(temps.Acquire(), left_high, High32Bits(value));
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001427 ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite));
Donghui Bai426b49c2016-11-08 14:55:38 +08001428 break;
1429 }
1430 default:
1431 LOG(FATAL) << "Unreachable";
1432 UNREACHABLE();
1433 }
1434
1435 return ret;
1436}
1437
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001438static std::pair<vixl32::Condition, vixl32::Condition> GenerateLongTest(
1439 HCondition* condition,
1440 bool invert,
1441 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001442 DCHECK_EQ(condition->GetLeft()->GetType(), DataType::Type::kInt64);
Donghui Bai426b49c2016-11-08 14:55:38 +08001443
1444 const LocationSummary* const locations = condition->GetLocations();
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001445 IfCondition cond = condition->GetCondition();
1446 IfCondition opposite = condition->GetOppositeCondition();
1447
1448 if (invert) {
1449 std::swap(cond, opposite);
1450 }
1451
1452 std::pair<vixl32::Condition, vixl32::Condition> ret(eq, ne);
Donghui Bai426b49c2016-11-08 14:55:38 +08001453 Location left = locations->InAt(0);
1454 Location right = locations->InAt(1);
1455
1456 DCHECK(right.IsRegisterPair());
1457
1458 switch (cond) {
1459 case kCondEQ:
1460 case kCondNE:
1461 case kCondB:
1462 case kCondBE:
1463 case kCondA:
1464 case kCondAE: {
1465 __ Cmp(HighRegisterFrom(left), HighRegisterFrom(right));
1466
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001467 // We use the scope because of the IT block that follows.
Donghui Bai426b49c2016-11-08 14:55:38 +08001468 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
1469 2 * vixl32::k16BitT32InstructionSizeInBytes,
1470 CodeBufferCheckScope::kExactSize);
1471
1472 __ it(eq);
1473 __ cmp(eq, LowRegisterFrom(left), LowRegisterFrom(right));
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001474 ret = std::make_pair(ARMUnsignedCondition(cond), ARMUnsignedCondition(opposite));
Donghui Bai426b49c2016-11-08 14:55:38 +08001475 break;
1476 }
1477 case kCondLE:
1478 case kCondGT:
1479 if (cond == kCondLE) {
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001480 DCHECK_EQ(opposite, kCondGT);
Donghui Bai426b49c2016-11-08 14:55:38 +08001481 cond = kCondGE;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001482 opposite = kCondLT;
Donghui Bai426b49c2016-11-08 14:55:38 +08001483 } else {
1484 DCHECK_EQ(cond, kCondGT);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001485 DCHECK_EQ(opposite, kCondLE);
Donghui Bai426b49c2016-11-08 14:55:38 +08001486 cond = kCondLT;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001487 opposite = kCondGE;
Donghui Bai426b49c2016-11-08 14:55:38 +08001488 }
1489
1490 std::swap(left, right);
1491 FALLTHROUGH_INTENDED;
1492 case kCondGE:
1493 case kCondLT: {
1494 UseScratchRegisterScope temps(codegen->GetVIXLAssembler());
1495
1496 __ Cmp(LowRegisterFrom(left), LowRegisterFrom(right));
1497 __ Sbcs(temps.Acquire(), HighRegisterFrom(left), HighRegisterFrom(right));
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001498 ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite));
Donghui Bai426b49c2016-11-08 14:55:38 +08001499 break;
1500 }
1501 default:
1502 LOG(FATAL) << "Unreachable";
1503 UNREACHABLE();
1504 }
1505
1506 return ret;
1507}
1508
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001509static std::pair<vixl32::Condition, vixl32::Condition> GenerateTest(HCondition* condition,
1510 bool invert,
1511 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001512 const DataType::Type type = condition->GetLeft()->GetType();
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001513 IfCondition cond = condition->GetCondition();
1514 IfCondition opposite = condition->GetOppositeCondition();
1515 std::pair<vixl32::Condition, vixl32::Condition> ret(eq, ne);
Donghui Bai426b49c2016-11-08 14:55:38 +08001516
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001517 if (invert) {
1518 std::swap(cond, opposite);
1519 }
Donghui Bai426b49c2016-11-08 14:55:38 +08001520
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001521 if (type == DataType::Type::kInt64) {
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001522 ret = condition->GetLocations()->InAt(1).IsConstant()
1523 ? GenerateLongTestConstant(condition, invert, codegen)
1524 : GenerateLongTest(condition, invert, codegen);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001525 } else if (DataType::IsFloatingPointType(type)) {
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001526 GenerateVcmp(condition, codegen);
1527 __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
1528 ret = std::make_pair(ARMFPCondition(cond, condition->IsGtBias()),
1529 ARMFPCondition(opposite, condition->IsGtBias()));
Donghui Bai426b49c2016-11-08 14:55:38 +08001530 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001531 DCHECK(DataType::IsIntegralType(type) || type == DataType::Type::kReference) << type;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001532 __ Cmp(InputRegisterAt(condition, 0), InputOperandAt(condition, 1));
1533 ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite));
Donghui Bai426b49c2016-11-08 14:55:38 +08001534 }
1535
1536 return ret;
1537}
1538
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001539static void GenerateConditionGeneric(HCondition* cond, CodeGeneratorARMVIXL* codegen) {
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001540 const vixl32::Register out = OutputRegister(cond);
1541 const auto condition = GenerateTest(cond, false, codegen);
1542
1543 __ Mov(LeaveFlags, out, 0);
1544
1545 if (out.IsLow()) {
1546 // We use the scope because of the IT block that follows.
1547 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
1548 2 * vixl32::k16BitT32InstructionSizeInBytes,
1549 CodeBufferCheckScope::kExactSize);
1550
1551 __ it(condition.first);
1552 __ mov(condition.first, out, 1);
1553 } else {
1554 vixl32::Label done_label;
1555 vixl32::Label* const final_label = codegen->GetFinalLabel(cond, &done_label);
1556
Andreas Gampe3db70682018-12-26 15:12:03 -08001557 __ B(condition.second, final_label, /* is_far_target= */ false);
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001558 __ Mov(out, 1);
1559
1560 if (done_label.IsReferenced()) {
1561 __ Bind(&done_label);
1562 }
1563 }
1564}
1565
1566static void GenerateEqualLong(HCondition* cond, CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001567 DCHECK_EQ(cond->GetLeft()->GetType(), DataType::Type::kInt64);
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001568
1569 const LocationSummary* const locations = cond->GetLocations();
1570 IfCondition condition = cond->GetCondition();
1571 const vixl32::Register out = OutputRegister(cond);
1572 const Location left = locations->InAt(0);
1573 const Location right = locations->InAt(1);
1574 vixl32::Register left_high = HighRegisterFrom(left);
1575 vixl32::Register left_low = LowRegisterFrom(left);
1576 vixl32::Register temp;
1577 UseScratchRegisterScope temps(codegen->GetVIXLAssembler());
1578
1579 if (right.IsConstant()) {
1580 IfCondition opposite = cond->GetOppositeCondition();
1581 const int64_t value = AdjustConstantForCondition(Int64ConstantFrom(right),
1582 &condition,
1583 &opposite);
1584 Operand right_high = High32Bits(value);
1585 Operand right_low = Low32Bits(value);
1586
1587 // The output uses Location::kNoOutputOverlap.
1588 if (out.Is(left_high)) {
1589 std::swap(left_low, left_high);
1590 std::swap(right_low, right_high);
1591 }
1592
1593 __ Sub(out, left_low, right_low);
1594 temp = temps.Acquire();
1595 __ Sub(temp, left_high, right_high);
1596 } else {
1597 DCHECK(right.IsRegisterPair());
1598 temp = temps.Acquire();
1599 __ Sub(temp, left_high, HighRegisterFrom(right));
1600 __ Sub(out, left_low, LowRegisterFrom(right));
1601 }
1602
1603 // Need to check after calling AdjustConstantForCondition().
1604 DCHECK(condition == kCondEQ || condition == kCondNE) << condition;
1605
1606 if (condition == kCondNE && out.IsLow()) {
1607 __ Orrs(out, out, temp);
1608
1609 // We use the scope because of the IT block that follows.
1610 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
1611 2 * vixl32::k16BitT32InstructionSizeInBytes,
1612 CodeBufferCheckScope::kExactSize);
1613
1614 __ it(ne);
1615 __ mov(ne, out, 1);
1616 } else {
1617 __ Orr(out, out, temp);
1618 codegen->GenerateConditionWithZero(condition, out, out, temp);
1619 }
1620}
1621
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001622static void GenerateConditionLong(HCondition* cond, CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001623 DCHECK_EQ(cond->GetLeft()->GetType(), DataType::Type::kInt64);
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001624
1625 const LocationSummary* const locations = cond->GetLocations();
1626 IfCondition condition = cond->GetCondition();
1627 const vixl32::Register out = OutputRegister(cond);
1628 const Location left = locations->InAt(0);
1629 const Location right = locations->InAt(1);
1630
1631 if (right.IsConstant()) {
1632 IfCondition opposite = cond->GetOppositeCondition();
1633
1634 // Comparisons against 0 are common enough to deserve special attention.
1635 if (AdjustConstantForCondition(Int64ConstantFrom(right), &condition, &opposite) == 0) {
1636 switch (condition) {
1637 case kCondNE:
1638 case kCondA:
1639 if (out.IsLow()) {
1640 // We only care if both input registers are 0 or not.
1641 __ Orrs(out, LowRegisterFrom(left), HighRegisterFrom(left));
1642
1643 // We use the scope because of the IT block that follows.
1644 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
1645 2 * vixl32::k16BitT32InstructionSizeInBytes,
1646 CodeBufferCheckScope::kExactSize);
1647
1648 __ it(ne);
1649 __ mov(ne, out, 1);
1650 return;
1651 }
1652
1653 FALLTHROUGH_INTENDED;
1654 case kCondEQ:
1655 case kCondBE:
1656 // We only care if both input registers are 0 or not.
1657 __ Orr(out, LowRegisterFrom(left), HighRegisterFrom(left));
1658 codegen->GenerateConditionWithZero(condition, out, out);
1659 return;
1660 case kCondLT:
1661 case kCondGE:
1662 // We only care about the sign bit.
1663 FALLTHROUGH_INTENDED;
1664 case kCondAE:
1665 case kCondB:
1666 codegen->GenerateConditionWithZero(condition, out, HighRegisterFrom(left));
1667 return;
1668 case kCondLE:
1669 case kCondGT:
1670 default:
1671 break;
1672 }
1673 }
1674 }
1675
Anton Kirilov23b752b2017-07-20 14:40:44 +01001676 // If `out` is a low register, then the GenerateConditionGeneric()
1677 // function generates a shorter code sequence that is still branchless.
1678 if ((condition == kCondEQ || condition == kCondNE) && !out.IsLow()) {
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001679 GenerateEqualLong(cond, codegen);
1680 return;
1681 }
1682
Anton Kirilov23b752b2017-07-20 14:40:44 +01001683 GenerateConditionGeneric(cond, codegen);
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001684}
1685
Roland Levillain6d729a72017-06-30 18:34:01 +01001686static void GenerateConditionIntegralOrNonPrimitive(HCondition* cond,
1687 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001688 const DataType::Type type = cond->GetLeft()->GetType();
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001689
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001690 DCHECK(DataType::IsIntegralType(type) || type == DataType::Type::kReference) << type;
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001691
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001692 if (type == DataType::Type::kInt64) {
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001693 GenerateConditionLong(cond, codegen);
1694 return;
1695 }
1696
1697 IfCondition condition = cond->GetCondition();
1698 vixl32::Register in = InputRegisterAt(cond, 0);
1699 const vixl32::Register out = OutputRegister(cond);
1700 const Location right = cond->GetLocations()->InAt(1);
1701 int64_t value;
1702
1703 if (right.IsConstant()) {
1704 IfCondition opposite = cond->GetOppositeCondition();
1705
1706 value = AdjustConstantForCondition(Int64ConstantFrom(right), &condition, &opposite);
1707
1708 // Comparisons against 0 are common enough to deserve special attention.
1709 if (value == 0) {
1710 switch (condition) {
1711 case kCondNE:
1712 case kCondA:
1713 if (out.IsLow() && out.Is(in)) {
1714 __ Cmp(out, 0);
1715
1716 // We use the scope because of the IT block that follows.
1717 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
1718 2 * vixl32::k16BitT32InstructionSizeInBytes,
1719 CodeBufferCheckScope::kExactSize);
1720
1721 __ it(ne);
1722 __ mov(ne, out, 1);
1723 return;
1724 }
1725
1726 FALLTHROUGH_INTENDED;
1727 case kCondEQ:
1728 case kCondBE:
1729 case kCondLT:
1730 case kCondGE:
1731 case kCondAE:
1732 case kCondB:
1733 codegen->GenerateConditionWithZero(condition, out, in);
1734 return;
1735 case kCondLE:
1736 case kCondGT:
1737 default:
1738 break;
1739 }
1740 }
1741 }
1742
1743 if (condition == kCondEQ || condition == kCondNE) {
1744 Operand operand(0);
1745
1746 if (right.IsConstant()) {
1747 operand = Operand::From(value);
1748 } else if (out.Is(RegisterFrom(right))) {
1749 // Avoid 32-bit instructions if possible.
1750 operand = InputOperandAt(cond, 0);
1751 in = RegisterFrom(right);
1752 } else {
1753 operand = InputOperandAt(cond, 1);
1754 }
1755
1756 if (condition == kCondNE && out.IsLow()) {
1757 __ Subs(out, in, operand);
1758
1759 // We use the scope because of the IT block that follows.
1760 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
1761 2 * vixl32::k16BitT32InstructionSizeInBytes,
1762 CodeBufferCheckScope::kExactSize);
1763
1764 __ it(ne);
1765 __ mov(ne, out, 1);
1766 } else {
1767 __ Sub(out, in, operand);
1768 codegen->GenerateConditionWithZero(condition, out, out);
1769 }
1770
1771 return;
1772 }
1773
1774 GenerateConditionGeneric(cond, codegen);
1775}
1776
Donghui Bai426b49c2016-11-08 14:55:38 +08001777static bool CanEncodeConstantAs8BitImmediate(HConstant* constant) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001778 const DataType::Type type = constant->GetType();
Donghui Bai426b49c2016-11-08 14:55:38 +08001779 bool ret = false;
1780
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001781 DCHECK(DataType::IsIntegralType(type) || type == DataType::Type::kReference) << type;
Donghui Bai426b49c2016-11-08 14:55:38 +08001782
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001783 if (type == DataType::Type::kInt64) {
Donghui Bai426b49c2016-11-08 14:55:38 +08001784 const uint64_t value = Uint64ConstantFrom(constant);
1785
1786 ret = IsUint<8>(Low32Bits(value)) && IsUint<8>(High32Bits(value));
1787 } else {
1788 ret = IsUint<8>(Int32ConstantFrom(constant));
1789 }
1790
1791 return ret;
1792}
1793
1794static Location Arm8BitEncodableConstantOrRegister(HInstruction* constant) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001795 DCHECK(!DataType::IsFloatingPointType(constant->GetType()));
Donghui Bai426b49c2016-11-08 14:55:38 +08001796
1797 if (constant->IsConstant() && CanEncodeConstantAs8BitImmediate(constant->AsConstant())) {
1798 return Location::ConstantLocation(constant->AsConstant());
1799 }
1800
1801 return Location::RequiresRegister();
1802}
1803
1804static bool CanGenerateConditionalMove(const Location& out, const Location& src) {
1805 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
1806 // we check that we are not dealing with floating-point output (there is no
1807 // 16-bit VMOV encoding).
1808 if (!out.IsRegister() && !out.IsRegisterPair()) {
1809 return false;
1810 }
1811
1812 // For constants, we also check that the output is in one or two low registers,
1813 // and that the constants fit in an 8-bit unsigned integer, so that a 16-bit
1814 // MOV encoding can be used.
1815 if (src.IsConstant()) {
1816 if (!CanEncodeConstantAs8BitImmediate(src.GetConstant())) {
1817 return false;
1818 }
1819
1820 if (out.IsRegister()) {
1821 if (!RegisterFrom(out).IsLow()) {
1822 return false;
1823 }
1824 } else {
1825 DCHECK(out.IsRegisterPair());
1826
1827 if (!HighRegisterFrom(out).IsLow()) {
1828 return false;
1829 }
1830 }
1831 }
1832
1833 return true;
1834}
1835
Scott Wakelingfe885462016-09-22 10:24:38 +01001836#undef __
1837
Donghui Bai426b49c2016-11-08 14:55:38 +08001838vixl32::Label* CodeGeneratorARMVIXL::GetFinalLabel(HInstruction* instruction,
1839 vixl32::Label* final_label) {
1840 DCHECK(!instruction->IsControlFlow() && !instruction->IsSuspendCheck());
Anton Kirilov6f644202017-02-27 18:29:45 +00001841 DCHECK(!instruction->IsInvoke() || !instruction->GetLocations()->CanCall());
Donghui Bai426b49c2016-11-08 14:55:38 +08001842
1843 const HBasicBlock* const block = instruction->GetBlock();
1844 const HLoopInformation* const info = block->GetLoopInformation();
1845 HInstruction* const next = instruction->GetNext();
1846
1847 // Avoid a branch to a branch.
1848 if (next->IsGoto() && (info == nullptr ||
1849 !info->IsBackEdge(*block) ||
1850 !info->HasSuspendCheck())) {
1851 final_label = GetLabelOf(next->AsGoto()->GetSuccessor());
1852 }
1853
1854 return final_label;
1855}
1856
Scott Wakelingfe885462016-09-22 10:24:38 +01001857CodeGeneratorARMVIXL::CodeGeneratorARMVIXL(HGraph* graph,
Scott Wakelingfe885462016-09-22 10:24:38 +01001858 const CompilerOptions& compiler_options,
1859 OptimizingCompilerStats* stats)
1860 : CodeGenerator(graph,
1861 kNumberOfCoreRegisters,
1862 kNumberOfSRegisters,
1863 kNumberOfRegisterPairs,
1864 kCoreCalleeSaves.GetList(),
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001865 ComputeSRegisterListMask(kFpuCalleeSaves),
Scott Wakelingfe885462016-09-22 10:24:38 +01001866 compiler_options,
1867 stats),
Vladimir Markoca6fff82017-10-03 14:49:14 +01001868 block_labels_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1869 jump_tables_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Scott Wakelingfe885462016-09-22 10:24:38 +01001870 location_builder_(graph, this),
1871 instruction_visitor_(graph, this),
Vladimir Markoca6fff82017-10-03 14:49:14 +01001872 move_resolver_(graph->GetAllocator(), this),
1873 assembler_(graph->GetAllocator()),
Vladimir Marko59eb30f2018-02-20 11:52:34 +00001874 boot_image_method_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Markoca6fff82017-10-03 14:49:14 +01001875 method_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Marko59eb30f2018-02-20 11:52:34 +00001876 boot_image_type_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Markoca6fff82017-10-03 14:49:14 +01001877 type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Marko8f63f102020-09-28 12:10:28 +01001878 public_type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1879 package_type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Marko59eb30f2018-02-20 11:52:34 +00001880 boot_image_string_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Markoca6fff82017-10-03 14:49:14 +01001881 string_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Marko2d06e022019-07-08 15:45:19 +01001882 boot_image_other_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Markof6675082019-05-17 12:05:28 +01001883 call_entrypoint_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Markoca6fff82017-10-03 14:49:14 +01001884 baker_read_barrier_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Markof6675082019-05-17 12:05:28 +01001885 uint32_literals_(std::less<uint32_t>(),
1886 graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Artem Serovc5fcb442016-12-02 19:19:58 +00001887 jit_string_patches_(StringReferenceValueComparator(),
Vladimir Markoca6fff82017-10-03 14:49:14 +01001888 graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Artem Serovc5fcb442016-12-02 19:19:58 +00001889 jit_class_patches_(TypeReferenceValueComparator(),
Vladimir Marko966b46f2018-08-03 10:20:19 +00001890 graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1891 jit_baker_read_barrier_slow_paths_(std::less<uint32_t>(),
1892 graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)) {
Scott Wakelingfe885462016-09-22 10:24:38 +01001893 // Always save the LR register to mimic Quick.
1894 AddAllocatedRegister(Location::RegisterLocation(LR));
Nicolas Geoffray13a797b2017-03-15 16:41:31 +00001895 // Give D30 and D31 as scratch register to VIXL. The register allocator only works on
1896 // S0-S31, which alias to D0-D15.
1897 GetVIXLAssembler()->GetScratchVRegisterList()->Combine(d31);
1898 GetVIXLAssembler()->GetScratchVRegisterList()->Combine(d30);
Scott Wakelingfe885462016-09-22 10:24:38 +01001899}
1900
Artem Serov551b28f2016-10-18 19:11:30 +01001901void JumpTableARMVIXL::EmitTable(CodeGeneratorARMVIXL* codegen) {
1902 uint32_t num_entries = switch_instr_->GetNumEntries();
1903 DCHECK_GE(num_entries, kPackedSwitchCompareJumpThreshold);
1904
1905 // We are about to use the assembler to place literals directly. Make sure we have enough
Scott Wakelingb77051e2016-11-21 19:46:00 +00001906 // underlying code buffer and we have generated a jump table of the right size, using
1907 // codegen->GetVIXLAssembler()->GetBuffer().Align();
Artem Serov0fb37192016-12-06 18:13:40 +00001908 ExactAssemblyScope aas(codegen->GetVIXLAssembler(),
1909 num_entries * sizeof(int32_t),
1910 CodeBufferCheckScope::kMaximumSize);
Artem Serov551b28f2016-10-18 19:11:30 +01001911 // TODO(VIXL): Check that using lower case bind is fine here.
1912 codegen->GetVIXLAssembler()->bind(&table_start_);
Artem Serov09a940d2016-11-11 16:15:11 +00001913 for (uint32_t i = 0; i < num_entries; i++) {
1914 codegen->GetVIXLAssembler()->place(bb_addresses_[i].get());
1915 }
1916}
1917
1918void JumpTableARMVIXL::FixTable(CodeGeneratorARMVIXL* codegen) {
1919 uint32_t num_entries = switch_instr_->GetNumEntries();
1920 DCHECK_GE(num_entries, kPackedSwitchCompareJumpThreshold);
1921
Artem Serov551b28f2016-10-18 19:11:30 +01001922 const ArenaVector<HBasicBlock*>& successors = switch_instr_->GetBlock()->GetSuccessors();
1923 for (uint32_t i = 0; i < num_entries; i++) {
1924 vixl32::Label* target_label = codegen->GetLabelOf(successors[i]);
1925 DCHECK(target_label->IsBound());
1926 int32_t jump_offset = target_label->GetLocation() - table_start_.GetLocation();
1927 // When doing BX to address we need to have lower bit set to 1 in T32.
1928 if (codegen->GetVIXLAssembler()->IsUsingT32()) {
1929 jump_offset++;
1930 }
1931 DCHECK_GT(jump_offset, std::numeric_limits<int32_t>::min());
1932 DCHECK_LE(jump_offset, std::numeric_limits<int32_t>::max());
Artem Serov09a940d2016-11-11 16:15:11 +00001933
Scott Wakelingb77051e2016-11-21 19:46:00 +00001934 bb_addresses_[i].get()->UpdateValue(jump_offset, codegen->GetVIXLAssembler()->GetBuffer());
Artem Serov551b28f2016-10-18 19:11:30 +01001935 }
1936}
1937
Artem Serov09a940d2016-11-11 16:15:11 +00001938void CodeGeneratorARMVIXL::FixJumpTables() {
Artem Serov551b28f2016-10-18 19:11:30 +01001939 for (auto&& jump_table : jump_tables_) {
Artem Serov09a940d2016-11-11 16:15:11 +00001940 jump_table->FixTable(this);
Artem Serov551b28f2016-10-18 19:11:30 +01001941 }
1942}
1943
Andreas Gampeca620d72016-11-08 08:09:33 -08001944#define __ reinterpret_cast<ArmVIXLAssembler*>(GetAssembler())->GetVIXLAssembler()-> // NOLINT
Scott Wakelingfe885462016-09-22 10:24:38 +01001945
1946void CodeGeneratorARMVIXL::Finalize(CodeAllocator* allocator) {
Artem Serov09a940d2016-11-11 16:15:11 +00001947 FixJumpTables();
Vladimir Marko966b46f2018-08-03 10:20:19 +00001948
1949 // Emit JIT baker read barrier slow paths.
Vladimir Marko695348f2020-05-19 14:42:02 +01001950 DCHECK(GetCompilerOptions().IsJitCompiler() || jit_baker_read_barrier_slow_paths_.empty());
Vladimir Marko966b46f2018-08-03 10:20:19 +00001951 for (auto& entry : jit_baker_read_barrier_slow_paths_) {
1952 uint32_t encoded_data = entry.first;
1953 vixl::aarch32::Label* slow_path_entry = &entry.second.label;
1954 __ Bind(slow_path_entry);
Andreas Gampe3db70682018-12-26 15:12:03 -08001955 CompileBakerReadBarrierThunk(*GetAssembler(), encoded_data, /* debug_name= */ nullptr);
Vladimir Marko966b46f2018-08-03 10:20:19 +00001956 }
1957
Scott Wakelingfe885462016-09-22 10:24:38 +01001958 GetAssembler()->FinalizeCode();
1959 CodeGenerator::Finalize(allocator);
Vladimir Markoca1e0382018-04-11 09:58:41 +00001960
1961 // Verify Baker read barrier linker patches.
1962 if (kIsDebugBuild) {
1963 ArrayRef<const uint8_t> code = allocator->GetMemory();
1964 for (const BakerReadBarrierPatchInfo& info : baker_read_barrier_patches_) {
1965 DCHECK(info.label.IsBound());
1966 uint32_t literal_offset = info.label.GetLocation();
1967 DCHECK_ALIGNED(literal_offset, 2u);
1968
1969 auto GetInsn16 = [&code](uint32_t offset) {
1970 DCHECK_ALIGNED(offset, 2u);
1971 return (static_cast<uint32_t>(code[offset + 0]) << 0) +
1972 (static_cast<uint32_t>(code[offset + 1]) << 8);
1973 };
1974 auto GetInsn32 = [=](uint32_t offset) {
1975 return (GetInsn16(offset) << 16) + (GetInsn16(offset + 2u) << 0);
1976 };
1977
1978 uint32_t encoded_data = info.custom_data;
1979 BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data);
1980 // Check that the next instruction matches the expected LDR.
1981 switch (kind) {
1982 case BakerReadBarrierKind::kField: {
1983 BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data);
1984 if (width == BakerReadBarrierWidth::kWide) {
1985 DCHECK_GE(code.size() - literal_offset, 8u);
1986 uint32_t next_insn = GetInsn32(literal_offset + 4u);
1987 // LDR (immediate), encoding T3, with correct base_reg.
1988 CheckValidReg((next_insn >> 12) & 0xfu); // Check destination register.
1989 const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
1990 CHECK_EQ(next_insn & 0xffff0000u, 0xf8d00000u | (base_reg << 16));
1991 } else {
1992 DCHECK_GE(code.size() - literal_offset, 6u);
1993 uint32_t next_insn = GetInsn16(literal_offset + 4u);
1994 // LDR (immediate), encoding T1, with correct base_reg.
1995 CheckValidReg(next_insn & 0x7u); // Check destination register.
1996 const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
1997 CHECK_EQ(next_insn & 0xf838u, 0x6800u | (base_reg << 3));
1998 }
1999 break;
2000 }
2001 case BakerReadBarrierKind::kArray: {
2002 DCHECK_GE(code.size() - literal_offset, 8u);
2003 uint32_t next_insn = GetInsn32(literal_offset + 4u);
2004 // LDR (register) with correct base_reg, S=1 and option=011 (LDR Wt, [Xn, Xm, LSL #2]).
2005 CheckValidReg((next_insn >> 12) & 0xfu); // Check destination register.
2006 const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
2007 CHECK_EQ(next_insn & 0xffff0ff0u, 0xf8500020u | (base_reg << 16));
2008 CheckValidReg(next_insn & 0xf); // Check index register
2009 break;
2010 }
2011 case BakerReadBarrierKind::kGcRoot: {
2012 BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data);
2013 if (width == BakerReadBarrierWidth::kWide) {
2014 DCHECK_GE(literal_offset, 4u);
2015 uint32_t prev_insn = GetInsn32(literal_offset - 4u);
Vladimir Markof28be432018-08-14 12:20:51 +00002016 // LDR (immediate), encoding T3, with correct root_reg.
Vladimir Markoca1e0382018-04-11 09:58:41 +00002017 const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
Vladimir Markof28be432018-08-14 12:20:51 +00002018 CHECK_EQ(prev_insn & 0xfff0f000u, 0xf8d00000u | (root_reg << 12));
Vladimir Markoca1e0382018-04-11 09:58:41 +00002019 } else {
2020 DCHECK_GE(literal_offset, 2u);
2021 uint32_t prev_insn = GetInsn16(literal_offset - 2u);
2022 // LDR (immediate), encoding T1, with correct root_reg.
2023 const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
2024 CHECK_EQ(prev_insn & 0xf807u, 0x6800u | root_reg);
2025 }
2026 break;
2027 }
Vladimir Markod887ed82018-08-14 13:52:12 +00002028 case BakerReadBarrierKind::kUnsafeCas: {
2029 DCHECK_GE(literal_offset, 4u);
2030 uint32_t prev_insn = GetInsn32(literal_offset - 4u);
2031 // ADD (register), encoding T3, with correct root_reg.
2032 const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
2033 CHECK_EQ(prev_insn & 0xfff0fff0u, 0xeb000000u | (root_reg << 8));
2034 break;
2035 }
Vladimir Markoca1e0382018-04-11 09:58:41 +00002036 default:
2037 LOG(FATAL) << "Unexpected kind: " << static_cast<uint32_t>(kind);
2038 UNREACHABLE();
2039 }
2040 }
2041 }
Scott Wakelingfe885462016-09-22 10:24:38 +01002042}
2043
2044void CodeGeneratorARMVIXL::SetupBlockedRegisters() const {
Scott Wakelingfe885462016-09-22 10:24:38 +01002045 // Stack register, LR and PC are always reserved.
2046 blocked_core_registers_[SP] = true;
2047 blocked_core_registers_[LR] = true;
2048 blocked_core_registers_[PC] = true;
2049
Roland Levillain6d729a72017-06-30 18:34:01 +01002050 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2051 // Reserve marking register.
2052 blocked_core_registers_[MR] = true;
2053 }
2054
Scott Wakelingfe885462016-09-22 10:24:38 +01002055 // Reserve thread register.
2056 blocked_core_registers_[TR] = true;
2057
2058 // Reserve temp register.
2059 blocked_core_registers_[IP] = true;
2060
2061 if (GetGraph()->IsDebuggable()) {
2062 // Stubs do not save callee-save floating point registers. If the graph
2063 // is debuggable, we need to deal with these registers differently. For
2064 // now, just block them.
2065 for (uint32_t i = kFpuCalleeSaves.GetFirstSRegister().GetCode();
2066 i <= kFpuCalleeSaves.GetLastSRegister().GetCode();
2067 ++i) {
2068 blocked_fpu_registers_[i] = true;
2069 }
2070 }
Scott Wakelingfe885462016-09-22 10:24:38 +01002071}
2072
Scott Wakelingfe885462016-09-22 10:24:38 +01002073InstructionCodeGeneratorARMVIXL::InstructionCodeGeneratorARMVIXL(HGraph* graph,
2074 CodeGeneratorARMVIXL* codegen)
2075 : InstructionCodeGenerator(graph, codegen),
2076 assembler_(codegen->GetAssembler()),
2077 codegen_(codegen) {}
2078
2079void CodeGeneratorARMVIXL::ComputeSpillMask() {
2080 core_spill_mask_ = allocated_registers_.GetCoreRegisters() & core_callee_save_mask_;
Vladimir Marko460f0542019-07-04 14:02:08 +01002081 DCHECK_NE(core_spill_mask_ & (1u << kLrCode), 0u)
2082 << "At least the return address register must be saved";
2083 // 16-bit PUSH/POP (T1) can save/restore just the LR/PC.
2084 DCHECK(GetVIXLAssembler()->IsUsingT32());
Scott Wakelingfe885462016-09-22 10:24:38 +01002085 fpu_spill_mask_ = allocated_registers_.GetFloatingPointRegisters() & fpu_callee_save_mask_;
2086 // We use vpush and vpop for saving and restoring floating point registers, which take
2087 // a SRegister and the number of registers to save/restore after that SRegister. We
2088 // therefore update the `fpu_spill_mask_` to also contain those registers not allocated,
2089 // but in the range.
2090 if (fpu_spill_mask_ != 0) {
2091 uint32_t least_significant_bit = LeastSignificantBit(fpu_spill_mask_);
2092 uint32_t most_significant_bit = MostSignificantBit(fpu_spill_mask_);
2093 for (uint32_t i = least_significant_bit + 1 ; i < most_significant_bit; ++i) {
2094 fpu_spill_mask_ |= (1 << i);
2095 }
2096 }
2097}
2098
Nicolas Geoffraya59af8a2019-11-27 17:42:32 +00002099void CodeGeneratorARMVIXL::MaybeIncrementHotness(bool is_frame_entry) {
2100 if (GetCompilerOptions().CountHotnessInCompiledCode()) {
2101 UseScratchRegisterScope temps(GetVIXLAssembler());
2102 vixl32::Register temp = temps.Acquire();
2103 static_assert(ArtMethod::MaxCounter() == 0xFFFF, "asm is probably wrong");
2104 if (!is_frame_entry) {
2105 __ Push(vixl32::Register(kMethodRegister));
Vladimir Markodec78172020-06-19 15:31:23 +01002106 GetAssembler()->cfi().AdjustCFAOffset(kArmWordSize);
Nicolas Geoffraya59af8a2019-11-27 17:42:32 +00002107 GetAssembler()->LoadFromOffset(kLoadWord, kMethodRegister, sp, kArmWordSize);
2108 }
2109 // Load with zero extend to clear the high bits for integer overflow check.
2110 __ Ldrh(temp, MemOperand(kMethodRegister, ArtMethod::HotnessCountOffset().Int32Value()));
2111 __ Add(temp, temp, 1);
2112 // Subtract one if the counter would overflow.
2113 __ Sub(temp, temp, Operand(temp, ShiftType::LSR, 16));
2114 __ Strh(temp, MemOperand(kMethodRegister, ArtMethod::HotnessCountOffset().Int32Value()));
2115 if (!is_frame_entry) {
2116 __ Pop(vixl32::Register(kMethodRegister));
Vladimir Markodec78172020-06-19 15:31:23 +01002117 GetAssembler()->cfi().AdjustCFAOffset(-static_cast<int>(kArmWordSize));
Nicolas Geoffraya59af8a2019-11-27 17:42:32 +00002118 }
2119 }
2120
2121 if (GetGraph()->IsCompilingBaseline() && !Runtime::Current()->IsAotCompiler()) {
Nicolas Geoffray095dc462020-08-17 16:40:28 +01002122 ScopedProfilingInfoUse spiu(
2123 Runtime::Current()->GetJit(), GetGraph()->GetArtMethod(), Thread::Current());
2124 ProfilingInfo* info = spiu.GetProfilingInfo();
Nicolas Geoffray796aa2c2019-12-17 10:20:05 +00002125 if (info != nullptr) {
2126 uint32_t address = reinterpret_cast32<uint32_t>(info);
2127 vixl::aarch32::Label done;
2128 UseScratchRegisterScope temps(GetVIXLAssembler());
2129 temps.Exclude(ip);
2130 if (!is_frame_entry) {
2131 __ Push(r4); // Will be used as temporary. For frame entry, r4 is always available.
Vladimir Markodec78172020-06-19 15:31:23 +01002132 GetAssembler()->cfi().AdjustCFAOffset(kArmWordSize);
Nicolas Geoffray796aa2c2019-12-17 10:20:05 +00002133 }
2134 __ Mov(r4, address);
2135 __ Ldrh(ip, MemOperand(r4, ProfilingInfo::BaselineHotnessCountOffset().Int32Value()));
2136 __ Add(ip, ip, 1);
Nicolas Geoffray8b8d93d2020-09-17 14:30:01 +01002137 instruction_visitor_.GenerateAndConst(ip, ip, interpreter::kTieredHotnessMask);
Nicolas Geoffray796aa2c2019-12-17 10:20:05 +00002138 __ Strh(ip, MemOperand(r4, ProfilingInfo::BaselineHotnessCountOffset().Int32Value()));
2139 if (!is_frame_entry) {
2140 __ Pop(r4);
Vladimir Markodec78172020-06-19 15:31:23 +01002141 GetAssembler()->cfi().AdjustCFAOffset(-static_cast<int>(kArmWordSize));
Nicolas Geoffray796aa2c2019-12-17 10:20:05 +00002142 }
2143 __ Lsls(ip, ip, 16);
2144 __ B(ne, &done);
2145 uint32_t entry_point_offset =
2146 GetThreadOffset<kArmPointerSize>(kQuickCompileOptimized).Int32Value();
2147 if (HasEmptyFrame()) {
2148 CHECK(is_frame_entry);
2149 // For leaf methods, we need to spill lr and r0. Also spill r1 and r2 for
2150 // alignment.
2151 uint32_t core_spill_mask =
2152 (1 << lr.GetCode()) | (1 << r0.GetCode()) | (1 << r1.GetCode()) | (1 << r2.GetCode());
2153 __ Push(RegisterList(core_spill_mask));
Vladimir Markodec78172020-06-19 15:31:23 +01002154 GetAssembler()->cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(core_spill_mask));
Nicolas Geoffray796aa2c2019-12-17 10:20:05 +00002155 __ Ldr(lr, MemOperand(tr, entry_point_offset));
2156 __ Blx(lr);
2157 __ Pop(RegisterList(core_spill_mask));
Vladimir Markodec78172020-06-19 15:31:23 +01002158 GetAssembler()->cfi().AdjustCFAOffset(
2159 -static_cast<int>(kArmWordSize) * POPCOUNT(core_spill_mask));
Nicolas Geoffray796aa2c2019-12-17 10:20:05 +00002160 } else {
2161 if (!RequiresCurrentMethod()) {
2162 CHECK(is_frame_entry);
2163 GetAssembler()->StoreToOffset(kStoreWord, kMethodRegister, sp, 0);
2164 }
Nicolas Geoffraya59af8a2019-11-27 17:42:32 +00002165 __ Ldr(lr, MemOperand(tr, entry_point_offset));
2166 __ Blx(lr);
Nicolas Geoffraya59af8a2019-11-27 17:42:32 +00002167 }
Nicolas Geoffray796aa2c2019-12-17 10:20:05 +00002168 __ Bind(&done);
Nicolas Geoffraya59af8a2019-11-27 17:42:32 +00002169 }
Nicolas Geoffraya59af8a2019-11-27 17:42:32 +00002170 }
2171}
2172
Scott Wakelingfe885462016-09-22 10:24:38 +01002173void CodeGeneratorARMVIXL::GenerateFrameEntry() {
2174 bool skip_overflow_check =
2175 IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kArm);
2176 DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks());
2177 __ Bind(&frame_entry_label_);
2178
2179 if (HasEmptyFrame()) {
David Srbecky30021842019-02-13 14:19:36 +00002180 // Ensure that the CFI opcode list is not empty.
2181 GetAssembler()->cfi().Nop();
Nicolas Geoffraya59af8a2019-11-27 17:42:32 +00002182 MaybeIncrementHotness(/* is_frame_entry= */ true);
Scott Wakelingfe885462016-09-22 10:24:38 +01002183 return;
2184 }
2185
Scott Wakelingfe885462016-09-22 10:24:38 +01002186 if (!skip_overflow_check) {
xueliang.zhong10049552018-01-31 17:10:36 +00002187 // Using r4 instead of IP saves 2 bytes.
Nicolas Geoffray1a4f3ca2018-01-25 14:07:15 +00002188 UseScratchRegisterScope temps(GetVIXLAssembler());
xueliang.zhong10049552018-01-31 17:10:36 +00002189 vixl32::Register temp;
2190 // TODO: Remove this check when R4 is made a callee-save register
2191 // in ART compiled code (b/72801708). Currently we need to make
2192 // sure r4 is not blocked, e.g. in special purpose
2193 // TestCodeGeneratorARMVIXL; also asserting that r4 is available
2194 // here.
2195 if (!blocked_core_registers_[R4]) {
2196 for (vixl32::Register reg : kParameterCoreRegistersVIXL) {
2197 DCHECK(!reg.Is(r4));
2198 }
2199 DCHECK(!kCoreCalleeSaves.Includes(r4));
2200 temp = r4;
2201 } else {
2202 temp = temps.Acquire();
2203 }
Vladimir Marko33bff252017-11-01 14:35:42 +00002204 __ Sub(temp, sp, Operand::From(GetStackOverflowReservedBytes(InstructionSet::kArm)));
Scott Wakelingfe885462016-09-22 10:24:38 +01002205 // The load must immediately precede RecordPcInfo.
Artem Serov0fb37192016-12-06 18:13:40 +00002206 ExactAssemblyScope aas(GetVIXLAssembler(),
2207 vixl32::kMaxInstructionSizeInBytes,
2208 CodeBufferCheckScope::kMaximumSize);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002209 __ ldr(temp, MemOperand(temp));
2210 RecordPcInfo(nullptr, 0);
Scott Wakelingfe885462016-09-22 10:24:38 +01002211 }
2212
Vladimir Marko1a225a72019-07-05 13:37:42 +01002213 uint32_t frame_size = GetFrameSize();
2214 uint32_t core_spills_offset = frame_size - GetCoreSpillSize();
2215 uint32_t fp_spills_offset = frame_size - FrameEntrySpillSize();
2216 if ((fpu_spill_mask_ == 0u || IsPowerOfTwo(fpu_spill_mask_)) &&
2217 core_spills_offset <= 3u * kArmWordSize) {
2218 // Do a single PUSH for core registers including the method and up to two
2219 // filler registers. Then store the single FP spill if any.
2220 // (The worst case is when the method is not required and we actually
2221 // store 3 extra registers but they are stored in the same properly
2222 // aligned 16-byte chunk where we're already writing anyway.)
2223 DCHECK_EQ(kMethodRegister.GetCode(), 0u);
2224 uint32_t extra_regs = MaxInt<uint32_t>(core_spills_offset / kArmWordSize);
2225 DCHECK_LT(MostSignificantBit(extra_regs), LeastSignificantBit(core_spill_mask_));
2226 __ Push(RegisterList(core_spill_mask_ | extra_regs));
2227 GetAssembler()->cfi().AdjustCFAOffset(frame_size);
2228 GetAssembler()->cfi().RelOffsetForMany(DWARFReg(kMethodRegister),
2229 core_spills_offset,
2230 core_spill_mask_,
2231 kArmWordSize);
2232 if (fpu_spill_mask_ != 0u) {
2233 DCHECK(IsPowerOfTwo(fpu_spill_mask_));
2234 vixl::aarch32::SRegister sreg(LeastSignificantBit(fpu_spill_mask_));
2235 GetAssembler()->StoreSToOffset(sreg, sp, fp_spills_offset);
2236 GetAssembler()->cfi().RelOffset(DWARFReg(sreg), /*offset=*/ fp_spills_offset);
2237 }
2238 } else {
2239 __ Push(RegisterList(core_spill_mask_));
2240 GetAssembler()->cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(core_spill_mask_));
2241 GetAssembler()->cfi().RelOffsetForMany(DWARFReg(kMethodRegister),
2242 /*offset=*/ 0,
2243 core_spill_mask_,
2244 kArmWordSize);
2245 if (fpu_spill_mask_ != 0) {
2246 uint32_t first = LeastSignificantBit(fpu_spill_mask_);
Scott Wakelingfe885462016-09-22 10:24:38 +01002247
Vladimir Marko1a225a72019-07-05 13:37:42 +01002248 // Check that list is contiguous.
2249 DCHECK_EQ(fpu_spill_mask_ >> CTZ(fpu_spill_mask_), ~0u >> (32 - POPCOUNT(fpu_spill_mask_)));
Scott Wakelingfe885462016-09-22 10:24:38 +01002250
Vladimir Marko1a225a72019-07-05 13:37:42 +01002251 __ Vpush(SRegisterList(vixl32::SRegister(first), POPCOUNT(fpu_spill_mask_)));
2252 GetAssembler()->cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(fpu_spill_mask_));
2253 GetAssembler()->cfi().RelOffsetForMany(DWARFReg(s0),
2254 /*offset=*/ 0,
2255 fpu_spill_mask_,
2256 kArmWordSize);
2257 }
Scott Wakelingbffdc702016-12-07 17:46:03 +00002258
Vladimir Marko1a225a72019-07-05 13:37:42 +01002259 // Adjust SP and save the current method if we need it. Note that we do
2260 // not save the method in HCurrentMethod, as the instruction might have
2261 // been removed in the SSA graph.
2262 if (RequiresCurrentMethod() && fp_spills_offset <= 3 * kArmWordSize) {
2263 DCHECK_EQ(kMethodRegister.GetCode(), 0u);
2264 __ Push(RegisterList(MaxInt<uint32_t>(fp_spills_offset / kArmWordSize)));
2265 GetAssembler()->cfi().AdjustCFAOffset(fp_spills_offset);
2266 } else {
Vladimir Markodec78172020-06-19 15:31:23 +01002267 IncreaseFrame(fp_spills_offset);
Vladimir Marko1a225a72019-07-05 13:37:42 +01002268 if (RequiresCurrentMethod()) {
2269 GetAssembler()->StoreToOffset(kStoreWord, kMethodRegister, sp, 0);
2270 }
2271 }
Scott Wakelingbffdc702016-12-07 17:46:03 +00002272 }
Nicolas Geoffrayf7893532017-06-15 12:34:36 +01002273
2274 if (GetGraph()->HasShouldDeoptimizeFlag()) {
2275 UseScratchRegisterScope temps(GetVIXLAssembler());
2276 vixl32::Register temp = temps.Acquire();
2277 // Initialize should_deoptimize flag to 0.
2278 __ Mov(temp, 0);
2279 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, GetStackOffsetOfShouldDeoptimizeFlag());
2280 }
Roland Levillain5daa4952017-07-03 17:23:56 +01002281
Nicolas Geoffraya59af8a2019-11-27 17:42:32 +00002282 MaybeIncrementHotness(/* is_frame_entry= */ true);
Andreas Gampe3db70682018-12-26 15:12:03 -08002283 MaybeGenerateMarkingRegisterCheck(/* code= */ 1);
Scott Wakelingfe885462016-09-22 10:24:38 +01002284}
2285
2286void CodeGeneratorARMVIXL::GenerateFrameExit() {
2287 if (HasEmptyFrame()) {
2288 __ Bx(lr);
2289 return;
2290 }
Scott Wakelingfe885462016-09-22 10:24:38 +01002291
Scott Wakelingfe885462016-09-22 10:24:38 +01002292 // Pop LR into PC to return.
2293 DCHECK_NE(core_spill_mask_ & (1 << kLrCode), 0U);
2294 uint32_t pop_mask = (core_spill_mask_ & (~(1 << kLrCode))) | 1 << kPcCode;
Vladimir Marko1a225a72019-07-05 13:37:42 +01002295
2296 uint32_t frame_size = GetFrameSize();
2297 uint32_t core_spills_offset = frame_size - GetCoreSpillSize();
2298 uint32_t fp_spills_offset = frame_size - FrameEntrySpillSize();
2299 if ((fpu_spill_mask_ == 0u || IsPowerOfTwo(fpu_spill_mask_)) &&
2300 // r4 is blocked by TestCodeGeneratorARMVIXL used by some tests.
2301 core_spills_offset <= (blocked_core_registers_[r4.GetCode()] ? 2u : 3u) * kArmWordSize) {
2302 // Load the FP spill if any and then do a single POP including the method
2303 // and up to two filler registers. If we have no FP spills, this also has
2304 // the advantage that we do not need to emit CFI directives.
2305 if (fpu_spill_mask_ != 0u) {
2306 DCHECK(IsPowerOfTwo(fpu_spill_mask_));
2307 vixl::aarch32::SRegister sreg(LeastSignificantBit(fpu_spill_mask_));
2308 GetAssembler()->cfi().RememberState();
2309 GetAssembler()->LoadSFromOffset(sreg, sp, fp_spills_offset);
2310 GetAssembler()->cfi().Restore(DWARFReg(sreg));
2311 }
2312 // Clobber registers r2-r4 as they are caller-save in ART managed ABI and
2313 // never hold the return value.
2314 uint32_t extra_regs = MaxInt<uint32_t>(core_spills_offset / kArmWordSize) << r2.GetCode();
2315 DCHECK_EQ(extra_regs & kCoreCalleeSaves.GetList(), 0u);
2316 DCHECK_LT(MostSignificantBit(extra_regs), LeastSignificantBit(pop_mask));
2317 __ Pop(RegisterList(pop_mask | extra_regs));
2318 if (fpu_spill_mask_ != 0u) {
2319 GetAssembler()->cfi().RestoreState();
2320 }
2321 } else {
2322 GetAssembler()->cfi().RememberState();
Vladimir Markodec78172020-06-19 15:31:23 +01002323 DecreaseFrame(fp_spills_offset);
Vladimir Marko1a225a72019-07-05 13:37:42 +01002324 if (fpu_spill_mask_ != 0) {
2325 uint32_t first = LeastSignificantBit(fpu_spill_mask_);
2326
2327 // Check that list is contiguous.
2328 DCHECK_EQ(fpu_spill_mask_ >> CTZ(fpu_spill_mask_), ~0u >> (32 - POPCOUNT(fpu_spill_mask_)));
2329
2330 __ Vpop(SRegisterList(vixl32::SRegister(first), POPCOUNT(fpu_spill_mask_)));
2331 GetAssembler()->cfi().AdjustCFAOffset(
2332 -static_cast<int>(kArmWordSize) * POPCOUNT(fpu_spill_mask_));
2333 GetAssembler()->cfi().RestoreMany(DWARFReg(vixl32::SRegister(0)), fpu_spill_mask_);
2334 }
2335 __ Pop(RegisterList(pop_mask));
2336 GetAssembler()->cfi().RestoreState();
2337 GetAssembler()->cfi().DefCFAOffset(GetFrameSize());
2338 }
Scott Wakelingfe885462016-09-22 10:24:38 +01002339}
2340
2341void CodeGeneratorARMVIXL::Bind(HBasicBlock* block) {
2342 __ Bind(GetLabelOf(block));
2343}
2344
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002345Location InvokeDexCallingConventionVisitorARMVIXL::GetNextLocation(DataType::Type type) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002346 switch (type) {
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01002347 case DataType::Type::kReference:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002348 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01002349 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002350 case DataType::Type::kInt8:
2351 case DataType::Type::kUint16:
2352 case DataType::Type::kInt16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01002353 case DataType::Type::kInt32: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002354 uint32_t index = gp_index_++;
2355 uint32_t stack_index = stack_index_++;
2356 if (index < calling_convention.GetNumberOfRegisters()) {
2357 return LocationFrom(calling_convention.GetRegisterAt(index));
2358 } else {
2359 return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index));
2360 }
2361 }
2362
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002363 case DataType::Type::kInt64: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002364 uint32_t index = gp_index_;
2365 uint32_t stack_index = stack_index_;
2366 gp_index_ += 2;
2367 stack_index_ += 2;
2368 if (index + 1 < calling_convention.GetNumberOfRegisters()) {
2369 if (calling_convention.GetRegisterAt(index).Is(r1)) {
2370 // Skip R1, and use R2_R3 instead.
2371 gp_index_++;
2372 index++;
2373 }
2374 }
2375 if (index + 1 < calling_convention.GetNumberOfRegisters()) {
2376 DCHECK_EQ(calling_convention.GetRegisterAt(index).GetCode() + 1,
2377 calling_convention.GetRegisterAt(index + 1).GetCode());
2378
2379 return LocationFrom(calling_convention.GetRegisterAt(index),
2380 calling_convention.GetRegisterAt(index + 1));
2381 } else {
2382 return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index));
2383 }
2384 }
2385
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002386 case DataType::Type::kFloat32: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002387 uint32_t stack_index = stack_index_++;
2388 if (float_index_ % 2 == 0) {
2389 float_index_ = std::max(double_index_, float_index_);
2390 }
2391 if (float_index_ < calling_convention.GetNumberOfFpuRegisters()) {
2392 return LocationFrom(calling_convention.GetFpuRegisterAt(float_index_++));
2393 } else {
2394 return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index));
2395 }
2396 }
2397
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002398 case DataType::Type::kFloat64: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002399 double_index_ = std::max(double_index_, RoundUp(float_index_, 2));
2400 uint32_t stack_index = stack_index_;
2401 stack_index_ += 2;
2402 if (double_index_ + 1 < calling_convention.GetNumberOfFpuRegisters()) {
2403 uint32_t index = double_index_;
2404 double_index_ += 2;
2405 Location result = LocationFrom(
2406 calling_convention.GetFpuRegisterAt(index),
2407 calling_convention.GetFpuRegisterAt(index + 1));
2408 DCHECK(ExpectedPairLayout(result));
2409 return result;
2410 } else {
2411 return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index));
2412 }
2413 }
2414
Aart Bik66c158e2018-01-31 12:55:04 -08002415 case DataType::Type::kUint32:
2416 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002417 case DataType::Type::kVoid:
Artem Serovd4cc5b22016-11-04 11:19:09 +00002418 LOG(FATAL) << "Unexpected parameter type " << type;
Elliott Hughesc1896c92018-11-29 11:33:18 -08002419 UNREACHABLE();
Artem Serovd4cc5b22016-11-04 11:19:09 +00002420 }
2421 return Location::NoLocation();
2422}
2423
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002424Location InvokeDexCallingConventionVisitorARMVIXL::GetReturnLocation(DataType::Type type) const {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002425 switch (type) {
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01002426 case DataType::Type::kReference:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002427 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01002428 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002429 case DataType::Type::kInt8:
2430 case DataType::Type::kUint16:
2431 case DataType::Type::kInt16:
Aart Bik66c158e2018-01-31 12:55:04 -08002432 case DataType::Type::kUint32:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01002433 case DataType::Type::kInt32: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002434 return LocationFrom(r0);
2435 }
2436
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002437 case DataType::Type::kFloat32: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002438 return LocationFrom(s0);
2439 }
2440
Aart Bik66c158e2018-01-31 12:55:04 -08002441 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002442 case DataType::Type::kInt64: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002443 return LocationFrom(r0, r1);
2444 }
2445
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002446 case DataType::Type::kFloat64: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002447 return LocationFrom(s0, s1);
2448 }
2449
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002450 case DataType::Type::kVoid:
Artem Serovd4cc5b22016-11-04 11:19:09 +00002451 return Location::NoLocation();
2452 }
2453
2454 UNREACHABLE();
2455}
2456
2457Location InvokeDexCallingConventionVisitorARMVIXL::GetMethodLocation() const {
2458 return LocationFrom(kMethodRegister);
2459}
2460
Vladimir Marko86c87522020-05-11 16:55:55 +01002461Location CriticalNativeCallingConventionVisitorARMVIXL::GetNextLocation(DataType::Type type) {
2462 DCHECK_NE(type, DataType::Type::kReference);
2463
2464 // Native ABI uses the same registers as managed, except that the method register r0
2465 // is a normal argument.
2466 Location location = Location::NoLocation();
2467 if (DataType::Is64BitType(type)) {
2468 gpr_index_ = RoundUp(gpr_index_, 2u);
2469 stack_offset_ = RoundUp(stack_offset_, 2 * kFramePointerSize);
2470 if (gpr_index_ < 1u + kParameterCoreRegistersLengthVIXL) {
2471 location = LocationFrom(gpr_index_ == 0u ? r0 : kParameterCoreRegistersVIXL[gpr_index_ - 1u],
2472 kParameterCoreRegistersVIXL[gpr_index_]);
2473 gpr_index_ += 2u;
2474 }
2475 } else {
2476 if (gpr_index_ < 1u + kParameterCoreRegistersLengthVIXL) {
2477 location = LocationFrom(gpr_index_ == 0u ? r0 : kParameterCoreRegistersVIXL[gpr_index_ - 1u]);
2478 ++gpr_index_;
2479 }
2480 }
2481 if (location.IsInvalid()) {
2482 if (DataType::Is64BitType(type)) {
2483 location = Location::DoubleStackSlot(stack_offset_);
2484 stack_offset_ += 2 * kFramePointerSize;
2485 } else {
2486 location = Location::StackSlot(stack_offset_);
2487 stack_offset_ += kFramePointerSize;
2488 }
2489
2490 if (for_register_allocation_) {
2491 location = Location::Any();
2492 }
2493 }
2494 return location;
2495}
2496
2497Location CriticalNativeCallingConventionVisitorARMVIXL::GetReturnLocation(DataType::Type type)
2498 const {
2499 // We perform conversion to the managed ABI return register after the call if needed.
2500 InvokeDexCallingConventionVisitorARMVIXL dex_calling_convention;
2501 return dex_calling_convention.GetReturnLocation(type);
2502}
2503
2504Location CriticalNativeCallingConventionVisitorARMVIXL::GetMethodLocation() const {
2505 // Pass the method in the hidden argument R4.
2506 return Location::RegisterLocation(R4);
2507}
2508
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002509void CodeGeneratorARMVIXL::Move32(Location destination, Location source) {
2510 if (source.Equals(destination)) {
2511 return;
2512 }
2513 if (destination.IsRegister()) {
2514 if (source.IsRegister()) {
2515 __ Mov(RegisterFrom(destination), RegisterFrom(source));
2516 } else if (source.IsFpuRegister()) {
2517 __ Vmov(RegisterFrom(destination), SRegisterFrom(source));
2518 } else {
2519 GetAssembler()->LoadFromOffset(kLoadWord,
2520 RegisterFrom(destination),
2521 sp,
2522 source.GetStackIndex());
2523 }
2524 } else if (destination.IsFpuRegister()) {
2525 if (source.IsRegister()) {
2526 __ Vmov(SRegisterFrom(destination), RegisterFrom(source));
2527 } else if (source.IsFpuRegister()) {
2528 __ Vmov(SRegisterFrom(destination), SRegisterFrom(source));
2529 } else {
2530 GetAssembler()->LoadSFromOffset(SRegisterFrom(destination), sp, source.GetStackIndex());
2531 }
2532 } else {
2533 DCHECK(destination.IsStackSlot()) << destination;
2534 if (source.IsRegister()) {
2535 GetAssembler()->StoreToOffset(kStoreWord,
2536 RegisterFrom(source),
2537 sp,
2538 destination.GetStackIndex());
2539 } else if (source.IsFpuRegister()) {
2540 GetAssembler()->StoreSToOffset(SRegisterFrom(source), sp, destination.GetStackIndex());
2541 } else {
2542 DCHECK(source.IsStackSlot()) << source;
2543 UseScratchRegisterScope temps(GetVIXLAssembler());
2544 vixl32::Register temp = temps.Acquire();
2545 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, source.GetStackIndex());
2546 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
2547 }
2548 }
2549}
2550
Artem Serovcfbe9132016-10-14 15:58:56 +01002551void CodeGeneratorARMVIXL::MoveConstant(Location location, int32_t value) {
2552 DCHECK(location.IsRegister());
2553 __ Mov(RegisterFrom(location), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01002554}
2555
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002556void CodeGeneratorARMVIXL::MoveLocation(Location dst, Location src, DataType::Type dst_type) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002557 // TODO(VIXL): Maybe refactor to have the 'move' implementation here and use it in
2558 // `ParallelMoveResolverARMVIXL::EmitMove`, as is done in the `arm64` backend.
Vladimir Markoca6fff82017-10-03 14:49:14 +01002559 HParallelMove move(GetGraph()->GetAllocator());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002560 move.AddMove(src, dst, dst_type, nullptr);
2561 GetMoveResolver()->EmitNativeCode(&move);
Scott Wakelingfe885462016-09-22 10:24:38 +01002562}
2563
Artem Serovcfbe9132016-10-14 15:58:56 +01002564void CodeGeneratorARMVIXL::AddLocationAsTemp(Location location, LocationSummary* locations) {
2565 if (location.IsRegister()) {
2566 locations->AddTemp(location);
2567 } else if (location.IsRegisterPair()) {
2568 locations->AddTemp(LocationFrom(LowRegisterFrom(location)));
2569 locations->AddTemp(LocationFrom(HighRegisterFrom(location)));
2570 } else {
2571 UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location;
2572 }
Scott Wakelingfe885462016-09-22 10:24:38 +01002573}
2574
2575void CodeGeneratorARMVIXL::InvokeRuntime(QuickEntrypointEnum entrypoint,
2576 HInstruction* instruction,
2577 uint32_t dex_pc,
2578 SlowPathCode* slow_path) {
2579 ValidateInvokeRuntime(entrypoint, instruction, slow_path);
Vladimir Markof6675082019-05-17 12:05:28 +01002580
2581 ThreadOffset32 entrypoint_offset = GetThreadOffset<kArmPointerSize>(entrypoint);
2582 // Reduce code size for AOT by using shared trampolines for slow path runtime calls across the
2583 // entire oat file. This adds an extra branch and we do not want to slow down the main path.
2584 // For JIT, thunk sharing is per-method, so the gains would be smaller or even negative.
Vladimir Marko695348f2020-05-19 14:42:02 +01002585 if (slow_path == nullptr || GetCompilerOptions().IsJitCompiler()) {
Vladimir Markof6675082019-05-17 12:05:28 +01002586 __ Ldr(lr, MemOperand(tr, entrypoint_offset.Int32Value()));
2587 // Ensure the pc position is recorded immediately after the `blx` instruction.
2588 // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
2589 ExactAssemblyScope aas(GetVIXLAssembler(),
2590 vixl32::k16BitT32InstructionSizeInBytes,
2591 CodeBufferCheckScope::kExactSize);
2592 __ blx(lr);
2593 if (EntrypointRequiresStackMap(entrypoint)) {
2594 RecordPcInfo(instruction, dex_pc, slow_path);
2595 }
2596 } else {
2597 // Ensure the pc position is recorded immediately after the `bl` instruction.
2598 ExactAssemblyScope aas(GetVIXLAssembler(),
2599 vixl32::k32BitT32InstructionSizeInBytes,
2600 CodeBufferCheckScope::kExactSize);
2601 EmitEntrypointThunkCall(entrypoint_offset);
2602 if (EntrypointRequiresStackMap(entrypoint)) {
2603 RecordPcInfo(instruction, dex_pc, slow_path);
2604 }
Scott Wakelingfe885462016-09-22 10:24:38 +01002605 }
2606}
2607
2608void CodeGeneratorARMVIXL::InvokeRuntimeWithoutRecordingPcInfo(int32_t entry_point_offset,
2609 HInstruction* instruction,
2610 SlowPathCode* slow_path) {
2611 ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction, slow_path);
Alexandre Rames374ddf32016-11-04 10:40:49 +00002612 __ Ldr(lr, MemOperand(tr, entry_point_offset));
Scott Wakelingfe885462016-09-22 10:24:38 +01002613 __ Blx(lr);
2614}
2615
Scott Wakelingfe885462016-09-22 10:24:38 +01002616void InstructionCodeGeneratorARMVIXL::HandleGoto(HInstruction* got, HBasicBlock* successor) {
Aart Bika8b8e9b2018-01-09 11:01:02 -08002617 if (successor->IsExitBlock()) {
2618 DCHECK(got->GetPrevious()->AlwaysThrows());
2619 return; // no code needed
2620 }
2621
Scott Wakelingfe885462016-09-22 10:24:38 +01002622 HBasicBlock* block = got->GetBlock();
2623 HInstruction* previous = got->GetPrevious();
2624 HLoopInformation* info = block->GetLoopInformation();
2625
2626 if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) {
Nicolas Geoffraya59af8a2019-11-27 17:42:32 +00002627 codegen_->MaybeIncrementHotness(/* is_frame_entry= */ false);
Scott Wakelingfe885462016-09-22 10:24:38 +01002628 GenerateSuspendCheck(info->GetSuspendCheck(), successor);
2629 return;
2630 }
2631 if (block->IsEntryBlock() && (previous != nullptr) && previous->IsSuspendCheck()) {
2632 GenerateSuspendCheck(previous->AsSuspendCheck(), nullptr);
Andreas Gampe3db70682018-12-26 15:12:03 -08002633 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 2);
Scott Wakelingfe885462016-09-22 10:24:38 +01002634 }
2635 if (!codegen_->GoesToNextBlock(block, successor)) {
2636 __ B(codegen_->GetLabelOf(successor));
2637 }
2638}
2639
2640void LocationsBuilderARMVIXL::VisitGoto(HGoto* got) {
2641 got->SetLocations(nullptr);
2642}
2643
2644void InstructionCodeGeneratorARMVIXL::VisitGoto(HGoto* got) {
2645 HandleGoto(got, got->GetSuccessor());
2646}
2647
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002648void LocationsBuilderARMVIXL::VisitTryBoundary(HTryBoundary* try_boundary) {
2649 try_boundary->SetLocations(nullptr);
2650}
2651
2652void InstructionCodeGeneratorARMVIXL::VisitTryBoundary(HTryBoundary* try_boundary) {
2653 HBasicBlock* successor = try_boundary->GetNormalFlowSuccessor();
2654 if (!successor->IsExitBlock()) {
2655 HandleGoto(try_boundary, successor);
2656 }
2657}
2658
Scott Wakelingfe885462016-09-22 10:24:38 +01002659void LocationsBuilderARMVIXL::VisitExit(HExit* exit) {
2660 exit->SetLocations(nullptr);
2661}
2662
2663void InstructionCodeGeneratorARMVIXL::VisitExit(HExit* exit ATTRIBUTE_UNUSED) {
2664}
2665
Scott Wakelingfe885462016-09-22 10:24:38 +01002666void InstructionCodeGeneratorARMVIXL::GenerateCompareTestAndBranch(HCondition* condition,
Anton Kirilov23b752b2017-07-20 14:40:44 +01002667 vixl32::Label* true_target,
2668 vixl32::Label* false_target,
Anton Kirilovfd522532017-05-10 12:46:57 +01002669 bool is_far_target) {
Anton Kirilov23b752b2017-07-20 14:40:44 +01002670 if (true_target == false_target) {
2671 DCHECK(true_target != nullptr);
2672 __ B(true_target);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00002673 return;
2674 }
2675
Anton Kirilov23b752b2017-07-20 14:40:44 +01002676 vixl32::Label* non_fallthrough_target;
2677 bool invert;
2678 bool emit_both_branches;
Scott Wakelingfe885462016-09-22 10:24:38 +01002679
Anton Kirilov23b752b2017-07-20 14:40:44 +01002680 if (true_target == nullptr) {
2681 // The true target is fallthrough.
2682 DCHECK(false_target != nullptr);
2683 non_fallthrough_target = false_target;
2684 invert = true;
2685 emit_both_branches = false;
2686 } else {
2687 non_fallthrough_target = true_target;
2688 invert = false;
2689 // Either the false target is fallthrough, or there is no fallthrough
2690 // and both branches must be emitted.
2691 emit_both_branches = (false_target != nullptr);
Scott Wakelingfe885462016-09-22 10:24:38 +01002692 }
2693
Anton Kirilov23b752b2017-07-20 14:40:44 +01002694 const auto cond = GenerateTest(condition, invert, codegen_);
2695
2696 __ B(cond.first, non_fallthrough_target, is_far_target);
2697
2698 if (emit_both_branches) {
2699 // No target falls through, we need to branch.
2700 __ B(false_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01002701 }
2702}
2703
2704void InstructionCodeGeneratorARMVIXL::GenerateTestAndBranch(HInstruction* instruction,
2705 size_t condition_input_index,
2706 vixl32::Label* true_target,
xueliang.zhongf51bc622016-11-04 09:23:32 +00002707 vixl32::Label* false_target,
2708 bool far_target) {
Scott Wakelingfe885462016-09-22 10:24:38 +01002709 HInstruction* cond = instruction->InputAt(condition_input_index);
2710
2711 if (true_target == nullptr && false_target == nullptr) {
2712 // Nothing to do. The code always falls through.
2713 return;
2714 } else if (cond->IsIntConstant()) {
2715 // Constant condition, statically compared against "true" (integer value 1).
2716 if (cond->AsIntConstant()->IsTrue()) {
2717 if (true_target != nullptr) {
2718 __ B(true_target);
2719 }
2720 } else {
Anton Kirilov644032c2016-12-06 17:51:43 +00002721 DCHECK(cond->AsIntConstant()->IsFalse()) << Int32ConstantFrom(cond);
Scott Wakelingfe885462016-09-22 10:24:38 +01002722 if (false_target != nullptr) {
2723 __ B(false_target);
2724 }
2725 }
2726 return;
2727 }
2728
2729 // The following code generates these patterns:
2730 // (1) true_target == nullptr && false_target != nullptr
2731 // - opposite condition true => branch to false_target
2732 // (2) true_target != nullptr && false_target == nullptr
2733 // - condition true => branch to true_target
2734 // (3) true_target != nullptr && false_target != nullptr
2735 // - condition true => branch to true_target
2736 // - branch to false_target
2737 if (IsBooleanValueOrMaterializedCondition(cond)) {
2738 // Condition has been materialized, compare the output to 0.
2739 if (kIsDebugBuild) {
2740 Location cond_val = instruction->GetLocations()->InAt(condition_input_index);
2741 DCHECK(cond_val.IsRegister());
2742 }
2743 if (true_target == nullptr) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00002744 __ CompareAndBranchIfZero(InputRegisterAt(instruction, condition_input_index),
2745 false_target,
2746 far_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01002747 } else {
xueliang.zhongf51bc622016-11-04 09:23:32 +00002748 __ CompareAndBranchIfNonZero(InputRegisterAt(instruction, condition_input_index),
2749 true_target,
2750 far_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01002751 }
2752 } else {
2753 // Condition has not been materialized. Use its inputs as the comparison and
2754 // its condition as the branch condition.
2755 HCondition* condition = cond->AsCondition();
2756
2757 // If this is a long or FP comparison that has been folded into
2758 // the HCondition, generate the comparison directly.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002759 DataType::Type type = condition->InputAt(0)->GetType();
2760 if (type == DataType::Type::kInt64 || DataType::IsFloatingPointType(type)) {
Anton Kirilovfd522532017-05-10 12:46:57 +01002761 GenerateCompareTestAndBranch(condition, true_target, false_target, far_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01002762 return;
2763 }
2764
Donghui Bai426b49c2016-11-08 14:55:38 +08002765 vixl32::Label* non_fallthrough_target;
2766 vixl32::Condition arm_cond = vixl32::Condition::None();
2767 const vixl32::Register left = InputRegisterAt(cond, 0);
2768 const Operand right = InputOperandAt(cond, 1);
2769
Scott Wakelingfe885462016-09-22 10:24:38 +01002770 if (true_target == nullptr) {
Donghui Bai426b49c2016-11-08 14:55:38 +08002771 arm_cond = ARMCondition(condition->GetOppositeCondition());
2772 non_fallthrough_target = false_target;
Scott Wakelingfe885462016-09-22 10:24:38 +01002773 } else {
Donghui Bai426b49c2016-11-08 14:55:38 +08002774 arm_cond = ARMCondition(condition->GetCondition());
2775 non_fallthrough_target = true_target;
2776 }
2777
2778 if (right.IsImmediate() && right.GetImmediate() == 0 && (arm_cond.Is(ne) || arm_cond.Is(eq))) {
2779 if (arm_cond.Is(eq)) {
Anton Kirilovfd522532017-05-10 12:46:57 +01002780 __ CompareAndBranchIfZero(left, non_fallthrough_target, far_target);
Donghui Bai426b49c2016-11-08 14:55:38 +08002781 } else {
2782 DCHECK(arm_cond.Is(ne));
Anton Kirilovfd522532017-05-10 12:46:57 +01002783 __ CompareAndBranchIfNonZero(left, non_fallthrough_target, far_target);
Donghui Bai426b49c2016-11-08 14:55:38 +08002784 }
2785 } else {
2786 __ Cmp(left, right);
Anton Kirilovfd522532017-05-10 12:46:57 +01002787 __ B(arm_cond, non_fallthrough_target, far_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01002788 }
2789 }
2790
2791 // If neither branch falls through (case 3), the conditional branch to `true_target`
2792 // was already emitted (case 2) and we need to emit a jump to `false_target`.
2793 if (true_target != nullptr && false_target != nullptr) {
2794 __ B(false_target);
2795 }
2796}
2797
2798void LocationsBuilderARMVIXL::VisitIf(HIf* if_instr) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002799 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(if_instr);
Scott Wakelingfe885462016-09-22 10:24:38 +01002800 if (IsBooleanValueOrMaterializedCondition(if_instr->InputAt(0))) {
2801 locations->SetInAt(0, Location::RequiresRegister());
2802 }
2803}
2804
2805void InstructionCodeGeneratorARMVIXL::VisitIf(HIf* if_instr) {
2806 HBasicBlock* true_successor = if_instr->IfTrueSuccessor();
2807 HBasicBlock* false_successor = if_instr->IfFalseSuccessor();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002808 vixl32::Label* true_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), true_successor) ?
2809 nullptr : codegen_->GetLabelOf(true_successor);
2810 vixl32::Label* false_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), false_successor) ?
2811 nullptr : codegen_->GetLabelOf(false_successor);
Andreas Gampe3db70682018-12-26 15:12:03 -08002812 GenerateTestAndBranch(if_instr, /* condition_input_index= */ 0, true_target, false_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01002813}
2814
Scott Wakelingc34dba72016-10-03 10:14:44 +01002815void LocationsBuilderARMVIXL::VisitDeoptimize(HDeoptimize* deoptimize) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002816 LocationSummary* locations = new (GetGraph()->GetAllocator())
Scott Wakelingc34dba72016-10-03 10:14:44 +01002817 LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath);
Nicolas Geoffray4e92c3c2017-05-08 09:34:26 +01002818 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2819 RegisterSet caller_saves = RegisterSet::Empty();
2820 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0)));
2821 locations->SetCustomSlowPathCallerSaves(caller_saves);
Scott Wakelingc34dba72016-10-03 10:14:44 +01002822 if (IsBooleanValueOrMaterializedCondition(deoptimize->InputAt(0))) {
2823 locations->SetInAt(0, Location::RequiresRegister());
2824 }
2825}
2826
2827void InstructionCodeGeneratorARMVIXL::VisitDeoptimize(HDeoptimize* deoptimize) {
2828 SlowPathCodeARMVIXL* slow_path =
2829 deopt_slow_paths_.NewSlowPath<DeoptimizationSlowPathARMVIXL>(deoptimize);
2830 GenerateTestAndBranch(deoptimize,
Andreas Gampe3db70682018-12-26 15:12:03 -08002831 /* condition_input_index= */ 0,
Scott Wakelingc34dba72016-10-03 10:14:44 +01002832 slow_path->GetEntryLabel(),
Andreas Gampe3db70682018-12-26 15:12:03 -08002833 /* false_target= */ nullptr);
Scott Wakelingc34dba72016-10-03 10:14:44 +01002834}
2835
Artem Serovd4cc5b22016-11-04 11:19:09 +00002836void LocationsBuilderARMVIXL::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002837 LocationSummary* locations = new (GetGraph()->GetAllocator())
Artem Serovd4cc5b22016-11-04 11:19:09 +00002838 LocationSummary(flag, LocationSummary::kNoCall);
2839 locations->SetOut(Location::RequiresRegister());
2840}
2841
2842void InstructionCodeGeneratorARMVIXL::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
2843 GetAssembler()->LoadFromOffset(kLoadWord,
2844 OutputRegister(flag),
2845 sp,
2846 codegen_->GetStackOffsetOfShouldDeoptimizeFlag());
2847}
2848
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002849void LocationsBuilderARMVIXL::VisitSelect(HSelect* select) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002850 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(select);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002851 const bool is_floating_point = DataType::IsFloatingPointType(select->GetType());
Donghui Bai426b49c2016-11-08 14:55:38 +08002852
2853 if (is_floating_point) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002854 locations->SetInAt(0, Location::RequiresFpuRegister());
Donghui Bai426b49c2016-11-08 14:55:38 +08002855 locations->SetInAt(1, Location::FpuRegisterOrConstant(select->GetTrueValue()));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002856 } else {
2857 locations->SetInAt(0, Location::RequiresRegister());
Donghui Bai426b49c2016-11-08 14:55:38 +08002858 locations->SetInAt(1, Arm8BitEncodableConstantOrRegister(select->GetTrueValue()));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002859 }
Donghui Bai426b49c2016-11-08 14:55:38 +08002860
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002861 if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) {
Donghui Bai426b49c2016-11-08 14:55:38 +08002862 locations->SetInAt(2, Location::RegisterOrConstant(select->GetCondition()));
2863 // The code generator handles overlap with the values, but not with the condition.
2864 locations->SetOut(Location::SameAsFirstInput());
2865 } else if (is_floating_point) {
2866 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2867 } else {
2868 if (!locations->InAt(1).IsConstant()) {
2869 locations->SetInAt(0, Arm8BitEncodableConstantOrRegister(select->GetFalseValue()));
2870 }
2871
2872 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002873 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002874}
2875
2876void InstructionCodeGeneratorARMVIXL::VisitSelect(HSelect* select) {
Donghui Bai426b49c2016-11-08 14:55:38 +08002877 HInstruction* const condition = select->GetCondition();
2878 const LocationSummary* const locations = select->GetLocations();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002879 const DataType::Type type = select->GetType();
Donghui Bai426b49c2016-11-08 14:55:38 +08002880 const Location first = locations->InAt(0);
2881 const Location out = locations->Out();
2882 const Location second = locations->InAt(1);
Nicolas Geoffray7b05c5f2018-09-21 11:31:38 +01002883
2884 // In the unlucky case the output of this instruction overlaps
2885 // with an input of an "emitted-at-use-site" condition, and
2886 // the output of this instruction is not one of its inputs, we'll
2887 // need to fallback to branches instead of conditional ARM instructions.
2888 bool output_overlaps_with_condition_inputs =
2889 !IsBooleanValueOrMaterializedCondition(condition) &&
2890 !out.Equals(first) &&
2891 !out.Equals(second) &&
2892 (condition->GetLocations()->InAt(0).Equals(out) ||
2893 condition->GetLocations()->InAt(1).Equals(out));
2894 DCHECK(!output_overlaps_with_condition_inputs || condition->IsCondition());
Donghui Bai426b49c2016-11-08 14:55:38 +08002895 Location src;
2896
2897 if (condition->IsIntConstant()) {
2898 if (condition->AsIntConstant()->IsFalse()) {
2899 src = first;
2900 } else {
2901 src = second;
2902 }
2903
2904 codegen_->MoveLocation(out, src, type);
2905 return;
2906 }
2907
Nicolas Geoffray7b05c5f2018-09-21 11:31:38 +01002908 if (!DataType::IsFloatingPointType(type) && !output_overlaps_with_condition_inputs) {
Donghui Bai426b49c2016-11-08 14:55:38 +08002909 bool invert = false;
2910
2911 if (out.Equals(second)) {
2912 src = first;
2913 invert = true;
2914 } else if (out.Equals(first)) {
2915 src = second;
2916 } else if (second.IsConstant()) {
2917 DCHECK(CanEncodeConstantAs8BitImmediate(second.GetConstant()));
2918 src = second;
2919 } else if (first.IsConstant()) {
2920 DCHECK(CanEncodeConstantAs8BitImmediate(first.GetConstant()));
2921 src = first;
2922 invert = true;
2923 } else {
2924 src = second;
2925 }
2926
2927 if (CanGenerateConditionalMove(out, src)) {
2928 if (!out.Equals(first) && !out.Equals(second)) {
2929 codegen_->MoveLocation(out, src.Equals(first) ? second : first, type);
2930 }
2931
Anton Kirilov217b2ce2017-03-16 11:47:12 +00002932 std::pair<vixl32::Condition, vixl32::Condition> cond(eq, ne);
2933
2934 if (IsBooleanValueOrMaterializedCondition(condition)) {
2935 __ Cmp(InputRegisterAt(select, 2), 0);
2936 cond = invert ? std::make_pair(eq, ne) : std::make_pair(ne, eq);
2937 } else {
2938 cond = GenerateTest(condition->AsCondition(), invert, codegen_);
2939 }
2940
Donghui Bai426b49c2016-11-08 14:55:38 +08002941 const size_t instr_count = out.IsRegisterPair() ? 4 : 2;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00002942 // We use the scope because of the IT block that follows.
Donghui Bai426b49c2016-11-08 14:55:38 +08002943 ExactAssemblyScope guard(GetVIXLAssembler(),
2944 instr_count * vixl32::k16BitT32InstructionSizeInBytes,
2945 CodeBufferCheckScope::kExactSize);
2946
2947 if (out.IsRegister()) {
Anton Kirilov217b2ce2017-03-16 11:47:12 +00002948 __ it(cond.first);
2949 __ mov(cond.first, RegisterFrom(out), OperandFrom(src, type));
Donghui Bai426b49c2016-11-08 14:55:38 +08002950 } else {
2951 DCHECK(out.IsRegisterPair());
2952
2953 Operand operand_high(0);
2954 Operand operand_low(0);
2955
2956 if (src.IsConstant()) {
2957 const int64_t value = Int64ConstantFrom(src);
2958
2959 operand_high = High32Bits(value);
2960 operand_low = Low32Bits(value);
2961 } else {
2962 DCHECK(src.IsRegisterPair());
2963 operand_high = HighRegisterFrom(src);
2964 operand_low = LowRegisterFrom(src);
2965 }
2966
Anton Kirilov217b2ce2017-03-16 11:47:12 +00002967 __ it(cond.first);
2968 __ mov(cond.first, LowRegisterFrom(out), operand_low);
2969 __ it(cond.first);
2970 __ mov(cond.first, HighRegisterFrom(out), operand_high);
Donghui Bai426b49c2016-11-08 14:55:38 +08002971 }
2972
2973 return;
2974 }
2975 }
2976
2977 vixl32::Label* false_target = nullptr;
2978 vixl32::Label* true_target = nullptr;
2979 vixl32::Label select_end;
Nicolas Geoffray7b05c5f2018-09-21 11:31:38 +01002980 vixl32::Label other_case;
Donghui Bai426b49c2016-11-08 14:55:38 +08002981 vixl32::Label* const target = codegen_->GetFinalLabel(select, &select_end);
2982
2983 if (out.Equals(second)) {
2984 true_target = target;
2985 src = first;
2986 } else {
2987 false_target = target;
2988 src = second;
2989
2990 if (!out.Equals(first)) {
Nicolas Geoffray7b05c5f2018-09-21 11:31:38 +01002991 if (output_overlaps_with_condition_inputs) {
2992 false_target = &other_case;
2993 } else {
2994 codegen_->MoveLocation(out, first, type);
2995 }
Donghui Bai426b49c2016-11-08 14:55:38 +08002996 }
2997 }
2998
Andreas Gampe3db70682018-12-26 15:12:03 -08002999 GenerateTestAndBranch(select, 2, true_target, false_target, /* far_target= */ false);
Donghui Bai426b49c2016-11-08 14:55:38 +08003000 codegen_->MoveLocation(out, src, type);
Nicolas Geoffray7b05c5f2018-09-21 11:31:38 +01003001 if (output_overlaps_with_condition_inputs) {
3002 __ B(target);
3003 __ Bind(&other_case);
3004 codegen_->MoveLocation(out, first, type);
3005 }
Donghui Bai426b49c2016-11-08 14:55:38 +08003006
3007 if (select_end.IsReferenced()) {
3008 __ Bind(&select_end);
3009 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003010}
3011
Artem Serov551b28f2016-10-18 19:11:30 +01003012void LocationsBuilderARMVIXL::VisitNativeDebugInfo(HNativeDebugInfo* info) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01003013 new (GetGraph()->GetAllocator()) LocationSummary(info);
Artem Serov551b28f2016-10-18 19:11:30 +01003014}
3015
3016void InstructionCodeGeneratorARMVIXL::VisitNativeDebugInfo(HNativeDebugInfo*) {
3017 // MaybeRecordNativeDebugInfo is already called implicitly in CodeGenerator::Compile.
3018}
3019
Vladimir Markodec78172020-06-19 15:31:23 +01003020void CodeGeneratorARMVIXL::IncreaseFrame(size_t adjustment) {
3021 __ Claim(adjustment);
3022 GetAssembler()->cfi().AdjustCFAOffset(adjustment);
3023}
3024
3025void CodeGeneratorARMVIXL::DecreaseFrame(size_t adjustment) {
3026 __ Drop(adjustment);
3027 GetAssembler()->cfi().AdjustCFAOffset(-adjustment);
3028}
3029
Scott Wakelingfe885462016-09-22 10:24:38 +01003030void CodeGeneratorARMVIXL::GenerateNop() {
3031 __ Nop();
3032}
3033
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003034// `temp` is an extra temporary register that is used for some conditions;
3035// callers may not specify it, in which case the method will use a scratch
3036// register instead.
3037void CodeGeneratorARMVIXL::GenerateConditionWithZero(IfCondition condition,
3038 vixl32::Register out,
3039 vixl32::Register in,
3040 vixl32::Register temp) {
3041 switch (condition) {
3042 case kCondEQ:
3043 // x <= 0 iff x == 0 when the comparison is unsigned.
3044 case kCondBE:
3045 if (!temp.IsValid() || (out.IsLow() && !out.Is(in))) {
3046 temp = out;
3047 }
3048
3049 // Avoid 32-bit instructions if possible; note that `in` and `temp` must be
3050 // different as well.
3051 if (in.IsLow() && temp.IsLow() && !in.Is(temp)) {
3052 // temp = - in; only 0 sets the carry flag.
3053 __ Rsbs(temp, in, 0);
3054
3055 if (out.Is(in)) {
3056 std::swap(in, temp);
3057 }
3058
3059 // out = - in + in + carry = carry
3060 __ Adc(out, temp, in);
3061 } else {
3062 // If `in` is 0, then it has 32 leading zeros, and less than that otherwise.
3063 __ Clz(out, in);
3064 // Any number less than 32 logically shifted right by 5 bits results in 0;
3065 // the same operation on 32 yields 1.
3066 __ Lsr(out, out, 5);
3067 }
3068
3069 break;
3070 case kCondNE:
3071 // x > 0 iff x != 0 when the comparison is unsigned.
3072 case kCondA: {
3073 UseScratchRegisterScope temps(GetVIXLAssembler());
3074
3075 if (out.Is(in)) {
3076 if (!temp.IsValid() || in.Is(temp)) {
3077 temp = temps.Acquire();
3078 }
3079 } else if (!temp.IsValid() || !temp.IsLow()) {
3080 temp = out;
3081 }
3082
3083 // temp = in - 1; only 0 does not set the carry flag.
3084 __ Subs(temp, in, 1);
3085 // out = in + ~temp + carry = in + (-(in - 1) - 1) + carry = in - in + 1 - 1 + carry = carry
3086 __ Sbc(out, in, temp);
3087 break;
3088 }
3089 case kCondGE:
3090 __ Mvn(out, in);
3091 in = out;
3092 FALLTHROUGH_INTENDED;
3093 case kCondLT:
3094 // We only care about the sign bit.
3095 __ Lsr(out, in, 31);
3096 break;
3097 case kCondAE:
3098 // Trivially true.
3099 __ Mov(out, 1);
3100 break;
3101 case kCondB:
3102 // Trivially false.
3103 __ Mov(out, 0);
3104 break;
3105 default:
3106 LOG(FATAL) << "Unexpected condition " << condition;
3107 UNREACHABLE();
3108 }
3109}
3110
Scott Wakelingfe885462016-09-22 10:24:38 +01003111void LocationsBuilderARMVIXL::HandleCondition(HCondition* cond) {
3112 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003113 new (GetGraph()->GetAllocator()) LocationSummary(cond, LocationSummary::kNoCall);
Nicolas Geoffray7b05c5f2018-09-21 11:31:38 +01003114 const DataType::Type type = cond->InputAt(0)->GetType();
3115 if (DataType::IsFloatingPointType(type)) {
3116 locations->SetInAt(0, Location::RequiresFpuRegister());
3117 locations->SetInAt(1, ArithmeticZeroOrFpuRegister(cond->InputAt(1)));
3118 } else {
3119 locations->SetInAt(0, Location::RequiresRegister());
3120 locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
3121 }
3122 if (!cond->IsEmittedAtUseSite()) {
3123 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Scott Wakelingfe885462016-09-22 10:24:38 +01003124 }
3125}
3126
3127void InstructionCodeGeneratorARMVIXL::HandleCondition(HCondition* cond) {
3128 if (cond->IsEmittedAtUseSite()) {
3129 return;
3130 }
3131
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003132 const DataType::Type type = cond->GetLeft()->GetType();
Scott Wakelingfe885462016-09-22 10:24:38 +01003133
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003134 if (DataType::IsFloatingPointType(type)) {
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003135 GenerateConditionGeneric(cond, codegen_);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003136 return;
Scott Wakelingfe885462016-09-22 10:24:38 +01003137 }
3138
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003139 DCHECK(DataType::IsIntegralType(type) || type == DataType::Type::kReference) << type;
Scott Wakelingfe885462016-09-22 10:24:38 +01003140
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003141 const IfCondition condition = cond->GetCondition();
Scott Wakelingfe885462016-09-22 10:24:38 +01003142
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003143 // A condition with only one boolean input, or two boolean inputs without being equality or
3144 // inequality results from transformations done by the instruction simplifier, and is handled
3145 // as a regular condition with integral inputs.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003146 if (type == DataType::Type::kBool &&
3147 cond->GetRight()->GetType() == DataType::Type::kBool &&
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003148 (condition == kCondEQ || condition == kCondNE)) {
3149 vixl32::Register left = InputRegisterAt(cond, 0);
3150 const vixl32::Register out = OutputRegister(cond);
3151 const Location right_loc = cond->GetLocations()->InAt(1);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003152
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003153 // The constant case is handled by the instruction simplifier.
3154 DCHECK(!right_loc.IsConstant());
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003155
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003156 vixl32::Register right = RegisterFrom(right_loc);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003157
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003158 // Avoid 32-bit instructions if possible.
3159 if (out.Is(right)) {
3160 std::swap(left, right);
3161 }
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003162
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003163 __ Eor(out, left, right);
3164
3165 if (condition == kCondEQ) {
3166 __ Eor(out, out, 1);
3167 }
3168
3169 return;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003170 }
Anton Kirilov6f644202017-02-27 18:29:45 +00003171
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003172 GenerateConditionIntegralOrNonPrimitive(cond, codegen_);
Scott Wakelingfe885462016-09-22 10:24:38 +01003173}
3174
3175void LocationsBuilderARMVIXL::VisitEqual(HEqual* comp) {
3176 HandleCondition(comp);
3177}
3178
3179void InstructionCodeGeneratorARMVIXL::VisitEqual(HEqual* comp) {
3180 HandleCondition(comp);
3181}
3182
3183void LocationsBuilderARMVIXL::VisitNotEqual(HNotEqual* comp) {
3184 HandleCondition(comp);
3185}
3186
3187void InstructionCodeGeneratorARMVIXL::VisitNotEqual(HNotEqual* comp) {
3188 HandleCondition(comp);
3189}
3190
3191void LocationsBuilderARMVIXL::VisitLessThan(HLessThan* comp) {
3192 HandleCondition(comp);
3193}
3194
3195void InstructionCodeGeneratorARMVIXL::VisitLessThan(HLessThan* comp) {
3196 HandleCondition(comp);
3197}
3198
3199void LocationsBuilderARMVIXL::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
3200 HandleCondition(comp);
3201}
3202
3203void InstructionCodeGeneratorARMVIXL::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
3204 HandleCondition(comp);
3205}
3206
3207void LocationsBuilderARMVIXL::VisitGreaterThan(HGreaterThan* comp) {
3208 HandleCondition(comp);
3209}
3210
3211void InstructionCodeGeneratorARMVIXL::VisitGreaterThan(HGreaterThan* comp) {
3212 HandleCondition(comp);
3213}
3214
3215void LocationsBuilderARMVIXL::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
3216 HandleCondition(comp);
3217}
3218
3219void InstructionCodeGeneratorARMVIXL::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
3220 HandleCondition(comp);
3221}
3222
3223void LocationsBuilderARMVIXL::VisitBelow(HBelow* comp) {
3224 HandleCondition(comp);
3225}
3226
3227void InstructionCodeGeneratorARMVIXL::VisitBelow(HBelow* comp) {
3228 HandleCondition(comp);
3229}
3230
3231void LocationsBuilderARMVIXL::VisitBelowOrEqual(HBelowOrEqual* comp) {
3232 HandleCondition(comp);
3233}
3234
3235void InstructionCodeGeneratorARMVIXL::VisitBelowOrEqual(HBelowOrEqual* comp) {
3236 HandleCondition(comp);
3237}
3238
3239void LocationsBuilderARMVIXL::VisitAbove(HAbove* comp) {
3240 HandleCondition(comp);
3241}
3242
3243void InstructionCodeGeneratorARMVIXL::VisitAbove(HAbove* comp) {
3244 HandleCondition(comp);
3245}
3246
3247void LocationsBuilderARMVIXL::VisitAboveOrEqual(HAboveOrEqual* comp) {
3248 HandleCondition(comp);
3249}
3250
3251void InstructionCodeGeneratorARMVIXL::VisitAboveOrEqual(HAboveOrEqual* comp) {
3252 HandleCondition(comp);
3253}
3254
3255void LocationsBuilderARMVIXL::VisitIntConstant(HIntConstant* constant) {
3256 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003257 new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
Scott Wakelingfe885462016-09-22 10:24:38 +01003258 locations->SetOut(Location::ConstantLocation(constant));
3259}
3260
3261void InstructionCodeGeneratorARMVIXL::VisitIntConstant(HIntConstant* constant ATTRIBUTE_UNUSED) {
3262 // Will be generated at use site.
3263}
3264
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003265void LocationsBuilderARMVIXL::VisitNullConstant(HNullConstant* constant) {
3266 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003267 new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003268 locations->SetOut(Location::ConstantLocation(constant));
3269}
3270
3271void InstructionCodeGeneratorARMVIXL::VisitNullConstant(HNullConstant* constant ATTRIBUTE_UNUSED) {
3272 // Will be generated at use site.
3273}
3274
Scott Wakelingfe885462016-09-22 10:24:38 +01003275void LocationsBuilderARMVIXL::VisitLongConstant(HLongConstant* constant) {
3276 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003277 new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
Scott Wakelingfe885462016-09-22 10:24:38 +01003278 locations->SetOut(Location::ConstantLocation(constant));
3279}
3280
3281void InstructionCodeGeneratorARMVIXL::VisitLongConstant(HLongConstant* constant ATTRIBUTE_UNUSED) {
3282 // Will be generated at use site.
3283}
3284
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01003285void LocationsBuilderARMVIXL::VisitFloatConstant(HFloatConstant* constant) {
3286 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003287 new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01003288 locations->SetOut(Location::ConstantLocation(constant));
3289}
3290
Scott Wakelingc34dba72016-10-03 10:14:44 +01003291void InstructionCodeGeneratorARMVIXL::VisitFloatConstant(
3292 HFloatConstant* constant ATTRIBUTE_UNUSED) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01003293 // Will be generated at use site.
3294}
3295
3296void LocationsBuilderARMVIXL::VisitDoubleConstant(HDoubleConstant* constant) {
3297 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003298 new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01003299 locations->SetOut(Location::ConstantLocation(constant));
3300}
3301
Scott Wakelingc34dba72016-10-03 10:14:44 +01003302void InstructionCodeGeneratorARMVIXL::VisitDoubleConstant(
3303 HDoubleConstant* constant ATTRIBUTE_UNUSED) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01003304 // Will be generated at use site.
3305}
3306
Igor Murashkind01745e2017-04-05 16:40:31 -07003307void LocationsBuilderARMVIXL::VisitConstructorFence(HConstructorFence* constructor_fence) {
3308 constructor_fence->SetLocations(nullptr);
3309}
3310
3311void InstructionCodeGeneratorARMVIXL::VisitConstructorFence(
3312 HConstructorFence* constructor_fence ATTRIBUTE_UNUSED) {
3313 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
3314}
3315
Scott Wakelingfe885462016-09-22 10:24:38 +01003316void LocationsBuilderARMVIXL::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
3317 memory_barrier->SetLocations(nullptr);
3318}
3319
3320void InstructionCodeGeneratorARMVIXL::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
3321 codegen_->GenerateMemoryBarrier(memory_barrier->GetBarrierKind());
3322}
3323
3324void LocationsBuilderARMVIXL::VisitReturnVoid(HReturnVoid* ret) {
3325 ret->SetLocations(nullptr);
3326}
3327
3328void InstructionCodeGeneratorARMVIXL::VisitReturnVoid(HReturnVoid* ret ATTRIBUTE_UNUSED) {
3329 codegen_->GenerateFrameExit();
3330}
3331
3332void LocationsBuilderARMVIXL::VisitReturn(HReturn* ret) {
3333 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003334 new (GetGraph()->GetAllocator()) LocationSummary(ret, LocationSummary::kNoCall);
Scott Wakelingfe885462016-09-22 10:24:38 +01003335 locations->SetInAt(0, parameter_visitor_.GetReturnLocation(ret->InputAt(0)->GetType()));
3336}
3337
Nicolas Geoffray57cacb72019-12-08 22:07:08 +00003338void InstructionCodeGeneratorARMVIXL::VisitReturn(HReturn* ret) {
3339 if (GetGraph()->IsCompilingOsr()) {
3340 // To simplify callers of an OSR method, we put the return value in both
3341 // floating point and core registers.
3342 switch (ret->InputAt(0)->GetType()) {
3343 case DataType::Type::kFloat32:
3344 __ Vmov(r0, s0);
3345 break;
3346 case DataType::Type::kFloat64:
3347 __ Vmov(r0, r1, d0);
3348 break;
3349 default:
3350 break;
3351 }
3352 }
Scott Wakelingfe885462016-09-22 10:24:38 +01003353 codegen_->GenerateFrameExit();
3354}
3355
Artem Serovcfbe9132016-10-14 15:58:56 +01003356void LocationsBuilderARMVIXL::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
3357 // The trampoline uses the same calling convention as dex calling conventions,
3358 // except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain
3359 // the method_idx.
3360 HandleInvoke(invoke);
3361}
3362
3363void InstructionCodeGeneratorARMVIXL::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
3364 codegen_->GenerateInvokeUnresolvedRuntimeCall(invoke);
Andreas Gampe3db70682018-12-26 15:12:03 -08003365 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 3);
Artem Serovcfbe9132016-10-14 15:58:56 +01003366}
3367
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003368void LocationsBuilderARMVIXL::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
3369 // Explicit clinit checks triggered by static invokes must have been pruned by
3370 // art::PrepareForRegisterAllocation.
3371 DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
3372
Anton Kirilov5ec62182016-10-13 20:16:02 +01003373 IntrinsicLocationsBuilderARMVIXL intrinsic(codegen_);
3374 if (intrinsic.TryDispatch(invoke)) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01003375 return;
3376 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003377
Nicolas Geoffray6d69b522020-09-23 14:47:28 +01003378 if (invoke->GetCodePtrLocation() == CodePtrLocation::kCallCriticalNative) {
Vladimir Marko86c87522020-05-11 16:55:55 +01003379 CriticalNativeCallingConventionVisitorARMVIXL calling_convention_visitor(
3380 /*for_register_allocation=*/ true);
3381 CodeGenerator::CreateCommonInvokeLocationSummary(invoke, &calling_convention_visitor);
3382 } else {
3383 HandleInvoke(invoke);
3384 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003385}
3386
Anton Kirilov5ec62182016-10-13 20:16:02 +01003387static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorARMVIXL* codegen) {
3388 if (invoke->GetLocations()->Intrinsified()) {
3389 IntrinsicCodeGeneratorARMVIXL intrinsic(codegen);
3390 intrinsic.Dispatch(invoke);
3391 return true;
3392 }
3393 return false;
3394}
3395
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003396void InstructionCodeGeneratorARMVIXL::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
3397 // Explicit clinit checks triggered by static invokes must have been pruned by
3398 // art::PrepareForRegisterAllocation.
3399 DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
3400
Anton Kirilov5ec62182016-10-13 20:16:02 +01003401 if (TryGenerateIntrinsicCode(invoke, codegen_)) {
Andreas Gampe3db70682018-12-26 15:12:03 -08003402 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 4);
Anton Kirilov5ec62182016-10-13 20:16:02 +01003403 return;
3404 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003405
3406 LocationSummary* locations = invoke->GetLocations();
Artem Serovd4cc5b22016-11-04 11:19:09 +00003407 codegen_->GenerateStaticOrDirectCall(
3408 invoke, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation());
Roland Levillain5daa4952017-07-03 17:23:56 +01003409
Andreas Gampe3db70682018-12-26 15:12:03 -08003410 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 5);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003411}
3412
3413void LocationsBuilderARMVIXL::HandleInvoke(HInvoke* invoke) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00003414 InvokeDexCallingConventionVisitorARMVIXL calling_convention_visitor;
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003415 CodeGenerator::CreateCommonInvokeLocationSummary(invoke, &calling_convention_visitor);
3416}
3417
3418void LocationsBuilderARMVIXL::VisitInvokeVirtual(HInvokeVirtual* invoke) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01003419 IntrinsicLocationsBuilderARMVIXL intrinsic(codegen_);
3420 if (intrinsic.TryDispatch(invoke)) {
3421 return;
3422 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003423
3424 HandleInvoke(invoke);
3425}
3426
3427void InstructionCodeGeneratorARMVIXL::VisitInvokeVirtual(HInvokeVirtual* invoke) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01003428 if (TryGenerateIntrinsicCode(invoke, codegen_)) {
Andreas Gampe3db70682018-12-26 15:12:03 -08003429 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 6);
Anton Kirilov5ec62182016-10-13 20:16:02 +01003430 return;
3431 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003432
3433 codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0));
Alexandre Rames374ddf32016-11-04 10:40:49 +00003434 DCHECK(!codegen_->IsLeafMethod());
Roland Levillain5daa4952017-07-03 17:23:56 +01003435
Andreas Gampe3db70682018-12-26 15:12:03 -08003436 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 7);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003437}
3438
Artem Serovcfbe9132016-10-14 15:58:56 +01003439void LocationsBuilderARMVIXL::VisitInvokeInterface(HInvokeInterface* invoke) {
3440 HandleInvoke(invoke);
3441 // Add the hidden argument.
Nicolas Geoffray8d34a182020-09-16 09:46:58 +01003442 if (invoke->GetHiddenArgumentLoadKind() == MethodLoadKind::kRecursive) {
3443 // We cannot request r12 as it's blocked by the register allocator.
3444 invoke->GetLocations()->SetInAt(invoke->GetNumberOfArguments() - 1, Location::Any());
3445 }
Artem Serovcfbe9132016-10-14 15:58:56 +01003446}
3447
Nicolas Geoffraye2a3aa92019-11-25 17:52:58 +00003448void CodeGeneratorARMVIXL::MaybeGenerateInlineCacheCheck(HInstruction* instruction,
3449 vixl32::Register klass) {
3450 DCHECK_EQ(r0.GetCode(), klass.GetCode());
Nicolas Geoffray20036d82019-11-28 16:15:00 +00003451 // We know the destination of an intrinsic, so no need to record inline
3452 // caches.
3453 if (!instruction->GetLocations()->Intrinsified() &&
Nicolas Geoffray9b5271e2019-12-04 14:39:46 +00003454 GetGraph()->IsCompilingBaseline() &&
Nicolas Geoffray20036d82019-11-28 16:15:00 +00003455 !Runtime::Current()->IsAotCompiler()) {
Nicolas Geoffraye2a3aa92019-11-25 17:52:58 +00003456 DCHECK(!instruction->GetEnvironment()->IsFromInlinedInvoke());
Nicolas Geoffray095dc462020-08-17 16:40:28 +01003457 ScopedProfilingInfoUse spiu(
3458 Runtime::Current()->GetJit(), GetGraph()->GetArtMethod(), Thread::Current());
3459 ProfilingInfo* info = spiu.GetProfilingInfo();
Nicolas Geoffray796aa2c2019-12-17 10:20:05 +00003460 if (info != nullptr) {
3461 InlineCache* cache = info->GetInlineCache(instruction->GetDexPc());
3462 uint32_t address = reinterpret_cast32<uint32_t>(cache);
3463 vixl32::Label done;
3464 UseScratchRegisterScope temps(GetVIXLAssembler());
3465 temps.Exclude(ip);
3466 __ Mov(r4, address);
3467 __ Ldr(ip, MemOperand(r4, InlineCache::ClassesOffset().Int32Value()));
3468 // Fast path for a monomorphic cache.
3469 __ Cmp(klass, ip);
3470 __ B(eq, &done, /* is_far_target= */ false);
3471 InvokeRuntime(kQuickUpdateInlineCache, instruction, instruction->GetDexPc());
3472 __ Bind(&done);
3473 }
Nicolas Geoffraye2a3aa92019-11-25 17:52:58 +00003474 }
3475}
3476
Artem Serovcfbe9132016-10-14 15:58:56 +01003477void InstructionCodeGeneratorARMVIXL::VisitInvokeInterface(HInvokeInterface* invoke) {
3478 // TODO: b/18116999, our IMTs can miss an IncompatibleClassChangeError.
3479 LocationSummary* locations = invoke->GetLocations();
3480 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
Artem Serovcfbe9132016-10-14 15:58:56 +01003481 Location receiver = locations->InAt(0);
3482 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
3483
3484 DCHECK(!receiver.IsStackSlot());
3485
Alexandre Rames374ddf32016-11-04 10:40:49 +00003486 // Ensure the pc position is recorded immediately after the `ldr` instruction.
3487 {
Artem Serov0fb37192016-12-06 18:13:40 +00003488 ExactAssemblyScope aas(GetVIXLAssembler(),
3489 vixl32::kMaxInstructionSizeInBytes,
3490 CodeBufferCheckScope::kMaximumSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00003491 // /* HeapReference<Class> */ temp = receiver->klass_
3492 __ ldr(temp, MemOperand(RegisterFrom(receiver), class_offset));
3493 codegen_->MaybeRecordImplicitNullCheck(invoke);
3494 }
Artem Serovcfbe9132016-10-14 15:58:56 +01003495 // Instead of simply (possibly) unpoisoning `temp` here, we should
3496 // emit a read barrier for the previous class reference load.
3497 // However this is not required in practice, as this is an
3498 // intermediate/temporary reference and because the current
3499 // concurrent copying collector keeps the from-space memory
3500 // intact/accessible until the end of the marking phase (the
3501 // concurrent copying collector may not in the future).
3502 GetAssembler()->MaybeUnpoisonHeapReference(temp);
Nicolas Geoffraye2a3aa92019-11-25 17:52:58 +00003503
3504 // If we're compiling baseline, update the inline cache.
3505 codegen_->MaybeGenerateInlineCacheCheck(invoke, temp);
3506
Artem Serovcfbe9132016-10-14 15:58:56 +01003507 GetAssembler()->LoadFromOffset(kLoadWord,
3508 temp,
3509 temp,
3510 mirror::Class::ImtPtrOffset(kArmPointerSize).Uint32Value());
Nicolas Geoffraye2a3aa92019-11-25 17:52:58 +00003511
Artem Serovcfbe9132016-10-14 15:58:56 +01003512 uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement(
3513 invoke->GetImtIndex(), kArmPointerSize));
3514 // temp = temp->GetImtEntryAt(method_offset);
3515 GetAssembler()->LoadFromOffset(kLoadWord, temp, temp, method_offset);
3516 uint32_t entry_point =
3517 ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value();
3518 // LR = temp->GetEntryPoint();
3519 GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, entry_point);
3520
Scott Wakelingb77051e2016-11-21 19:46:00 +00003521 {
Nicolas Geoffray8d34a182020-09-16 09:46:58 +01003522 // Set the hidden (in r12) argument. It is done here, right before a BLX to prevent other
3523 // instruction from clobbering it as they might use r12 as a scratch register.
3524 Location hidden_reg = Location::RegisterLocation(r12.GetCode());
Scott Wakelingb77051e2016-11-21 19:46:00 +00003525 // The VIXL macro assembler may clobber any of the scratch registers that are available to it,
3526 // so it checks if the application is using them (by passing them to the macro assembler
3527 // methods). The following application of UseScratchRegisterScope corrects VIXL's notion of
3528 // what is available, and is the opposite of the standard usage: Instead of requesting a
3529 // temporary location, it imposes an external constraint (i.e. a specific register is reserved
3530 // for the hidden argument). Note that this works even if VIXL needs a scratch register itself
3531 // (to materialize the constant), since the destination register becomes available for such use
3532 // internally for the duration of the macro instruction.
3533 UseScratchRegisterScope temps(GetVIXLAssembler());
Nicolas Geoffray8d34a182020-09-16 09:46:58 +01003534 temps.Exclude(RegisterFrom(hidden_reg));
3535 if (invoke->GetHiddenArgumentLoadKind() == MethodLoadKind::kRecursive) {
3536 Location current_method = locations->InAt(invoke->GetNumberOfArguments() - 1);
3537 if (current_method.IsStackSlot()) {
3538 GetAssembler()->LoadFromOffset(
3539 kLoadWord, RegisterFrom(hidden_reg), sp, current_method.GetStackIndex());
3540 } else {
3541 __ Mov(RegisterFrom(hidden_reg), RegisterFrom(current_method));
3542 }
Nicolas Geoffrayd6bd1072020-11-30 18:42:01 +00003543 } else if (invoke->GetHiddenArgumentLoadKind() == MethodLoadKind::kRuntimeCall) {
3544 // We pass the method from the IMT in case of a conflict. This will ensure
3545 // we go into the runtime to resolve the actual method.
3546 CHECK_NE(temp.GetCode(), lr.GetCode());
3547 __ Mov(RegisterFrom(hidden_reg), temp);
Nicolas Geoffray8d34a182020-09-16 09:46:58 +01003548 } else {
3549 codegen_->LoadMethod(invoke->GetHiddenArgumentLoadKind(), hidden_reg, invoke);
3550 }
Scott Wakelingb77051e2016-11-21 19:46:00 +00003551 }
Artem Serovcfbe9132016-10-14 15:58:56 +01003552 {
Alexandre Rames374ddf32016-11-04 10:40:49 +00003553 // Ensure the pc position is recorded immediately after the `blx` instruction.
3554 // 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 +00003555 ExactAssemblyScope aas(GetVIXLAssembler(),
Alexandre Rames374ddf32016-11-04 10:40:49 +00003556 vixl32::k16BitT32InstructionSizeInBytes,
3557 CodeBufferCheckScope::kExactSize);
Artem Serovcfbe9132016-10-14 15:58:56 +01003558 // LR();
3559 __ blx(lr);
Artem Serovcfbe9132016-10-14 15:58:56 +01003560 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
Alexandre Rames374ddf32016-11-04 10:40:49 +00003561 DCHECK(!codegen_->IsLeafMethod());
Artem Serovcfbe9132016-10-14 15:58:56 +01003562 }
Roland Levillain5daa4952017-07-03 17:23:56 +01003563
Andreas Gampe3db70682018-12-26 15:12:03 -08003564 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 8);
Artem Serovcfbe9132016-10-14 15:58:56 +01003565}
3566
Orion Hodsonac141392017-01-13 11:53:47 +00003567void LocationsBuilderARMVIXL::VisitInvokePolymorphic(HInvokePolymorphic* invoke) {
Andra Danciua0130e82020-07-23 12:34:56 +00003568 IntrinsicLocationsBuilderARMVIXL intrinsic(codegen_);
3569 if (intrinsic.TryDispatch(invoke)) {
3570 return;
3571 }
Orion Hodsonac141392017-01-13 11:53:47 +00003572 HandleInvoke(invoke);
3573}
3574
3575void InstructionCodeGeneratorARMVIXL::VisitInvokePolymorphic(HInvokePolymorphic* invoke) {
Andra Danciua0130e82020-07-23 12:34:56 +00003576 if (TryGenerateIntrinsicCode(invoke, codegen_)) {
3577 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 9);
3578 return;
3579 }
Orion Hodsonac141392017-01-13 11:53:47 +00003580 codegen_->GenerateInvokePolymorphicCall(invoke);
Andra Danciua0130e82020-07-23 12:34:56 +00003581 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 10);
Orion Hodsonac141392017-01-13 11:53:47 +00003582}
3583
Orion Hodson4c8e12e2018-05-18 08:33:20 +01003584void LocationsBuilderARMVIXL::VisitInvokeCustom(HInvokeCustom* invoke) {
3585 HandleInvoke(invoke);
3586}
3587
3588void InstructionCodeGeneratorARMVIXL::VisitInvokeCustom(HInvokeCustom* invoke) {
3589 codegen_->GenerateInvokeCustomCall(invoke);
Andra Danciua0130e82020-07-23 12:34:56 +00003590 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 11);
Orion Hodson4c8e12e2018-05-18 08:33:20 +01003591}
3592
Artem Serov02109dd2016-09-23 17:17:54 +01003593void LocationsBuilderARMVIXL::VisitNeg(HNeg* neg) {
3594 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003595 new (GetGraph()->GetAllocator()) LocationSummary(neg, LocationSummary::kNoCall);
Artem Serov02109dd2016-09-23 17:17:54 +01003596 switch (neg->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003597 case DataType::Type::kInt32: {
Artem Serov02109dd2016-09-23 17:17:54 +01003598 locations->SetInAt(0, Location::RequiresRegister());
3599 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3600 break;
3601 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003602 case DataType::Type::kInt64: {
Artem Serov02109dd2016-09-23 17:17:54 +01003603 locations->SetInAt(0, Location::RequiresRegister());
3604 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
3605 break;
3606 }
3607
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003608 case DataType::Type::kFloat32:
3609 case DataType::Type::kFloat64:
Artem Serov02109dd2016-09-23 17:17:54 +01003610 locations->SetInAt(0, Location::RequiresFpuRegister());
3611 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3612 break;
3613
3614 default:
3615 LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
3616 }
3617}
3618
3619void InstructionCodeGeneratorARMVIXL::VisitNeg(HNeg* neg) {
3620 LocationSummary* locations = neg->GetLocations();
3621 Location out = locations->Out();
3622 Location in = locations->InAt(0);
3623 switch (neg->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003624 case DataType::Type::kInt32:
Artem Serov02109dd2016-09-23 17:17:54 +01003625 __ Rsb(OutputRegister(neg), InputRegisterAt(neg, 0), 0);
3626 break;
3627
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003628 case DataType::Type::kInt64:
Artem Serov02109dd2016-09-23 17:17:54 +01003629 // out.lo = 0 - in.lo (and update the carry/borrow (C) flag)
3630 __ Rsbs(LowRegisterFrom(out), LowRegisterFrom(in), 0);
3631 // We cannot emit an RSC (Reverse Subtract with Carry)
3632 // instruction here, as it does not exist in the Thumb-2
3633 // instruction set. We use the following approach
3634 // using SBC and SUB instead.
3635 //
3636 // out.hi = -C
3637 __ Sbc(HighRegisterFrom(out), HighRegisterFrom(out), HighRegisterFrom(out));
3638 // out.hi = out.hi - in.hi
3639 __ Sub(HighRegisterFrom(out), HighRegisterFrom(out), HighRegisterFrom(in));
3640 break;
3641
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003642 case DataType::Type::kFloat32:
3643 case DataType::Type::kFloat64:
Anton Kirilov644032c2016-12-06 17:51:43 +00003644 __ Vneg(OutputVRegister(neg), InputVRegister(neg));
Artem Serov02109dd2016-09-23 17:17:54 +01003645 break;
3646
3647 default:
3648 LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
3649 }
3650}
3651
Scott Wakelingfe885462016-09-22 10:24:38 +01003652void LocationsBuilderARMVIXL::VisitTypeConversion(HTypeConversion* conversion) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003653 DataType::Type result_type = conversion->GetResultType();
3654 DataType::Type input_type = conversion->GetInputType();
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003655 DCHECK(!DataType::IsTypeConversionImplicit(input_type, result_type))
3656 << input_type << " -> " << result_type;
Scott Wakelingfe885462016-09-22 10:24:38 +01003657
3658 // The float-to-long, double-to-long and long-to-float type conversions
3659 // rely on a call to the runtime.
3660 LocationSummary::CallKind call_kind =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003661 (((input_type == DataType::Type::kFloat32 || input_type == DataType::Type::kFloat64)
3662 && result_type == DataType::Type::kInt64)
3663 || (input_type == DataType::Type::kInt64 && result_type == DataType::Type::kFloat32))
Scott Wakelingfe885462016-09-22 10:24:38 +01003664 ? LocationSummary::kCallOnMainOnly
3665 : LocationSummary::kNoCall;
3666 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003667 new (GetGraph()->GetAllocator()) LocationSummary(conversion, call_kind);
Scott Wakelingfe885462016-09-22 10:24:38 +01003668
Scott Wakelingfe885462016-09-22 10:24:38 +01003669 switch (result_type) {
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003670 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003671 case DataType::Type::kInt8:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003672 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003673 case DataType::Type::kInt16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003674 DCHECK(DataType::IsIntegralType(input_type)) << input_type;
3675 locations->SetInAt(0, Location::RequiresRegister());
3676 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Scott Wakelingfe885462016-09-22 10:24:38 +01003677 break;
3678
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003679 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003680 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003681 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003682 locations->SetInAt(0, Location::Any());
3683 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3684 break;
3685
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003686 case DataType::Type::kFloat32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003687 locations->SetInAt(0, Location::RequiresFpuRegister());
3688 locations->SetOut(Location::RequiresRegister());
3689 locations->AddTemp(Location::RequiresFpuRegister());
3690 break;
3691
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003692 case DataType::Type::kFloat64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003693 locations->SetInAt(0, Location::RequiresFpuRegister());
3694 locations->SetOut(Location::RequiresRegister());
3695 locations->AddTemp(Location::RequiresFpuRegister());
3696 break;
3697
3698 default:
3699 LOG(FATAL) << "Unexpected type conversion from " << input_type
3700 << " to " << result_type;
3701 }
3702 break;
3703
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003704 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003705 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003706 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003707 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003708 case DataType::Type::kInt8:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003709 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003710 case DataType::Type::kInt16:
3711 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003712 locations->SetInAt(0, Location::RequiresRegister());
3713 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3714 break;
3715
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003716 case DataType::Type::kFloat32: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003717 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3718 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
3719 locations->SetOut(LocationFrom(r0, r1));
Scott Wakelingfe885462016-09-22 10:24:38 +01003720 break;
3721 }
3722
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003723 case DataType::Type::kFloat64: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003724 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3725 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0),
3726 calling_convention.GetFpuRegisterAt(1)));
3727 locations->SetOut(LocationFrom(r0, r1));
Scott Wakelingfe885462016-09-22 10:24:38 +01003728 break;
3729 }
3730
3731 default:
3732 LOG(FATAL) << "Unexpected type conversion from " << input_type
3733 << " to " << result_type;
3734 }
3735 break;
3736
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003737 case DataType::Type::kFloat32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003738 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003739 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003740 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003741 case DataType::Type::kInt8:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003742 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003743 case DataType::Type::kInt16:
3744 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003745 locations->SetInAt(0, Location::RequiresRegister());
3746 locations->SetOut(Location::RequiresFpuRegister());
3747 break;
3748
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003749 case DataType::Type::kInt64: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003750 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3751 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0),
3752 calling_convention.GetRegisterAt(1)));
3753 locations->SetOut(LocationFrom(calling_convention.GetFpuRegisterAt(0)));
Scott Wakelingfe885462016-09-22 10:24:38 +01003754 break;
3755 }
3756
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003757 case DataType::Type::kFloat64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003758 locations->SetInAt(0, Location::RequiresFpuRegister());
3759 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3760 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 Wakelingfe885462016-09-22 10:24:38 +01003776 locations->SetInAt(0, Location::RequiresRegister());
3777 locations->SetOut(Location::RequiresFpuRegister());
3778 break;
3779
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003780 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003781 locations->SetInAt(0, Location::RequiresRegister());
3782 locations->SetOut(Location::RequiresFpuRegister());
3783 locations->AddTemp(Location::RequiresFpuRegister());
3784 locations->AddTemp(Location::RequiresFpuRegister());
3785 break;
3786
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003787 case DataType::Type::kFloat32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003788 locations->SetInAt(0, Location::RequiresFpuRegister());
3789 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3790 break;
3791
3792 default:
3793 LOG(FATAL) << "Unexpected type conversion from " << input_type
3794 << " to " << result_type;
Igor Murashkin2ffb7032017-11-08 13:35:21 -08003795 }
Scott Wakelingfe885462016-09-22 10:24:38 +01003796 break;
3797
3798 default:
3799 LOG(FATAL) << "Unexpected type conversion from " << input_type
3800 << " to " << result_type;
3801 }
3802}
3803
3804void InstructionCodeGeneratorARMVIXL::VisitTypeConversion(HTypeConversion* conversion) {
3805 LocationSummary* locations = conversion->GetLocations();
3806 Location out = locations->Out();
3807 Location in = locations->InAt(0);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003808 DataType::Type result_type = conversion->GetResultType();
3809 DataType::Type input_type = conversion->GetInputType();
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003810 DCHECK(!DataType::IsTypeConversionImplicit(input_type, result_type))
3811 << input_type << " -> " << result_type;
Scott Wakelingfe885462016-09-22 10:24:38 +01003812 switch (result_type) {
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003813 case DataType::Type::kUint8:
Scott Wakelingfe885462016-09-22 10:24:38 +01003814 switch (input_type) {
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003815 case DataType::Type::kInt8:
3816 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003817 case DataType::Type::kInt16:
3818 case DataType::Type::kInt32:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003819 __ Ubfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 8);
3820 break;
3821 case DataType::Type::kInt64:
3822 __ Ubfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 8);
3823 break;
3824
3825 default:
3826 LOG(FATAL) << "Unexpected type conversion from " << input_type
3827 << " to " << result_type;
3828 }
3829 break;
3830
3831 case DataType::Type::kInt8:
3832 switch (input_type) {
3833 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003834 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003835 case DataType::Type::kInt16:
3836 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003837 __ Sbfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 8);
3838 break;
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003839 case DataType::Type::kInt64:
3840 __ Sbfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 8);
3841 break;
3842
3843 default:
3844 LOG(FATAL) << "Unexpected type conversion from " << input_type
3845 << " to " << result_type;
3846 }
3847 break;
3848
3849 case DataType::Type::kUint16:
3850 switch (input_type) {
3851 case DataType::Type::kInt8:
3852 case DataType::Type::kInt16:
3853 case DataType::Type::kInt32:
3854 __ Ubfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 16);
3855 break;
3856 case DataType::Type::kInt64:
3857 __ Ubfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 16);
3858 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01003859
3860 default:
3861 LOG(FATAL) << "Unexpected type conversion from " << input_type
3862 << " to " << result_type;
3863 }
3864 break;
3865
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003866 case DataType::Type::kInt16:
Scott Wakelingfe885462016-09-22 10:24:38 +01003867 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003868 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003869 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003870 __ Sbfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 16);
3871 break;
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003872 case DataType::Type::kInt64:
3873 __ Sbfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 16);
3874 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01003875
3876 default:
3877 LOG(FATAL) << "Unexpected type conversion from " << input_type
3878 << " to " << result_type;
3879 }
3880 break;
3881
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003882 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003883 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003884 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003885 DCHECK(out.IsRegister());
3886 if (in.IsRegisterPair()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003887 __ Mov(OutputRegister(conversion), LowRegisterFrom(in));
Scott Wakelingfe885462016-09-22 10:24:38 +01003888 } else if (in.IsDoubleStackSlot()) {
3889 GetAssembler()->LoadFromOffset(kLoadWord,
3890 OutputRegister(conversion),
3891 sp,
3892 in.GetStackIndex());
3893 } else {
3894 DCHECK(in.IsConstant());
3895 DCHECK(in.GetConstant()->IsLongConstant());
Vladimir Markoba1a48e2017-04-13 11:50:14 +01003896 int64_t value = in.GetConstant()->AsLongConstant()->GetValue();
3897 __ Mov(OutputRegister(conversion), static_cast<int32_t>(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01003898 }
3899 break;
3900
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003901 case DataType::Type::kFloat32: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003902 vixl32::SRegister temp = LowSRegisterFrom(locations->GetTemp(0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01003903 __ Vcvt(S32, F32, temp, InputSRegisterAt(conversion, 0));
Scott Wakelingfe885462016-09-22 10:24:38 +01003904 __ Vmov(OutputRegister(conversion), temp);
3905 break;
3906 }
3907
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003908 case DataType::Type::kFloat64: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003909 vixl32::SRegister temp_s = LowSRegisterFrom(locations->GetTemp(0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01003910 __ Vcvt(S32, F64, temp_s, DRegisterFrom(in));
Scott Wakelingfe885462016-09-22 10:24:38 +01003911 __ Vmov(OutputRegister(conversion), temp_s);
3912 break;
3913 }
3914
3915 default:
3916 LOG(FATAL) << "Unexpected type conversion from " << input_type
3917 << " to " << result_type;
3918 }
3919 break;
3920
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003921 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003922 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003923 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003924 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003925 case DataType::Type::kInt8:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003926 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003927 case DataType::Type::kInt16:
3928 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003929 DCHECK(out.IsRegisterPair());
3930 DCHECK(in.IsRegister());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003931 __ Mov(LowRegisterFrom(out), InputRegisterAt(conversion, 0));
Scott Wakelingfe885462016-09-22 10:24:38 +01003932 // Sign extension.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003933 __ Asr(HighRegisterFrom(out), LowRegisterFrom(out), 31);
Scott Wakelingfe885462016-09-22 10:24:38 +01003934 break;
3935
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003936 case DataType::Type::kFloat32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003937 codegen_->InvokeRuntime(kQuickF2l, conversion, conversion->GetDexPc());
3938 CheckEntrypointTypes<kQuickF2l, int64_t, float>();
3939 break;
3940
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003941 case DataType::Type::kFloat64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003942 codegen_->InvokeRuntime(kQuickD2l, conversion, conversion->GetDexPc());
3943 CheckEntrypointTypes<kQuickD2l, int64_t, double>();
3944 break;
3945
3946 default:
3947 LOG(FATAL) << "Unexpected type conversion from " << input_type
3948 << " to " << result_type;
3949 }
3950 break;
3951
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003952 case DataType::Type::kFloat32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003953 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003954 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003955 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003956 case DataType::Type::kInt8:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003957 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003958 case DataType::Type::kInt16:
3959 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003960 __ Vmov(OutputSRegister(conversion), InputRegisterAt(conversion, 0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01003961 __ Vcvt(F32, S32, OutputSRegister(conversion), OutputSRegister(conversion));
Scott Wakelingfe885462016-09-22 10:24:38 +01003962 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01003963
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003964 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003965 codegen_->InvokeRuntime(kQuickL2f, conversion, conversion->GetDexPc());
3966 CheckEntrypointTypes<kQuickL2f, float, int64_t>();
3967 break;
3968
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003969 case DataType::Type::kFloat64:
Scott Wakelingc34dba72016-10-03 10:14:44 +01003970 __ Vcvt(F32, F64, OutputSRegister(conversion), DRegisterFrom(in));
Scott Wakelingfe885462016-09-22 10:24:38 +01003971 break;
3972
3973 default:
3974 LOG(FATAL) << "Unexpected type conversion from " << input_type
3975 << " to " << result_type;
Igor Murashkin2ffb7032017-11-08 13:35:21 -08003976 }
Scott Wakelingfe885462016-09-22 10:24:38 +01003977 break;
3978
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003979 case DataType::Type::kFloat64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003980 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003981 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003982 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003983 case DataType::Type::kInt8:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003984 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003985 case DataType::Type::kInt16:
3986 case DataType::Type::kInt32:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003987 __ Vmov(LowSRegisterFrom(out), InputRegisterAt(conversion, 0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01003988 __ Vcvt(F64, S32, DRegisterFrom(out), LowSRegisterFrom(out));
Scott Wakelingfe885462016-09-22 10:24:38 +01003989 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01003990
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003991 case DataType::Type::kInt64: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003992 vixl32::Register low = LowRegisterFrom(in);
3993 vixl32::Register high = HighRegisterFrom(in);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003994 vixl32::SRegister out_s = LowSRegisterFrom(out);
Scott Wakelingc34dba72016-10-03 10:14:44 +01003995 vixl32::DRegister out_d = DRegisterFrom(out);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003996 vixl32::SRegister temp_s = LowSRegisterFrom(locations->GetTemp(0));
Scott Wakelingc34dba72016-10-03 10:14:44 +01003997 vixl32::DRegister temp_d = DRegisterFrom(locations->GetTemp(0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01003998 vixl32::DRegister constant_d = DRegisterFrom(locations->GetTemp(1));
Scott Wakelingfe885462016-09-22 10:24:38 +01003999
4000 // temp_d = int-to-double(high)
4001 __ Vmov(temp_s, high);
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01004002 __ Vcvt(F64, S32, temp_d, temp_s);
Scott Wakelingfe885462016-09-22 10:24:38 +01004003 // constant_d = k2Pow32EncodingForDouble
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004004 __ Vmov(constant_d, bit_cast<double, int64_t>(k2Pow32EncodingForDouble));
Scott Wakelingfe885462016-09-22 10:24:38 +01004005 // out_d = unsigned-to-double(low)
4006 __ Vmov(out_s, low);
4007 __ Vcvt(F64, U32, out_d, out_s);
4008 // out_d += temp_d * constant_d
4009 __ Vmla(F64, out_d, temp_d, constant_d);
4010 break;
4011 }
4012
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004013 case DataType::Type::kFloat32:
Scott Wakelingc34dba72016-10-03 10:14:44 +01004014 __ Vcvt(F64, F32, DRegisterFrom(out), InputSRegisterAt(conversion, 0));
Scott Wakelingfe885462016-09-22 10:24:38 +01004015 break;
4016
4017 default:
4018 LOG(FATAL) << "Unexpected type conversion from " << input_type
4019 << " to " << result_type;
Igor Murashkin2ffb7032017-11-08 13:35:21 -08004020 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004021 break;
4022
4023 default:
4024 LOG(FATAL) << "Unexpected type conversion from " << input_type
4025 << " to " << result_type;
4026 }
4027}
4028
4029void LocationsBuilderARMVIXL::VisitAdd(HAdd* add) {
4030 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01004031 new (GetGraph()->GetAllocator()) LocationSummary(add, LocationSummary::kNoCall);
Scott Wakelingfe885462016-09-22 10:24:38 +01004032 switch (add->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004033 case DataType::Type::kInt32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004034 locations->SetInAt(0, Location::RequiresRegister());
4035 locations->SetInAt(1, Location::RegisterOrConstant(add->InputAt(1)));
4036 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4037 break;
4038 }
4039
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004040 case DataType::Type::kInt64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004041 locations->SetInAt(0, Location::RequiresRegister());
Anton Kirilovdda43962016-11-21 19:55:20 +00004042 locations->SetInAt(1, ArmEncodableConstantOrRegister(add->InputAt(1), ADD));
Scott Wakelingfe885462016-09-22 10:24:38 +01004043 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4044 break;
4045 }
4046
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004047 case DataType::Type::kFloat32:
4048 case DataType::Type::kFloat64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004049 locations->SetInAt(0, Location::RequiresFpuRegister());
4050 locations->SetInAt(1, Location::RequiresFpuRegister());
4051 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4052 break;
4053 }
4054
4055 default:
4056 LOG(FATAL) << "Unexpected add type " << add->GetResultType();
4057 }
4058}
4059
4060void InstructionCodeGeneratorARMVIXL::VisitAdd(HAdd* add) {
4061 LocationSummary* locations = add->GetLocations();
4062 Location out = locations->Out();
4063 Location first = locations->InAt(0);
4064 Location second = locations->InAt(1);
4065
4066 switch (add->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004067 case DataType::Type::kInt32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004068 __ Add(OutputRegister(add), InputRegisterAt(add, 0), InputOperandAt(add, 1));
4069 }
4070 break;
4071
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004072 case DataType::Type::kInt64: {
Anton Kirilovdda43962016-11-21 19:55:20 +00004073 if (second.IsConstant()) {
4074 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
4075 GenerateAddLongConst(out, first, value);
4076 } else {
4077 DCHECK(second.IsRegisterPair());
4078 __ Adds(LowRegisterFrom(out), LowRegisterFrom(first), LowRegisterFrom(second));
4079 __ Adc(HighRegisterFrom(out), HighRegisterFrom(first), HighRegisterFrom(second));
4080 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004081 break;
4082 }
4083
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004084 case DataType::Type::kFloat32:
4085 case DataType::Type::kFloat64:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004086 __ Vadd(OutputVRegister(add), InputVRegisterAt(add, 0), InputVRegisterAt(add, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004087 break;
4088
4089 default:
4090 LOG(FATAL) << "Unexpected add type " << add->GetResultType();
4091 }
4092}
4093
4094void LocationsBuilderARMVIXL::VisitSub(HSub* sub) {
4095 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01004096 new (GetGraph()->GetAllocator()) LocationSummary(sub, LocationSummary::kNoCall);
Scott Wakelingfe885462016-09-22 10:24:38 +01004097 switch (sub->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004098 case DataType::Type::kInt32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004099 locations->SetInAt(0, Location::RequiresRegister());
4100 locations->SetInAt(1, Location::RegisterOrConstant(sub->InputAt(1)));
4101 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4102 break;
4103 }
4104
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004105 case DataType::Type::kInt64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004106 locations->SetInAt(0, Location::RequiresRegister());
Anton Kirilovdda43962016-11-21 19:55:20 +00004107 locations->SetInAt(1, ArmEncodableConstantOrRegister(sub->InputAt(1), SUB));
Scott Wakelingfe885462016-09-22 10:24:38 +01004108 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4109 break;
4110 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004111 case DataType::Type::kFloat32:
4112 case DataType::Type::kFloat64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004113 locations->SetInAt(0, Location::RequiresFpuRegister());
4114 locations->SetInAt(1, Location::RequiresFpuRegister());
4115 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4116 break;
4117 }
4118 default:
4119 LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
4120 }
4121}
4122
4123void InstructionCodeGeneratorARMVIXL::VisitSub(HSub* sub) {
4124 LocationSummary* locations = sub->GetLocations();
4125 Location out = locations->Out();
4126 Location first = locations->InAt(0);
4127 Location second = locations->InAt(1);
4128 switch (sub->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004129 case DataType::Type::kInt32: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004130 __ Sub(OutputRegister(sub), InputRegisterAt(sub, 0), InputOperandAt(sub, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004131 break;
4132 }
4133
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004134 case DataType::Type::kInt64: {
Anton Kirilovdda43962016-11-21 19:55:20 +00004135 if (second.IsConstant()) {
4136 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
4137 GenerateAddLongConst(out, first, -value);
4138 } else {
4139 DCHECK(second.IsRegisterPair());
4140 __ Subs(LowRegisterFrom(out), LowRegisterFrom(first), LowRegisterFrom(second));
4141 __ Sbc(HighRegisterFrom(out), HighRegisterFrom(first), HighRegisterFrom(second));
4142 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004143 break;
4144 }
4145
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004146 case DataType::Type::kFloat32:
4147 case DataType::Type::kFloat64:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004148 __ Vsub(OutputVRegister(sub), InputVRegisterAt(sub, 0), InputVRegisterAt(sub, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004149 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01004150
4151 default:
4152 LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
4153 }
4154}
4155
4156void LocationsBuilderARMVIXL::VisitMul(HMul* mul) {
4157 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01004158 new (GetGraph()->GetAllocator()) LocationSummary(mul, LocationSummary::kNoCall);
Scott Wakelingfe885462016-09-22 10:24:38 +01004159 switch (mul->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004160 case DataType::Type::kInt32:
4161 case DataType::Type::kInt64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004162 locations->SetInAt(0, Location::RequiresRegister());
4163 locations->SetInAt(1, Location::RequiresRegister());
4164 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4165 break;
4166 }
4167
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004168 case DataType::Type::kFloat32:
4169 case DataType::Type::kFloat64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004170 locations->SetInAt(0, Location::RequiresFpuRegister());
4171 locations->SetInAt(1, Location::RequiresFpuRegister());
4172 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4173 break;
4174 }
4175
4176 default:
4177 LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
4178 }
4179}
4180
4181void InstructionCodeGeneratorARMVIXL::VisitMul(HMul* mul) {
4182 LocationSummary* locations = mul->GetLocations();
4183 Location out = locations->Out();
4184 Location first = locations->InAt(0);
4185 Location second = locations->InAt(1);
4186 switch (mul->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004187 case DataType::Type::kInt32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004188 __ Mul(OutputRegister(mul), InputRegisterAt(mul, 0), InputRegisterAt(mul, 1));
4189 break;
4190 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004191 case DataType::Type::kInt64: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004192 vixl32::Register out_hi = HighRegisterFrom(out);
4193 vixl32::Register out_lo = LowRegisterFrom(out);
4194 vixl32::Register in1_hi = HighRegisterFrom(first);
4195 vixl32::Register in1_lo = LowRegisterFrom(first);
4196 vixl32::Register in2_hi = HighRegisterFrom(second);
4197 vixl32::Register in2_lo = LowRegisterFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01004198
4199 // Extra checks to protect caused by the existence of R1_R2.
4200 // The algorithm is wrong if out.hi is either in1.lo or in2.lo:
4201 // (e.g. in1=r0_r1, in2=r2_r3 and out=r1_r2);
Anton Kirilov644032c2016-12-06 17:51:43 +00004202 DCHECK(!out_hi.Is(in1_lo));
4203 DCHECK(!out_hi.Is(in2_lo));
Scott Wakelingfe885462016-09-22 10:24:38 +01004204
4205 // input: in1 - 64 bits, in2 - 64 bits
4206 // output: out
4207 // formula: out.hi : out.lo = (in1.lo * in2.hi + in1.hi * in2.lo)* 2^32 + in1.lo * in2.lo
4208 // parts: out.hi = in1.lo * in2.hi + in1.hi * in2.lo + (in1.lo * in2.lo)[63:32]
4209 // parts: out.lo = (in1.lo * in2.lo)[31:0]
4210
4211 UseScratchRegisterScope temps(GetVIXLAssembler());
4212 vixl32::Register temp = temps.Acquire();
4213 // temp <- in1.lo * in2.hi
4214 __ Mul(temp, in1_lo, in2_hi);
4215 // out.hi <- in1.lo * in2.hi + in1.hi * in2.lo
4216 __ Mla(out_hi, in1_hi, in2_lo, temp);
4217 // out.lo <- (in1.lo * in2.lo)[31:0];
4218 __ Umull(out_lo, temp, in1_lo, in2_lo);
4219 // out.hi <- in2.hi * in1.lo + in2.lo * in1.hi + (in1.lo * in2.lo)[63:32]
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004220 __ Add(out_hi, out_hi, temp);
Scott Wakelingfe885462016-09-22 10:24:38 +01004221 break;
4222 }
4223
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004224 case DataType::Type::kFloat32:
4225 case DataType::Type::kFloat64:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004226 __ Vmul(OutputVRegister(mul), InputVRegisterAt(mul, 0), InputVRegisterAt(mul, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004227 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01004228
4229 default:
4230 LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
4231 }
4232}
4233
Scott Wakelingfe885462016-09-22 10:24:38 +01004234void InstructionCodeGeneratorARMVIXL::DivRemOneOrMinusOne(HBinaryOperation* instruction) {
4235 DCHECK(instruction->IsDiv() || instruction->IsRem());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004236 DCHECK(instruction->GetResultType() == DataType::Type::kInt32);
Scott Wakelingfe885462016-09-22 10:24:38 +01004237
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004238 Location second = instruction->GetLocations()->InAt(1);
Scott Wakelingfe885462016-09-22 10:24:38 +01004239 DCHECK(second.IsConstant());
4240
4241 vixl32::Register out = OutputRegister(instruction);
4242 vixl32::Register dividend = InputRegisterAt(instruction, 0);
Anton Kirilov644032c2016-12-06 17:51:43 +00004243 int32_t imm = Int32ConstantFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01004244 DCHECK(imm == 1 || imm == -1);
4245
4246 if (instruction->IsRem()) {
4247 __ Mov(out, 0);
4248 } else {
4249 if (imm == 1) {
4250 __ Mov(out, dividend);
4251 } else {
4252 __ Rsb(out, dividend, 0);
4253 }
4254 }
4255}
4256
4257void InstructionCodeGeneratorARMVIXL::DivRemByPowerOfTwo(HBinaryOperation* instruction) {
4258 DCHECK(instruction->IsDiv() || instruction->IsRem());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004259 DCHECK(instruction->GetResultType() == DataType::Type::kInt32);
Scott Wakelingfe885462016-09-22 10:24:38 +01004260
4261 LocationSummary* locations = instruction->GetLocations();
4262 Location second = locations->InAt(1);
4263 DCHECK(second.IsConstant());
4264
4265 vixl32::Register out = OutputRegister(instruction);
4266 vixl32::Register dividend = InputRegisterAt(instruction, 0);
Anton Kirilov644032c2016-12-06 17:51:43 +00004267 int32_t imm = Int32ConstantFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01004268 uint32_t abs_imm = static_cast<uint32_t>(AbsOrMin(imm));
4269 int ctz_imm = CTZ(abs_imm);
4270
Evgeny Astigeevichaf92a0f2020-06-26 13:28:33 +01004271 auto generate_div_code = [this, imm, ctz_imm](vixl32::Register out, vixl32::Register in) {
4272 __ Asr(out, in, ctz_imm);
Scott Wakelingfe885462016-09-22 10:24:38 +01004273 if (imm < 0) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004274 __ Rsb(out, out, 0);
Scott Wakelingfe885462016-09-22 10:24:38 +01004275 }
Evgeny Astigeevichaf92a0f2020-06-26 13:28:33 +01004276 };
4277
Evgeny Astigeevich0f3d7ac2020-08-06 16:28:37 +01004278 if (HasNonNegativeOrMinIntInputAt(instruction, 0)) {
Evgeny Astigeevichaf92a0f2020-06-26 13:28:33 +01004279 // No need to adjust the result for non-negative dividends or the INT32_MIN dividend.
4280 // NOTE: The generated code for HDiv/HRem correctly works for the INT32_MIN dividend:
4281 // imm == 2
4282 // HDiv
4283 // add out, dividend(0x80000000), dividend(0x80000000), lsr #31 => out = 0x80000001
4284 // asr out, out(0x80000001), #1 => out = 0xc0000000
4285 // This is the same as 'asr out, dividend(0x80000000), #1'
4286 //
4287 // imm > 2
4288 // HDiv
4289 // asr out, dividend(0x80000000), #31 => out = -1
4290 // add out, dividend(0x80000000), out(-1), lsr #(32 - ctz_imm) => out = 0b10..01..1,
4291 // where the number of the rightmost 1s is ctz_imm.
4292 // asr out, out(0b10..01..1), #ctz_imm => out = 0b1..10..0, where the number of the
4293 // leftmost 1s is ctz_imm + 1.
4294 // This is the same as 'asr out, dividend(0x80000000), #ctz_imm'.
4295 //
4296 // imm == INT32_MIN
4297 // HDiv
4298 // asr out, dividend(0x80000000), #31 => out = -1
4299 // add out, dividend(0x80000000), out(-1), lsr #1 => out = 0xc0000000
4300 // asr out, out(0xc0000000), #31 => out = -1
4301 // rsb out, out(-1), #0 => out = 1
4302 // This is the same as
4303 // asr out, dividend(0x80000000), #31
4304 // rsb out, out, #0
4305 //
4306 //
4307 // INT_MIN % imm must be 0 for any imm of power 2. 'and' and 'ubfx' work only with bits
4308 // 0..30 of a dividend. For INT32_MIN those bits are zeros. So 'and' and 'ubfx' always
4309 // produce zero.
4310 if (instruction->IsDiv()) {
4311 generate_div_code(out, dividend);
4312 } else {
4313 if (GetVIXLAssembler()->IsModifiedImmediate(abs_imm - 1)) {
4314 __ And(out, dividend, abs_imm - 1);
4315 } else {
4316 __ Ubfx(out, dividend, 0, ctz_imm);
4317 }
4318 return;
4319 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004320 } else {
Evgeny Astigeevichaf92a0f2020-06-26 13:28:33 +01004321 vixl32::Register add_right_input = dividend;
4322 if (ctz_imm > 1) {
4323 __ Asr(out, dividend, 31);
4324 add_right_input = out;
4325 }
4326 __ Add(out, dividend, Operand(add_right_input, vixl32::LSR, 32 - ctz_imm));
4327
4328 if (instruction->IsDiv()) {
4329 generate_div_code(out, out);
4330 } else {
4331 __ Bfc(out, 0, ctz_imm);
4332 __ Sub(out, dividend, out);
4333 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004334 }
4335}
4336
4337void InstructionCodeGeneratorARMVIXL::GenerateDivRemWithAnyConstant(HBinaryOperation* instruction) {
4338 DCHECK(instruction->IsDiv() || instruction->IsRem());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004339 DCHECK(instruction->GetResultType() == DataType::Type::kInt32);
Scott Wakelingfe885462016-09-22 10:24:38 +01004340
4341 LocationSummary* locations = instruction->GetLocations();
4342 Location second = locations->InAt(1);
4343 DCHECK(second.IsConstant());
4344
4345 vixl32::Register out = OutputRegister(instruction);
4346 vixl32::Register dividend = InputRegisterAt(instruction, 0);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004347 vixl32::Register temp1 = RegisterFrom(locations->GetTemp(0));
4348 vixl32::Register temp2 = RegisterFrom(locations->GetTemp(1));
Scott Wakelingb77051e2016-11-21 19:46:00 +00004349 int32_t imm = Int32ConstantFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01004350
4351 int64_t magic;
4352 int shift;
Andreas Gampe3db70682018-12-26 15:12:03 -08004353 CalculateMagicAndShiftForDivRem(imm, /* is_long= */ false, &magic, &shift);
Scott Wakelingfe885462016-09-22 10:24:38 +01004354
Evgeny Astigeevichf9388412020-07-02 15:25:13 +01004355 auto generate_unsigned_div_code =[this, magic, shift](vixl32::Register out,
4356 vixl32::Register dividend,
4357 vixl32::Register temp1,
4358 vixl32::Register temp2) {
4359 // TODO(VIXL): Change the static cast to Operand::From() after VIXL is fixed.
4360 __ Mov(temp1, static_cast<int32_t>(magic));
4361 if (magic > 0 && shift == 0) {
4362 __ Smull(temp2, out, dividend, temp1);
4363 } else {
4364 __ Smull(temp2, temp1, dividend, temp1);
4365 if (magic < 0) {
4366 // The negative magic M = static_cast<int>(m) means that the multiplier m is greater
4367 // than INT32_MAX. In such a case shift is never 0.
4368 // Proof:
4369 // m = (2^p + d - 2^p % d) / d, where p = 32 + shift, d > 2
4370 //
4371 // If shift == 0, m = (2^32 + d - 2^32 % d) / d =
4372 // = (2^32 + d - (2^32 - (2^32 / d) * d)) / d =
4373 // = (d + (2^32 / d) * d) / d = 1 + (2^32 / d), here '/' is the integer division.
4374 //
4375 // 1 + (2^32 / d) is decreasing when d is increasing.
4376 // The maximum is 1 431 655 766, when d == 3. This value is less than INT32_MAX.
4377 // the minimum is 3, when d = 2^31 -1.
4378 // So for all values of d in [3, INT32_MAX] m with p == 32 is in [3, INT32_MAX) and
4379 // is never less than 0.
4380 __ Add(temp1, temp1, dividend);
4381 }
4382 DCHECK_NE(shift, 0);
4383 __ Lsr(out, temp1, shift);
4384 }
4385 };
Scott Wakelingfe885462016-09-22 10:24:38 +01004386
Evgeny Astigeevich0f3d7ac2020-08-06 16:28:37 +01004387 if (imm > 0 && HasNonNegativeInputAt(instruction, 0)) {
Evgeny Astigeevichf9388412020-07-02 15:25:13 +01004388 // No need to adjust the result for a non-negative dividend and a positive divisor.
4389 if (instruction->IsDiv()) {
4390 generate_unsigned_div_code(out, dividend, temp1, temp2);
4391 } else {
4392 generate_unsigned_div_code(temp1, dividend, temp1, temp2);
4393 __ Mov(temp2, imm);
4394 __ Mls(out, temp1, temp2, dividend);
4395 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004396 } else {
Evgeny Astigeevichf9388412020-07-02 15:25:13 +01004397 // TODO(VIXL): Change the static cast to Operand::From() after VIXL is fixed.
4398 __ Mov(temp1, static_cast<int32_t>(magic));
4399 __ Smull(temp2, temp1, dividend, temp1);
4400
4401 if (imm > 0 && magic < 0) {
4402 __ Add(temp1, temp1, dividend);
4403 } else if (imm < 0 && magic > 0) {
4404 __ Sub(temp1, temp1, dividend);
4405 }
4406
4407 if (shift != 0) {
4408 __ Asr(temp1, temp1, shift);
4409 }
4410
4411 if (instruction->IsDiv()) {
4412 __ Sub(out, temp1, Operand(temp1, vixl32::Shift(ASR), 31));
4413 } else {
4414 __ Sub(temp1, temp1, Operand(temp1, vixl32::Shift(ASR), 31));
4415 // TODO: Strength reduction for mls.
4416 __ Mov(temp2, imm);
4417 __ Mls(out, temp1, temp2, dividend);
4418 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004419 }
4420}
4421
4422void InstructionCodeGeneratorARMVIXL::GenerateDivRemConstantIntegral(
4423 HBinaryOperation* instruction) {
4424 DCHECK(instruction->IsDiv() || instruction->IsRem());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004425 DCHECK(instruction->GetResultType() == DataType::Type::kInt32);
Scott Wakelingfe885462016-09-22 10:24:38 +01004426
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004427 Location second = instruction->GetLocations()->InAt(1);
Scott Wakelingfe885462016-09-22 10:24:38 +01004428 DCHECK(second.IsConstant());
4429
Anton Kirilov644032c2016-12-06 17:51:43 +00004430 int32_t imm = Int32ConstantFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01004431 if (imm == 0) {
4432 // Do not generate anything. DivZeroCheck would prevent any code to be executed.
4433 } else if (imm == 1 || imm == -1) {
4434 DivRemOneOrMinusOne(instruction);
4435 } else if (IsPowerOfTwo(AbsOrMin(imm))) {
4436 DivRemByPowerOfTwo(instruction);
4437 } else {
4438 DCHECK(imm <= -2 || imm >= 2);
4439 GenerateDivRemWithAnyConstant(instruction);
4440 }
4441}
4442
4443void LocationsBuilderARMVIXL::VisitDiv(HDiv* div) {
4444 LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004445 if (div->GetResultType() == DataType::Type::kInt64) {
Scott Wakelingfe885462016-09-22 10:24:38 +01004446 // pLdiv runtime call.
4447 call_kind = LocationSummary::kCallOnMainOnly;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004448 } else if (div->GetResultType() == DataType::Type::kInt32 && div->InputAt(1)->IsConstant()) {
Scott Wakelingfe885462016-09-22 10:24:38 +01004449 // sdiv will be replaced by other instruction sequence.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004450 } else if (div->GetResultType() == DataType::Type::kInt32 &&
Scott Wakelingfe885462016-09-22 10:24:38 +01004451 !codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4452 // pIdivmod runtime call.
4453 call_kind = LocationSummary::kCallOnMainOnly;
4454 }
4455
Vladimir Markoca6fff82017-10-03 14:49:14 +01004456 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(div, call_kind);
Scott Wakelingfe885462016-09-22 10:24:38 +01004457
4458 switch (div->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004459 case DataType::Type::kInt32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004460 if (div->InputAt(1)->IsConstant()) {
4461 locations->SetInAt(0, Location::RequiresRegister());
4462 locations->SetInAt(1, Location::ConstantLocation(div->InputAt(1)->AsConstant()));
Anton Kirilov644032c2016-12-06 17:51:43 +00004463 int32_t value = Int32ConstantFrom(div->InputAt(1));
Evgeny Astigeevich2d101172020-06-25 16:52:03 +01004464 Location::OutputOverlap out_overlaps = Location::kNoOutputOverlap;
Scott Wakelingfe885462016-09-22 10:24:38 +01004465 if (value == 1 || value == 0 || value == -1) {
4466 // No temp register required.
Evgeny Astigeevichaf92a0f2020-06-26 13:28:33 +01004467 } else if (IsPowerOfTwo(AbsOrMin(value)) &&
4468 value != 2 &&
4469 value != -2 &&
Evgeny Astigeevich0f3d7ac2020-08-06 16:28:37 +01004470 !HasNonNegativeOrMinIntInputAt(div, 0)) {
Evgeny Astigeevich2d101172020-06-25 16:52:03 +01004471 // The "out" register is used as a temporary, so it overlaps with the inputs.
4472 out_overlaps = Location::kOutputOverlap;
Scott Wakelingfe885462016-09-22 10:24:38 +01004473 } else {
Evgeny Astigeevich2d101172020-06-25 16:52:03 +01004474 locations->AddRegisterTemps(2);
Scott Wakelingfe885462016-09-22 10:24:38 +01004475 }
Evgeny Astigeevich2d101172020-06-25 16:52:03 +01004476 locations->SetOut(Location::RequiresRegister(), out_overlaps);
Scott Wakelingfe885462016-09-22 10:24:38 +01004477 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4478 locations->SetInAt(0, Location::RequiresRegister());
4479 locations->SetInAt(1, Location::RequiresRegister());
4480 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4481 } else {
Artem Serov551b28f2016-10-18 19:11:30 +01004482 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4483 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
4484 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
Roland Levillain5e8d5f02016-10-18 18:03:43 +01004485 // Note: divmod will compute both the quotient and the remainder as the pair R0 and R1, but
Artem Serov551b28f2016-10-18 19:11:30 +01004486 // we only need the former.
4487 locations->SetOut(LocationFrom(r0));
Scott Wakelingfe885462016-09-22 10:24:38 +01004488 }
4489 break;
4490 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004491 case DataType::Type::kInt64: {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004492 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4493 locations->SetInAt(0, LocationFrom(
4494 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
4495 locations->SetInAt(1, LocationFrom(
4496 calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3)));
4497 locations->SetOut(LocationFrom(r0, r1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004498 break;
4499 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004500 case DataType::Type::kFloat32:
4501 case DataType::Type::kFloat64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004502 locations->SetInAt(0, Location::RequiresFpuRegister());
4503 locations->SetInAt(1, Location::RequiresFpuRegister());
4504 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4505 break;
4506 }
4507
4508 default:
4509 LOG(FATAL) << "Unexpected div type " << div->GetResultType();
4510 }
4511}
4512
4513void InstructionCodeGeneratorARMVIXL::VisitDiv(HDiv* div) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004514 Location lhs = div->GetLocations()->InAt(0);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004515 Location rhs = div->GetLocations()->InAt(1);
Scott Wakelingfe885462016-09-22 10:24:38 +01004516
4517 switch (div->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004518 case DataType::Type::kInt32: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004519 if (rhs.IsConstant()) {
Scott Wakelingfe885462016-09-22 10:24:38 +01004520 GenerateDivRemConstantIntegral(div);
4521 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4522 __ Sdiv(OutputRegister(div), InputRegisterAt(div, 0), InputRegisterAt(div, 1));
4523 } else {
Artem Serov551b28f2016-10-18 19:11:30 +01004524 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4525 DCHECK(calling_convention.GetRegisterAt(0).Is(RegisterFrom(lhs)));
4526 DCHECK(calling_convention.GetRegisterAt(1).Is(RegisterFrom(rhs)));
4527 DCHECK(r0.Is(OutputRegister(div)));
4528
4529 codegen_->InvokeRuntime(kQuickIdivmod, div, div->GetDexPc());
4530 CheckEntrypointTypes<kQuickIdivmod, int32_t, int32_t, int32_t>();
Scott Wakelingfe885462016-09-22 10:24:38 +01004531 }
4532 break;
4533 }
4534
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004535 case DataType::Type::kInt64: {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004536 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4537 DCHECK(calling_convention.GetRegisterAt(0).Is(LowRegisterFrom(lhs)));
4538 DCHECK(calling_convention.GetRegisterAt(1).Is(HighRegisterFrom(lhs)));
4539 DCHECK(calling_convention.GetRegisterAt(2).Is(LowRegisterFrom(rhs)));
4540 DCHECK(calling_convention.GetRegisterAt(3).Is(HighRegisterFrom(rhs)));
4541 DCHECK(LowRegisterFrom(div->GetLocations()->Out()).Is(r0));
4542 DCHECK(HighRegisterFrom(div->GetLocations()->Out()).Is(r1));
4543
4544 codegen_->InvokeRuntime(kQuickLdiv, div, div->GetDexPc());
4545 CheckEntrypointTypes<kQuickLdiv, int64_t, int64_t, int64_t>();
Scott Wakelingfe885462016-09-22 10:24:38 +01004546 break;
4547 }
4548
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004549 case DataType::Type::kFloat32:
4550 case DataType::Type::kFloat64:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004551 __ Vdiv(OutputVRegister(div), InputVRegisterAt(div, 0), InputVRegisterAt(div, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004552 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01004553
4554 default:
4555 LOG(FATAL) << "Unexpected div type " << div->GetResultType();
4556 }
4557}
4558
Artem Serov551b28f2016-10-18 19:11:30 +01004559void LocationsBuilderARMVIXL::VisitRem(HRem* rem) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004560 DataType::Type type = rem->GetResultType();
Artem Serov551b28f2016-10-18 19:11:30 +01004561
4562 // Most remainders are implemented in the runtime.
4563 LocationSummary::CallKind call_kind = LocationSummary::kCallOnMainOnly;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004564 if (rem->GetResultType() == DataType::Type::kInt32 && rem->InputAt(1)->IsConstant()) {
Artem Serov551b28f2016-10-18 19:11:30 +01004565 // sdiv will be replaced by other instruction sequence.
4566 call_kind = LocationSummary::kNoCall;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004567 } else if ((rem->GetResultType() == DataType::Type::kInt32)
Artem Serov551b28f2016-10-18 19:11:30 +01004568 && codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4569 // Have hardware divide instruction for int, do it with three instructions.
4570 call_kind = LocationSummary::kNoCall;
4571 }
4572
Vladimir Markoca6fff82017-10-03 14:49:14 +01004573 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(rem, call_kind);
Artem Serov551b28f2016-10-18 19:11:30 +01004574
4575 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004576 case DataType::Type::kInt32: {
Artem Serov551b28f2016-10-18 19:11:30 +01004577 if (rem->InputAt(1)->IsConstant()) {
4578 locations->SetInAt(0, Location::RequiresRegister());
4579 locations->SetInAt(1, Location::ConstantLocation(rem->InputAt(1)->AsConstant()));
Anton Kirilov644032c2016-12-06 17:51:43 +00004580 int32_t value = Int32ConstantFrom(rem->InputAt(1));
Evgeny Astigeevich2d101172020-06-25 16:52:03 +01004581 Location::OutputOverlap out_overlaps = Location::kNoOutputOverlap;
Artem Serov551b28f2016-10-18 19:11:30 +01004582 if (value == 1 || value == 0 || value == -1) {
4583 // No temp register required.
Evgeny Astigeevich0f3d7ac2020-08-06 16:28:37 +01004584 } else if (IsPowerOfTwo(AbsOrMin(value)) && !HasNonNegativeOrMinIntInputAt(rem, 0)) {
Evgeny Astigeevich2d101172020-06-25 16:52:03 +01004585 // The "out" register is used as a temporary, so it overlaps with the inputs.
4586 out_overlaps = Location::kOutputOverlap;
Artem Serov551b28f2016-10-18 19:11:30 +01004587 } else {
Evgeny Astigeevich2d101172020-06-25 16:52:03 +01004588 locations->AddRegisterTemps(2);
Artem Serov551b28f2016-10-18 19:11:30 +01004589 }
Evgeny Astigeevich2d101172020-06-25 16:52:03 +01004590 locations->SetOut(Location::RequiresRegister(), out_overlaps);
Artem Serov551b28f2016-10-18 19:11:30 +01004591 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4592 locations->SetInAt(0, Location::RequiresRegister());
4593 locations->SetInAt(1, Location::RequiresRegister());
4594 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4595 locations->AddTemp(Location::RequiresRegister());
4596 } else {
4597 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4598 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
4599 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
Roland Levillain5e8d5f02016-10-18 18:03:43 +01004600 // Note: divmod will compute both the quotient and the remainder as the pair R0 and R1, but
Artem Serov551b28f2016-10-18 19:11:30 +01004601 // we only need the latter.
4602 locations->SetOut(LocationFrom(r1));
4603 }
4604 break;
4605 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004606 case DataType::Type::kInt64: {
Artem Serov551b28f2016-10-18 19:11:30 +01004607 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4608 locations->SetInAt(0, LocationFrom(
4609 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
4610 locations->SetInAt(1, LocationFrom(
4611 calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3)));
4612 // The runtime helper puts the output in R2,R3.
4613 locations->SetOut(LocationFrom(r2, r3));
4614 break;
4615 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004616 case DataType::Type::kFloat32: {
Artem Serov551b28f2016-10-18 19:11:30 +01004617 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4618 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
4619 locations->SetInAt(1, LocationFrom(calling_convention.GetFpuRegisterAt(1)));
4620 locations->SetOut(LocationFrom(s0));
4621 break;
4622 }
4623
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004624 case DataType::Type::kFloat64: {
Artem Serov551b28f2016-10-18 19:11:30 +01004625 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4626 locations->SetInAt(0, LocationFrom(
4627 calling_convention.GetFpuRegisterAt(0), calling_convention.GetFpuRegisterAt(1)));
4628 locations->SetInAt(1, LocationFrom(
4629 calling_convention.GetFpuRegisterAt(2), calling_convention.GetFpuRegisterAt(3)));
4630 locations->SetOut(LocationFrom(s0, s1));
4631 break;
4632 }
4633
4634 default:
4635 LOG(FATAL) << "Unexpected rem type " << type;
4636 }
4637}
4638
4639void InstructionCodeGeneratorARMVIXL::VisitRem(HRem* rem) {
4640 LocationSummary* locations = rem->GetLocations();
4641 Location second = locations->InAt(1);
4642
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004643 DataType::Type type = rem->GetResultType();
Artem Serov551b28f2016-10-18 19:11:30 +01004644 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004645 case DataType::Type::kInt32: {
Artem Serov551b28f2016-10-18 19:11:30 +01004646 vixl32::Register reg1 = InputRegisterAt(rem, 0);
4647 vixl32::Register out_reg = OutputRegister(rem);
4648 if (second.IsConstant()) {
4649 GenerateDivRemConstantIntegral(rem);
4650 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4651 vixl32::Register reg2 = RegisterFrom(second);
4652 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
4653
4654 // temp = reg1 / reg2 (integer division)
4655 // dest = reg1 - temp * reg2
4656 __ Sdiv(temp, reg1, reg2);
4657 __ Mls(out_reg, temp, reg2, reg1);
4658 } else {
4659 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4660 DCHECK(reg1.Is(calling_convention.GetRegisterAt(0)));
4661 DCHECK(RegisterFrom(second).Is(calling_convention.GetRegisterAt(1)));
4662 DCHECK(out_reg.Is(r1));
4663
4664 codegen_->InvokeRuntime(kQuickIdivmod, rem, rem->GetDexPc());
4665 CheckEntrypointTypes<kQuickIdivmod, int32_t, int32_t, int32_t>();
4666 }
4667 break;
4668 }
4669
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004670 case DataType::Type::kInt64: {
Artem Serov551b28f2016-10-18 19:11:30 +01004671 codegen_->InvokeRuntime(kQuickLmod, rem, rem->GetDexPc());
4672 CheckEntrypointTypes<kQuickLmod, int64_t, int64_t, int64_t>();
4673 break;
4674 }
4675
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004676 case DataType::Type::kFloat32: {
Artem Serov551b28f2016-10-18 19:11:30 +01004677 codegen_->InvokeRuntime(kQuickFmodf, rem, rem->GetDexPc());
4678 CheckEntrypointTypes<kQuickFmodf, float, float, float>();
4679 break;
4680 }
4681
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004682 case DataType::Type::kFloat64: {
Artem Serov551b28f2016-10-18 19:11:30 +01004683 codegen_->InvokeRuntime(kQuickFmod, rem, rem->GetDexPc());
4684 CheckEntrypointTypes<kQuickFmod, double, double, double>();
4685 break;
4686 }
4687
4688 default:
4689 LOG(FATAL) << "Unexpected rem type " << type;
4690 }
4691}
4692
Aart Bik1f8d51b2018-02-15 10:42:37 -08004693static void CreateMinMaxLocations(ArenaAllocator* allocator, HBinaryOperation* minmax) {
4694 LocationSummary* locations = new (allocator) LocationSummary(minmax);
4695 switch (minmax->GetResultType()) {
4696 case DataType::Type::kInt32:
4697 locations->SetInAt(0, Location::RequiresRegister());
4698 locations->SetInAt(1, Location::RequiresRegister());
4699 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4700 break;
4701 case DataType::Type::kInt64:
4702 locations->SetInAt(0, Location::RequiresRegister());
4703 locations->SetInAt(1, Location::RequiresRegister());
4704 locations->SetOut(Location::SameAsFirstInput());
4705 break;
4706 case DataType::Type::kFloat32:
4707 locations->SetInAt(0, Location::RequiresFpuRegister());
4708 locations->SetInAt(1, Location::RequiresFpuRegister());
4709 locations->SetOut(Location::SameAsFirstInput());
4710 locations->AddTemp(Location::RequiresRegister());
4711 break;
4712 case DataType::Type::kFloat64:
4713 locations->SetInAt(0, Location::RequiresFpuRegister());
4714 locations->SetInAt(1, Location::RequiresFpuRegister());
4715 locations->SetOut(Location::SameAsFirstInput());
4716 break;
4717 default:
4718 LOG(FATAL) << "Unexpected type for HMinMax " << minmax->GetResultType();
4719 }
4720}
4721
Aart Bik351df3e2018-03-07 11:54:57 -08004722void InstructionCodeGeneratorARMVIXL::GenerateMinMaxInt(LocationSummary* locations, bool is_min) {
Aart Bik1f8d51b2018-02-15 10:42:37 -08004723 Location op1_loc = locations->InAt(0);
4724 Location op2_loc = locations->InAt(1);
4725 Location out_loc = locations->Out();
4726
4727 vixl32::Register op1 = RegisterFrom(op1_loc);
4728 vixl32::Register op2 = RegisterFrom(op2_loc);
4729 vixl32::Register out = RegisterFrom(out_loc);
4730
4731 __ Cmp(op1, op2);
4732
4733 {
4734 ExactAssemblyScope aas(GetVIXLAssembler(),
4735 3 * kMaxInstructionSizeInBytes,
4736 CodeBufferCheckScope::kMaximumSize);
4737
4738 __ ite(is_min ? lt : gt);
4739 __ mov(is_min ? lt : gt, out, op1);
4740 __ mov(is_min ? ge : le, out, op2);
4741 }
4742}
4743
4744void InstructionCodeGeneratorARMVIXL::GenerateMinMaxLong(LocationSummary* locations, bool is_min) {
4745 Location op1_loc = locations->InAt(0);
4746 Location op2_loc = locations->InAt(1);
4747 Location out_loc = locations->Out();
4748
4749 // Optimization: don't generate any code if inputs are the same.
4750 if (op1_loc.Equals(op2_loc)) {
4751 DCHECK(out_loc.Equals(op1_loc)); // out_loc is set as SameAsFirstInput() in location builder.
4752 return;
4753 }
4754
4755 vixl32::Register op1_lo = LowRegisterFrom(op1_loc);
4756 vixl32::Register op1_hi = HighRegisterFrom(op1_loc);
4757 vixl32::Register op2_lo = LowRegisterFrom(op2_loc);
4758 vixl32::Register op2_hi = HighRegisterFrom(op2_loc);
4759 vixl32::Register out_lo = LowRegisterFrom(out_loc);
4760 vixl32::Register out_hi = HighRegisterFrom(out_loc);
4761 UseScratchRegisterScope temps(GetVIXLAssembler());
4762 const vixl32::Register temp = temps.Acquire();
4763
4764 DCHECK(op1_lo.Is(out_lo));
4765 DCHECK(op1_hi.Is(out_hi));
4766
4767 // Compare op1 >= op2, or op1 < op2.
4768 __ Cmp(out_lo, op2_lo);
4769 __ Sbcs(temp, out_hi, op2_hi);
4770
4771 // Now GE/LT condition code is correct for the long comparison.
4772 {
4773 vixl32::ConditionType cond = is_min ? ge : lt;
4774 ExactAssemblyScope it_scope(GetVIXLAssembler(),
4775 3 * kMaxInstructionSizeInBytes,
4776 CodeBufferCheckScope::kMaximumSize);
4777 __ itt(cond);
4778 __ mov(cond, out_lo, op2_lo);
4779 __ mov(cond, out_hi, op2_hi);
4780 }
4781}
4782
Aart Bik351df3e2018-03-07 11:54:57 -08004783void InstructionCodeGeneratorARMVIXL::GenerateMinMaxFloat(HInstruction* minmax, bool is_min) {
4784 LocationSummary* locations = minmax->GetLocations();
Aart Bik1f8d51b2018-02-15 10:42:37 -08004785 Location op1_loc = locations->InAt(0);
4786 Location op2_loc = locations->InAt(1);
4787 Location out_loc = locations->Out();
4788
4789 // Optimization: don't generate any code if inputs are the same.
4790 if (op1_loc.Equals(op2_loc)) {
4791 DCHECK(out_loc.Equals(op1_loc)); // out_loc is set as SameAsFirstInput() in location builder.
4792 return;
4793 }
4794
4795 vixl32::SRegister op1 = SRegisterFrom(op1_loc);
4796 vixl32::SRegister op2 = SRegisterFrom(op2_loc);
4797 vixl32::SRegister out = SRegisterFrom(out_loc);
4798
4799 UseScratchRegisterScope temps(GetVIXLAssembler());
4800 const vixl32::Register temp1 = temps.Acquire();
4801 vixl32::Register temp2 = RegisterFrom(locations->GetTemp(0));
4802 vixl32::Label nan, done;
Aart Bik351df3e2018-03-07 11:54:57 -08004803 vixl32::Label* final_label = codegen_->GetFinalLabel(minmax, &done);
Aart Bik1f8d51b2018-02-15 10:42:37 -08004804
4805 DCHECK(op1.Is(out));
4806
4807 __ Vcmp(op1, op2);
4808 __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
Andreas Gampe3db70682018-12-26 15:12:03 -08004809 __ B(vs, &nan, /* is_far_target= */ false); // if un-ordered, go to NaN handling.
Aart Bik1f8d51b2018-02-15 10:42:37 -08004810
4811 // op1 <> op2
4812 vixl32::ConditionType cond = is_min ? gt : lt;
4813 {
4814 ExactAssemblyScope it_scope(GetVIXLAssembler(),
4815 2 * kMaxInstructionSizeInBytes,
4816 CodeBufferCheckScope::kMaximumSize);
4817 __ it(cond);
4818 __ vmov(cond, F32, out, op2);
4819 }
4820 // for <>(not equal), we've done min/max calculation.
Andreas Gampe3db70682018-12-26 15:12:03 -08004821 __ B(ne, final_label, /* is_far_target= */ false);
Aart Bik1f8d51b2018-02-15 10:42:37 -08004822
4823 // handle op1 == op2, max(+0.0,-0.0), min(+0.0,-0.0).
4824 __ Vmov(temp1, op1);
4825 __ Vmov(temp2, op2);
4826 if (is_min) {
4827 __ Orr(temp1, temp1, temp2);
4828 } else {
4829 __ And(temp1, temp1, temp2);
4830 }
4831 __ Vmov(out, temp1);
4832 __ B(final_label);
4833
4834 // handle NaN input.
4835 __ Bind(&nan);
4836 __ Movt(temp1, High16Bits(kNanFloat)); // 0x7FC0xxxx is a NaN.
4837 __ Vmov(out, temp1);
4838
4839 if (done.IsReferenced()) {
4840 __ Bind(&done);
4841 }
4842}
4843
Aart Bik351df3e2018-03-07 11:54:57 -08004844void InstructionCodeGeneratorARMVIXL::GenerateMinMaxDouble(HInstruction* minmax, bool is_min) {
4845 LocationSummary* locations = minmax->GetLocations();
Aart Bik1f8d51b2018-02-15 10:42:37 -08004846 Location op1_loc = locations->InAt(0);
4847 Location op2_loc = locations->InAt(1);
4848 Location out_loc = locations->Out();
4849
4850 // Optimization: don't generate any code if inputs are the same.
4851 if (op1_loc.Equals(op2_loc)) {
4852 DCHECK(out_loc.Equals(op1_loc)); // out_loc is set as SameAsFirstInput() in.
4853 return;
4854 }
4855
4856 vixl32::DRegister op1 = DRegisterFrom(op1_loc);
4857 vixl32::DRegister op2 = DRegisterFrom(op2_loc);
4858 vixl32::DRegister out = DRegisterFrom(out_loc);
4859 vixl32::Label handle_nan_eq, done;
Aart Bik351df3e2018-03-07 11:54:57 -08004860 vixl32::Label* final_label = codegen_->GetFinalLabel(minmax, &done);
Aart Bik1f8d51b2018-02-15 10:42:37 -08004861
4862 DCHECK(op1.Is(out));
4863
4864 __ Vcmp(op1, op2);
4865 __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
Andreas Gampe3db70682018-12-26 15:12:03 -08004866 __ B(vs, &handle_nan_eq, /* is_far_target= */ false); // if un-ordered, go to NaN handling.
Aart Bik1f8d51b2018-02-15 10:42:37 -08004867
4868 // op1 <> op2
4869 vixl32::ConditionType cond = is_min ? gt : lt;
4870 {
4871 ExactAssemblyScope it_scope(GetVIXLAssembler(),
4872 2 * kMaxInstructionSizeInBytes,
4873 CodeBufferCheckScope::kMaximumSize);
4874 __ it(cond);
4875 __ vmov(cond, F64, out, op2);
4876 }
4877 // for <>(not equal), we've done min/max calculation.
Andreas Gampe3db70682018-12-26 15:12:03 -08004878 __ B(ne, final_label, /* is_far_target= */ false);
Aart Bik1f8d51b2018-02-15 10:42:37 -08004879
4880 // handle op1 == op2, max(+0.0,-0.0).
4881 if (!is_min) {
4882 __ Vand(F64, out, op1, op2);
4883 __ B(final_label);
4884 }
4885
4886 // handle op1 == op2, min(+0.0,-0.0), NaN input.
4887 __ Bind(&handle_nan_eq);
4888 __ Vorr(F64, out, op1, op2); // assemble op1/-0.0/NaN.
4889
4890 if (done.IsReferenced()) {
4891 __ Bind(&done);
4892 }
4893}
4894
Aart Bik351df3e2018-03-07 11:54:57 -08004895void InstructionCodeGeneratorARMVIXL::GenerateMinMax(HBinaryOperation* minmax, bool is_min) {
4896 DataType::Type type = minmax->GetResultType();
4897 switch (type) {
4898 case DataType::Type::kInt32:
4899 GenerateMinMaxInt(minmax->GetLocations(), is_min);
4900 break;
4901 case DataType::Type::kInt64:
4902 GenerateMinMaxLong(minmax->GetLocations(), is_min);
4903 break;
4904 case DataType::Type::kFloat32:
4905 GenerateMinMaxFloat(minmax, is_min);
4906 break;
4907 case DataType::Type::kFloat64:
4908 GenerateMinMaxDouble(minmax, is_min);
4909 break;
4910 default:
4911 LOG(FATAL) << "Unexpected type for HMinMax " << type;
4912 }
4913}
4914
Aart Bik1f8d51b2018-02-15 10:42:37 -08004915void LocationsBuilderARMVIXL::VisitMin(HMin* min) {
4916 CreateMinMaxLocations(GetGraph()->GetAllocator(), min);
4917}
4918
4919void InstructionCodeGeneratorARMVIXL::VisitMin(HMin* min) {
Aart Bik351df3e2018-03-07 11:54:57 -08004920 GenerateMinMax(min, /*is_min*/ true);
Aart Bik1f8d51b2018-02-15 10:42:37 -08004921}
4922
4923void LocationsBuilderARMVIXL::VisitMax(HMax* max) {
4924 CreateMinMaxLocations(GetGraph()->GetAllocator(), max);
4925}
4926
4927void InstructionCodeGeneratorARMVIXL::VisitMax(HMax* max) {
Aart Bik351df3e2018-03-07 11:54:57 -08004928 GenerateMinMax(max, /*is_min*/ false);
Aart Bik1f8d51b2018-02-15 10:42:37 -08004929}
4930
Aart Bik3dad3412018-02-28 12:01:46 -08004931void LocationsBuilderARMVIXL::VisitAbs(HAbs* abs) {
4932 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(abs);
4933 switch (abs->GetResultType()) {
4934 case DataType::Type::kInt32:
4935 case DataType::Type::kInt64:
4936 locations->SetInAt(0, Location::RequiresRegister());
4937 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4938 locations->AddTemp(Location::RequiresRegister());
4939 break;
4940 case DataType::Type::kFloat32:
4941 case DataType::Type::kFloat64:
4942 locations->SetInAt(0, Location::RequiresFpuRegister());
4943 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4944 break;
4945 default:
4946 LOG(FATAL) << "Unexpected type for abs operation " << abs->GetResultType();
4947 }
4948}
4949
4950void InstructionCodeGeneratorARMVIXL::VisitAbs(HAbs* abs) {
4951 LocationSummary* locations = abs->GetLocations();
4952 switch (abs->GetResultType()) {
4953 case DataType::Type::kInt32: {
4954 vixl32::Register in_reg = RegisterFrom(locations->InAt(0));
4955 vixl32::Register out_reg = RegisterFrom(locations->Out());
4956 vixl32::Register mask = RegisterFrom(locations->GetTemp(0));
4957 __ Asr(mask, in_reg, 31);
4958 __ Add(out_reg, in_reg, mask);
4959 __ Eor(out_reg, out_reg, mask);
4960 break;
4961 }
4962 case DataType::Type::kInt64: {
4963 Location in = locations->InAt(0);
4964 vixl32::Register in_reg_lo = LowRegisterFrom(in);
4965 vixl32::Register in_reg_hi = HighRegisterFrom(in);
4966 Location output = locations->Out();
4967 vixl32::Register out_reg_lo = LowRegisterFrom(output);
4968 vixl32::Register out_reg_hi = HighRegisterFrom(output);
4969 DCHECK(!out_reg_lo.Is(in_reg_hi)) << "Diagonal overlap unexpected.";
4970 vixl32::Register mask = RegisterFrom(locations->GetTemp(0));
4971 __ Asr(mask, in_reg_hi, 31);
4972 __ Adds(out_reg_lo, in_reg_lo, mask);
4973 __ Adc(out_reg_hi, in_reg_hi, mask);
4974 __ Eor(out_reg_lo, out_reg_lo, mask);
4975 __ Eor(out_reg_hi, out_reg_hi, mask);
4976 break;
4977 }
4978 case DataType::Type::kFloat32:
4979 case DataType::Type::kFloat64:
4980 __ Vabs(OutputVRegister(abs), InputVRegisterAt(abs, 0));
4981 break;
4982 default:
4983 LOG(FATAL) << "Unexpected type for abs operation " << abs->GetResultType();
4984 }
4985}
Artem Serov551b28f2016-10-18 19:11:30 +01004986
Scott Wakelingfe885462016-09-22 10:24:38 +01004987void LocationsBuilderARMVIXL::VisitDivZeroCheck(HDivZeroCheck* instruction) {
Artem Serov657022c2016-11-23 14:19:38 +00004988 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
Scott Wakelingfe885462016-09-22 10:24:38 +01004989 locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0)));
Scott Wakelingfe885462016-09-22 10:24:38 +01004990}
4991
4992void InstructionCodeGeneratorARMVIXL::VisitDivZeroCheck(HDivZeroCheck* instruction) {
4993 DivZeroCheckSlowPathARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01004994 new (codegen_->GetScopedAllocator()) DivZeroCheckSlowPathARMVIXL(instruction);
Scott Wakelingfe885462016-09-22 10:24:38 +01004995 codegen_->AddSlowPath(slow_path);
4996
4997 LocationSummary* locations = instruction->GetLocations();
4998 Location value = locations->InAt(0);
4999
5000 switch (instruction->GetType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005001 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005002 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005003 case DataType::Type::kInt8:
5004 case DataType::Type::kUint16:
5005 case DataType::Type::kInt16:
5006 case DataType::Type::kInt32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01005007 if (value.IsRegister()) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00005008 __ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
Scott Wakelingfe885462016-09-22 10:24:38 +01005009 } else {
5010 DCHECK(value.IsConstant()) << value;
Anton Kirilov644032c2016-12-06 17:51:43 +00005011 if (Int32ConstantFrom(value) == 0) {
Scott Wakelingfe885462016-09-22 10:24:38 +01005012 __ B(slow_path->GetEntryLabel());
5013 }
5014 }
5015 break;
5016 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005017 case DataType::Type::kInt64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01005018 if (value.IsRegisterPair()) {
5019 UseScratchRegisterScope temps(GetVIXLAssembler());
5020 vixl32::Register temp = temps.Acquire();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005021 __ Orrs(temp, LowRegisterFrom(value), HighRegisterFrom(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01005022 __ B(eq, slow_path->GetEntryLabel());
5023 } else {
5024 DCHECK(value.IsConstant()) << value;
Anton Kirilov644032c2016-12-06 17:51:43 +00005025 if (Int64ConstantFrom(value) == 0) {
Scott Wakelingfe885462016-09-22 10:24:38 +01005026 __ B(slow_path->GetEntryLabel());
5027 }
5028 }
5029 break;
5030 }
5031 default:
5032 LOG(FATAL) << "Unexpected type for HDivZeroCheck " << instruction->GetType();
5033 }
5034}
5035
Artem Serov02109dd2016-09-23 17:17:54 +01005036void InstructionCodeGeneratorARMVIXL::HandleIntegerRotate(HRor* ror) {
5037 LocationSummary* locations = ror->GetLocations();
5038 vixl32::Register in = InputRegisterAt(ror, 0);
5039 Location rhs = locations->InAt(1);
5040 vixl32::Register out = OutputRegister(ror);
5041
5042 if (rhs.IsConstant()) {
5043 // Arm32 and Thumb2 assemblers require a rotation on the interval [1,31],
5044 // so map all rotations to a +ve. equivalent in that range.
5045 // (e.g. left *or* right by -2 bits == 30 bits in the same direction.)
5046 uint32_t rot = CodeGenerator::GetInt32ValueOf(rhs.GetConstant()) & 0x1F;
5047 if (rot) {
5048 // Rotate, mapping left rotations to right equivalents if necessary.
5049 // (e.g. left by 2 bits == right by 30.)
5050 __ Ror(out, in, rot);
5051 } else if (!out.Is(in)) {
5052 __ Mov(out, in);
5053 }
5054 } else {
5055 __ Ror(out, in, RegisterFrom(rhs));
5056 }
5057}
5058
5059// Gain some speed by mapping all Long rotates onto equivalent pairs of Integer
5060// rotates by swapping input regs (effectively rotating by the first 32-bits of
5061// a larger rotation) or flipping direction (thus treating larger right/left
5062// rotations as sub-word sized rotations in the other direction) as appropriate.
5063void InstructionCodeGeneratorARMVIXL::HandleLongRotate(HRor* ror) {
5064 LocationSummary* locations = ror->GetLocations();
5065 vixl32::Register in_reg_lo = LowRegisterFrom(locations->InAt(0));
5066 vixl32::Register in_reg_hi = HighRegisterFrom(locations->InAt(0));
5067 Location rhs = locations->InAt(1);
5068 vixl32::Register out_reg_lo = LowRegisterFrom(locations->Out());
5069 vixl32::Register out_reg_hi = HighRegisterFrom(locations->Out());
5070
5071 if (rhs.IsConstant()) {
5072 uint64_t rot = CodeGenerator::GetInt64ValueOf(rhs.GetConstant());
5073 // Map all rotations to +ve. equivalents on the interval [0,63].
5074 rot &= kMaxLongShiftDistance;
5075 // For rotates over a word in size, 'pre-rotate' by 32-bits to keep rotate
5076 // logic below to a simple pair of binary orr.
5077 // (e.g. 34 bits == in_reg swap + 2 bits right.)
5078 if (rot >= kArmBitsPerWord) {
5079 rot -= kArmBitsPerWord;
5080 std::swap(in_reg_hi, in_reg_lo);
5081 }
5082 // Rotate, or mov to out for zero or word size rotations.
5083 if (rot != 0u) {
Scott Wakelingb77051e2016-11-21 19:46:00 +00005084 __ Lsr(out_reg_hi, in_reg_hi, Operand::From(rot));
Artem Serov02109dd2016-09-23 17:17:54 +01005085 __ Orr(out_reg_hi, out_reg_hi, Operand(in_reg_lo, ShiftType::LSL, kArmBitsPerWord - rot));
Scott Wakelingb77051e2016-11-21 19:46:00 +00005086 __ Lsr(out_reg_lo, in_reg_lo, Operand::From(rot));
Artem Serov02109dd2016-09-23 17:17:54 +01005087 __ Orr(out_reg_lo, out_reg_lo, Operand(in_reg_hi, ShiftType::LSL, kArmBitsPerWord - rot));
5088 } else {
5089 __ Mov(out_reg_lo, in_reg_lo);
5090 __ Mov(out_reg_hi, in_reg_hi);
5091 }
5092 } else {
5093 vixl32::Register shift_right = RegisterFrom(locations->GetTemp(0));
5094 vixl32::Register shift_left = RegisterFrom(locations->GetTemp(1));
5095 vixl32::Label end;
5096 vixl32::Label shift_by_32_plus_shift_right;
Anton Kirilov6f644202017-02-27 18:29:45 +00005097 vixl32::Label* final_label = codegen_->GetFinalLabel(ror, &end);
Artem Serov02109dd2016-09-23 17:17:54 +01005098
5099 __ And(shift_right, RegisterFrom(rhs), 0x1F);
5100 __ Lsrs(shift_left, RegisterFrom(rhs), 6);
Scott Wakelingbffdc702016-12-07 17:46:03 +00005101 __ Rsb(LeaveFlags, shift_left, shift_right, Operand::From(kArmBitsPerWord));
Andreas Gampe3db70682018-12-26 15:12:03 -08005102 __ B(cc, &shift_by_32_plus_shift_right, /* is_far_target= */ false);
Artem Serov02109dd2016-09-23 17:17:54 +01005103
5104 // out_reg_hi = (reg_hi << shift_left) | (reg_lo >> shift_right).
5105 // out_reg_lo = (reg_lo << shift_left) | (reg_hi >> shift_right).
5106 __ Lsl(out_reg_hi, in_reg_hi, shift_left);
5107 __ Lsr(out_reg_lo, in_reg_lo, shift_right);
5108 __ Add(out_reg_hi, out_reg_hi, out_reg_lo);
5109 __ Lsl(out_reg_lo, in_reg_lo, shift_left);
5110 __ Lsr(shift_left, in_reg_hi, shift_right);
5111 __ Add(out_reg_lo, out_reg_lo, shift_left);
Anton Kirilov6f644202017-02-27 18:29:45 +00005112 __ B(final_label);
Artem Serov02109dd2016-09-23 17:17:54 +01005113
5114 __ Bind(&shift_by_32_plus_shift_right); // Shift by 32+shift_right.
5115 // out_reg_hi = (reg_hi >> shift_right) | (reg_lo << shift_left).
5116 // out_reg_lo = (reg_lo >> shift_right) | (reg_hi << shift_left).
5117 __ Lsr(out_reg_hi, in_reg_hi, shift_right);
5118 __ Lsl(out_reg_lo, in_reg_lo, shift_left);
5119 __ Add(out_reg_hi, out_reg_hi, out_reg_lo);
5120 __ Lsr(out_reg_lo, in_reg_lo, shift_right);
5121 __ Lsl(shift_right, in_reg_hi, shift_left);
5122 __ Add(out_reg_lo, out_reg_lo, shift_right);
5123
Anton Kirilov6f644202017-02-27 18:29:45 +00005124 if (end.IsReferenced()) {
5125 __ Bind(&end);
5126 }
Artem Serov02109dd2016-09-23 17:17:54 +01005127 }
5128}
5129
5130void LocationsBuilderARMVIXL::VisitRor(HRor* ror) {
5131 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005132 new (GetGraph()->GetAllocator()) LocationSummary(ror, LocationSummary::kNoCall);
Artem Serov02109dd2016-09-23 17:17:54 +01005133 switch (ror->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005134 case DataType::Type::kInt32: {
Artem Serov02109dd2016-09-23 17:17:54 +01005135 locations->SetInAt(0, Location::RequiresRegister());
5136 locations->SetInAt(1, Location::RegisterOrConstant(ror->InputAt(1)));
5137 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5138 break;
5139 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005140 case DataType::Type::kInt64: {
Artem Serov02109dd2016-09-23 17:17:54 +01005141 locations->SetInAt(0, Location::RequiresRegister());
5142 if (ror->InputAt(1)->IsConstant()) {
5143 locations->SetInAt(1, Location::ConstantLocation(ror->InputAt(1)->AsConstant()));
5144 } else {
5145 locations->SetInAt(1, Location::RequiresRegister());
5146 locations->AddTemp(Location::RequiresRegister());
5147 locations->AddTemp(Location::RequiresRegister());
5148 }
5149 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
5150 break;
5151 }
5152 default:
5153 LOG(FATAL) << "Unexpected operation type " << ror->GetResultType();
5154 }
5155}
5156
5157void InstructionCodeGeneratorARMVIXL::VisitRor(HRor* ror) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005158 DataType::Type type = ror->GetResultType();
Artem Serov02109dd2016-09-23 17:17:54 +01005159 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005160 case DataType::Type::kInt32: {
Artem Serov02109dd2016-09-23 17:17:54 +01005161 HandleIntegerRotate(ror);
5162 break;
5163 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005164 case DataType::Type::kInt64: {
Artem Serov02109dd2016-09-23 17:17:54 +01005165 HandleLongRotate(ror);
5166 break;
5167 }
5168 default:
5169 LOG(FATAL) << "Unexpected operation type " << type;
5170 UNREACHABLE();
5171 }
5172}
5173
Artem Serov02d37832016-10-25 15:25:33 +01005174void LocationsBuilderARMVIXL::HandleShift(HBinaryOperation* op) {
5175 DCHECK(op->IsShl() || op->IsShr() || op->IsUShr());
5176
5177 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005178 new (GetGraph()->GetAllocator()) LocationSummary(op, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01005179
5180 switch (op->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005181 case DataType::Type::kInt32: {
Artem Serov02d37832016-10-25 15:25:33 +01005182 locations->SetInAt(0, Location::RequiresRegister());
5183 if (op->InputAt(1)->IsConstant()) {
5184 locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant()));
5185 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5186 } else {
5187 locations->SetInAt(1, Location::RequiresRegister());
5188 // Make the output overlap, as it will be used to hold the masked
5189 // second input.
5190 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
5191 }
5192 break;
5193 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005194 case DataType::Type::kInt64: {
Artem Serov02d37832016-10-25 15:25:33 +01005195 locations->SetInAt(0, Location::RequiresRegister());
5196 if (op->InputAt(1)->IsConstant()) {
5197 locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant()));
5198 // For simplicity, use kOutputOverlap even though we only require that low registers
5199 // don't clash with high registers which the register allocator currently guarantees.
5200 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
5201 } else {
5202 locations->SetInAt(1, Location::RequiresRegister());
5203 locations->AddTemp(Location::RequiresRegister());
5204 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
5205 }
5206 break;
5207 }
5208 default:
5209 LOG(FATAL) << "Unexpected operation type " << op->GetResultType();
5210 }
5211}
5212
5213void InstructionCodeGeneratorARMVIXL::HandleShift(HBinaryOperation* op) {
5214 DCHECK(op->IsShl() || op->IsShr() || op->IsUShr());
5215
5216 LocationSummary* locations = op->GetLocations();
5217 Location out = locations->Out();
5218 Location first = locations->InAt(0);
5219 Location second = locations->InAt(1);
5220
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005221 DataType::Type type = op->GetResultType();
Artem Serov02d37832016-10-25 15:25:33 +01005222 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005223 case DataType::Type::kInt32: {
Artem Serov02d37832016-10-25 15:25:33 +01005224 vixl32::Register out_reg = OutputRegister(op);
5225 vixl32::Register first_reg = InputRegisterAt(op, 0);
5226 if (second.IsRegister()) {
5227 vixl32::Register second_reg = RegisterFrom(second);
5228 // ARM doesn't mask the shift count so we need to do it ourselves.
5229 __ And(out_reg, second_reg, kMaxIntShiftDistance);
5230 if (op->IsShl()) {
5231 __ Lsl(out_reg, first_reg, out_reg);
5232 } else if (op->IsShr()) {
5233 __ Asr(out_reg, first_reg, out_reg);
5234 } else {
5235 __ Lsr(out_reg, first_reg, out_reg);
5236 }
5237 } else {
Anton Kirilov644032c2016-12-06 17:51:43 +00005238 int32_t cst = Int32ConstantFrom(second);
Artem Serov02d37832016-10-25 15:25:33 +01005239 uint32_t shift_value = cst & kMaxIntShiftDistance;
5240 if (shift_value == 0) { // ARM does not support shifting with 0 immediate.
5241 __ Mov(out_reg, first_reg);
5242 } else if (op->IsShl()) {
5243 __ Lsl(out_reg, first_reg, shift_value);
5244 } else if (op->IsShr()) {
5245 __ Asr(out_reg, first_reg, shift_value);
5246 } else {
5247 __ Lsr(out_reg, first_reg, shift_value);
5248 }
5249 }
5250 break;
5251 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005252 case DataType::Type::kInt64: {
Artem Serov02d37832016-10-25 15:25:33 +01005253 vixl32::Register o_h = HighRegisterFrom(out);
5254 vixl32::Register o_l = LowRegisterFrom(out);
5255
5256 vixl32::Register high = HighRegisterFrom(first);
5257 vixl32::Register low = LowRegisterFrom(first);
5258
5259 if (second.IsRegister()) {
5260 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
5261
5262 vixl32::Register second_reg = RegisterFrom(second);
5263
5264 if (op->IsShl()) {
5265 __ And(o_l, second_reg, kMaxLongShiftDistance);
5266 // Shift the high part
5267 __ Lsl(o_h, high, o_l);
5268 // Shift the low part and `or` what overflew on the high part
Scott Wakelingb77051e2016-11-21 19:46:00 +00005269 __ Rsb(temp, o_l, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01005270 __ Lsr(temp, low, temp);
5271 __ Orr(o_h, o_h, temp);
5272 // If the shift is > 32 bits, override the high part
Scott Wakelingb77051e2016-11-21 19:46:00 +00005273 __ Subs(temp, o_l, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01005274 {
Artem Serov0fb37192016-12-06 18:13:40 +00005275 ExactAssemblyScope guard(GetVIXLAssembler(),
5276 2 * vixl32::kMaxInstructionSizeInBytes,
5277 CodeBufferCheckScope::kMaximumSize);
Artem Serov02d37832016-10-25 15:25:33 +01005278 __ it(pl);
5279 __ lsl(pl, o_h, low, temp);
5280 }
5281 // Shift the low part
5282 __ Lsl(o_l, low, o_l);
5283 } else if (op->IsShr()) {
5284 __ And(o_h, second_reg, kMaxLongShiftDistance);
5285 // Shift the low part
5286 __ Lsr(o_l, low, o_h);
5287 // Shift the high part and `or` what underflew on the low part
Scott Wakelingb77051e2016-11-21 19:46:00 +00005288 __ Rsb(temp, o_h, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01005289 __ Lsl(temp, high, temp);
5290 __ Orr(o_l, o_l, temp);
5291 // If the shift is > 32 bits, override the low part
Scott Wakelingb77051e2016-11-21 19:46:00 +00005292 __ Subs(temp, o_h, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01005293 {
Artem Serov0fb37192016-12-06 18:13:40 +00005294 ExactAssemblyScope guard(GetVIXLAssembler(),
5295 2 * vixl32::kMaxInstructionSizeInBytes,
5296 CodeBufferCheckScope::kMaximumSize);
Artem Serov02d37832016-10-25 15:25:33 +01005297 __ it(pl);
5298 __ asr(pl, o_l, high, temp);
5299 }
5300 // Shift the high part
5301 __ Asr(o_h, high, o_h);
5302 } else {
5303 __ And(o_h, second_reg, kMaxLongShiftDistance);
5304 // same as Shr except we use `Lsr`s and not `Asr`s
5305 __ Lsr(o_l, low, o_h);
Scott Wakelingb77051e2016-11-21 19:46:00 +00005306 __ Rsb(temp, o_h, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01005307 __ Lsl(temp, high, temp);
5308 __ Orr(o_l, o_l, temp);
Scott Wakelingb77051e2016-11-21 19:46:00 +00005309 __ Subs(temp, o_h, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01005310 {
Artem Serov0fb37192016-12-06 18:13:40 +00005311 ExactAssemblyScope guard(GetVIXLAssembler(),
5312 2 * vixl32::kMaxInstructionSizeInBytes,
5313 CodeBufferCheckScope::kMaximumSize);
Artem Serov02d37832016-10-25 15:25:33 +01005314 __ it(pl);
5315 __ lsr(pl, o_l, high, temp);
5316 }
5317 __ Lsr(o_h, high, o_h);
5318 }
5319 } else {
5320 // Register allocator doesn't create partial overlap.
5321 DCHECK(!o_l.Is(high));
5322 DCHECK(!o_h.Is(low));
Anton Kirilov644032c2016-12-06 17:51:43 +00005323 int32_t cst = Int32ConstantFrom(second);
Artem Serov02d37832016-10-25 15:25:33 +01005324 uint32_t shift_value = cst & kMaxLongShiftDistance;
5325 if (shift_value > 32) {
5326 if (op->IsShl()) {
5327 __ Lsl(o_h, low, shift_value - 32);
5328 __ Mov(o_l, 0);
5329 } else if (op->IsShr()) {
5330 __ Asr(o_l, high, shift_value - 32);
5331 __ Asr(o_h, high, 31);
5332 } else {
5333 __ Lsr(o_l, high, shift_value - 32);
5334 __ Mov(o_h, 0);
5335 }
5336 } else if (shift_value == 32) {
5337 if (op->IsShl()) {
5338 __ Mov(o_h, low);
5339 __ Mov(o_l, 0);
5340 } else if (op->IsShr()) {
5341 __ Mov(o_l, high);
5342 __ Asr(o_h, high, 31);
5343 } else {
5344 __ Mov(o_l, high);
5345 __ Mov(o_h, 0);
5346 }
5347 } else if (shift_value == 1) {
5348 if (op->IsShl()) {
5349 __ Lsls(o_l, low, 1);
5350 __ Adc(o_h, high, high);
5351 } else if (op->IsShr()) {
5352 __ Asrs(o_h, high, 1);
5353 __ Rrx(o_l, low);
5354 } else {
5355 __ Lsrs(o_h, high, 1);
5356 __ Rrx(o_l, low);
5357 }
Nicolas Geoffray9b195cc2019-04-02 08:29:00 +01005358 } else if (shift_value == 0) {
5359 __ Mov(o_l, low);
5360 __ Mov(o_h, high);
Artem Serov02d37832016-10-25 15:25:33 +01005361 } else {
Nicolas Geoffray9b195cc2019-04-02 08:29:00 +01005362 DCHECK(0 < shift_value && shift_value < 32) << shift_value;
Artem Serov02d37832016-10-25 15:25:33 +01005363 if (op->IsShl()) {
5364 __ Lsl(o_h, high, shift_value);
5365 __ Orr(o_h, o_h, Operand(low, ShiftType::LSR, 32 - shift_value));
5366 __ Lsl(o_l, low, shift_value);
5367 } else if (op->IsShr()) {
5368 __ Lsr(o_l, low, shift_value);
5369 __ Orr(o_l, o_l, Operand(high, ShiftType::LSL, 32 - shift_value));
5370 __ Asr(o_h, high, shift_value);
5371 } else {
5372 __ Lsr(o_l, low, shift_value);
5373 __ Orr(o_l, o_l, Operand(high, ShiftType::LSL, 32 - shift_value));
5374 __ Lsr(o_h, high, shift_value);
5375 }
5376 }
5377 }
5378 break;
5379 }
5380 default:
5381 LOG(FATAL) << "Unexpected operation type " << type;
5382 UNREACHABLE();
5383 }
5384}
5385
5386void LocationsBuilderARMVIXL::VisitShl(HShl* shl) {
5387 HandleShift(shl);
5388}
5389
5390void InstructionCodeGeneratorARMVIXL::VisitShl(HShl* shl) {
5391 HandleShift(shl);
5392}
5393
5394void LocationsBuilderARMVIXL::VisitShr(HShr* shr) {
5395 HandleShift(shr);
5396}
5397
5398void InstructionCodeGeneratorARMVIXL::VisitShr(HShr* shr) {
5399 HandleShift(shr);
5400}
5401
5402void LocationsBuilderARMVIXL::VisitUShr(HUShr* ushr) {
5403 HandleShift(ushr);
5404}
5405
5406void InstructionCodeGeneratorARMVIXL::VisitUShr(HUShr* ushr) {
5407 HandleShift(ushr);
5408}
5409
5410void LocationsBuilderARMVIXL::VisitNewInstance(HNewInstance* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01005411 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
5412 instruction, LocationSummary::kCallOnMainOnly);
Alex Lightd109e302018-06-27 10:25:41 -07005413 InvokeRuntimeCallingConventionARMVIXL calling_convention;
5414 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
Artem Serov02d37832016-10-25 15:25:33 +01005415 locations->SetOut(LocationFrom(r0));
5416}
5417
5418void InstructionCodeGeneratorARMVIXL::VisitNewInstance(HNewInstance* instruction) {
Alex Lightd109e302018-06-27 10:25:41 -07005419 codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
5420 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
Andra Danciua0130e82020-07-23 12:34:56 +00005421 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 12);
Artem Serov02d37832016-10-25 15:25:33 +01005422}
5423
5424void LocationsBuilderARMVIXL::VisitNewArray(HNewArray* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01005425 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
5426 instruction, LocationSummary::kCallOnMainOnly);
Artem Serov02d37832016-10-25 15:25:33 +01005427 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Artem Serov02d37832016-10-25 15:25:33 +01005428 locations->SetOut(LocationFrom(r0));
Nicolas Geoffray8c7c4f12017-01-26 10:13:11 +00005429 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
5430 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
Artem Serov02d37832016-10-25 15:25:33 +01005431}
5432
5433void InstructionCodeGeneratorARMVIXL::VisitNewArray(HNewArray* instruction) {
Vladimir Markob5461632018-10-15 14:24:21 +01005434 // Note: if heap poisoning is enabled, the entry point takes care of poisoning the reference.
5435 QuickEntrypointEnum entrypoint = CodeGenerator::GetArrayAllocationEntrypoint(instruction);
Artem Serov7b3672e2017-02-03 17:30:34 +00005436 codegen_->InvokeRuntime(entrypoint, instruction, instruction->GetDexPc());
Nicolas Geoffraye761bcc2017-01-19 08:59:37 +00005437 CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>();
Artem Serov7b3672e2017-02-03 17:30:34 +00005438 DCHECK(!codegen_->IsLeafMethod());
Andra Danciua0130e82020-07-23 12:34:56 +00005439 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 13);
Artem Serov02d37832016-10-25 15:25:33 +01005440}
5441
5442void LocationsBuilderARMVIXL::VisitParameterValue(HParameterValue* instruction) {
5443 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005444 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01005445 Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
5446 if (location.IsStackSlot()) {
5447 location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
5448 } else if (location.IsDoubleStackSlot()) {
5449 location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
5450 }
5451 locations->SetOut(location);
5452}
5453
5454void InstructionCodeGeneratorARMVIXL::VisitParameterValue(
5455 HParameterValue* instruction ATTRIBUTE_UNUSED) {
5456 // Nothing to do, the parameter is already at its location.
5457}
5458
5459void LocationsBuilderARMVIXL::VisitCurrentMethod(HCurrentMethod* instruction) {
5460 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005461 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01005462 locations->SetOut(LocationFrom(kMethodRegister));
5463}
5464
5465void InstructionCodeGeneratorARMVIXL::VisitCurrentMethod(
5466 HCurrentMethod* instruction ATTRIBUTE_UNUSED) {
5467 // Nothing to do, the method is already at its location.
5468}
5469
5470void LocationsBuilderARMVIXL::VisitNot(HNot* not_) {
5471 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005472 new (GetGraph()->GetAllocator()) LocationSummary(not_, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01005473 locations->SetInAt(0, Location::RequiresRegister());
5474 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5475}
5476
5477void InstructionCodeGeneratorARMVIXL::VisitNot(HNot* not_) {
5478 LocationSummary* locations = not_->GetLocations();
5479 Location out = locations->Out();
5480 Location in = locations->InAt(0);
5481 switch (not_->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005482 case DataType::Type::kInt32:
Artem Serov02d37832016-10-25 15:25:33 +01005483 __ Mvn(OutputRegister(not_), InputRegisterAt(not_, 0));
5484 break;
5485
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005486 case DataType::Type::kInt64:
Artem Serov02d37832016-10-25 15:25:33 +01005487 __ Mvn(LowRegisterFrom(out), LowRegisterFrom(in));
5488 __ Mvn(HighRegisterFrom(out), HighRegisterFrom(in));
5489 break;
5490
5491 default:
5492 LOG(FATAL) << "Unimplemented type for not operation " << not_->GetResultType();
5493 }
5494}
5495
Scott Wakelingc34dba72016-10-03 10:14:44 +01005496void LocationsBuilderARMVIXL::VisitBooleanNot(HBooleanNot* bool_not) {
5497 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005498 new (GetGraph()->GetAllocator()) LocationSummary(bool_not, LocationSummary::kNoCall);
Scott Wakelingc34dba72016-10-03 10:14:44 +01005499 locations->SetInAt(0, Location::RequiresRegister());
5500 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5501}
5502
5503void InstructionCodeGeneratorARMVIXL::VisitBooleanNot(HBooleanNot* bool_not) {
5504 __ Eor(OutputRegister(bool_not), InputRegister(bool_not), 1);
5505}
5506
Artem Serov02d37832016-10-25 15:25:33 +01005507void LocationsBuilderARMVIXL::VisitCompare(HCompare* compare) {
5508 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005509 new (GetGraph()->GetAllocator()) LocationSummary(compare, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01005510 switch (compare->InputAt(0)->GetType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005511 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005512 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005513 case DataType::Type::kInt8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005514 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005515 case DataType::Type::kInt16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005516 case DataType::Type::kInt32:
5517 case DataType::Type::kInt64: {
Artem Serov02d37832016-10-25 15:25:33 +01005518 locations->SetInAt(0, Location::RequiresRegister());
5519 locations->SetInAt(1, Location::RequiresRegister());
5520 // Output overlaps because it is written before doing the low comparison.
5521 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
5522 break;
5523 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005524 case DataType::Type::kFloat32:
5525 case DataType::Type::kFloat64: {
Artem Serov02d37832016-10-25 15:25:33 +01005526 locations->SetInAt(0, Location::RequiresFpuRegister());
5527 locations->SetInAt(1, ArithmeticZeroOrFpuRegister(compare->InputAt(1)));
5528 locations->SetOut(Location::RequiresRegister());
5529 break;
5530 }
5531 default:
5532 LOG(FATAL) << "Unexpected type for compare operation " << compare->InputAt(0)->GetType();
5533 }
5534}
5535
5536void InstructionCodeGeneratorARMVIXL::VisitCompare(HCompare* compare) {
5537 LocationSummary* locations = compare->GetLocations();
5538 vixl32::Register out = OutputRegister(compare);
5539 Location left = locations->InAt(0);
5540 Location right = locations->InAt(1);
5541
5542 vixl32::Label less, greater, done;
Anton Kirilov6f644202017-02-27 18:29:45 +00005543 vixl32::Label* final_label = codegen_->GetFinalLabel(compare, &done);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005544 DataType::Type type = compare->InputAt(0)->GetType();
Vladimir Marko33bff252017-11-01 14:35:42 +00005545 vixl32::Condition less_cond = vixl32::Condition::None();
Artem Serov02d37832016-10-25 15:25:33 +01005546 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005547 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005548 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005549 case DataType::Type::kInt8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005550 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005551 case DataType::Type::kInt16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005552 case DataType::Type::kInt32: {
Artem Serov02d37832016-10-25 15:25:33 +01005553 // Emit move to `out` before the `Cmp`, as `Mov` might affect the status flags.
5554 __ Mov(out, 0);
5555 __ Cmp(RegisterFrom(left), RegisterFrom(right)); // Signed compare.
5556 less_cond = lt;
5557 break;
5558 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005559 case DataType::Type::kInt64: {
Artem Serov02d37832016-10-25 15:25:33 +01005560 __ Cmp(HighRegisterFrom(left), HighRegisterFrom(right)); // Signed compare.
Andreas Gampe3db70682018-12-26 15:12:03 -08005561 __ B(lt, &less, /* is_far_target= */ false);
5562 __ B(gt, &greater, /* is_far_target= */ false);
Artem Serov02d37832016-10-25 15:25:33 +01005563 // Emit move to `out` before the last `Cmp`, as `Mov` might affect the status flags.
5564 __ Mov(out, 0);
5565 __ Cmp(LowRegisterFrom(left), LowRegisterFrom(right)); // Unsigned compare.
5566 less_cond = lo;
5567 break;
5568 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005569 case DataType::Type::kFloat32:
5570 case DataType::Type::kFloat64: {
Artem Serov02d37832016-10-25 15:25:33 +01005571 __ Mov(out, 0);
Donghui Bai426b49c2016-11-08 14:55:38 +08005572 GenerateVcmp(compare, codegen_);
Artem Serov02d37832016-10-25 15:25:33 +01005573 // To branch on the FP compare result we transfer FPSCR to APSR (encoded as PC in VMRS).
5574 __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
5575 less_cond = ARMFPCondition(kCondLT, compare->IsGtBias());
5576 break;
5577 }
5578 default:
5579 LOG(FATAL) << "Unexpected compare type " << type;
5580 UNREACHABLE();
5581 }
5582
Andreas Gampe3db70682018-12-26 15:12:03 -08005583 __ B(eq, final_label, /* is_far_target= */ false);
5584 __ B(less_cond, &less, /* is_far_target= */ false);
Artem Serov02d37832016-10-25 15:25:33 +01005585
5586 __ Bind(&greater);
5587 __ Mov(out, 1);
Anton Kirilov6f644202017-02-27 18:29:45 +00005588 __ B(final_label);
Artem Serov02d37832016-10-25 15:25:33 +01005589
5590 __ Bind(&less);
5591 __ Mov(out, -1);
5592
Anton Kirilov6f644202017-02-27 18:29:45 +00005593 if (done.IsReferenced()) {
5594 __ Bind(&done);
5595 }
Artem Serov02d37832016-10-25 15:25:33 +01005596}
5597
5598void LocationsBuilderARMVIXL::VisitPhi(HPhi* instruction) {
5599 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005600 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01005601 for (size_t i = 0, e = locations->GetInputCount(); i < e; ++i) {
5602 locations->SetInAt(i, Location::Any());
5603 }
5604 locations->SetOut(Location::Any());
5605}
5606
5607void InstructionCodeGeneratorARMVIXL::VisitPhi(HPhi* instruction ATTRIBUTE_UNUSED) {
5608 LOG(FATAL) << "Unreachable";
5609}
5610
5611void CodeGeneratorARMVIXL::GenerateMemoryBarrier(MemBarrierKind kind) {
5612 // TODO (ported from quick): revisit ARM barrier kinds.
5613 DmbOptions flavor = DmbOptions::ISH; // Quiet C++ warnings.
5614 switch (kind) {
5615 case MemBarrierKind::kAnyStore:
5616 case MemBarrierKind::kLoadAny:
5617 case MemBarrierKind::kAnyAny: {
5618 flavor = DmbOptions::ISH;
5619 break;
5620 }
5621 case MemBarrierKind::kStoreStore: {
5622 flavor = DmbOptions::ISHST;
5623 break;
5624 }
5625 default:
5626 LOG(FATAL) << "Unexpected memory barrier " << kind;
5627 }
5628 __ Dmb(flavor);
5629}
5630
5631void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicLoad(vixl32::Register addr,
5632 uint32_t offset,
5633 vixl32::Register out_lo,
5634 vixl32::Register out_hi) {
5635 UseScratchRegisterScope temps(GetVIXLAssembler());
5636 if (offset != 0) {
5637 vixl32::Register temp = temps.Acquire();
5638 __ Add(temp, addr, offset);
5639 addr = temp;
5640 }
Scott Wakelingb77051e2016-11-21 19:46:00 +00005641 __ Ldrexd(out_lo, out_hi, MemOperand(addr));
Artem Serov02d37832016-10-25 15:25:33 +01005642}
5643
5644void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicStore(vixl32::Register addr,
5645 uint32_t offset,
5646 vixl32::Register value_lo,
5647 vixl32::Register value_hi,
5648 vixl32::Register temp1,
5649 vixl32::Register temp2,
5650 HInstruction* instruction) {
5651 UseScratchRegisterScope temps(GetVIXLAssembler());
5652 vixl32::Label fail;
5653 if (offset != 0) {
5654 vixl32::Register temp = temps.Acquire();
5655 __ Add(temp, addr, offset);
5656 addr = temp;
5657 }
5658 __ Bind(&fail);
Alexandre Rames374ddf32016-11-04 10:40:49 +00005659 {
5660 // Ensure the pc position is recorded immediately after the `ldrexd` instruction.
Artem Serov0fb37192016-12-06 18:13:40 +00005661 ExactAssemblyScope aas(GetVIXLAssembler(),
5662 vixl32::kMaxInstructionSizeInBytes,
5663 CodeBufferCheckScope::kMaximumSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00005664 // We need a load followed by store. (The address used in a STREX instruction must
5665 // be the same as the address in the most recently executed LDREX instruction.)
5666 __ ldrexd(temp1, temp2, MemOperand(addr));
5667 codegen_->MaybeRecordImplicitNullCheck(instruction);
5668 }
Scott Wakelingb77051e2016-11-21 19:46:00 +00005669 __ Strexd(temp1, value_lo, value_hi, MemOperand(addr));
xueliang.zhongf51bc622016-11-04 09:23:32 +00005670 __ CompareAndBranchIfNonZero(temp1, &fail);
Artem Serov02d37832016-10-25 15:25:33 +01005671}
Artem Serov02109dd2016-09-23 17:17:54 +01005672
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005673void LocationsBuilderARMVIXL::HandleFieldSet(
5674 HInstruction* instruction, const FieldInfo& field_info) {
5675 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
5676
5677 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005678 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005679 locations->SetInAt(0, Location::RequiresRegister());
5680
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005681 DataType::Type field_type = field_info.GetFieldType();
5682 if (DataType::IsFloatingPointType(field_type)) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005683 locations->SetInAt(1, Location::RequiresFpuRegister());
5684 } else {
5685 locations->SetInAt(1, Location::RequiresRegister());
5686 }
5687
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005688 bool is_wide = field_type == DataType::Type::kInt64 || field_type == DataType::Type::kFloat64;
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005689 bool generate_volatile = field_info.IsVolatile()
5690 && is_wide
5691 && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
5692 bool needs_write_barrier =
5693 CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1));
5694 // Temporary registers for the write barrier.
5695 // TODO: consider renaming StoreNeedsWriteBarrier to StoreNeedsGCMark.
5696 if (needs_write_barrier) {
5697 locations->AddTemp(Location::RequiresRegister()); // Possibly used for reference poisoning too.
5698 locations->AddTemp(Location::RequiresRegister());
5699 } else if (generate_volatile) {
5700 // ARM encoding have some additional constraints for ldrexd/strexd:
5701 // - registers need to be consecutive
5702 // - the first register should be even but not R14.
5703 // We don't test for ARM yet, and the assertion makes sure that we
5704 // revisit this if we ever enable ARM encoding.
5705 DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet());
5706
5707 locations->AddTemp(Location::RequiresRegister());
5708 locations->AddTemp(Location::RequiresRegister());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005709 if (field_type == DataType::Type::kFloat64) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005710 // For doubles we need two more registers to copy the value.
5711 locations->AddTemp(LocationFrom(r2));
5712 locations->AddTemp(LocationFrom(r3));
5713 }
5714 }
5715}
5716
5717void InstructionCodeGeneratorARMVIXL::HandleFieldSet(HInstruction* instruction,
5718 const FieldInfo& field_info,
5719 bool value_can_be_null) {
5720 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
5721
5722 LocationSummary* locations = instruction->GetLocations();
5723 vixl32::Register base = InputRegisterAt(instruction, 0);
5724 Location value = locations->InAt(1);
5725
5726 bool is_volatile = field_info.IsVolatile();
5727 bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005728 DataType::Type field_type = field_info.GetFieldType();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005729 uint32_t offset = field_info.GetFieldOffset().Uint32Value();
5730 bool needs_write_barrier =
5731 CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1));
5732
5733 if (is_volatile) {
5734 codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
5735 }
5736
5737 switch (field_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005738 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005739 case DataType::Type::kUint8:
5740 case DataType::Type::kInt8:
5741 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005742 case DataType::Type::kInt16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005743 case DataType::Type::kInt32: {
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01005744 // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
5745 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005746 StoreOperandType operand_type = GetStoreOperandType(field_type);
5747 GetAssembler()->StoreToOffset(operand_type, RegisterFrom(value), base, offset);
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01005748 codegen_->MaybeRecordImplicitNullCheck(instruction);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005749 break;
5750 }
5751
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005752 case DataType::Type::kReference: {
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01005753 vixl32::Register value_reg = RegisterFrom(value);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005754 if (kPoisonHeapReferences && needs_write_barrier) {
5755 // Note that in the case where `value` is a null reference,
5756 // we do not enter this block, as a null reference does not
5757 // need poisoning.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005758 DCHECK_EQ(field_type, DataType::Type::kReference);
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01005759 value_reg = RegisterFrom(locations->GetTemp(0));
5760 __ Mov(value_reg, RegisterFrom(value));
5761 GetAssembler()->PoisonHeapReference(value_reg);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005762 }
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01005763 // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
5764 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
5765 GetAssembler()->StoreToOffset(kStoreWord, value_reg, base, offset);
5766 codegen_->MaybeRecordImplicitNullCheck(instruction);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005767 break;
5768 }
5769
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005770 case DataType::Type::kInt64: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005771 if (is_volatile && !atomic_ldrd_strd) {
5772 GenerateWideAtomicStore(base,
5773 offset,
5774 LowRegisterFrom(value),
5775 HighRegisterFrom(value),
5776 RegisterFrom(locations->GetTemp(0)),
5777 RegisterFrom(locations->GetTemp(1)),
5778 instruction);
5779 } else {
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01005780 // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
5781 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005782 GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), base, offset);
5783 codegen_->MaybeRecordImplicitNullCheck(instruction);
5784 }
5785 break;
5786 }
5787
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005788 case DataType::Type::kFloat32: {
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01005789 // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
5790 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005791 GetAssembler()->StoreSToOffset(SRegisterFrom(value), base, offset);
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01005792 codegen_->MaybeRecordImplicitNullCheck(instruction);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005793 break;
5794 }
5795
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005796 case DataType::Type::kFloat64: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01005797 vixl32::DRegister value_reg = DRegisterFrom(value);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005798 if (is_volatile && !atomic_ldrd_strd) {
5799 vixl32::Register value_reg_lo = RegisterFrom(locations->GetTemp(0));
5800 vixl32::Register value_reg_hi = RegisterFrom(locations->GetTemp(1));
5801
5802 __ Vmov(value_reg_lo, value_reg_hi, value_reg);
5803
5804 GenerateWideAtomicStore(base,
5805 offset,
5806 value_reg_lo,
5807 value_reg_hi,
5808 RegisterFrom(locations->GetTemp(2)),
5809 RegisterFrom(locations->GetTemp(3)),
5810 instruction);
5811 } else {
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01005812 // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
5813 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005814 GetAssembler()->StoreDToOffset(value_reg, base, offset);
5815 codegen_->MaybeRecordImplicitNullCheck(instruction);
5816 }
5817 break;
5818 }
5819
Aart Bik66c158e2018-01-31 12:55:04 -08005820 case DataType::Type::kUint32:
5821 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005822 case DataType::Type::kVoid:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005823 LOG(FATAL) << "Unreachable type " << field_type;
5824 UNREACHABLE();
5825 }
5826
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005827 if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
5828 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
5829 vixl32::Register card = RegisterFrom(locations->GetTemp(1));
5830 codegen_->MarkGCCard(temp, card, base, RegisterFrom(value), value_can_be_null);
5831 }
5832
5833 if (is_volatile) {
5834 codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
5835 }
5836}
5837
Artem Serov02d37832016-10-25 15:25:33 +01005838void LocationsBuilderARMVIXL::HandleFieldGet(HInstruction* instruction,
5839 const FieldInfo& field_info) {
5840 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
5841
5842 bool object_field_get_with_read_barrier =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005843 kEmitCompilerReadBarrier && (field_info.GetFieldType() == DataType::Type::kReference);
Artem Serov02d37832016-10-25 15:25:33 +01005844 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005845 new (GetGraph()->GetAllocator()) LocationSummary(instruction,
5846 object_field_get_with_read_barrier
5847 ? LocationSummary::kCallOnSlowPath
5848 : LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01005849 if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
5850 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
5851 }
5852 locations->SetInAt(0, Location::RequiresRegister());
5853
5854 bool volatile_for_double = field_info.IsVolatile()
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005855 && (field_info.GetFieldType() == DataType::Type::kFloat64)
Artem Serov02d37832016-10-25 15:25:33 +01005856 && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
5857 // The output overlaps in case of volatile long: we don't want the
5858 // code generated by GenerateWideAtomicLoad to overwrite the
5859 // object's location. Likewise, in the case of an object field get
5860 // with read barriers enabled, we do not want the load to overwrite
5861 // the object's location, as we need it to emit the read barrier.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005862 bool overlap =
5863 (field_info.IsVolatile() && (field_info.GetFieldType() == DataType::Type::kInt64)) ||
Artem Serov02d37832016-10-25 15:25:33 +01005864 object_field_get_with_read_barrier;
5865
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005866 if (DataType::IsFloatingPointType(instruction->GetType())) {
Artem Serov02d37832016-10-25 15:25:33 +01005867 locations->SetOut(Location::RequiresFpuRegister());
5868 } else {
5869 locations->SetOut(Location::RequiresRegister(),
5870 (overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap));
5871 }
5872 if (volatile_for_double) {
5873 // ARM encoding have some additional constraints for ldrexd/strexd:
5874 // - registers need to be consecutive
5875 // - the first register should be even but not R14.
5876 // We don't test for ARM yet, and the assertion makes sure that we
5877 // revisit this if we ever enable ARM encoding.
5878 DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet());
5879 locations->AddTemp(Location::RequiresRegister());
5880 locations->AddTemp(Location::RequiresRegister());
5881 } else if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
Vladimir Marko008e09f32018-08-06 15:42:43 +01005882 // We need a temporary register for the read barrier load in
5883 // CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier()
5884 // only if the offset is too big.
5885 if (field_info.GetFieldOffset().Uint32Value() >= kReferenceLoadMinFarOffset) {
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01005886 locations->AddTemp(Location::RequiresRegister());
5887 }
Artem Serov02d37832016-10-25 15:25:33 +01005888 }
5889}
5890
5891Location LocationsBuilderARMVIXL::ArithmeticZeroOrFpuRegister(HInstruction* input) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005892 DCHECK(DataType::IsFloatingPointType(input->GetType())) << input->GetType();
Artem Serov02d37832016-10-25 15:25:33 +01005893 if ((input->IsFloatConstant() && (input->AsFloatConstant()->IsArithmeticZero())) ||
5894 (input->IsDoubleConstant() && (input->AsDoubleConstant()->IsArithmeticZero()))) {
5895 return Location::ConstantLocation(input->AsConstant());
5896 } else {
5897 return Location::RequiresFpuRegister();
5898 }
5899}
5900
Artem Serov02109dd2016-09-23 17:17:54 +01005901Location LocationsBuilderARMVIXL::ArmEncodableConstantOrRegister(HInstruction* constant,
5902 Opcode opcode) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005903 DCHECK(!DataType::IsFloatingPointType(constant->GetType()));
Artem Serov02109dd2016-09-23 17:17:54 +01005904 if (constant->IsConstant() &&
5905 CanEncodeConstantAsImmediate(constant->AsConstant(), opcode)) {
5906 return Location::ConstantLocation(constant->AsConstant());
5907 }
5908 return Location::RequiresRegister();
5909}
5910
Vladimir Markof0a6a1d2018-01-08 14:23:56 +00005911static bool CanEncode32BitConstantAsImmediate(
5912 CodeGeneratorARMVIXL* codegen,
5913 uint32_t value,
5914 Opcode opcode,
5915 vixl32::FlagsUpdate flags_update = vixl32::FlagsUpdate::DontCare) {
5916 ArmVIXLAssembler* assembler = codegen->GetAssembler();
5917 if (assembler->ShifterOperandCanHold(opcode, value, flags_update)) {
Artem Serov02109dd2016-09-23 17:17:54 +01005918 return true;
5919 }
5920 Opcode neg_opcode = kNoOperand;
Anton Kiriloveffd5bf2017-02-28 16:59:15 +00005921 uint32_t neg_value = 0;
Artem Serov02109dd2016-09-23 17:17:54 +01005922 switch (opcode) {
Anton Kiriloveffd5bf2017-02-28 16:59:15 +00005923 case AND: neg_opcode = BIC; neg_value = ~value; break;
5924 case ORR: neg_opcode = ORN; neg_value = ~value; break;
5925 case ADD: neg_opcode = SUB; neg_value = -value; break;
5926 case ADC: neg_opcode = SBC; neg_value = ~value; break;
5927 case SUB: neg_opcode = ADD; neg_value = -value; break;
5928 case SBC: neg_opcode = ADC; neg_value = ~value; break;
5929 case MOV: neg_opcode = MVN; neg_value = ~value; break;
Artem Serov02109dd2016-09-23 17:17:54 +01005930 default:
5931 return false;
5932 }
Anton Kiriloveffd5bf2017-02-28 16:59:15 +00005933
Vladimir Markof0a6a1d2018-01-08 14:23:56 +00005934 if (assembler->ShifterOperandCanHold(neg_opcode, neg_value, flags_update)) {
Anton Kiriloveffd5bf2017-02-28 16:59:15 +00005935 return true;
5936 }
5937
5938 return opcode == AND && IsPowerOfTwo(value + 1);
Artem Serov02109dd2016-09-23 17:17:54 +01005939}
5940
Vladimir Markof0a6a1d2018-01-08 14:23:56 +00005941bool LocationsBuilderARMVIXL::CanEncodeConstantAsImmediate(HConstant* input_cst, Opcode opcode) {
5942 uint64_t value = static_cast<uint64_t>(Int64FromConstant(input_cst));
5943 if (DataType::Is64BitType(input_cst->GetType())) {
5944 Opcode high_opcode = opcode;
5945 vixl32::FlagsUpdate low_flags_update = vixl32::FlagsUpdate::DontCare;
5946 switch (opcode) {
5947 case SUB:
5948 // Flip the operation to an ADD.
5949 value = -value;
5950 opcode = ADD;
5951 FALLTHROUGH_INTENDED;
5952 case ADD:
5953 if (Low32Bits(value) == 0u) {
5954 return CanEncode32BitConstantAsImmediate(codegen_, High32Bits(value), opcode);
5955 }
5956 high_opcode = ADC;
5957 low_flags_update = vixl32::FlagsUpdate::SetFlags;
5958 break;
5959 default:
5960 break;
5961 }
5962 return CanEncode32BitConstantAsImmediate(codegen_, High32Bits(value), high_opcode) &&
5963 CanEncode32BitConstantAsImmediate(codegen_, Low32Bits(value), opcode, low_flags_update);
5964 } else {
5965 return CanEncode32BitConstantAsImmediate(codegen_, Low32Bits(value), opcode);
5966 }
5967}
5968
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005969void InstructionCodeGeneratorARMVIXL::HandleFieldGet(HInstruction* instruction,
5970 const FieldInfo& field_info) {
5971 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
5972
5973 LocationSummary* locations = instruction->GetLocations();
5974 vixl32::Register base = InputRegisterAt(instruction, 0);
5975 Location out = locations->Out();
5976 bool is_volatile = field_info.IsVolatile();
5977 bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
Vladimir Marko61b92282017-10-11 13:23:17 +01005978 DCHECK_EQ(DataType::Size(field_info.GetFieldType()), DataType::Size(instruction->GetType()));
5979 DataType::Type load_type = instruction->GetType();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005980 uint32_t offset = field_info.GetFieldOffset().Uint32Value();
5981
Vladimir Marko61b92282017-10-11 13:23:17 +01005982 switch (load_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005983 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005984 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005985 case DataType::Type::kInt8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005986 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005987 case DataType::Type::kInt16:
5988 case DataType::Type::kInt32: {
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01005989 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
5990 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
Vladimir Marko61b92282017-10-11 13:23:17 +01005991 LoadOperandType operand_type = GetLoadOperandType(load_type);
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005992 GetAssembler()->LoadFromOffset(operand_type, RegisterFrom(out), base, offset);
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01005993 codegen_->MaybeRecordImplicitNullCheck(instruction);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005994 break;
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005995 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005996
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005997 case DataType::Type::kReference: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005998 // /* HeapReference<Object> */ out = *(base + offset)
5999 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
Vladimir Markodcd117e2018-04-19 11:54:00 +01006000 Location maybe_temp = (locations->GetTempCount() != 0) ? locations->GetTemp(0) : Location();
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006001 // Note that a potential implicit null check is handled in this
6002 // CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier call.
6003 codegen_->GenerateFieldLoadWithBakerReadBarrier(
Andreas Gampe3db70682018-12-26 15:12:03 -08006004 instruction, out, base, offset, maybe_temp, /* needs_null_check= */ true);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006005 if (is_volatile) {
6006 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
6007 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006008 } else {
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006009 {
6010 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
6011 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
6012 GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(out), base, offset);
6013 codegen_->MaybeRecordImplicitNullCheck(instruction);
6014 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006015 if (is_volatile) {
6016 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
6017 }
6018 // If read barriers are enabled, emit read barriers other than
6019 // Baker's using a slow path (and also unpoison the loaded
6020 // reference, if heap poisoning is enabled).
6021 codegen_->MaybeGenerateReadBarrierSlow(instruction, out, out, locations->InAt(0), offset);
6022 }
6023 break;
6024 }
6025
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006026 case DataType::Type::kInt64: {
6027 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
6028 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006029 if (is_volatile && !atomic_ldrd_strd) {
6030 GenerateWideAtomicLoad(base, offset, LowRegisterFrom(out), HighRegisterFrom(out));
6031 } else {
6032 GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out), base, offset);
6033 }
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006034 codegen_->MaybeRecordImplicitNullCheck(instruction);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006035 break;
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006036 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006037
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006038 case DataType::Type::kFloat32: {
6039 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
6040 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006041 GetAssembler()->LoadSFromOffset(SRegisterFrom(out), base, offset);
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006042 codegen_->MaybeRecordImplicitNullCheck(instruction);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006043 break;
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006044 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006045
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006046 case DataType::Type::kFloat64: {
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006047 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
6048 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006049 vixl32::DRegister out_dreg = DRegisterFrom(out);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006050 if (is_volatile && !atomic_ldrd_strd) {
6051 vixl32::Register lo = RegisterFrom(locations->GetTemp(0));
6052 vixl32::Register hi = RegisterFrom(locations->GetTemp(1));
6053 GenerateWideAtomicLoad(base, offset, lo, hi);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006054 codegen_->MaybeRecordImplicitNullCheck(instruction);
6055 __ Vmov(out_dreg, lo, hi);
6056 } else {
6057 GetAssembler()->LoadDFromOffset(out_dreg, base, offset);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006058 codegen_->MaybeRecordImplicitNullCheck(instruction);
6059 }
6060 break;
6061 }
6062
Aart Bik66c158e2018-01-31 12:55:04 -08006063 case DataType::Type::kUint32:
6064 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006065 case DataType::Type::kVoid:
Vladimir Marko61b92282017-10-11 13:23:17 +01006066 LOG(FATAL) << "Unreachable type " << load_type;
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006067 UNREACHABLE();
6068 }
6069
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006070 if (is_volatile) {
Vladimir Marko61b92282017-10-11 13:23:17 +01006071 if (load_type == DataType::Type::kReference) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006072 // Memory barriers, in the case of references, are also handled
6073 // in the previous switch statement.
6074 } else {
6075 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
6076 }
6077 }
6078}
6079
6080void LocationsBuilderARMVIXL::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
6081 HandleFieldSet(instruction, instruction->GetFieldInfo());
6082}
6083
6084void InstructionCodeGeneratorARMVIXL::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
6085 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
6086}
6087
6088void LocationsBuilderARMVIXL::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
6089 HandleFieldGet(instruction, instruction->GetFieldInfo());
6090}
6091
6092void InstructionCodeGeneratorARMVIXL::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
6093 HandleFieldGet(instruction, instruction->GetFieldInfo());
6094}
6095
6096void LocationsBuilderARMVIXL::VisitStaticFieldGet(HStaticFieldGet* instruction) {
6097 HandleFieldGet(instruction, instruction->GetFieldInfo());
6098}
6099
6100void InstructionCodeGeneratorARMVIXL::VisitStaticFieldGet(HStaticFieldGet* instruction) {
6101 HandleFieldGet(instruction, instruction->GetFieldInfo());
6102}
6103
Scott Wakelingc34dba72016-10-03 10:14:44 +01006104void LocationsBuilderARMVIXL::VisitStaticFieldSet(HStaticFieldSet* instruction) {
6105 HandleFieldSet(instruction, instruction->GetFieldInfo());
6106}
6107
6108void InstructionCodeGeneratorARMVIXL::VisitStaticFieldSet(HStaticFieldSet* instruction) {
6109 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
6110}
6111
Vladimir Marko552a1342017-10-31 10:56:47 +00006112void LocationsBuilderARMVIXL::VisitStringBuilderAppend(HStringBuilderAppend* instruction) {
6113 codegen_->CreateStringBuilderAppendLocations(instruction, LocationFrom(r0));
6114}
6115
6116void InstructionCodeGeneratorARMVIXL::VisitStringBuilderAppend(HStringBuilderAppend* instruction) {
6117 __ Mov(r0, instruction->GetFormat()->GetValue());
6118 codegen_->InvokeRuntime(kQuickStringBuilderAppend, instruction, instruction->GetDexPc());
6119}
6120
Artem Serovcfbe9132016-10-14 15:58:56 +01006121void LocationsBuilderARMVIXL::VisitUnresolvedInstanceFieldGet(
6122 HUnresolvedInstanceFieldGet* instruction) {
6123 FieldAccessCallingConventionARMVIXL calling_convention;
6124 codegen_->CreateUnresolvedFieldLocationSummary(
6125 instruction, instruction->GetFieldType(), calling_convention);
6126}
6127
6128void InstructionCodeGeneratorARMVIXL::VisitUnresolvedInstanceFieldGet(
6129 HUnresolvedInstanceFieldGet* instruction) {
6130 FieldAccessCallingConventionARMVIXL calling_convention;
6131 codegen_->GenerateUnresolvedFieldAccess(instruction,
6132 instruction->GetFieldType(),
6133 instruction->GetFieldIndex(),
6134 instruction->GetDexPc(),
6135 calling_convention);
6136}
6137
6138void LocationsBuilderARMVIXL::VisitUnresolvedInstanceFieldSet(
6139 HUnresolvedInstanceFieldSet* instruction) {
6140 FieldAccessCallingConventionARMVIXL calling_convention;
6141 codegen_->CreateUnresolvedFieldLocationSummary(
6142 instruction, instruction->GetFieldType(), calling_convention);
6143}
6144
6145void InstructionCodeGeneratorARMVIXL::VisitUnresolvedInstanceFieldSet(
6146 HUnresolvedInstanceFieldSet* instruction) {
6147 FieldAccessCallingConventionARMVIXL calling_convention;
6148 codegen_->GenerateUnresolvedFieldAccess(instruction,
6149 instruction->GetFieldType(),
6150 instruction->GetFieldIndex(),
6151 instruction->GetDexPc(),
6152 calling_convention);
6153}
6154
6155void LocationsBuilderARMVIXL::VisitUnresolvedStaticFieldGet(
6156 HUnresolvedStaticFieldGet* instruction) {
6157 FieldAccessCallingConventionARMVIXL calling_convention;
6158 codegen_->CreateUnresolvedFieldLocationSummary(
6159 instruction, instruction->GetFieldType(), calling_convention);
6160}
6161
6162void InstructionCodeGeneratorARMVIXL::VisitUnresolvedStaticFieldGet(
6163 HUnresolvedStaticFieldGet* instruction) {
6164 FieldAccessCallingConventionARMVIXL calling_convention;
6165 codegen_->GenerateUnresolvedFieldAccess(instruction,
6166 instruction->GetFieldType(),
6167 instruction->GetFieldIndex(),
6168 instruction->GetDexPc(),
6169 calling_convention);
6170}
6171
6172void LocationsBuilderARMVIXL::VisitUnresolvedStaticFieldSet(
6173 HUnresolvedStaticFieldSet* instruction) {
6174 FieldAccessCallingConventionARMVIXL calling_convention;
6175 codegen_->CreateUnresolvedFieldLocationSummary(
6176 instruction, instruction->GetFieldType(), calling_convention);
6177}
6178
6179void InstructionCodeGeneratorARMVIXL::VisitUnresolvedStaticFieldSet(
6180 HUnresolvedStaticFieldSet* instruction) {
6181 FieldAccessCallingConventionARMVIXL calling_convention;
6182 codegen_->GenerateUnresolvedFieldAccess(instruction,
6183 instruction->GetFieldType(),
6184 instruction->GetFieldIndex(),
6185 instruction->GetDexPc(),
6186 calling_convention);
6187}
6188
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006189void LocationsBuilderARMVIXL::VisitNullCheck(HNullCheck* instruction) {
Artem Serov657022c2016-11-23 14:19:38 +00006190 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006191 locations->SetInAt(0, Location::RequiresRegister());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006192}
6193
6194void CodeGeneratorARMVIXL::GenerateImplicitNullCheck(HNullCheck* instruction) {
6195 if (CanMoveNullCheckToUser(instruction)) {
6196 return;
6197 }
6198
6199 UseScratchRegisterScope temps(GetVIXLAssembler());
Alexandre Rames374ddf32016-11-04 10:40:49 +00006200 // Ensure the pc position is recorded immediately after the `ldr` instruction.
Artem Serov0fb37192016-12-06 18:13:40 +00006201 ExactAssemblyScope aas(GetVIXLAssembler(),
6202 vixl32::kMaxInstructionSizeInBytes,
6203 CodeBufferCheckScope::kMaximumSize);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006204 __ ldr(temps.Acquire(), MemOperand(InputRegisterAt(instruction, 0)));
6205 RecordPcInfo(instruction, instruction->GetDexPc());
6206}
6207
6208void CodeGeneratorARMVIXL::GenerateExplicitNullCheck(HNullCheck* instruction) {
6209 NullCheckSlowPathARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01006210 new (GetScopedAllocator()) NullCheckSlowPathARMVIXL(instruction);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006211 AddSlowPath(slow_path);
xueliang.zhongf51bc622016-11-04 09:23:32 +00006212 __ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006213}
6214
6215void InstructionCodeGeneratorARMVIXL::VisitNullCheck(HNullCheck* instruction) {
6216 codegen_->GenerateNullCheck(instruction);
6217}
6218
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006219void CodeGeneratorARMVIXL::LoadFromShiftedRegOffset(DataType::Type type,
Scott Wakelingc34dba72016-10-03 10:14:44 +01006220 Location out_loc,
6221 vixl32::Register base,
6222 vixl32::Register reg_index,
6223 vixl32::Condition cond) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006224 uint32_t shift_count = DataType::SizeShift(type);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006225 MemOperand mem_address(base, reg_index, vixl32::LSL, shift_count);
6226
6227 switch (type) {
Vladimir Marko61b92282017-10-11 13:23:17 +01006228 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006229 case DataType::Type::kUint8:
Vladimir Marko61b92282017-10-11 13:23:17 +01006230 __ Ldrb(cond, RegisterFrom(out_loc), mem_address);
6231 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006232 case DataType::Type::kInt8:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006233 __ Ldrsb(cond, RegisterFrom(out_loc), mem_address);
6234 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006235 case DataType::Type::kUint16:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006236 __ Ldrh(cond, RegisterFrom(out_loc), mem_address);
6237 break;
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006238 case DataType::Type::kInt16:
6239 __ Ldrsh(cond, RegisterFrom(out_loc), mem_address);
6240 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006241 case DataType::Type::kReference:
6242 case DataType::Type::kInt32:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006243 __ Ldr(cond, RegisterFrom(out_loc), mem_address);
6244 break;
6245 // T32 doesn't support LoadFromShiftedRegOffset mem address mode for these types.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006246 case DataType::Type::kInt64:
6247 case DataType::Type::kFloat32:
6248 case DataType::Type::kFloat64:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006249 default:
6250 LOG(FATAL) << "Unreachable type " << type;
6251 UNREACHABLE();
6252 }
6253}
6254
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006255void CodeGeneratorARMVIXL::StoreToShiftedRegOffset(DataType::Type type,
Scott Wakelingc34dba72016-10-03 10:14:44 +01006256 Location loc,
6257 vixl32::Register base,
6258 vixl32::Register reg_index,
6259 vixl32::Condition cond) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006260 uint32_t shift_count = DataType::SizeShift(type);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006261 MemOperand mem_address(base, reg_index, vixl32::LSL, shift_count);
6262
6263 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006264 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006265 case DataType::Type::kUint8:
6266 case DataType::Type::kInt8:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006267 __ Strb(cond, RegisterFrom(loc), mem_address);
6268 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006269 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006270 case DataType::Type::kInt16:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006271 __ Strh(cond, RegisterFrom(loc), mem_address);
6272 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006273 case DataType::Type::kReference:
6274 case DataType::Type::kInt32:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006275 __ Str(cond, RegisterFrom(loc), mem_address);
6276 break;
6277 // T32 doesn't support StoreToShiftedRegOffset mem address mode for these types.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006278 case DataType::Type::kInt64:
6279 case DataType::Type::kFloat32:
6280 case DataType::Type::kFloat64:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006281 default:
6282 LOG(FATAL) << "Unreachable type " << type;
6283 UNREACHABLE();
6284 }
6285}
6286
6287void LocationsBuilderARMVIXL::VisitArrayGet(HArrayGet* instruction) {
6288 bool object_array_get_with_read_barrier =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006289 kEmitCompilerReadBarrier && (instruction->GetType() == DataType::Type::kReference);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006290 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01006291 new (GetGraph()->GetAllocator()) LocationSummary(instruction,
6292 object_array_get_with_read_barrier
6293 ? LocationSummary::kCallOnSlowPath
6294 : LocationSummary::kNoCall);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006295 if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006296 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Scott Wakelingc34dba72016-10-03 10:14:44 +01006297 }
6298 locations->SetInAt(0, Location::RequiresRegister());
6299 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006300 if (DataType::IsFloatingPointType(instruction->GetType())) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006301 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
6302 } else {
6303 // The output overlaps in the case of an object array get with
6304 // read barriers enabled: we do not want the move to overwrite the
6305 // array's location, as we need it to emit the read barrier.
6306 locations->SetOut(
6307 Location::RequiresRegister(),
6308 object_array_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap);
6309 }
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006310 if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
Vladimir Marko008e09f32018-08-06 15:42:43 +01006311 if (instruction->GetIndex()->IsConstant()) {
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006312 // Array loads with constant index are treated as field loads.
Vladimir Marko008e09f32018-08-06 15:42:43 +01006313 // We need a temporary register for the read barrier load in
6314 // CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier()
6315 // only if the offset is too big.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006316 uint32_t offset = CodeGenerator::GetArrayDataOffset(instruction);
6317 uint32_t index = instruction->GetIndex()->AsIntConstant()->GetValue();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006318 offset += index << DataType::SizeShift(DataType::Type::kReference);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006319 if (offset >= kReferenceLoadMinFarOffset) {
6320 locations->AddTemp(Location::RequiresRegister());
6321 }
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006322 } else {
Vladimir Marko008e09f32018-08-06 15:42:43 +01006323 // We need a non-scratch temporary for the array data pointer in
6324 // CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier().
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006325 locations->AddTemp(Location::RequiresRegister());
6326 }
6327 } else if (mirror::kUseStringCompression && instruction->IsStringCharAt()) {
6328 // Also need a temporary for String compression feature.
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006329 locations->AddTemp(Location::RequiresRegister());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006330 }
6331}
6332
6333void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006334 LocationSummary* locations = instruction->GetLocations();
6335 Location obj_loc = locations->InAt(0);
6336 vixl32::Register obj = InputRegisterAt(instruction, 0);
6337 Location index = locations->InAt(1);
6338 Location out_loc = locations->Out();
6339 uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006340 DataType::Type type = instruction->GetType();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006341 const bool maybe_compressed_char_at = mirror::kUseStringCompression &&
6342 instruction->IsStringCharAt();
6343 HInstruction* array_instr = instruction->GetArray();
6344 bool has_intermediate_address = array_instr->IsIntermediateAddress();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006345
6346 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006347 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006348 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006349 case DataType::Type::kInt8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006350 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006351 case DataType::Type::kInt16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006352 case DataType::Type::kInt32: {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01006353 vixl32::Register length;
6354 if (maybe_compressed_char_at) {
6355 length = RegisterFrom(locations->GetTemp(0));
6356 uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006357 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
6358 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01006359 GetAssembler()->LoadFromOffset(kLoadWord, length, obj, count_offset);
6360 codegen_->MaybeRecordImplicitNullCheck(instruction);
6361 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006362 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006363 int32_t const_index = Int32ConstantFrom(index);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006364 if (maybe_compressed_char_at) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006365 vixl32::Label uncompressed_load, done;
Anton Kirilov6f644202017-02-27 18:29:45 +00006366 vixl32::Label* final_label = codegen_->GetFinalLabel(instruction, &done);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01006367 __ Lsrs(length, length, 1u); // LSRS has a 16-bit encoding, TST (immediate) does not.
6368 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
6369 "Expecting 0=compressed, 1=uncompressed");
Andreas Gampe3db70682018-12-26 15:12:03 -08006370 __ B(cs, &uncompressed_load, /* is_far_target= */ false);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006371 GetAssembler()->LoadFromOffset(kLoadUnsignedByte,
6372 RegisterFrom(out_loc),
6373 obj,
6374 data_offset + const_index);
Anton Kirilov6f644202017-02-27 18:29:45 +00006375 __ B(final_label);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006376 __ Bind(&uncompressed_load);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006377 GetAssembler()->LoadFromOffset(GetLoadOperandType(DataType::Type::kUint16),
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006378 RegisterFrom(out_loc),
6379 obj,
6380 data_offset + (const_index << 1));
Anton Kirilov6f644202017-02-27 18:29:45 +00006381 if (done.IsReferenced()) {
6382 __ Bind(&done);
6383 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006384 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006385 uint32_t full_offset = data_offset + (const_index << DataType::SizeShift(type));
Scott Wakelingc34dba72016-10-03 10:14:44 +01006386
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006387 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
6388 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006389 LoadOperandType load_type = GetLoadOperandType(type);
6390 GetAssembler()->LoadFromOffset(load_type, RegisterFrom(out_loc), obj, full_offset);
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006391 codegen_->MaybeRecordImplicitNullCheck(instruction);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006392 }
6393 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006394 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006395 vixl32::Register temp = temps.Acquire();
6396
6397 if (has_intermediate_address) {
Artem Serov2bbc9532016-10-21 11:51:50 +01006398 // We do not need to compute the intermediate address from the array: the
6399 // input instruction has done it already. See the comment in
6400 // `TryExtractArrayAccessAddress()`.
6401 if (kIsDebugBuild) {
6402 HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
Anton Kirilov644032c2016-12-06 17:51:43 +00006403 DCHECK_EQ(Uint64ConstantFrom(tmp->GetOffset()), data_offset);
Artem Serov2bbc9532016-10-21 11:51:50 +01006404 }
6405 temp = obj;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006406 } else {
6407 __ Add(temp, obj, data_offset);
6408 }
6409 if (maybe_compressed_char_at) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006410 vixl32::Label uncompressed_load, done;
Anton Kirilov6f644202017-02-27 18:29:45 +00006411 vixl32::Label* final_label = codegen_->GetFinalLabel(instruction, &done);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01006412 __ Lsrs(length, length, 1u); // LSRS has a 16-bit encoding, TST (immediate) does not.
6413 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
6414 "Expecting 0=compressed, 1=uncompressed");
Andreas Gampe3db70682018-12-26 15:12:03 -08006415 __ B(cs, &uncompressed_load, /* is_far_target= */ false);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006416 __ Ldrb(RegisterFrom(out_loc), MemOperand(temp, RegisterFrom(index), vixl32::LSL, 0));
Anton Kirilov6f644202017-02-27 18:29:45 +00006417 __ B(final_label);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006418 __ Bind(&uncompressed_load);
6419 __ Ldrh(RegisterFrom(out_loc), MemOperand(temp, RegisterFrom(index), vixl32::LSL, 1));
Anton Kirilov6f644202017-02-27 18:29:45 +00006420 if (done.IsReferenced()) {
6421 __ Bind(&done);
6422 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006423 } else {
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006424 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
6425 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006426 codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index));
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006427 codegen_->MaybeRecordImplicitNullCheck(instruction);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006428 }
6429 }
6430 break;
6431 }
6432
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006433 case DataType::Type::kReference: {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006434 // The read barrier instrumentation of object ArrayGet
6435 // instructions does not support the HIntermediateAddress
6436 // instruction.
6437 DCHECK(!(has_intermediate_address && kEmitCompilerReadBarrier));
6438
Scott Wakelingc34dba72016-10-03 10:14:44 +01006439 static_assert(
6440 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
6441 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
6442 // /* HeapReference<Object> */ out =
6443 // *(obj + data_offset + index * sizeof(HeapReference<Object>))
6444 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006445 // Note that a potential implicit null check is handled in this
6446 // CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier call.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006447 DCHECK(!instruction->CanDoImplicitNullCheckOn(instruction->InputAt(0)));
6448 if (index.IsConstant()) {
6449 // Array load with a constant index can be treated as a field load.
Vladimir Markodcd117e2018-04-19 11:54:00 +01006450 Location maybe_temp =
6451 (locations->GetTempCount() != 0) ? locations->GetTemp(0) : Location();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006452 data_offset += Int32ConstantFrom(index) << DataType::SizeShift(type);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006453 codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction,
6454 out_loc,
6455 obj,
6456 data_offset,
Vladimir Markodcd117e2018-04-19 11:54:00 +01006457 maybe_temp,
Andreas Gampe3db70682018-12-26 15:12:03 -08006458 /* needs_null_check= */ false);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006459 } else {
Vladimir Markodcd117e2018-04-19 11:54:00 +01006460 Location temp = locations->GetTemp(0);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006461 codegen_->GenerateArrayLoadWithBakerReadBarrier(
Andreas Gampe3db70682018-12-26 15:12:03 -08006462 out_loc, obj, data_offset, index, temp, /* needs_null_check= */ false);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006463 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006464 } else {
6465 vixl32::Register out = OutputRegister(instruction);
6466 if (index.IsConstant()) {
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006467 size_t offset = (Int32ConstantFrom(index) << TIMES_4) + data_offset;
6468 {
6469 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
6470 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
6471 GetAssembler()->LoadFromOffset(kLoadWord, out, obj, offset);
6472 codegen_->MaybeRecordImplicitNullCheck(instruction);
6473 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006474 // If read barriers are enabled, emit read barriers other than
6475 // Baker's using a slow path (and also unpoison the loaded
6476 // reference, if heap poisoning is enabled).
6477 codegen_->MaybeGenerateReadBarrierSlow(instruction, out_loc, out_loc, obj_loc, offset);
6478 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006479 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006480 vixl32::Register temp = temps.Acquire();
6481
6482 if (has_intermediate_address) {
Artem Serov2bbc9532016-10-21 11:51:50 +01006483 // We do not need to compute the intermediate address from the array: the
6484 // input instruction has done it already. See the comment in
6485 // `TryExtractArrayAccessAddress()`.
6486 if (kIsDebugBuild) {
6487 HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
Anton Kirilov644032c2016-12-06 17:51:43 +00006488 DCHECK_EQ(Uint64ConstantFrom(tmp->GetOffset()), data_offset);
Artem Serov2bbc9532016-10-21 11:51:50 +01006489 }
6490 temp = obj;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006491 } else {
6492 __ Add(temp, obj, data_offset);
6493 }
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006494 {
6495 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
6496 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
6497 codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index));
6498 temps.Close();
6499 codegen_->MaybeRecordImplicitNullCheck(instruction);
6500 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006501 // If read barriers are enabled, emit read barriers other than
6502 // Baker's using a slow path (and also unpoison the loaded
6503 // reference, if heap poisoning is enabled).
6504 codegen_->MaybeGenerateReadBarrierSlow(
6505 instruction, out_loc, out_loc, obj_loc, data_offset, index);
6506 }
6507 }
6508 break;
6509 }
6510
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006511 case DataType::Type::kInt64: {
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006512 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
6513 // As two macro instructions can be emitted the max size is doubled.
6514 EmissionCheckScope guard(GetVIXLAssembler(), 2 * kMaxMacroInstructionSizeInBytes);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006515 if (index.IsConstant()) {
6516 size_t offset =
Anton Kirilov644032c2016-12-06 17:51:43 +00006517 (Int32ConstantFrom(index) << TIMES_8) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006518 GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out_loc), obj, offset);
6519 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006520 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006521 vixl32::Register temp = temps.Acquire();
6522 __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
6523 GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out_loc), temp, data_offset);
6524 }
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006525 codegen_->MaybeRecordImplicitNullCheck(instruction);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006526 break;
6527 }
6528
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006529 case DataType::Type::kFloat32: {
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006530 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
6531 // As two macro instructions can be emitted the max size is doubled.
6532 EmissionCheckScope guard(GetVIXLAssembler(), 2 * kMaxMacroInstructionSizeInBytes);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006533 vixl32::SRegister out = SRegisterFrom(out_loc);
6534 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006535 size_t offset = (Int32ConstantFrom(index) << TIMES_4) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006536 GetAssembler()->LoadSFromOffset(out, obj, offset);
6537 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006538 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006539 vixl32::Register temp = temps.Acquire();
6540 __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_4));
6541 GetAssembler()->LoadSFromOffset(out, temp, data_offset);
6542 }
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006543 codegen_->MaybeRecordImplicitNullCheck(instruction);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006544 break;
6545 }
6546
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006547 case DataType::Type::kFloat64: {
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006548 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
6549 // As two macro instructions can be emitted the max size is doubled.
6550 EmissionCheckScope guard(GetVIXLAssembler(), 2 * kMaxMacroInstructionSizeInBytes);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006551 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006552 size_t offset = (Int32ConstantFrom(index) << TIMES_8) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006553 GetAssembler()->LoadDFromOffset(DRegisterFrom(out_loc), obj, offset);
6554 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006555 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006556 vixl32::Register temp = temps.Acquire();
6557 __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
6558 GetAssembler()->LoadDFromOffset(DRegisterFrom(out_loc), temp, data_offset);
6559 }
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006560 codegen_->MaybeRecordImplicitNullCheck(instruction);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006561 break;
6562 }
6563
Aart Bik66c158e2018-01-31 12:55:04 -08006564 case DataType::Type::kUint32:
6565 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006566 case DataType::Type::kVoid:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006567 LOG(FATAL) << "Unreachable type " << type;
6568 UNREACHABLE();
6569 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006570}
6571
6572void LocationsBuilderARMVIXL::VisitArraySet(HArraySet* instruction) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006573 DataType::Type value_type = instruction->GetComponentType();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006574
6575 bool needs_write_barrier =
6576 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
Vladimir Marko8fa839c2019-05-16 12:50:47 +00006577 bool needs_type_check = instruction->NeedsTypeCheck();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006578
Vladimir Markoca6fff82017-10-03 14:49:14 +01006579 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
Scott Wakelingc34dba72016-10-03 10:14:44 +01006580 instruction,
Vladimir Marko8fa839c2019-05-16 12:50:47 +00006581 needs_type_check ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006582
6583 locations->SetInAt(0, Location::RequiresRegister());
6584 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006585 if (DataType::IsFloatingPointType(value_type)) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006586 locations->SetInAt(2, Location::RequiresFpuRegister());
6587 } else {
6588 locations->SetInAt(2, Location::RequiresRegister());
6589 }
6590 if (needs_write_barrier) {
6591 // Temporary registers for the write barrier.
6592 locations->AddTemp(Location::RequiresRegister()); // Possibly used for ref. poisoning too.
6593 locations->AddTemp(Location::RequiresRegister());
6594 }
6595}
6596
6597void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006598 LocationSummary* locations = instruction->GetLocations();
6599 vixl32::Register array = InputRegisterAt(instruction, 0);
6600 Location index = locations->InAt(1);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006601 DataType::Type value_type = instruction->GetComponentType();
Vladimir Marko8fa839c2019-05-16 12:50:47 +00006602 bool needs_type_check = instruction->NeedsTypeCheck();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006603 bool needs_write_barrier =
6604 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
6605 uint32_t data_offset =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006606 mirror::Array::DataOffset(DataType::Size(value_type)).Uint32Value();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006607 Location value_loc = locations->InAt(2);
6608 HInstruction* array_instr = instruction->GetArray();
6609 bool has_intermediate_address = array_instr->IsIntermediateAddress();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006610
6611 switch (value_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006612 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006613 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006614 case DataType::Type::kInt8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006615 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006616 case DataType::Type::kInt16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006617 case DataType::Type::kInt32: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006618 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006619 int32_t const_index = Int32ConstantFrom(index);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006620 uint32_t full_offset =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006621 data_offset + (const_index << DataType::SizeShift(value_type));
Scott Wakelingc34dba72016-10-03 10:14:44 +01006622 StoreOperandType store_type = GetStoreOperandType(value_type);
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006623 // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
6624 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006625 GetAssembler()->StoreToOffset(store_type, RegisterFrom(value_loc), array, full_offset);
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006626 codegen_->MaybeRecordImplicitNullCheck(instruction);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006627 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006628 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006629 vixl32::Register temp = temps.Acquire();
6630
6631 if (has_intermediate_address) {
Artem Serov2bbc9532016-10-21 11:51:50 +01006632 // We do not need to compute the intermediate address from the array: the
6633 // input instruction has done it already. See the comment in
6634 // `TryExtractArrayAccessAddress()`.
6635 if (kIsDebugBuild) {
6636 HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
Anton Kirilov644032c2016-12-06 17:51:43 +00006637 DCHECK_EQ(Uint64ConstantFrom(tmp->GetOffset()), data_offset);
Artem Serov2bbc9532016-10-21 11:51:50 +01006638 }
6639 temp = array;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006640 } else {
6641 __ Add(temp, array, data_offset);
6642 }
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006643 // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
6644 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006645 codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006646 codegen_->MaybeRecordImplicitNullCheck(instruction);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006647 }
6648 break;
6649 }
6650
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006651 case DataType::Type::kReference: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006652 vixl32::Register value = RegisterFrom(value_loc);
6653 // TryExtractArrayAccessAddress optimization is never applied for non-primitive ArraySet.
6654 // See the comment in instruction_simplifier_shared.cc.
6655 DCHECK(!has_intermediate_address);
6656
6657 if (instruction->InputAt(2)->IsNullConstant()) {
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006658 // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
6659 // As two macro instructions can be emitted the max size is doubled.
6660 EmissionCheckScope guard(GetVIXLAssembler(), 2 * kMaxMacroInstructionSizeInBytes);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006661 // Just setting null.
6662 if (index.IsConstant()) {
Vladimir Marko8fa839c2019-05-16 12:50:47 +00006663 size_t offset = (Int32ConstantFrom(index) << TIMES_4) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006664 GetAssembler()->StoreToOffset(kStoreWord, value, array, offset);
6665 } else {
6666 DCHECK(index.IsRegister()) << index;
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006667 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006668 vixl32::Register temp = temps.Acquire();
6669 __ Add(temp, array, data_offset);
6670 codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
6671 }
6672 codegen_->MaybeRecordImplicitNullCheck(instruction);
6673 DCHECK(!needs_write_barrier);
Vladimir Marko8fa839c2019-05-16 12:50:47 +00006674 DCHECK(!needs_type_check);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006675 break;
6676 }
6677
6678 DCHECK(needs_write_barrier);
6679 Location temp1_loc = locations->GetTemp(0);
6680 vixl32::Register temp1 = RegisterFrom(temp1_loc);
6681 Location temp2_loc = locations->GetTemp(1);
6682 vixl32::Register temp2 = RegisterFrom(temp2_loc);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006683
Vladimir Marko8fa839c2019-05-16 12:50:47 +00006684 bool can_value_be_null = instruction->GetValueCanBeNull();
6685 vixl32::Label do_store;
6686 if (can_value_be_null) {
6687 __ CompareAndBranchIfZero(value, &do_store, /* is_far_target= */ false);
6688 }
6689
6690 SlowPathCodeARMVIXL* slow_path = nullptr;
6691 if (needs_type_check) {
Vladimir Marko0dda8c82019-05-16 12:47:40 +00006692 slow_path = new (codegen_->GetScopedAllocator()) ArraySetSlowPathARMVIXL(instruction);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006693 codegen_->AddSlowPath(slow_path);
Vladimir Marko8fa839c2019-05-16 12:50:47 +00006694
6695 const uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
6696 const uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
6697 const uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006698
6699 // Note that when read barriers are enabled, the type checks
6700 // are performed without read barriers. This is fine, even in
6701 // the case where a class object is in the from-space after
6702 // the flip, as a comparison involving such a type would not
6703 // produce a false positive; it may of course produce a false
6704 // negative, in which case we would take the ArraySet slow
6705 // path.
6706
Alexandre Rames374ddf32016-11-04 10:40:49 +00006707 {
6708 // Ensure we record the pc position immediately after the `ldr` instruction.
Artem Serov0fb37192016-12-06 18:13:40 +00006709 ExactAssemblyScope aas(GetVIXLAssembler(),
6710 vixl32::kMaxInstructionSizeInBytes,
6711 CodeBufferCheckScope::kMaximumSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00006712 // /* HeapReference<Class> */ temp1 = array->klass_
6713 __ ldr(temp1, MemOperand(array, class_offset));
6714 codegen_->MaybeRecordImplicitNullCheck(instruction);
6715 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006716 GetAssembler()->MaybeUnpoisonHeapReference(temp1);
6717
6718 // /* HeapReference<Class> */ temp1 = temp1->component_type_
6719 GetAssembler()->LoadFromOffset(kLoadWord, temp1, temp1, component_offset);
6720 // /* HeapReference<Class> */ temp2 = value->klass_
6721 GetAssembler()->LoadFromOffset(kLoadWord, temp2, value, class_offset);
6722 // If heap poisoning is enabled, no need to unpoison `temp1`
6723 // nor `temp2`, as we are comparing two poisoned references.
6724 __ Cmp(temp1, temp2);
6725
6726 if (instruction->StaticTypeOfArrayIsObjectArray()) {
Vladimir Marko0dda8c82019-05-16 12:47:40 +00006727 vixl32::Label do_put;
6728 __ B(eq, &do_put, /* is_far_target= */ false);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006729 // If heap poisoning is enabled, the `temp1` reference has
6730 // not been unpoisoned yet; unpoison it now.
6731 GetAssembler()->MaybeUnpoisonHeapReference(temp1);
6732
6733 // /* HeapReference<Class> */ temp1 = temp1->super_class_
6734 GetAssembler()->LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
6735 // If heap poisoning is enabled, no need to unpoison
6736 // `temp1`, as we are comparing against null below.
xueliang.zhongf51bc622016-11-04 09:23:32 +00006737 __ CompareAndBranchIfNonZero(temp1, slow_path->GetEntryLabel());
Vladimir Marko0dda8c82019-05-16 12:47:40 +00006738 __ Bind(&do_put);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006739 } else {
6740 __ B(ne, slow_path->GetEntryLabel());
6741 }
6742 }
6743
Vladimir Markoac3fcff2020-11-17 12:17:58 +00006744 codegen_->MarkGCCard(temp1, temp2, array, value, /* value_can_be_null= */ false);
Vladimir Marko8fa839c2019-05-16 12:50:47 +00006745
6746 if (can_value_be_null) {
6747 DCHECK(do_store.IsReferenced());
6748 __ Bind(&do_store);
6749 }
6750
Scott Wakelingc34dba72016-10-03 10:14:44 +01006751 vixl32::Register source = value;
6752 if (kPoisonHeapReferences) {
6753 // Note that in the case where `value` is a null reference,
6754 // we do not enter this block, as a null reference does not
6755 // need poisoning.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006756 DCHECK_EQ(value_type, DataType::Type::kReference);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006757 __ Mov(temp1, value);
6758 GetAssembler()->PoisonHeapReference(temp1);
6759 source = temp1;
6760 }
6761
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006762 {
6763 // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
6764 // As two macro instructions can be emitted the max size is doubled.
6765 EmissionCheckScope guard(GetVIXLAssembler(), 2 * kMaxMacroInstructionSizeInBytes);
6766 if (index.IsConstant()) {
6767 size_t offset = (Int32ConstantFrom(index) << TIMES_4) + data_offset;
6768 GetAssembler()->StoreToOffset(kStoreWord, source, array, offset);
6769 } else {
6770 DCHECK(index.IsRegister()) << index;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006771
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006772 UseScratchRegisterScope temps(GetVIXLAssembler());
6773 vixl32::Register temp = temps.Acquire();
6774 __ Add(temp, array, data_offset);
6775 codegen_->StoreToShiftedRegOffset(value_type,
6776 LocationFrom(source),
6777 temp,
6778 RegisterFrom(index));
6779 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006780
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006781 if (can_value_be_null || !needs_type_check) {
6782 codegen_->MaybeRecordImplicitNullCheck(instruction);
6783 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006784 }
6785
Vladimir Marko0dda8c82019-05-16 12:47:40 +00006786 if (slow_path != nullptr) {
6787 __ Bind(slow_path->GetExitLabel());
6788 }
6789
Scott Wakelingc34dba72016-10-03 10:14:44 +01006790 break;
6791 }
6792
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006793 case DataType::Type::kInt64: {
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006794 // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
6795 // As two macro instructions can be emitted the max size is doubled.
6796 EmissionCheckScope guard(GetVIXLAssembler(), 2 * kMaxMacroInstructionSizeInBytes);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006797 Location value = locations->InAt(2);
6798 if (index.IsConstant()) {
6799 size_t offset =
Anton Kirilov644032c2016-12-06 17:51:43 +00006800 (Int32ConstantFrom(index) << TIMES_8) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006801 GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), array, offset);
6802 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006803 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006804 vixl32::Register temp = temps.Acquire();
6805 __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
6806 GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), temp, data_offset);
6807 }
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006808 codegen_->MaybeRecordImplicitNullCheck(instruction);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006809 break;
6810 }
6811
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006812 case DataType::Type::kFloat32: {
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006813 // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
6814 // As two macro instructions can be emitted the max size is doubled.
6815 EmissionCheckScope guard(GetVIXLAssembler(), 2 * kMaxMacroInstructionSizeInBytes);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006816 Location value = locations->InAt(2);
6817 DCHECK(value.IsFpuRegister());
6818 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006819 size_t offset = (Int32ConstantFrom(index) << TIMES_4) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006820 GetAssembler()->StoreSToOffset(SRegisterFrom(value), array, offset);
6821 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006822 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006823 vixl32::Register temp = temps.Acquire();
6824 __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_4));
6825 GetAssembler()->StoreSToOffset(SRegisterFrom(value), temp, data_offset);
6826 }
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006827 codegen_->MaybeRecordImplicitNullCheck(instruction);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006828 break;
6829 }
6830
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006831 case DataType::Type::kFloat64: {
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006832 // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
6833 // As two macro instructions can be emitted the max size is doubled.
6834 EmissionCheckScope guard(GetVIXLAssembler(), 2 * kMaxMacroInstructionSizeInBytes);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006835 Location value = locations->InAt(2);
6836 DCHECK(value.IsFpuRegisterPair());
6837 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006838 size_t offset = (Int32ConstantFrom(index) << TIMES_8) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006839 GetAssembler()->StoreDToOffset(DRegisterFrom(value), array, offset);
6840 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006841 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006842 vixl32::Register temp = temps.Acquire();
6843 __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
6844 GetAssembler()->StoreDToOffset(DRegisterFrom(value), temp, data_offset);
6845 }
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006846 codegen_->MaybeRecordImplicitNullCheck(instruction);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006847 break;
6848 }
6849
Aart Bik66c158e2018-01-31 12:55:04 -08006850 case DataType::Type::kUint32:
6851 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006852 case DataType::Type::kVoid:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006853 LOG(FATAL) << "Unreachable type " << value_type;
6854 UNREACHABLE();
6855 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006856}
6857
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006858void LocationsBuilderARMVIXL::VisitArrayLength(HArrayLength* instruction) {
6859 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01006860 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006861 locations->SetInAt(0, Location::RequiresRegister());
6862 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
6863}
6864
6865void InstructionCodeGeneratorARMVIXL::VisitArrayLength(HArrayLength* instruction) {
6866 uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction);
6867 vixl32::Register obj = InputRegisterAt(instruction, 0);
6868 vixl32::Register out = OutputRegister(instruction);
Alexandre Rames374ddf32016-11-04 10:40:49 +00006869 {
Artem Serov0fb37192016-12-06 18:13:40 +00006870 ExactAssemblyScope aas(GetVIXLAssembler(),
6871 vixl32::kMaxInstructionSizeInBytes,
6872 CodeBufferCheckScope::kMaximumSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00006873 __ ldr(out, MemOperand(obj, offset));
6874 codegen_->MaybeRecordImplicitNullCheck(instruction);
6875 }
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006876 // Mask out compression flag from String's array length.
6877 if (mirror::kUseStringCompression && instruction->IsStringLength()) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01006878 __ Lsr(out, out, 1u);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006879 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006880}
6881
Artem Serov2bbc9532016-10-21 11:51:50 +01006882void LocationsBuilderARMVIXL::VisitIntermediateAddress(HIntermediateAddress* instruction) {
Artem Serov2bbc9532016-10-21 11:51:50 +01006883 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01006884 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Artem Serov2bbc9532016-10-21 11:51:50 +01006885
6886 locations->SetInAt(0, Location::RequiresRegister());
6887 locations->SetInAt(1, Location::RegisterOrConstant(instruction->GetOffset()));
6888 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
6889}
6890
6891void InstructionCodeGeneratorARMVIXL::VisitIntermediateAddress(HIntermediateAddress* instruction) {
6892 vixl32::Register out = OutputRegister(instruction);
6893 vixl32::Register first = InputRegisterAt(instruction, 0);
6894 Location second = instruction->GetLocations()->InAt(1);
6895
Artem Serov2bbc9532016-10-21 11:51:50 +01006896 if (second.IsRegister()) {
6897 __ Add(out, first, RegisterFrom(second));
6898 } else {
Anton Kirilov644032c2016-12-06 17:51:43 +00006899 __ Add(out, first, Int32ConstantFrom(second));
Artem Serov2bbc9532016-10-21 11:51:50 +01006900 }
6901}
6902
Artem Serove1811ed2017-04-27 16:50:47 +01006903void LocationsBuilderARMVIXL::VisitIntermediateAddressIndex(
6904 HIntermediateAddressIndex* instruction) {
6905 LOG(FATAL) << "Unreachable " << instruction->GetId();
6906}
6907
6908void InstructionCodeGeneratorARMVIXL::VisitIntermediateAddressIndex(
6909 HIntermediateAddressIndex* instruction) {
6910 LOG(FATAL) << "Unreachable " << instruction->GetId();
6911}
6912
Scott Wakelingc34dba72016-10-03 10:14:44 +01006913void LocationsBuilderARMVIXL::VisitBoundsCheck(HBoundsCheck* instruction) {
6914 RegisterSet caller_saves = RegisterSet::Empty();
6915 InvokeRuntimeCallingConventionARMVIXL calling_convention;
6916 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0)));
6917 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(1)));
6918 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction, caller_saves);
Artem Serov2dd053d2017-03-08 14:54:06 +00006919
6920 HInstruction* index = instruction->InputAt(0);
6921 HInstruction* length = instruction->InputAt(1);
6922 // If both index and length are constants we can statically check the bounds. But if at least one
6923 // of them is not encodable ArmEncodableConstantOrRegister will create
6924 // Location::RequiresRegister() which is not desired to happen. Instead we create constant
6925 // locations.
6926 bool both_const = index->IsConstant() && length->IsConstant();
6927 locations->SetInAt(0, both_const
6928 ? Location::ConstantLocation(index->AsConstant())
6929 : ArmEncodableConstantOrRegister(index, CMP));
6930 locations->SetInAt(1, both_const
6931 ? Location::ConstantLocation(length->AsConstant())
6932 : ArmEncodableConstantOrRegister(length, CMP));
Scott Wakelingc34dba72016-10-03 10:14:44 +01006933}
6934
6935void InstructionCodeGeneratorARMVIXL::VisitBoundsCheck(HBoundsCheck* instruction) {
Artem Serov2dd053d2017-03-08 14:54:06 +00006936 LocationSummary* locations = instruction->GetLocations();
6937 Location index_loc = locations->InAt(0);
6938 Location length_loc = locations->InAt(1);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006939
Artem Serov2dd053d2017-03-08 14:54:06 +00006940 if (length_loc.IsConstant()) {
6941 int32_t length = Int32ConstantFrom(length_loc);
6942 if (index_loc.IsConstant()) {
6943 // BCE will remove the bounds check if we are guaranteed to pass.
6944 int32_t index = Int32ConstantFrom(index_loc);
6945 if (index < 0 || index >= length) {
6946 SlowPathCodeARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01006947 new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathARMVIXL(instruction);
Artem Serov2dd053d2017-03-08 14:54:06 +00006948 codegen_->AddSlowPath(slow_path);
6949 __ B(slow_path->GetEntryLabel());
6950 } else {
6951 // Some optimization after BCE may have generated this, and we should not
6952 // generate a bounds check if it is a valid range.
6953 }
6954 return;
6955 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006956
Artem Serov2dd053d2017-03-08 14:54:06 +00006957 SlowPathCodeARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01006958 new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathARMVIXL(instruction);
Artem Serov2dd053d2017-03-08 14:54:06 +00006959 __ Cmp(RegisterFrom(index_loc), length);
6960 codegen_->AddSlowPath(slow_path);
6961 __ B(hs, slow_path->GetEntryLabel());
6962 } else {
6963 SlowPathCodeARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01006964 new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathARMVIXL(instruction);
Artem Serov2dd053d2017-03-08 14:54:06 +00006965 __ Cmp(RegisterFrom(length_loc), InputOperandAt(instruction, 0));
6966 codegen_->AddSlowPath(slow_path);
6967 __ B(ls, slow_path->GetEntryLabel());
6968 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006969}
6970
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006971void CodeGeneratorARMVIXL::MarkGCCard(vixl32::Register temp,
6972 vixl32::Register card,
6973 vixl32::Register object,
6974 vixl32::Register value,
Vladimir Markoac3fcff2020-11-17 12:17:58 +00006975 bool value_can_be_null) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006976 vixl32::Label is_null;
Vladimir Markoac3fcff2020-11-17 12:17:58 +00006977 if (value_can_be_null) {
6978 __ CompareAndBranchIfZero(value, &is_null, /* is_far_target=*/ false);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006979 }
Roland Levillainc73f0522018-08-14 15:16:50 +01006980 // Load the address of the card table into `card`.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006981 GetAssembler()->LoadFromOffset(
6982 kLoadWord, card, tr, Thread::CardTableOffset<kArmPointerSize>().Int32Value());
Roland Levillainc73f0522018-08-14 15:16:50 +01006983 // Calculate the offset (in the card table) of the card corresponding to
6984 // `object`.
Scott Wakelingb77051e2016-11-21 19:46:00 +00006985 __ Lsr(temp, object, Operand::From(gc::accounting::CardTable::kCardShift));
Roland Levillainc73f0522018-08-14 15:16:50 +01006986 // Write the `art::gc::accounting::CardTable::kCardDirty` value into the
6987 // `object`'s card.
6988 //
6989 // Register `card` contains the address of the card table. Note that the card
6990 // table's base is biased during its creation so that it always starts at an
6991 // address whose least-significant byte is equal to `kCardDirty` (see
6992 // art::gc::accounting::CardTable::Create). Therefore the STRB instruction
6993 // below writes the `kCardDirty` (byte) value into the `object`'s card
6994 // (located at `card + object >> kCardShift`).
6995 //
6996 // This dual use of the value in register `card` (1. to calculate the location
6997 // of the card to mark; and 2. to load the `kCardDirty` value) saves a load
6998 // (no need to explicitly load `kCardDirty` as an immediate value).
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006999 __ Strb(card, MemOperand(card, temp));
Vladimir Markoac3fcff2020-11-17 12:17:58 +00007000 if (value_can_be_null) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007001 __ Bind(&is_null);
7002 }
7003}
7004
Scott Wakelingfe885462016-09-22 10:24:38 +01007005void LocationsBuilderARMVIXL::VisitParallelMove(HParallelMove* instruction ATTRIBUTE_UNUSED) {
7006 LOG(FATAL) << "Unreachable";
7007}
7008
7009void InstructionCodeGeneratorARMVIXL::VisitParallelMove(HParallelMove* instruction) {
Vladimir Markobea75ff2017-10-11 20:39:54 +01007010 if (instruction->GetNext()->IsSuspendCheck() &&
7011 instruction->GetBlock()->GetLoopInformation() != nullptr) {
7012 HSuspendCheck* suspend_check = instruction->GetNext()->AsSuspendCheck();
7013 // The back edge will generate the suspend check.
7014 codegen_->ClearSpillSlotsFromLoopPhisInStackMap(suspend_check, instruction);
7015 }
7016
Scott Wakelingfe885462016-09-22 10:24:38 +01007017 codegen_->GetMoveResolver()->EmitNativeCode(instruction);
7018}
7019
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007020void LocationsBuilderARMVIXL::VisitSuspendCheck(HSuspendCheck* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01007021 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
7022 instruction, LocationSummary::kCallOnSlowPath);
Artem Serov657022c2016-11-23 14:19:38 +00007023 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007024}
7025
7026void InstructionCodeGeneratorARMVIXL::VisitSuspendCheck(HSuspendCheck* instruction) {
7027 HBasicBlock* block = instruction->GetBlock();
7028 if (block->GetLoopInformation() != nullptr) {
7029 DCHECK(block->GetLoopInformation()->GetSuspendCheck() == instruction);
7030 // The back edge will generate the suspend check.
7031 return;
7032 }
7033 if (block->IsEntryBlock() && instruction->GetNext()->IsGoto()) {
7034 // The goto will generate the suspend check.
7035 return;
7036 }
7037 GenerateSuspendCheck(instruction, nullptr);
Andra Danciua0130e82020-07-23 12:34:56 +00007038 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 14);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007039}
7040
7041void InstructionCodeGeneratorARMVIXL::GenerateSuspendCheck(HSuspendCheck* instruction,
7042 HBasicBlock* successor) {
7043 SuspendCheckSlowPathARMVIXL* slow_path =
7044 down_cast<SuspendCheckSlowPathARMVIXL*>(instruction->GetSlowPath());
7045 if (slow_path == nullptr) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01007046 slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01007047 new (codegen_->GetScopedAllocator()) SuspendCheckSlowPathARMVIXL(instruction, successor);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007048 instruction->SetSlowPath(slow_path);
7049 codegen_->AddSlowPath(slow_path);
7050 if (successor != nullptr) {
7051 DCHECK(successor->IsLoopHeader());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007052 }
7053 } else {
7054 DCHECK_EQ(slow_path->GetSuccessor(), successor);
7055 }
7056
Anton Kirilovedb2ac32016-11-30 15:14:10 +00007057 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007058 vixl32::Register temp = temps.Acquire();
7059 GetAssembler()->LoadFromOffset(
7060 kLoadUnsignedHalfword, temp, tr, Thread::ThreadFlagsOffset<kArmPointerSize>().Int32Value());
7061 if (successor == nullptr) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00007062 __ CompareAndBranchIfNonZero(temp, slow_path->GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007063 __ Bind(slow_path->GetReturnLabel());
7064 } else {
xueliang.zhongf51bc622016-11-04 09:23:32 +00007065 __ CompareAndBranchIfZero(temp, codegen_->GetLabelOf(successor));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007066 __ B(slow_path->GetEntryLabel());
7067 }
7068}
7069
Scott Wakelingfe885462016-09-22 10:24:38 +01007070ArmVIXLAssembler* ParallelMoveResolverARMVIXL::GetAssembler() const {
7071 return codegen_->GetAssembler();
7072}
7073
7074void ParallelMoveResolverARMVIXL::EmitMove(size_t index) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007075 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
Scott Wakelingfe885462016-09-22 10:24:38 +01007076 MoveOperands* move = moves_[index];
7077 Location source = move->GetSource();
7078 Location destination = move->GetDestination();
7079
7080 if (source.IsRegister()) {
7081 if (destination.IsRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007082 __ Mov(RegisterFrom(destination), RegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01007083 } else if (destination.IsFpuRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007084 __ Vmov(SRegisterFrom(destination), RegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01007085 } else {
7086 DCHECK(destination.IsStackSlot());
7087 GetAssembler()->StoreToOffset(kStoreWord,
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007088 RegisterFrom(source),
Scott Wakelingfe885462016-09-22 10:24:38 +01007089 sp,
7090 destination.GetStackIndex());
7091 }
7092 } else if (source.IsStackSlot()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007093 if (destination.IsRegister()) {
7094 GetAssembler()->LoadFromOffset(kLoadWord,
7095 RegisterFrom(destination),
7096 sp,
7097 source.GetStackIndex());
7098 } else if (destination.IsFpuRegister()) {
7099 GetAssembler()->LoadSFromOffset(SRegisterFrom(destination), sp, source.GetStackIndex());
7100 } else {
7101 DCHECK(destination.IsStackSlot());
7102 vixl32::Register temp = temps.Acquire();
7103 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, source.GetStackIndex());
7104 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
7105 }
Scott Wakelingfe885462016-09-22 10:24:38 +01007106 } else if (source.IsFpuRegister()) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01007107 if (destination.IsRegister()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01007108 __ Vmov(RegisterFrom(destination), SRegisterFrom(source));
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01007109 } else if (destination.IsFpuRegister()) {
7110 __ Vmov(SRegisterFrom(destination), SRegisterFrom(source));
7111 } else {
7112 DCHECK(destination.IsStackSlot());
7113 GetAssembler()->StoreSToOffset(SRegisterFrom(source), sp, destination.GetStackIndex());
7114 }
Scott Wakelingfe885462016-09-22 10:24:38 +01007115 } else if (source.IsDoubleStackSlot()) {
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007116 if (destination.IsDoubleStackSlot()) {
7117 vixl32::DRegister temp = temps.AcquireD();
7118 GetAssembler()->LoadDFromOffset(temp, sp, source.GetStackIndex());
7119 GetAssembler()->StoreDToOffset(temp, sp, destination.GetStackIndex());
7120 } else if (destination.IsRegisterPair()) {
7121 DCHECK(ExpectedPairLayout(destination));
7122 GetAssembler()->LoadFromOffset(
7123 kLoadWordPair, LowRegisterFrom(destination), sp, source.GetStackIndex());
7124 } else {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01007125 DCHECK(destination.IsFpuRegisterPair()) << destination;
7126 GetAssembler()->LoadDFromOffset(DRegisterFrom(destination), sp, source.GetStackIndex());
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007127 }
Scott Wakelingfe885462016-09-22 10:24:38 +01007128 } else if (source.IsRegisterPair()) {
7129 if (destination.IsRegisterPair()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007130 __ Mov(LowRegisterFrom(destination), LowRegisterFrom(source));
7131 __ Mov(HighRegisterFrom(destination), HighRegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01007132 } else if (destination.IsFpuRegisterPair()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01007133 __ Vmov(DRegisterFrom(destination), LowRegisterFrom(source), HighRegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01007134 } else {
7135 DCHECK(destination.IsDoubleStackSlot()) << destination;
7136 DCHECK(ExpectedPairLayout(source));
7137 GetAssembler()->StoreToOffset(kStoreWordPair,
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007138 LowRegisterFrom(source),
Scott Wakelingfe885462016-09-22 10:24:38 +01007139 sp,
7140 destination.GetStackIndex());
7141 }
7142 } else if (source.IsFpuRegisterPair()) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01007143 if (destination.IsRegisterPair()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01007144 __ Vmov(LowRegisterFrom(destination), HighRegisterFrom(destination), DRegisterFrom(source));
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01007145 } else if (destination.IsFpuRegisterPair()) {
7146 __ Vmov(DRegisterFrom(destination), DRegisterFrom(source));
7147 } else {
7148 DCHECK(destination.IsDoubleStackSlot()) << destination;
7149 GetAssembler()->StoreDToOffset(DRegisterFrom(source), sp, destination.GetStackIndex());
7150 }
Scott Wakelingfe885462016-09-22 10:24:38 +01007151 } else {
7152 DCHECK(source.IsConstant()) << source;
7153 HConstant* constant = source.GetConstant();
7154 if (constant->IsIntConstant() || constant->IsNullConstant()) {
7155 int32_t value = CodeGenerator::GetInt32ValueOf(constant);
7156 if (destination.IsRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007157 __ Mov(RegisterFrom(destination), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01007158 } else {
7159 DCHECK(destination.IsStackSlot());
Scott Wakelingfe885462016-09-22 10:24:38 +01007160 vixl32::Register temp = temps.Acquire();
7161 __ Mov(temp, value);
7162 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
7163 }
7164 } else if (constant->IsLongConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00007165 int64_t value = Int64ConstantFrom(source);
Scott Wakelingfe885462016-09-22 10:24:38 +01007166 if (destination.IsRegisterPair()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007167 __ Mov(LowRegisterFrom(destination), Low32Bits(value));
7168 __ Mov(HighRegisterFrom(destination), High32Bits(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01007169 } else {
7170 DCHECK(destination.IsDoubleStackSlot()) << destination;
Scott Wakelingfe885462016-09-22 10:24:38 +01007171 vixl32::Register temp = temps.Acquire();
7172 __ Mov(temp, Low32Bits(value));
7173 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
7174 __ Mov(temp, High32Bits(value));
7175 GetAssembler()->StoreToOffset(kStoreWord,
7176 temp,
7177 sp,
7178 destination.GetHighStackIndex(kArmWordSize));
7179 }
7180 } else if (constant->IsDoubleConstant()) {
7181 double value = constant->AsDoubleConstant()->GetValue();
7182 if (destination.IsFpuRegisterPair()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01007183 __ Vmov(DRegisterFrom(destination), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01007184 } else {
7185 DCHECK(destination.IsDoubleStackSlot()) << destination;
7186 uint64_t int_value = bit_cast<uint64_t, double>(value);
Scott Wakelingfe885462016-09-22 10:24:38 +01007187 vixl32::Register temp = temps.Acquire();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007188 __ Mov(temp, Low32Bits(int_value));
Scott Wakelingfe885462016-09-22 10:24:38 +01007189 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007190 __ Mov(temp, High32Bits(int_value));
Scott Wakelingfe885462016-09-22 10:24:38 +01007191 GetAssembler()->StoreToOffset(kStoreWord,
7192 temp,
7193 sp,
7194 destination.GetHighStackIndex(kArmWordSize));
7195 }
7196 } else {
7197 DCHECK(constant->IsFloatConstant()) << constant->DebugName();
7198 float value = constant->AsFloatConstant()->GetValue();
7199 if (destination.IsFpuRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007200 __ Vmov(SRegisterFrom(destination), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01007201 } else {
7202 DCHECK(destination.IsStackSlot());
Scott Wakelingfe885462016-09-22 10:24:38 +01007203 vixl32::Register temp = temps.Acquire();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007204 __ Mov(temp, bit_cast<int32_t, float>(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01007205 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
7206 }
7207 }
7208 }
7209}
7210
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007211void ParallelMoveResolverARMVIXL::Exchange(vixl32::Register reg, int mem) {
7212 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
7213 vixl32::Register temp = temps.Acquire();
7214 __ Mov(temp, reg);
7215 GetAssembler()->LoadFromOffset(kLoadWord, reg, sp, mem);
7216 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, mem);
Scott Wakelingfe885462016-09-22 10:24:38 +01007217}
7218
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007219void ParallelMoveResolverARMVIXL::Exchange(int mem1, int mem2) {
7220 // TODO(VIXL32): Double check the performance of this implementation.
7221 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
Nicolas Geoffray13a797b2017-03-15 16:41:31 +00007222 vixl32::Register temp1 = temps.Acquire();
7223 ScratchRegisterScope ensure_scratch(
7224 this, temp1.GetCode(), r0.GetCode(), codegen_->GetNumberOfCoreRegisters());
7225 vixl32::Register temp2(ensure_scratch.GetRegister());
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007226
Nicolas Geoffray13a797b2017-03-15 16:41:31 +00007227 int stack_offset = ensure_scratch.IsSpilled() ? kArmWordSize : 0;
7228 GetAssembler()->LoadFromOffset(kLoadWord, temp1, sp, mem1 + stack_offset);
7229 GetAssembler()->LoadFromOffset(kLoadWord, temp2, sp, mem2 + stack_offset);
7230 GetAssembler()->StoreToOffset(kStoreWord, temp1, sp, mem2 + stack_offset);
7231 GetAssembler()->StoreToOffset(kStoreWord, temp2, sp, mem1 + stack_offset);
Scott Wakelingfe885462016-09-22 10:24:38 +01007232}
7233
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007234void ParallelMoveResolverARMVIXL::EmitSwap(size_t index) {
7235 MoveOperands* move = moves_[index];
7236 Location source = move->GetSource();
7237 Location destination = move->GetDestination();
7238 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
7239
7240 if (source.IsRegister() && destination.IsRegister()) {
7241 vixl32::Register temp = temps.Acquire();
7242 DCHECK(!RegisterFrom(source).Is(temp));
7243 DCHECK(!RegisterFrom(destination).Is(temp));
7244 __ Mov(temp, RegisterFrom(destination));
7245 __ Mov(RegisterFrom(destination), RegisterFrom(source));
7246 __ Mov(RegisterFrom(source), temp);
7247 } else if (source.IsRegister() && destination.IsStackSlot()) {
7248 Exchange(RegisterFrom(source), destination.GetStackIndex());
7249 } else if (source.IsStackSlot() && destination.IsRegister()) {
7250 Exchange(RegisterFrom(destination), source.GetStackIndex());
7251 } else if (source.IsStackSlot() && destination.IsStackSlot()) {
Anton Kirilovdda43962016-11-21 19:55:20 +00007252 Exchange(source.GetStackIndex(), destination.GetStackIndex());
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007253 } else if (source.IsFpuRegister() && destination.IsFpuRegister()) {
Nicolas Geoffray13a797b2017-03-15 16:41:31 +00007254 vixl32::Register temp = temps.Acquire();
Anton Kirilovdda43962016-11-21 19:55:20 +00007255 __ Vmov(temp, SRegisterFrom(source));
7256 __ Vmov(SRegisterFrom(source), SRegisterFrom(destination));
7257 __ Vmov(SRegisterFrom(destination), temp);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007258 } else if (source.IsRegisterPair() && destination.IsRegisterPair()) {
7259 vixl32::DRegister temp = temps.AcquireD();
7260 __ Vmov(temp, LowRegisterFrom(source), HighRegisterFrom(source));
7261 __ Mov(LowRegisterFrom(source), LowRegisterFrom(destination));
7262 __ Mov(HighRegisterFrom(source), HighRegisterFrom(destination));
7263 __ Vmov(LowRegisterFrom(destination), HighRegisterFrom(destination), temp);
7264 } else if (source.IsRegisterPair() || destination.IsRegisterPair()) {
7265 vixl32::Register low_reg = LowRegisterFrom(source.IsRegisterPair() ? source : destination);
7266 int mem = source.IsRegisterPair() ? destination.GetStackIndex() : source.GetStackIndex();
7267 DCHECK(ExpectedPairLayout(source.IsRegisterPair() ? source : destination));
7268 vixl32::DRegister temp = temps.AcquireD();
7269 __ Vmov(temp, low_reg, vixl32::Register(low_reg.GetCode() + 1));
7270 GetAssembler()->LoadFromOffset(kLoadWordPair, low_reg, sp, mem);
7271 GetAssembler()->StoreDToOffset(temp, sp, mem);
7272 } else if (source.IsFpuRegisterPair() && destination.IsFpuRegisterPair()) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007273 vixl32::DRegister first = DRegisterFrom(source);
7274 vixl32::DRegister second = DRegisterFrom(destination);
7275 vixl32::DRegister temp = temps.AcquireD();
7276 __ Vmov(temp, first);
7277 __ Vmov(first, second);
7278 __ Vmov(second, temp);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007279 } else if (source.IsFpuRegisterPair() || destination.IsFpuRegisterPair()) {
Anton Kirilovdda43962016-11-21 19:55:20 +00007280 vixl32::DRegister reg = source.IsFpuRegisterPair()
7281 ? DRegisterFrom(source)
7282 : DRegisterFrom(destination);
7283 int mem = source.IsFpuRegisterPair()
7284 ? destination.GetStackIndex()
7285 : source.GetStackIndex();
7286 vixl32::DRegister temp = temps.AcquireD();
7287 __ Vmov(temp, reg);
7288 GetAssembler()->LoadDFromOffset(reg, sp, mem);
7289 GetAssembler()->StoreDToOffset(temp, sp, mem);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007290 } else if (source.IsFpuRegister() || destination.IsFpuRegister()) {
Anton Kirilovdda43962016-11-21 19:55:20 +00007291 vixl32::SRegister reg = source.IsFpuRegister()
7292 ? SRegisterFrom(source)
7293 : SRegisterFrom(destination);
7294 int mem = source.IsFpuRegister()
7295 ? destination.GetStackIndex()
7296 : source.GetStackIndex();
7297 vixl32::Register temp = temps.Acquire();
7298 __ Vmov(temp, reg);
7299 GetAssembler()->LoadSFromOffset(reg, sp, mem);
7300 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, mem);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007301 } else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) {
7302 vixl32::DRegister temp1 = temps.AcquireD();
7303 vixl32::DRegister temp2 = temps.AcquireD();
7304 __ Vldr(temp1, MemOperand(sp, source.GetStackIndex()));
7305 __ Vldr(temp2, MemOperand(sp, destination.GetStackIndex()));
7306 __ Vstr(temp1, MemOperand(sp, destination.GetStackIndex()));
7307 __ Vstr(temp2, MemOperand(sp, source.GetStackIndex()));
7308 } else {
7309 LOG(FATAL) << "Unimplemented" << source << " <-> " << destination;
7310 }
Scott Wakelingfe885462016-09-22 10:24:38 +01007311}
7312
Nicolas Geoffray13a797b2017-03-15 16:41:31 +00007313void ParallelMoveResolverARMVIXL::SpillScratch(int reg) {
7314 __ Push(vixl32::Register(reg));
Scott Wakelingfe885462016-09-22 10:24:38 +01007315}
7316
Nicolas Geoffray13a797b2017-03-15 16:41:31 +00007317void ParallelMoveResolverARMVIXL::RestoreScratch(int reg) {
7318 __ Pop(vixl32::Register(reg));
Scott Wakelingfe885462016-09-22 10:24:38 +01007319}
7320
Artem Serov02d37832016-10-25 15:25:33 +01007321HLoadClass::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadClassKind(
Artem Serovd4cc5b22016-11-04 11:19:09 +00007322 HLoadClass::LoadKind desired_class_load_kind) {
7323 switch (desired_class_load_kind) {
Nicolas Geoffray83c8e272017-01-31 14:36:37 +00007324 case HLoadClass::LoadKind::kInvalid:
7325 LOG(FATAL) << "UNREACHABLE";
7326 UNREACHABLE();
Artem Serovd4cc5b22016-11-04 11:19:09 +00007327 case HLoadClass::LoadKind::kReferrersClass:
7328 break;
Artem Serovd4cc5b22016-11-04 11:19:09 +00007329 case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
Vladimir Markoe47f60c2018-02-21 13:43:28 +00007330 case HLoadClass::LoadKind::kBootImageRelRo:
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007331 case HLoadClass::LoadKind::kBssEntry:
Vladimir Marko8f63f102020-09-28 12:10:28 +01007332 case HLoadClass::LoadKind::kBssEntryPublic:
7333 case HLoadClass::LoadKind::kBssEntryPackage:
Vladimir Marko695348f2020-05-19 14:42:02 +01007334 DCHECK(!GetCompilerOptions().IsJitCompiler());
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007335 break;
Vladimir Marko8e524ad2018-07-13 10:27:43 +01007336 case HLoadClass::LoadKind::kJitBootImageAddress:
Nicolas Geoffray22384ae2016-12-12 22:33:36 +00007337 case HLoadClass::LoadKind::kJitTableAddress:
Vladimir Marko695348f2020-05-19 14:42:02 +01007338 DCHECK(GetCompilerOptions().IsJitCompiler());
Artem Serovc5fcb442016-12-02 19:19:58 +00007339 break;
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007340 case HLoadClass::LoadKind::kRuntimeCall:
Artem Serovd4cc5b22016-11-04 11:19:09 +00007341 break;
7342 }
7343 return desired_class_load_kind;
Artem Serov02d37832016-10-25 15:25:33 +01007344}
7345
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007346void LocationsBuilderARMVIXL::VisitLoadClass(HLoadClass* cls) {
Vladimir Marko41559982017-01-06 14:04:23 +00007347 HLoadClass::LoadKind load_kind = cls->GetLoadKind();
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007348 if (load_kind == HLoadClass::LoadKind::kRuntimeCall) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007349 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Vladimir Marko41559982017-01-06 14:04:23 +00007350 CodeGenerator::CreateLoadClassRuntimeCallLocationSummary(
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007351 cls,
7352 LocationFrom(calling_convention.GetRegisterAt(0)),
Vladimir Marko41559982017-01-06 14:04:23 +00007353 LocationFrom(r0));
Vladimir Markoea4c1262017-02-06 19:59:33 +00007354 DCHECK(calling_convention.GetRegisterAt(0).Is(r0));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007355 return;
7356 }
Vladimir Marko8f63f102020-09-28 12:10:28 +01007357 DCHECK_EQ(cls->NeedsAccessCheck(),
7358 load_kind == HLoadClass::LoadKind::kBssEntryPublic ||
7359 load_kind == HLoadClass::LoadKind::kBssEntryPackage);
Scott Wakelingfe885462016-09-22 10:24:38 +01007360
Artem Serovd4cc5b22016-11-04 11:19:09 +00007361 const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
7362 LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier)
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007363 ? LocationSummary::kCallOnSlowPath
7364 : LocationSummary::kNoCall;
Vladimir Markoca6fff82017-10-03 14:49:14 +01007365 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(cls, call_kind);
Artem Serovd4cc5b22016-11-04 11:19:09 +00007366 if (kUseBakerReadBarrier && requires_read_barrier && !cls->NeedsEnvironment()) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00007367 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Artem Serovd4cc5b22016-11-04 11:19:09 +00007368 }
7369
Vladimir Marko41559982017-01-06 14:04:23 +00007370 if (load_kind == HLoadClass::LoadKind::kReferrersClass) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007371 locations->SetInAt(0, Location::RequiresRegister());
7372 }
7373 locations->SetOut(Location::RequiresRegister());
Vladimir Markoea4c1262017-02-06 19:59:33 +00007374 if (load_kind == HLoadClass::LoadKind::kBssEntry) {
7375 if (!kUseReadBarrier || kUseBakerReadBarrier) {
7376 // Rely on the type resolution or initialization and marking to save everything we need.
Vladimir Marko3232dbb2018-07-25 15:42:46 +01007377 locations->SetCustomSlowPathCallerSaves(OneRegInReferenceOutSaveEverythingCallerSaves());
Vladimir Markoea4c1262017-02-06 19:59:33 +00007378 } else {
7379 // For non-Baker read barrier we have a temp-clobbering call.
7380 }
7381 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007382}
7383
Nicolas Geoffray5247c082017-01-13 14:17:29 +00007384// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
7385// move.
7386void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFETY_ANALYSIS {
Vladimir Marko41559982017-01-06 14:04:23 +00007387 HLoadClass::LoadKind load_kind = cls->GetLoadKind();
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007388 if (load_kind == HLoadClass::LoadKind::kRuntimeCall) {
Vladimir Marko41559982017-01-06 14:04:23 +00007389 codegen_->GenerateLoadClassRuntimeCall(cls);
Andra Danciua0130e82020-07-23 12:34:56 +00007390 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 15);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007391 return;
7392 }
Vladimir Marko8f63f102020-09-28 12:10:28 +01007393 DCHECK_EQ(cls->NeedsAccessCheck(),
7394 load_kind == HLoadClass::LoadKind::kBssEntryPublic ||
7395 load_kind == HLoadClass::LoadKind::kBssEntryPackage);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007396
Vladimir Marko41559982017-01-06 14:04:23 +00007397 LocationSummary* locations = cls->GetLocations();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007398 Location out_loc = locations->Out();
7399 vixl32::Register out = OutputRegister(cls);
7400
Artem Serovd4cc5b22016-11-04 11:19:09 +00007401 const ReadBarrierOption read_barrier_option = cls->IsInBootImage()
7402 ? kWithoutReadBarrier
7403 : kCompilerReadBarrierOption;
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007404 bool generate_null_check = false;
Vladimir Marko41559982017-01-06 14:04:23 +00007405 switch (load_kind) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007406 case HLoadClass::LoadKind::kReferrersClass: {
7407 DCHECK(!cls->CanCallRuntime());
7408 DCHECK(!cls->MustGenerateClinitCheck());
7409 // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
7410 vixl32::Register current_method = InputRegisterAt(cls, 0);
Vladimir Markoca1e0382018-04-11 09:58:41 +00007411 codegen_->GenerateGcRootFieldLoad(cls,
7412 out_loc,
7413 current_method,
7414 ArtMethod::DeclaringClassOffset().Int32Value(),
7415 read_barrier_option);
Artem Serovd4cc5b22016-11-04 11:19:09 +00007416 break;
7417 }
Artem Serovd4cc5b22016-11-04 11:19:09 +00007418 case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: {
Vladimir Marko44ca0752019-07-29 10:18:25 +01007419 DCHECK(codegen_->GetCompilerOptions().IsBootImage() ||
7420 codegen_->GetCompilerOptions().IsBootImageExtension());
Artem Serovd4cc5b22016-11-04 11:19:09 +00007421 DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
7422 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
Vladimir Marko59eb30f2018-02-20 11:52:34 +00007423 codegen_->NewBootImageTypePatch(cls->GetDexFile(), cls->GetTypeIndex());
Artem Serovd4cc5b22016-11-04 11:19:09 +00007424 codegen_->EmitMovwMovtPlaceholder(labels, out);
7425 break;
7426 }
Vladimir Markoe47f60c2018-02-21 13:43:28 +00007427 case HLoadClass::LoadKind::kBootImageRelRo: {
Vladimir Marko94ec2db2017-09-06 17:21:03 +01007428 DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
7429 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
Vladimir Markode91ca92020-10-27 13:41:40 +00007430 codegen_->NewBootImageRelRoPatch(CodeGenerator::GetBootImageOffset(cls));
Vladimir Marko94ec2db2017-09-06 17:21:03 +01007431 codegen_->EmitMovwMovtPlaceholder(labels, out);
Andreas Gampe3db70682018-12-26 15:12:03 -08007432 __ Ldr(out, MemOperand(out, /* offset= */ 0));
Vladimir Marko94ec2db2017-09-06 17:21:03 +01007433 break;
7434 }
Vladimir Marko8f63f102020-09-28 12:10:28 +01007435 case HLoadClass::LoadKind::kBssEntry:
7436 case HLoadClass::LoadKind::kBssEntryPublic:
7437 case HLoadClass::LoadKind::kBssEntryPackage: {
7438 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels = codegen_->NewTypeBssEntryPatch(cls);
Vladimir Markof3c52b42017-11-17 17:32:12 +00007439 codegen_->EmitMovwMovtPlaceholder(labels, out);
Vladimir Markod5fd5c32019-07-02 14:46:32 +01007440 // All aligned loads are implicitly atomic consume operations on ARM.
Andreas Gampe3db70682018-12-26 15:12:03 -08007441 codegen_->GenerateGcRootFieldLoad(cls, out_loc, out, /* offset= */ 0, read_barrier_option);
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007442 generate_null_check = true;
7443 break;
7444 }
Vladimir Marko8e524ad2018-07-13 10:27:43 +01007445 case HLoadClass::LoadKind::kJitBootImageAddress: {
7446 DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
7447 uint32_t address = reinterpret_cast32<uint32_t>(cls->GetClass().Get());
7448 DCHECK_NE(address, 0u);
7449 __ Ldr(out, codegen_->DeduplicateBootImageAddressLiteral(address));
7450 break;
7451 }
Nicolas Geoffray22384ae2016-12-12 22:33:36 +00007452 case HLoadClass::LoadKind::kJitTableAddress: {
Artem Serovc5fcb442016-12-02 19:19:58 +00007453 __ Ldr(out, codegen_->DeduplicateJitClassLiteral(cls->GetDexFile(),
7454 cls->GetTypeIndex(),
Nicolas Geoffray5247c082017-01-13 14:17:29 +00007455 cls->GetClass()));
Artem Serovc5fcb442016-12-02 19:19:58 +00007456 // /* GcRoot<mirror::Class> */ out = *out
Andreas Gampe3db70682018-12-26 15:12:03 -08007457 codegen_->GenerateGcRootFieldLoad(cls, out_loc, out, /* offset= */ 0, read_barrier_option);
Artem Serovd4cc5b22016-11-04 11:19:09 +00007458 break;
7459 }
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007460 case HLoadClass::LoadKind::kRuntimeCall:
Nicolas Geoffray83c8e272017-01-31 14:36:37 +00007461 case HLoadClass::LoadKind::kInvalid:
Vladimir Marko41559982017-01-06 14:04:23 +00007462 LOG(FATAL) << "UNREACHABLE";
7463 UNREACHABLE();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007464 }
7465
7466 if (generate_null_check || cls->MustGenerateClinitCheck()) {
7467 DCHECK(cls->CanCallRuntime());
Vladimir Marko174b2e22017-10-12 13:34:49 +01007468 LoadClassSlowPathARMVIXL* slow_path =
Vladimir Markoa9f303c2018-07-20 16:43:56 +01007469 new (codegen_->GetScopedAllocator()) LoadClassSlowPathARMVIXL(cls, cls);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007470 codegen_->AddSlowPath(slow_path);
7471 if (generate_null_check) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00007472 __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007473 }
7474 if (cls->MustGenerateClinitCheck()) {
7475 GenerateClassInitializationCheck(slow_path, out);
7476 } else {
7477 __ Bind(slow_path->GetExitLabel());
7478 }
Andra Danciua0130e82020-07-23 12:34:56 +00007479 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 16);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007480 }
7481}
7482
Orion Hodsondbaa5c72018-05-10 08:22:46 +01007483void LocationsBuilderARMVIXL::VisitLoadMethodHandle(HLoadMethodHandle* load) {
7484 InvokeRuntimeCallingConventionARMVIXL calling_convention;
7485 Location location = LocationFrom(calling_convention.GetRegisterAt(0));
7486 CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(load, location, location);
7487}
7488
7489void InstructionCodeGeneratorARMVIXL::VisitLoadMethodHandle(HLoadMethodHandle* load) {
7490 codegen_->GenerateLoadMethodHandleRuntimeCall(load);
7491}
7492
Orion Hodson18259d72018-04-12 11:18:23 +01007493void LocationsBuilderARMVIXL::VisitLoadMethodType(HLoadMethodType* load) {
7494 InvokeRuntimeCallingConventionARMVIXL calling_convention;
7495 Location location = LocationFrom(calling_convention.GetRegisterAt(0));
7496 CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, location, location);
7497}
7498
7499void InstructionCodeGeneratorARMVIXL::VisitLoadMethodType(HLoadMethodType* load) {
7500 codegen_->GenerateLoadMethodTypeRuntimeCall(load);
7501}
7502
Artem Serov02d37832016-10-25 15:25:33 +01007503void LocationsBuilderARMVIXL::VisitClinitCheck(HClinitCheck* check) {
7504 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01007505 new (GetGraph()->GetAllocator()) LocationSummary(check, LocationSummary::kCallOnSlowPath);
Artem Serov02d37832016-10-25 15:25:33 +01007506 locations->SetInAt(0, Location::RequiresRegister());
7507 if (check->HasUses()) {
7508 locations->SetOut(Location::SameAsFirstInput());
7509 }
Vladimir Marko3232dbb2018-07-25 15:42:46 +01007510 // Rely on the type initialization to save everything we need.
7511 locations->SetCustomSlowPathCallerSaves(OneRegInReferenceOutSaveEverythingCallerSaves());
Artem Serov02d37832016-10-25 15:25:33 +01007512}
7513
7514void InstructionCodeGeneratorARMVIXL::VisitClinitCheck(HClinitCheck* check) {
7515 // We assume the class is not null.
7516 LoadClassSlowPathARMVIXL* slow_path =
Vladimir Markoa9f303c2018-07-20 16:43:56 +01007517 new (codegen_->GetScopedAllocator()) LoadClassSlowPathARMVIXL(check->GetLoadClass(), check);
Artem Serov02d37832016-10-25 15:25:33 +01007518 codegen_->AddSlowPath(slow_path);
7519 GenerateClassInitializationCheck(slow_path, InputRegisterAt(check, 0));
7520}
7521
7522void InstructionCodeGeneratorARMVIXL::GenerateClassInitializationCheck(
7523 LoadClassSlowPathARMVIXL* slow_path, vixl32::Register class_reg) {
7524 UseScratchRegisterScope temps(GetVIXLAssembler());
7525 vixl32::Register temp = temps.Acquire();
Vladimir Markodc682aa2018-01-04 18:42:57 +00007526 constexpr size_t status_lsb_position = SubtypeCheckBits::BitStructSizeOf();
Vladimir Markobf121912019-06-04 13:49:05 +01007527 constexpr uint32_t shifted_visibly_initialized_value =
7528 enum_cast<uint32_t>(ClassStatus::kVisiblyInitialized) << status_lsb_position;
Vladimir Markodc682aa2018-01-04 18:42:57 +00007529
Vladimir Markobf121912019-06-04 13:49:05 +01007530 const size_t status_offset = mirror::Class::StatusOffset().SizeValue();
7531 GetAssembler()->LoadFromOffset(kLoadWord, temp, class_reg, status_offset);
7532 __ Cmp(temp, shifted_visibly_initialized_value);
Vladimir Marko2c64a832018-01-04 11:31:56 +00007533 __ B(lo, slow_path->GetEntryLabel());
Artem Serov02d37832016-10-25 15:25:33 +01007534 __ Bind(slow_path->GetExitLabel());
7535}
7536
Vladimir Marko175e7862018-03-27 09:03:13 +00007537void InstructionCodeGeneratorARMVIXL::GenerateBitstringTypeCheckCompare(
7538 HTypeCheckInstruction* check,
7539 vixl32::Register temp,
7540 vixl32::FlagsUpdate flags_update) {
7541 uint32_t path_to_root = check->GetBitstringPathToRoot();
7542 uint32_t mask = check->GetBitstringMask();
7543 DCHECK(IsPowerOfTwo(mask + 1));
7544 size_t mask_bits = WhichPowerOf2(mask + 1);
7545
7546 // Note that HInstanceOf shall check for zero value in `temp` but HCheckCast needs
7547 // the Z flag for BNE. This is indicated by the `flags_update` parameter.
7548 if (mask_bits == 16u) {
7549 // Load only the bitstring part of the status word.
7550 __ Ldrh(temp, MemOperand(temp, mirror::Class::StatusOffset().Int32Value()));
7551 // Check if the bitstring bits are equal to `path_to_root`.
7552 if (flags_update == SetFlags) {
7553 __ Cmp(temp, path_to_root);
7554 } else {
7555 __ Sub(temp, temp, path_to_root);
7556 }
7557 } else {
7558 // /* uint32_t */ temp = temp->status_
7559 __ Ldr(temp, MemOperand(temp, mirror::Class::StatusOffset().Int32Value()));
7560 if (GetAssembler()->ShifterOperandCanHold(SUB, path_to_root)) {
7561 // Compare the bitstring bits using SUB.
7562 __ Sub(temp, temp, path_to_root);
7563 // Shift out bits that do not contribute to the comparison.
7564 __ Lsl(flags_update, temp, temp, dchecked_integral_cast<uint32_t>(32u - mask_bits));
7565 } else if (IsUint<16>(path_to_root)) {
7566 if (temp.IsLow()) {
7567 // Note: Optimized for size but contains one more dependent instruction than necessary.
7568 // MOVW+SUB(register) would be 8 bytes unless we find a low-reg temporary but the
7569 // macro assembler would use the high reg IP for the constant by default.
7570 // Compare the bitstring bits using SUB.
7571 __ Sub(temp, temp, path_to_root & 0x00ffu); // 16-bit SUB (immediate) T2
7572 __ Sub(temp, temp, path_to_root & 0xff00u); // 32-bit SUB (immediate) T3
7573 // Shift out bits that do not contribute to the comparison.
7574 __ Lsl(flags_update, temp, temp, dchecked_integral_cast<uint32_t>(32u - mask_bits));
7575 } else {
7576 // Extract the bitstring bits.
7577 __ Ubfx(temp, temp, 0, mask_bits);
7578 // Check if the bitstring bits are equal to `path_to_root`.
7579 if (flags_update == SetFlags) {
7580 __ Cmp(temp, path_to_root);
7581 } else {
7582 __ Sub(temp, temp, path_to_root);
7583 }
7584 }
7585 } else {
7586 // Shift out bits that do not contribute to the comparison.
7587 __ Lsl(temp, temp, dchecked_integral_cast<uint32_t>(32u - mask_bits));
7588 // Check if the shifted bitstring bits are equal to `path_to_root << (32u - mask_bits)`.
7589 if (flags_update == SetFlags) {
7590 __ Cmp(temp, path_to_root << (32u - mask_bits));
7591 } else {
7592 __ Sub(temp, temp, path_to_root << (32u - mask_bits));
7593 }
7594 }
7595 }
7596}
7597
Artem Serov02d37832016-10-25 15:25:33 +01007598HLoadString::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadStringKind(
Artem Serovd4cc5b22016-11-04 11:19:09 +00007599 HLoadString::LoadKind desired_string_load_kind) {
7600 switch (desired_string_load_kind) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00007601 case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
Vladimir Markoe47f60c2018-02-21 13:43:28 +00007602 case HLoadString::LoadKind::kBootImageRelRo:
Artem Serovd4cc5b22016-11-04 11:19:09 +00007603 case HLoadString::LoadKind::kBssEntry:
Vladimir Marko695348f2020-05-19 14:42:02 +01007604 DCHECK(!GetCompilerOptions().IsJitCompiler());
Artem Serovd4cc5b22016-11-04 11:19:09 +00007605 break;
Vladimir Marko8e524ad2018-07-13 10:27:43 +01007606 case HLoadString::LoadKind::kJitBootImageAddress:
Artem Serovd4cc5b22016-11-04 11:19:09 +00007607 case HLoadString::LoadKind::kJitTableAddress:
Vladimir Marko695348f2020-05-19 14:42:02 +01007608 DCHECK(GetCompilerOptions().IsJitCompiler());
Artem Serovc5fcb442016-12-02 19:19:58 +00007609 break;
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007610 case HLoadString::LoadKind::kRuntimeCall:
Artem Serovd4cc5b22016-11-04 11:19:09 +00007611 break;
7612 }
7613 return desired_string_load_kind;
Artem Serov02d37832016-10-25 15:25:33 +01007614}
7615
7616void LocationsBuilderARMVIXL::VisitLoadString(HLoadString* load) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00007617 LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load);
Vladimir Markoca6fff82017-10-03 14:49:14 +01007618 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(load, call_kind);
Artem Serov02d37832016-10-25 15:25:33 +01007619 HLoadString::LoadKind load_kind = load->GetLoadKind();
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007620 if (load_kind == HLoadString::LoadKind::kRuntimeCall) {
Artem Serov02d37832016-10-25 15:25:33 +01007621 locations->SetOut(LocationFrom(r0));
7622 } else {
7623 locations->SetOut(Location::RequiresRegister());
Artem Serovd4cc5b22016-11-04 11:19:09 +00007624 if (load_kind == HLoadString::LoadKind::kBssEntry) {
7625 if (!kUseReadBarrier || kUseBakerReadBarrier) {
Vladimir Markoea4c1262017-02-06 19:59:33 +00007626 // Rely on the pResolveString and marking to save everything we need, including temps.
Vladimir Marko3232dbb2018-07-25 15:42:46 +01007627 locations->SetCustomSlowPathCallerSaves(OneRegInReferenceOutSaveEverythingCallerSaves());
Artem Serovd4cc5b22016-11-04 11:19:09 +00007628 } else {
7629 // For non-Baker read barrier we have a temp-clobbering call.
7630 }
7631 }
Artem Serov02d37832016-10-25 15:25:33 +01007632 }
7633}
7634
Nicolas Geoffrayf0acfe72017-01-09 20:54:52 +00007635// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
7636// move.
7637void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) NO_THREAD_SAFETY_ANALYSIS {
Artem Serovd4cc5b22016-11-04 11:19:09 +00007638 LocationSummary* locations = load->GetLocations();
7639 Location out_loc = locations->Out();
7640 vixl32::Register out = OutputRegister(load);
7641 HLoadString::LoadKind load_kind = load->GetLoadKind();
7642
7643 switch (load_kind) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00007644 case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
Vladimir Marko44ca0752019-07-29 10:18:25 +01007645 DCHECK(codegen_->GetCompilerOptions().IsBootImage() ||
7646 codegen_->GetCompilerOptions().IsBootImageExtension());
Artem Serovd4cc5b22016-11-04 11:19:09 +00007647 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
Vladimir Marko59eb30f2018-02-20 11:52:34 +00007648 codegen_->NewBootImageStringPatch(load->GetDexFile(), load->GetStringIndex());
Artem Serovd4cc5b22016-11-04 11:19:09 +00007649 codegen_->EmitMovwMovtPlaceholder(labels, out);
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01007650 return;
Artem Serovd4cc5b22016-11-04 11:19:09 +00007651 }
Vladimir Markoe47f60c2018-02-21 13:43:28 +00007652 case HLoadString::LoadKind::kBootImageRelRo: {
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01007653 DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
7654 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
Vladimir Markode91ca92020-10-27 13:41:40 +00007655 codegen_->NewBootImageRelRoPatch(CodeGenerator::GetBootImageOffset(load));
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01007656 codegen_->EmitMovwMovtPlaceholder(labels, out);
Andreas Gampe3db70682018-12-26 15:12:03 -08007657 __ Ldr(out, MemOperand(out, /* offset= */ 0));
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01007658 return;
Artem Serovd4cc5b22016-11-04 11:19:09 +00007659 }
7660 case HLoadString::LoadKind::kBssEntry: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00007661 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01007662 codegen_->NewStringBssEntryPatch(load->GetDexFile(), load->GetStringIndex());
Vladimir Markof3c52b42017-11-17 17:32:12 +00007663 codegen_->EmitMovwMovtPlaceholder(labels, out);
Vladimir Markod5fd5c32019-07-02 14:46:32 +01007664 // All aligned loads are implicitly atomic consume operations on ARM.
Vladimir Markoca1e0382018-04-11 09:58:41 +00007665 codegen_->GenerateGcRootFieldLoad(
Andreas Gampe3db70682018-12-26 15:12:03 -08007666 load, out_loc, out, /* offset= */ 0, kCompilerReadBarrierOption);
Artem Serovd4cc5b22016-11-04 11:19:09 +00007667 LoadStringSlowPathARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01007668 new (codegen_->GetScopedAllocator()) LoadStringSlowPathARMVIXL(load);
Artem Serovd4cc5b22016-11-04 11:19:09 +00007669 codegen_->AddSlowPath(slow_path);
7670 __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
7671 __ Bind(slow_path->GetExitLabel());
Andra Danciua0130e82020-07-23 12:34:56 +00007672 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 17);
Artem Serovd4cc5b22016-11-04 11:19:09 +00007673 return;
7674 }
Vladimir Marko8e524ad2018-07-13 10:27:43 +01007675 case HLoadString::LoadKind::kJitBootImageAddress: {
7676 uint32_t address = reinterpret_cast32<uint32_t>(load->GetString().Get());
7677 DCHECK_NE(address, 0u);
7678 __ Ldr(out, codegen_->DeduplicateBootImageAddressLiteral(address));
7679 return;
7680 }
Artem Serovd4cc5b22016-11-04 11:19:09 +00007681 case HLoadString::LoadKind::kJitTableAddress: {
Artem Serovc5fcb442016-12-02 19:19:58 +00007682 __ Ldr(out, codegen_->DeduplicateJitStringLiteral(load->GetDexFile(),
Nicolas Geoffrayf0acfe72017-01-09 20:54:52 +00007683 load->GetStringIndex(),
7684 load->GetString()));
Artem Serovc5fcb442016-12-02 19:19:58 +00007685 // /* GcRoot<mirror::String> */ out = *out
Vladimir Markoca1e0382018-04-11 09:58:41 +00007686 codegen_->GenerateGcRootFieldLoad(
Andreas Gampe3db70682018-12-26 15:12:03 -08007687 load, out_loc, out, /* offset= */ 0, kCompilerReadBarrierOption);
Artem Serovc5fcb442016-12-02 19:19:58 +00007688 return;
Artem Serovd4cc5b22016-11-04 11:19:09 +00007689 }
7690 default:
7691 break;
7692 }
Artem Serov02d37832016-10-25 15:25:33 +01007693
7694 // TODO: Re-add the compiler code to do string dex cache lookup again.
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007695 DCHECK_EQ(load->GetLoadKind(), HLoadString::LoadKind::kRuntimeCall);
Artem Serov02d37832016-10-25 15:25:33 +01007696 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Andreas Gampe8a0128a2016-11-28 07:38:35 -08007697 __ Mov(calling_convention.GetRegisterAt(0), load->GetStringIndex().index_);
Artem Serov02d37832016-10-25 15:25:33 +01007698 codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc());
7699 CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
Andra Danciua0130e82020-07-23 12:34:56 +00007700 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 18);
Artem Serov02d37832016-10-25 15:25:33 +01007701}
7702
7703static int32_t GetExceptionTlsOffset() {
7704 return Thread::ExceptionOffset<kArmPointerSize>().Int32Value();
7705}
7706
7707void LocationsBuilderARMVIXL::VisitLoadException(HLoadException* load) {
7708 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01007709 new (GetGraph()->GetAllocator()) LocationSummary(load, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01007710 locations->SetOut(Location::RequiresRegister());
7711}
7712
7713void InstructionCodeGeneratorARMVIXL::VisitLoadException(HLoadException* load) {
7714 vixl32::Register out = OutputRegister(load);
7715 GetAssembler()->LoadFromOffset(kLoadWord, out, tr, GetExceptionTlsOffset());
7716}
7717
7718
7719void LocationsBuilderARMVIXL::VisitClearException(HClearException* clear) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01007720 new (GetGraph()->GetAllocator()) LocationSummary(clear, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01007721}
7722
7723void InstructionCodeGeneratorARMVIXL::VisitClearException(HClearException* clear ATTRIBUTE_UNUSED) {
7724 UseScratchRegisterScope temps(GetVIXLAssembler());
7725 vixl32::Register temp = temps.Acquire();
7726 __ Mov(temp, 0);
7727 GetAssembler()->StoreToOffset(kStoreWord, temp, tr, GetExceptionTlsOffset());
7728}
7729
7730void LocationsBuilderARMVIXL::VisitThrow(HThrow* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01007731 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
7732 instruction, LocationSummary::kCallOnMainOnly);
Artem Serov02d37832016-10-25 15:25:33 +01007733 InvokeRuntimeCallingConventionARMVIXL calling_convention;
7734 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
7735}
7736
7737void InstructionCodeGeneratorARMVIXL::VisitThrow(HThrow* instruction) {
7738 codegen_->InvokeRuntime(kQuickDeliverException, instruction, instruction->GetDexPc());
7739 CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
7740}
7741
Artem Serov657022c2016-11-23 14:19:38 +00007742// Temp is used for read barrier.
7743static size_t NumberOfInstanceOfTemps(TypeCheckKind type_check_kind) {
7744 if (kEmitCompilerReadBarrier &&
7745 (kUseBakerReadBarrier ||
7746 type_check_kind == TypeCheckKind::kAbstractClassCheck ||
7747 type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
7748 type_check_kind == TypeCheckKind::kArrayObjectCheck)) {
7749 return 1;
7750 }
7751 return 0;
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007752}
7753
Artem Serov657022c2016-11-23 14:19:38 +00007754// Interface case has 3 temps, one for holding the number of interfaces, one for the current
7755// interface pointer, one for loading the current interface.
7756// The other checks have one temp for loading the object's class.
7757static size_t NumberOfCheckCastTemps(TypeCheckKind type_check_kind) {
7758 if (type_check_kind == TypeCheckKind::kInterfaceCheck) {
7759 return 3;
7760 }
7761 return 1 + NumberOfInstanceOfTemps(type_check_kind);
7762}
Artem Serovcfbe9132016-10-14 15:58:56 +01007763
7764void LocationsBuilderARMVIXL::VisitInstanceOf(HInstanceOf* instruction) {
7765 LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
7766 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
7767 bool baker_read_barrier_slow_path = false;
7768 switch (type_check_kind) {
7769 case TypeCheckKind::kExactCheck:
7770 case TypeCheckKind::kAbstractClassCheck:
7771 case TypeCheckKind::kClassHierarchyCheck:
Vladimir Marko87584542017-12-12 17:47:52 +00007772 case TypeCheckKind::kArrayObjectCheck: {
7773 bool needs_read_barrier = CodeGenerator::InstanceOfNeedsReadBarrier(instruction);
7774 call_kind = needs_read_barrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall;
7775 baker_read_barrier_slow_path = kUseBakerReadBarrier && needs_read_barrier;
Artem Serovcfbe9132016-10-14 15:58:56 +01007776 break;
Vladimir Marko87584542017-12-12 17:47:52 +00007777 }
Artem Serovcfbe9132016-10-14 15:58:56 +01007778 case TypeCheckKind::kArrayCheck:
7779 case TypeCheckKind::kUnresolvedCheck:
7780 case TypeCheckKind::kInterfaceCheck:
7781 call_kind = LocationSummary::kCallOnSlowPath;
7782 break;
Vladimir Marko175e7862018-03-27 09:03:13 +00007783 case TypeCheckKind::kBitstringCheck:
7784 break;
Artem Serovcfbe9132016-10-14 15:58:56 +01007785 }
7786
Vladimir Markoca6fff82017-10-03 14:49:14 +01007787 LocationSummary* locations =
7788 new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
Artem Serovcfbe9132016-10-14 15:58:56 +01007789 if (baker_read_barrier_slow_path) {
7790 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
7791 }
7792 locations->SetInAt(0, Location::RequiresRegister());
Vladimir Marko175e7862018-03-27 09:03:13 +00007793 if (type_check_kind == TypeCheckKind::kBitstringCheck) {
7794 locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
7795 locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
7796 locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
7797 } else {
7798 locations->SetInAt(1, Location::RequiresRegister());
7799 }
Artem Serovcfbe9132016-10-14 15:58:56 +01007800 // The "out" register is used as a temporary, so it overlaps with the inputs.
7801 // Note that TypeCheckSlowPathARM uses this register too.
7802 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
Artem Serov657022c2016-11-23 14:19:38 +00007803 locations->AddRegisterTemps(NumberOfInstanceOfTemps(type_check_kind));
Artem Serovcfbe9132016-10-14 15:58:56 +01007804}
7805
7806void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) {
7807 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
7808 LocationSummary* locations = instruction->GetLocations();
7809 Location obj_loc = locations->InAt(0);
7810 vixl32::Register obj = InputRegisterAt(instruction, 0);
Vladimir Marko175e7862018-03-27 09:03:13 +00007811 vixl32::Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck)
7812 ? vixl32::Register()
7813 : InputRegisterAt(instruction, 1);
Artem Serovcfbe9132016-10-14 15:58:56 +01007814 Location out_loc = locations->Out();
7815 vixl32::Register out = OutputRegister(instruction);
Artem Serov657022c2016-11-23 14:19:38 +00007816 const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind);
7817 DCHECK_LE(num_temps, 1u);
7818 Location maybe_temp_loc = (num_temps >= 1) ? locations->GetTemp(0) : Location::NoLocation();
Artem Serovcfbe9132016-10-14 15:58:56 +01007819 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
7820 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
7821 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
7822 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007823 vixl32::Label done;
7824 vixl32::Label* const final_label = codegen_->GetFinalLabel(instruction, &done);
Artem Serovcfbe9132016-10-14 15:58:56 +01007825 SlowPathCodeARMVIXL* slow_path = nullptr;
7826
7827 // Return 0 if `obj` is null.
7828 // avoid null check if we know obj is not null.
7829 if (instruction->MustDoNullCheck()) {
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007830 DCHECK(!out.Is(obj));
7831 __ Mov(out, 0);
Andreas Gampe3db70682018-12-26 15:12:03 -08007832 __ CompareAndBranchIfZero(obj, final_label, /* is_far_target= */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007833 }
7834
Artem Serovcfbe9132016-10-14 15:58:56 +01007835 switch (type_check_kind) {
7836 case TypeCheckKind::kExactCheck: {
Vladimir Marko87584542017-12-12 17:47:52 +00007837 ReadBarrierOption read_barrier_option =
7838 CodeGenerator::ReadBarrierOptionForInstanceOf(instruction);
Mathieu Chartier6beced42016-11-15 15:51:31 -08007839 // /* HeapReference<Class> */ out = obj->klass_
7840 GenerateReferenceLoadTwoRegisters(instruction,
7841 out_loc,
7842 obj_loc,
7843 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00007844 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00007845 read_barrier_option);
Artem Serovcfbe9132016-10-14 15:58:56 +01007846 // Classes must be equal for the instanceof to succeed.
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007847 __ Cmp(out, cls);
7848 // We speculatively set the result to false without changing the condition
7849 // flags, which allows us to avoid some branching later.
7850 __ Mov(LeaveFlags, out, 0);
7851
7852 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
7853 // we check that the output is in a low register, so that a 16-bit MOV
7854 // encoding can be used.
7855 if (out.IsLow()) {
7856 // We use the scope because of the IT block that follows.
7857 ExactAssemblyScope guard(GetVIXLAssembler(),
7858 2 * vixl32::k16BitT32InstructionSizeInBytes,
7859 CodeBufferCheckScope::kExactSize);
7860
7861 __ it(eq);
7862 __ mov(eq, out, 1);
7863 } else {
Andreas Gampe3db70682018-12-26 15:12:03 -08007864 __ B(ne, final_label, /* is_far_target= */ false);
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007865 __ Mov(out, 1);
7866 }
7867
Artem Serovcfbe9132016-10-14 15:58:56 +01007868 break;
7869 }
7870
7871 case TypeCheckKind::kAbstractClassCheck: {
Vladimir Marko87584542017-12-12 17:47:52 +00007872 ReadBarrierOption read_barrier_option =
7873 CodeGenerator::ReadBarrierOptionForInstanceOf(instruction);
Mathieu Chartier6beced42016-11-15 15:51:31 -08007874 // /* HeapReference<Class> */ out = obj->klass_
7875 GenerateReferenceLoadTwoRegisters(instruction,
7876 out_loc,
7877 obj_loc,
7878 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00007879 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00007880 read_barrier_option);
Artem Serovcfbe9132016-10-14 15:58:56 +01007881 // If the class is abstract, we eagerly fetch the super class of the
7882 // object to avoid doing a comparison we know will fail.
7883 vixl32::Label loop;
7884 __ Bind(&loop);
7885 // /* HeapReference<Class> */ out = out->super_class_
Artem Serov657022c2016-11-23 14:19:38 +00007886 GenerateReferenceLoadOneRegister(instruction,
7887 out_loc,
7888 super_offset,
7889 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00007890 read_barrier_option);
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007891 // If `out` is null, we use it for the result, and jump to the final label.
Andreas Gampe3db70682018-12-26 15:12:03 -08007892 __ CompareAndBranchIfZero(out, final_label, /* is_far_target= */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007893 __ Cmp(out, cls);
Andreas Gampe3db70682018-12-26 15:12:03 -08007894 __ B(ne, &loop, /* is_far_target= */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007895 __ Mov(out, 1);
Artem Serovcfbe9132016-10-14 15:58:56 +01007896 break;
7897 }
7898
7899 case TypeCheckKind::kClassHierarchyCheck: {
Vladimir Marko87584542017-12-12 17:47:52 +00007900 ReadBarrierOption read_barrier_option =
7901 CodeGenerator::ReadBarrierOptionForInstanceOf(instruction);
Mathieu Chartier6beced42016-11-15 15:51:31 -08007902 // /* HeapReference<Class> */ out = obj->klass_
7903 GenerateReferenceLoadTwoRegisters(instruction,
7904 out_loc,
7905 obj_loc,
7906 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00007907 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00007908 read_barrier_option);
Artem Serovcfbe9132016-10-14 15:58:56 +01007909 // Walk over the class hierarchy to find a match.
7910 vixl32::Label loop, success;
7911 __ Bind(&loop);
7912 __ Cmp(out, cls);
Andreas Gampe3db70682018-12-26 15:12:03 -08007913 __ B(eq, &success, /* is_far_target= */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007914 // /* HeapReference<Class> */ out = out->super_class_
Artem Serov657022c2016-11-23 14:19:38 +00007915 GenerateReferenceLoadOneRegister(instruction,
7916 out_loc,
7917 super_offset,
7918 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00007919 read_barrier_option);
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007920 // This is essentially a null check, but it sets the condition flags to the
7921 // proper value for the code that follows the loop, i.e. not `eq`.
7922 __ Cmp(out, 1);
Andreas Gampe3db70682018-12-26 15:12:03 -08007923 __ B(hs, &loop, /* is_far_target= */ false);
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007924
7925 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
7926 // we check that the output is in a low register, so that a 16-bit MOV
7927 // encoding can be used.
7928 if (out.IsLow()) {
7929 // If `out` is null, we use it for the result, and the condition flags
7930 // have already been set to `ne`, so the IT block that comes afterwards
7931 // (and which handles the successful case) turns into a NOP (instead of
7932 // overwriting `out`).
7933 __ Bind(&success);
7934
7935 // We use the scope because of the IT block that follows.
7936 ExactAssemblyScope guard(GetVIXLAssembler(),
7937 2 * vixl32::k16BitT32InstructionSizeInBytes,
7938 CodeBufferCheckScope::kExactSize);
7939
7940 // There is only one branch to the `success` label (which is bound to this
7941 // IT block), and it has the same condition, `eq`, so in that case the MOV
7942 // is executed.
7943 __ it(eq);
7944 __ mov(eq, out, 1);
7945 } else {
7946 // If `out` is null, we use it for the result, and jump to the final label.
Anton Kirilov6f644202017-02-27 18:29:45 +00007947 __ B(final_label);
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007948 __ Bind(&success);
7949 __ Mov(out, 1);
Artem Serovcfbe9132016-10-14 15:58:56 +01007950 }
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007951
Artem Serovcfbe9132016-10-14 15:58:56 +01007952 break;
7953 }
7954
7955 case TypeCheckKind::kArrayObjectCheck: {
Vladimir Marko87584542017-12-12 17:47:52 +00007956 ReadBarrierOption read_barrier_option =
7957 CodeGenerator::ReadBarrierOptionForInstanceOf(instruction);
Mathieu Chartier6beced42016-11-15 15:51:31 -08007958 // /* HeapReference<Class> */ out = obj->klass_
7959 GenerateReferenceLoadTwoRegisters(instruction,
7960 out_loc,
7961 obj_loc,
7962 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00007963 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00007964 read_barrier_option);
Artem Serovcfbe9132016-10-14 15:58:56 +01007965 // Do an exact check.
7966 vixl32::Label exact_check;
7967 __ Cmp(out, cls);
Andreas Gampe3db70682018-12-26 15:12:03 -08007968 __ B(eq, &exact_check, /* is_far_target= */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007969 // Otherwise, we need to check that the object's class is a non-primitive array.
7970 // /* HeapReference<Class> */ out = out->component_type_
Artem Serov657022c2016-11-23 14:19:38 +00007971 GenerateReferenceLoadOneRegister(instruction,
7972 out_loc,
7973 component_offset,
7974 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00007975 read_barrier_option);
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007976 // If `out` is null, we use it for the result, and jump to the final label.
Andreas Gampe3db70682018-12-26 15:12:03 -08007977 __ CompareAndBranchIfZero(out, final_label, /* is_far_target= */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007978 GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset);
7979 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007980 __ Cmp(out, 0);
7981 // We speculatively set the result to false without changing the condition
7982 // flags, which allows us to avoid some branching later.
7983 __ Mov(LeaveFlags, out, 0);
7984
7985 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
7986 // we check that the output is in a low register, so that a 16-bit MOV
7987 // encoding can be used.
7988 if (out.IsLow()) {
7989 __ Bind(&exact_check);
7990
7991 // We use the scope because of the IT block that follows.
7992 ExactAssemblyScope guard(GetVIXLAssembler(),
7993 2 * vixl32::k16BitT32InstructionSizeInBytes,
7994 CodeBufferCheckScope::kExactSize);
7995
7996 __ it(eq);
7997 __ mov(eq, out, 1);
7998 } else {
Andreas Gampe3db70682018-12-26 15:12:03 -08007999 __ B(ne, final_label, /* is_far_target= */ false);
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00008000 __ Bind(&exact_check);
8001 __ Mov(out, 1);
8002 }
8003
Artem Serovcfbe9132016-10-14 15:58:56 +01008004 break;
8005 }
8006
8007 case TypeCheckKind::kArrayCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00008008 // No read barrier since the slow path will retry upon failure.
Mathieu Chartier6beced42016-11-15 15:51:31 -08008009 // /* HeapReference<Class> */ out = obj->klass_
8010 GenerateReferenceLoadTwoRegisters(instruction,
8011 out_loc,
8012 obj_loc,
8013 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00008014 maybe_temp_loc,
8015 kWithoutReadBarrier);
Artem Serovcfbe9132016-10-14 15:58:56 +01008016 __ Cmp(out, cls);
8017 DCHECK(locations->OnlyCallsOnSlowPath());
Vladimir Marko174b2e22017-10-12 13:34:49 +01008018 slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathARMVIXL(
Andreas Gampe3db70682018-12-26 15:12:03 -08008019 instruction, /* is_fatal= */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01008020 codegen_->AddSlowPath(slow_path);
8021 __ B(ne, slow_path->GetEntryLabel());
8022 __ Mov(out, 1);
Artem Serovcfbe9132016-10-14 15:58:56 +01008023 break;
8024 }
8025
8026 case TypeCheckKind::kUnresolvedCheck:
8027 case TypeCheckKind::kInterfaceCheck: {
8028 // Note that we indeed only call on slow path, but we always go
8029 // into the slow path for the unresolved and interface check
8030 // cases.
8031 //
8032 // We cannot directly call the InstanceofNonTrivial runtime
8033 // entry point without resorting to a type checking slow path
8034 // here (i.e. by calling InvokeRuntime directly), as it would
8035 // require to assign fixed registers for the inputs of this
8036 // HInstanceOf instruction (following the runtime calling
8037 // convention), which might be cluttered by the potential first
8038 // read barrier emission at the beginning of this method.
8039 //
8040 // TODO: Introduce a new runtime entry point taking the object
8041 // to test (instead of its class) as argument, and let it deal
8042 // with the read barrier issues. This will let us refactor this
8043 // case of the `switch` code as it was previously (with a direct
8044 // call to the runtime not using a type checking slow path).
8045 // This should also be beneficial for the other cases above.
8046 DCHECK(locations->OnlyCallsOnSlowPath());
Vladimir Marko174b2e22017-10-12 13:34:49 +01008047 slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathARMVIXL(
Andreas Gampe3db70682018-12-26 15:12:03 -08008048 instruction, /* is_fatal= */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01008049 codegen_->AddSlowPath(slow_path);
8050 __ B(slow_path->GetEntryLabel());
Artem Serovcfbe9132016-10-14 15:58:56 +01008051 break;
8052 }
Vladimir Marko175e7862018-03-27 09:03:13 +00008053
8054 case TypeCheckKind::kBitstringCheck: {
8055 // /* HeapReference<Class> */ temp = obj->klass_
8056 GenerateReferenceLoadTwoRegisters(instruction,
8057 out_loc,
8058 obj_loc,
8059 class_offset,
8060 maybe_temp_loc,
8061 kWithoutReadBarrier);
8062
8063 GenerateBitstringTypeCheckCompare(instruction, out, DontCare);
8064 // If `out` is a low reg and we would have another low reg temp, we could
8065 // optimize this as RSBS+ADC, see GenerateConditionWithZero().
8066 //
8067 // Also, in some cases when `out` is a low reg and we're loading a constant to IP
8068 // it would make sense to use CMP+MOV+IT+MOV instead of SUB+CLZ+LSR as the code size
8069 // would be the same and we would have fewer direct data dependencies.
8070 codegen_->GenerateConditionWithZero(kCondEQ, out, out); // CLZ+LSR
8071 break;
8072 }
Artem Serovcfbe9132016-10-14 15:58:56 +01008073 }
8074
Artem Serovcfbe9132016-10-14 15:58:56 +01008075 if (done.IsReferenced()) {
8076 __ Bind(&done);
8077 }
8078
8079 if (slow_path != nullptr) {
8080 __ Bind(slow_path->GetExitLabel());
8081 }
8082}
8083
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008084void LocationsBuilderARMVIXL::VisitCheckCast(HCheckCast* instruction) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008085 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
Vladimir Marko87584542017-12-12 17:47:52 +00008086 LocationSummary::CallKind call_kind = CodeGenerator::GetCheckCastCallKind(instruction);
Vladimir Markoca6fff82017-10-03 14:49:14 +01008087 LocationSummary* locations =
8088 new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008089 locations->SetInAt(0, Location::RequiresRegister());
Vladimir Marko175e7862018-03-27 09:03:13 +00008090 if (type_check_kind == TypeCheckKind::kBitstringCheck) {
8091 locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
8092 locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
8093 locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
8094 } else {
8095 locations->SetInAt(1, Location::RequiresRegister());
8096 }
Artem Serov657022c2016-11-23 14:19:38 +00008097 locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind));
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008098}
8099
8100void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) {
8101 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
8102 LocationSummary* locations = instruction->GetLocations();
8103 Location obj_loc = locations->InAt(0);
8104 vixl32::Register obj = InputRegisterAt(instruction, 0);
Vladimir Marko175e7862018-03-27 09:03:13 +00008105 vixl32::Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck)
8106 ? vixl32::Register()
8107 : InputRegisterAt(instruction, 1);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008108 Location temp_loc = locations->GetTemp(0);
8109 vixl32::Register temp = RegisterFrom(temp_loc);
Artem Serov657022c2016-11-23 14:19:38 +00008110 const size_t num_temps = NumberOfCheckCastTemps(type_check_kind);
8111 DCHECK_LE(num_temps, 3u);
8112 Location maybe_temp2_loc = (num_temps >= 2) ? locations->GetTemp(1) : Location::NoLocation();
8113 Location maybe_temp3_loc = (num_temps >= 3) ? locations->GetTemp(2) : Location::NoLocation();
8114 const uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
8115 const uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
8116 const uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
8117 const uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
8118 const uint32_t iftable_offset = mirror::Class::IfTableOffset().Uint32Value();
8119 const uint32_t array_length_offset = mirror::Array::LengthOffset().Uint32Value();
8120 const uint32_t object_array_data_offset =
8121 mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value();
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008122
Vladimir Marko87584542017-12-12 17:47:52 +00008123 bool is_type_check_slow_path_fatal = CodeGenerator::IsTypeCheckSlowPathFatal(instruction);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008124 SlowPathCodeARMVIXL* type_check_slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01008125 new (codegen_->GetScopedAllocator()) TypeCheckSlowPathARMVIXL(
8126 instruction, is_type_check_slow_path_fatal);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008127 codegen_->AddSlowPath(type_check_slow_path);
8128
8129 vixl32::Label done;
Anton Kirilov6f644202017-02-27 18:29:45 +00008130 vixl32::Label* final_label = codegen_->GetFinalLabel(instruction, &done);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008131 // Avoid null check if we know obj is not null.
8132 if (instruction->MustDoNullCheck()) {
Andreas Gampe3db70682018-12-26 15:12:03 -08008133 __ CompareAndBranchIfZero(obj, final_label, /* is_far_target= */ false);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008134 }
8135
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008136 switch (type_check_kind) {
8137 case TypeCheckKind::kExactCheck:
8138 case TypeCheckKind::kArrayCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00008139 // /* HeapReference<Class> */ temp = obj->klass_
8140 GenerateReferenceLoadTwoRegisters(instruction,
8141 temp_loc,
8142 obj_loc,
8143 class_offset,
8144 maybe_temp2_loc,
8145 kWithoutReadBarrier);
8146
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008147 __ Cmp(temp, cls);
8148 // Jump to slow path for throwing the exception or doing a
8149 // more involved array check.
8150 __ B(ne, type_check_slow_path->GetEntryLabel());
8151 break;
8152 }
8153
8154 case TypeCheckKind::kAbstractClassCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00008155 // /* HeapReference<Class> */ temp = obj->klass_
8156 GenerateReferenceLoadTwoRegisters(instruction,
8157 temp_loc,
8158 obj_loc,
8159 class_offset,
8160 maybe_temp2_loc,
8161 kWithoutReadBarrier);
8162
Artem Serovcfbe9132016-10-14 15:58:56 +01008163 // If the class is abstract, we eagerly fetch the super class of the
8164 // object to avoid doing a comparison we know will fail.
8165 vixl32::Label loop;
8166 __ Bind(&loop);
8167 // /* HeapReference<Class> */ temp = temp->super_class_
Artem Serov657022c2016-11-23 14:19:38 +00008168 GenerateReferenceLoadOneRegister(instruction,
8169 temp_loc,
8170 super_offset,
8171 maybe_temp2_loc,
8172 kWithoutReadBarrier);
Artem Serovcfbe9132016-10-14 15:58:56 +01008173
8174 // If the class reference currently in `temp` is null, jump to the slow path to throw the
8175 // exception.
xueliang.zhongf51bc622016-11-04 09:23:32 +00008176 __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
Artem Serovcfbe9132016-10-14 15:58:56 +01008177
8178 // Otherwise, compare the classes.
8179 __ Cmp(temp, cls);
Andreas Gampe3db70682018-12-26 15:12:03 -08008180 __ B(ne, &loop, /* is_far_target= */ false);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008181 break;
8182 }
8183
8184 case TypeCheckKind::kClassHierarchyCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00008185 // /* HeapReference<Class> */ temp = obj->klass_
8186 GenerateReferenceLoadTwoRegisters(instruction,
8187 temp_loc,
8188 obj_loc,
8189 class_offset,
8190 maybe_temp2_loc,
8191 kWithoutReadBarrier);
8192
Artem Serovcfbe9132016-10-14 15:58:56 +01008193 // Walk over the class hierarchy to find a match.
8194 vixl32::Label loop;
8195 __ Bind(&loop);
8196 __ Cmp(temp, cls);
Andreas Gampe3db70682018-12-26 15:12:03 -08008197 __ B(eq, final_label, /* is_far_target= */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01008198
8199 // /* HeapReference<Class> */ temp = temp->super_class_
Artem Serov657022c2016-11-23 14:19:38 +00008200 GenerateReferenceLoadOneRegister(instruction,
8201 temp_loc,
8202 super_offset,
8203 maybe_temp2_loc,
8204 kWithoutReadBarrier);
Artem Serovcfbe9132016-10-14 15:58:56 +01008205
8206 // If the class reference currently in `temp` is null, jump to the slow path to throw the
8207 // exception.
xueliang.zhongf51bc622016-11-04 09:23:32 +00008208 __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
Artem Serovcfbe9132016-10-14 15:58:56 +01008209 // Otherwise, jump to the beginning of the loop.
8210 __ B(&loop);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008211 break;
8212 }
8213
Artem Serovcfbe9132016-10-14 15:58:56 +01008214 case TypeCheckKind::kArrayObjectCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00008215 // /* HeapReference<Class> */ temp = obj->klass_
8216 GenerateReferenceLoadTwoRegisters(instruction,
8217 temp_loc,
8218 obj_loc,
8219 class_offset,
8220 maybe_temp2_loc,
8221 kWithoutReadBarrier);
8222
Artem Serovcfbe9132016-10-14 15:58:56 +01008223 // Do an exact check.
8224 __ Cmp(temp, cls);
Andreas Gampe3db70682018-12-26 15:12:03 -08008225 __ B(eq, final_label, /* is_far_target= */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01008226
8227 // Otherwise, we need to check that the object's class is a non-primitive array.
8228 // /* HeapReference<Class> */ temp = temp->component_type_
Artem Serov657022c2016-11-23 14:19:38 +00008229 GenerateReferenceLoadOneRegister(instruction,
8230 temp_loc,
8231 component_offset,
8232 maybe_temp2_loc,
8233 kWithoutReadBarrier);
Artem Serovcfbe9132016-10-14 15:58:56 +01008234 // If the component type is null, jump to the slow path to throw the exception.
xueliang.zhongf51bc622016-11-04 09:23:32 +00008235 __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
Artem Serovcfbe9132016-10-14 15:58:56 +01008236 // Otherwise,the object is indeed an array, jump to label `check_non_primitive_component_type`
8237 // to further check that this component type is not a primitive type.
8238 GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, temp, temp, primitive_offset);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008239 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
xueliang.zhongf51bc622016-11-04 09:23:32 +00008240 __ CompareAndBranchIfNonZero(temp, type_check_slow_path->GetEntryLabel());
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008241 break;
8242 }
8243
8244 case TypeCheckKind::kUnresolvedCheck:
Artem Serov657022c2016-11-23 14:19:38 +00008245 // We always go into the type check slow path for the unresolved check case.
Artem Serovcfbe9132016-10-14 15:58:56 +01008246 // We cannot directly call the CheckCast runtime entry point
8247 // without resorting to a type checking slow path here (i.e. by
8248 // calling InvokeRuntime directly), as it would require to
8249 // assign fixed registers for the inputs of this HInstanceOf
8250 // instruction (following the runtime calling convention), which
8251 // might be cluttered by the potential first read barrier
8252 // emission at the beginning of this method.
Artem Serov657022c2016-11-23 14:19:38 +00008253
Artem Serovcfbe9132016-10-14 15:58:56 +01008254 __ B(type_check_slow_path->GetEntryLabel());
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008255 break;
Artem Serov657022c2016-11-23 14:19:38 +00008256
8257 case TypeCheckKind::kInterfaceCheck: {
8258 // Avoid read barriers to improve performance of the fast path. We can not get false
8259 // positives by doing this.
8260 // /* HeapReference<Class> */ temp = obj->klass_
8261 GenerateReferenceLoadTwoRegisters(instruction,
8262 temp_loc,
8263 obj_loc,
8264 class_offset,
8265 maybe_temp2_loc,
8266 kWithoutReadBarrier);
8267
8268 // /* HeapReference<Class> */ temp = temp->iftable_
8269 GenerateReferenceLoadTwoRegisters(instruction,
8270 temp_loc,
8271 temp_loc,
8272 iftable_offset,
8273 maybe_temp2_loc,
8274 kWithoutReadBarrier);
8275 // Iftable is never null.
8276 __ Ldr(RegisterFrom(maybe_temp2_loc), MemOperand(temp, array_length_offset));
8277 // Loop through the iftable and check if any class matches.
8278 vixl32::Label start_loop;
8279 __ Bind(&start_loop);
8280 __ CompareAndBranchIfZero(RegisterFrom(maybe_temp2_loc),
8281 type_check_slow_path->GetEntryLabel());
8282 __ Ldr(RegisterFrom(maybe_temp3_loc), MemOperand(temp, object_array_data_offset));
8283 GetAssembler()->MaybeUnpoisonHeapReference(RegisterFrom(maybe_temp3_loc));
8284 // Go to next interface.
8285 __ Add(temp, temp, Operand::From(2 * kHeapReferenceSize));
8286 __ Sub(RegisterFrom(maybe_temp2_loc), RegisterFrom(maybe_temp2_loc), 2);
8287 // Compare the classes and continue the loop if they do not match.
8288 __ Cmp(cls, RegisterFrom(maybe_temp3_loc));
Andreas Gampe3db70682018-12-26 15:12:03 -08008289 __ B(ne, &start_loop, /* is_far_target= */ false);
Artem Serov657022c2016-11-23 14:19:38 +00008290 break;
8291 }
Vladimir Marko175e7862018-03-27 09:03:13 +00008292
8293 case TypeCheckKind::kBitstringCheck: {
8294 // /* HeapReference<Class> */ temp = obj->klass_
8295 GenerateReferenceLoadTwoRegisters(instruction,
8296 temp_loc,
8297 obj_loc,
8298 class_offset,
8299 maybe_temp2_loc,
8300 kWithoutReadBarrier);
8301
8302 GenerateBitstringTypeCheckCompare(instruction, temp, SetFlags);
8303 __ B(ne, type_check_slow_path->GetEntryLabel());
8304 break;
8305 }
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008306 }
Anton Kirilov6f644202017-02-27 18:29:45 +00008307 if (done.IsReferenced()) {
8308 __ Bind(&done);
8309 }
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008310
8311 __ Bind(type_check_slow_path->GetExitLabel());
8312}
8313
Artem Serov551b28f2016-10-18 19:11:30 +01008314void LocationsBuilderARMVIXL::VisitMonitorOperation(HMonitorOperation* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01008315 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
8316 instruction, LocationSummary::kCallOnMainOnly);
Artem Serov551b28f2016-10-18 19:11:30 +01008317 InvokeRuntimeCallingConventionARMVIXL calling_convention;
8318 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
8319}
8320
8321void InstructionCodeGeneratorARMVIXL::VisitMonitorOperation(HMonitorOperation* instruction) {
8322 codegen_->InvokeRuntime(instruction->IsEnter() ? kQuickLockObject : kQuickUnlockObject,
8323 instruction,
8324 instruction->GetDexPc());
8325 if (instruction->IsEnter()) {
8326 CheckEntrypointTypes<kQuickLockObject, void, mirror::Object*>();
8327 } else {
8328 CheckEntrypointTypes<kQuickUnlockObject, void, mirror::Object*>();
8329 }
Andra Danciua0130e82020-07-23 12:34:56 +00008330 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 19);
Artem Serov551b28f2016-10-18 19:11:30 +01008331}
8332
Artem Serov02109dd2016-09-23 17:17:54 +01008333void LocationsBuilderARMVIXL::VisitAnd(HAnd* instruction) {
8334 HandleBitwiseOperation(instruction, AND);
8335}
8336
8337void LocationsBuilderARMVIXL::VisitOr(HOr* instruction) {
8338 HandleBitwiseOperation(instruction, ORR);
8339}
8340
8341void LocationsBuilderARMVIXL::VisitXor(HXor* instruction) {
8342 HandleBitwiseOperation(instruction, EOR);
8343}
8344
8345void LocationsBuilderARMVIXL::HandleBitwiseOperation(HBinaryOperation* instruction, Opcode opcode) {
8346 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01008347 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008348 DCHECK(instruction->GetResultType() == DataType::Type::kInt32
8349 || instruction->GetResultType() == DataType::Type::kInt64);
Artem Serov02109dd2016-09-23 17:17:54 +01008350 // Note: GVN reorders commutative operations to have the constant on the right hand side.
8351 locations->SetInAt(0, Location::RequiresRegister());
8352 locations->SetInAt(1, ArmEncodableConstantOrRegister(instruction->InputAt(1), opcode));
8353 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
8354}
8355
8356void InstructionCodeGeneratorARMVIXL::VisitAnd(HAnd* instruction) {
8357 HandleBitwiseOperation(instruction);
8358}
8359
8360void InstructionCodeGeneratorARMVIXL::VisitOr(HOr* instruction) {
8361 HandleBitwiseOperation(instruction);
8362}
8363
8364void InstructionCodeGeneratorARMVIXL::VisitXor(HXor* instruction) {
8365 HandleBitwiseOperation(instruction);
8366}
8367
Artem Serov2bbc9532016-10-21 11:51:50 +01008368void LocationsBuilderARMVIXL::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) {
8369 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01008370 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008371 DCHECK(instruction->GetResultType() == DataType::Type::kInt32
8372 || instruction->GetResultType() == DataType::Type::kInt64);
Artem Serov2bbc9532016-10-21 11:51:50 +01008373
8374 locations->SetInAt(0, Location::RequiresRegister());
8375 locations->SetInAt(1, Location::RequiresRegister());
8376 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
8377}
8378
8379void InstructionCodeGeneratorARMVIXL::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) {
8380 LocationSummary* locations = instruction->GetLocations();
8381 Location first = locations->InAt(0);
8382 Location second = locations->InAt(1);
8383 Location out = locations->Out();
8384
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008385 if (instruction->GetResultType() == DataType::Type::kInt32) {
Artem Serov2bbc9532016-10-21 11:51:50 +01008386 vixl32::Register first_reg = RegisterFrom(first);
8387 vixl32::Register second_reg = RegisterFrom(second);
8388 vixl32::Register out_reg = RegisterFrom(out);
8389
8390 switch (instruction->GetOpKind()) {
8391 case HInstruction::kAnd:
8392 __ Bic(out_reg, first_reg, second_reg);
8393 break;
8394 case HInstruction::kOr:
8395 __ Orn(out_reg, first_reg, second_reg);
8396 break;
8397 // There is no EON on arm.
8398 case HInstruction::kXor:
8399 default:
8400 LOG(FATAL) << "Unexpected instruction " << instruction->DebugName();
8401 UNREACHABLE();
8402 }
8403 return;
8404
8405 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008406 DCHECK_EQ(instruction->GetResultType(), DataType::Type::kInt64);
Artem Serov2bbc9532016-10-21 11:51:50 +01008407 vixl32::Register first_low = LowRegisterFrom(first);
8408 vixl32::Register first_high = HighRegisterFrom(first);
8409 vixl32::Register second_low = LowRegisterFrom(second);
8410 vixl32::Register second_high = HighRegisterFrom(second);
8411 vixl32::Register out_low = LowRegisterFrom(out);
8412 vixl32::Register out_high = HighRegisterFrom(out);
8413
8414 switch (instruction->GetOpKind()) {
8415 case HInstruction::kAnd:
8416 __ Bic(out_low, first_low, second_low);
8417 __ Bic(out_high, first_high, second_high);
8418 break;
8419 case HInstruction::kOr:
8420 __ Orn(out_low, first_low, second_low);
8421 __ Orn(out_high, first_high, second_high);
8422 break;
8423 // There is no EON on arm.
8424 case HInstruction::kXor:
8425 default:
8426 LOG(FATAL) << "Unexpected instruction " << instruction->DebugName();
8427 UNREACHABLE();
8428 }
8429 }
8430}
8431
Anton Kirilov74234da2017-01-13 14:42:47 +00008432void LocationsBuilderARMVIXL::VisitDataProcWithShifterOp(
8433 HDataProcWithShifterOp* instruction) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008434 DCHECK(instruction->GetType() == DataType::Type::kInt32 ||
8435 instruction->GetType() == DataType::Type::kInt64);
Anton Kirilov74234da2017-01-13 14:42:47 +00008436 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01008437 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008438 const bool overlap = instruction->GetType() == DataType::Type::kInt64 &&
Anton Kirilov74234da2017-01-13 14:42:47 +00008439 HDataProcWithShifterOp::IsExtensionOp(instruction->GetOpKind());
8440
8441 locations->SetInAt(0, Location::RequiresRegister());
8442 locations->SetInAt(1, Location::RequiresRegister());
8443 locations->SetOut(Location::RequiresRegister(),
8444 overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap);
8445}
8446
8447void InstructionCodeGeneratorARMVIXL::VisitDataProcWithShifterOp(
8448 HDataProcWithShifterOp* instruction) {
8449 const LocationSummary* const locations = instruction->GetLocations();
8450 const HInstruction::InstructionKind kind = instruction->GetInstrKind();
8451 const HDataProcWithShifterOp::OpKind op_kind = instruction->GetOpKind();
8452
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008453 if (instruction->GetType() == DataType::Type::kInt32) {
Anton Kirilov420ee302017-02-21 18:10:26 +00008454 const vixl32::Register first = InputRegisterAt(instruction, 0);
8455 const vixl32::Register output = OutputRegister(instruction);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008456 const vixl32::Register second = instruction->InputAt(1)->GetType() == DataType::Type::kInt64
Anton Kirilov74234da2017-01-13 14:42:47 +00008457 ? LowRegisterFrom(locations->InAt(1))
8458 : InputRegisterAt(instruction, 1);
8459
Anton Kirilov420ee302017-02-21 18:10:26 +00008460 if (HDataProcWithShifterOp::IsExtensionOp(op_kind)) {
8461 DCHECK_EQ(kind, HInstruction::kAdd);
8462
8463 switch (op_kind) {
8464 case HDataProcWithShifterOp::kUXTB:
8465 __ Uxtab(output, first, second);
8466 break;
8467 case HDataProcWithShifterOp::kUXTH:
8468 __ Uxtah(output, first, second);
8469 break;
8470 case HDataProcWithShifterOp::kSXTB:
8471 __ Sxtab(output, first, second);
8472 break;
8473 case HDataProcWithShifterOp::kSXTH:
8474 __ Sxtah(output, first, second);
8475 break;
8476 default:
8477 LOG(FATAL) << "Unexpected operation kind: " << op_kind;
8478 UNREACHABLE();
8479 }
8480 } else {
8481 GenerateDataProcInstruction(kind,
8482 output,
8483 first,
8484 Operand(second,
8485 ShiftFromOpKind(op_kind),
8486 instruction->GetShiftAmount()),
8487 codegen_);
8488 }
Anton Kirilov74234da2017-01-13 14:42:47 +00008489 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008490 DCHECK_EQ(instruction->GetType(), DataType::Type::kInt64);
Anton Kirilov74234da2017-01-13 14:42:47 +00008491
8492 if (HDataProcWithShifterOp::IsExtensionOp(op_kind)) {
8493 const vixl32::Register second = InputRegisterAt(instruction, 1);
8494
8495 DCHECK(!LowRegisterFrom(locations->Out()).Is(second));
8496 GenerateDataProc(kind,
8497 locations->Out(),
8498 locations->InAt(0),
8499 second,
8500 Operand(second, ShiftType::ASR, 31),
8501 codegen_);
8502 } else {
8503 GenerateLongDataProc(instruction, codegen_);
8504 }
8505 }
8506}
8507
Artem Serov02109dd2016-09-23 17:17:54 +01008508// TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
8509void InstructionCodeGeneratorARMVIXL::GenerateAndConst(vixl32::Register out,
8510 vixl32::Register first,
8511 uint32_t value) {
8512 // Optimize special cases for individual halfs of `and-long` (`and` is simplified earlier).
8513 if (value == 0xffffffffu) {
8514 if (!out.Is(first)) {
8515 __ Mov(out, first);
8516 }
8517 return;
8518 }
8519 if (value == 0u) {
8520 __ Mov(out, 0);
8521 return;
8522 }
8523 if (GetAssembler()->ShifterOperandCanHold(AND, value)) {
Anton Kiriloveffd5bf2017-02-28 16:59:15 +00008524 __ And(out, first, value);
8525 } else if (GetAssembler()->ShifterOperandCanHold(BIC, ~value)) {
8526 __ Bic(out, first, ~value);
Artem Serov02109dd2016-09-23 17:17:54 +01008527 } else {
Anton Kiriloveffd5bf2017-02-28 16:59:15 +00008528 DCHECK(IsPowerOfTwo(value + 1));
8529 __ Ubfx(out, first, 0, WhichPowerOf2(value + 1));
Artem Serov02109dd2016-09-23 17:17:54 +01008530 }
8531}
8532
8533// TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
8534void InstructionCodeGeneratorARMVIXL::GenerateOrrConst(vixl32::Register out,
8535 vixl32::Register first,
8536 uint32_t value) {
8537 // Optimize special cases for individual halfs of `or-long` (`or` is simplified earlier).
8538 if (value == 0u) {
8539 if (!out.Is(first)) {
8540 __ Mov(out, first);
8541 }
8542 return;
8543 }
8544 if (value == 0xffffffffu) {
8545 __ Mvn(out, 0);
8546 return;
8547 }
8548 if (GetAssembler()->ShifterOperandCanHold(ORR, value)) {
8549 __ Orr(out, first, value);
8550 } else {
8551 DCHECK(GetAssembler()->ShifterOperandCanHold(ORN, ~value));
8552 __ Orn(out, first, ~value);
8553 }
8554}
8555
8556// TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
8557void InstructionCodeGeneratorARMVIXL::GenerateEorConst(vixl32::Register out,
8558 vixl32::Register first,
8559 uint32_t value) {
8560 // Optimize special case for individual halfs of `xor-long` (`xor` is simplified earlier).
8561 if (value == 0u) {
8562 if (!out.Is(first)) {
8563 __ Mov(out, first);
8564 }
8565 return;
8566 }
8567 __ Eor(out, first, value);
8568}
8569
Anton Kirilovdda43962016-11-21 19:55:20 +00008570void InstructionCodeGeneratorARMVIXL::GenerateAddLongConst(Location out,
8571 Location first,
8572 uint64_t value) {
8573 vixl32::Register out_low = LowRegisterFrom(out);
8574 vixl32::Register out_high = HighRegisterFrom(out);
8575 vixl32::Register first_low = LowRegisterFrom(first);
8576 vixl32::Register first_high = HighRegisterFrom(first);
8577 uint32_t value_low = Low32Bits(value);
8578 uint32_t value_high = High32Bits(value);
8579 if (value_low == 0u) {
8580 if (!out_low.Is(first_low)) {
8581 __ Mov(out_low, first_low);
8582 }
8583 __ Add(out_high, first_high, value_high);
8584 return;
8585 }
8586 __ Adds(out_low, first_low, value_low);
Vladimir Markof0a6a1d2018-01-08 14:23:56 +00008587 if (GetAssembler()->ShifterOperandCanHold(ADC, value_high)) {
Anton Kirilovdda43962016-11-21 19:55:20 +00008588 __ Adc(out_high, first_high, value_high);
Anton Kirilovdda43962016-11-21 19:55:20 +00008589 } else {
Vladimir Markof0a6a1d2018-01-08 14:23:56 +00008590 DCHECK(GetAssembler()->ShifterOperandCanHold(SBC, ~value_high));
8591 __ Sbc(out_high, first_high, ~value_high);
Anton Kirilovdda43962016-11-21 19:55:20 +00008592 }
8593}
8594
Artem Serov02109dd2016-09-23 17:17:54 +01008595void InstructionCodeGeneratorARMVIXL::HandleBitwiseOperation(HBinaryOperation* instruction) {
8596 LocationSummary* locations = instruction->GetLocations();
8597 Location first = locations->InAt(0);
8598 Location second = locations->InAt(1);
8599 Location out = locations->Out();
8600
8601 if (second.IsConstant()) {
8602 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
8603 uint32_t value_low = Low32Bits(value);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008604 if (instruction->GetResultType() == DataType::Type::kInt32) {
Artem Serov02109dd2016-09-23 17:17:54 +01008605 vixl32::Register first_reg = InputRegisterAt(instruction, 0);
8606 vixl32::Register out_reg = OutputRegister(instruction);
8607 if (instruction->IsAnd()) {
8608 GenerateAndConst(out_reg, first_reg, value_low);
8609 } else if (instruction->IsOr()) {
8610 GenerateOrrConst(out_reg, first_reg, value_low);
8611 } else {
8612 DCHECK(instruction->IsXor());
8613 GenerateEorConst(out_reg, first_reg, value_low);
8614 }
8615 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008616 DCHECK_EQ(instruction->GetResultType(), DataType::Type::kInt64);
Artem Serov02109dd2016-09-23 17:17:54 +01008617 uint32_t value_high = High32Bits(value);
8618 vixl32::Register first_low = LowRegisterFrom(first);
8619 vixl32::Register first_high = HighRegisterFrom(first);
8620 vixl32::Register out_low = LowRegisterFrom(out);
8621 vixl32::Register out_high = HighRegisterFrom(out);
8622 if (instruction->IsAnd()) {
8623 GenerateAndConst(out_low, first_low, value_low);
8624 GenerateAndConst(out_high, first_high, value_high);
8625 } else if (instruction->IsOr()) {
8626 GenerateOrrConst(out_low, first_low, value_low);
8627 GenerateOrrConst(out_high, first_high, value_high);
8628 } else {
8629 DCHECK(instruction->IsXor());
8630 GenerateEorConst(out_low, first_low, value_low);
8631 GenerateEorConst(out_high, first_high, value_high);
8632 }
8633 }
8634 return;
8635 }
8636
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008637 if (instruction->GetResultType() == DataType::Type::kInt32) {
Artem Serov02109dd2016-09-23 17:17:54 +01008638 vixl32::Register first_reg = InputRegisterAt(instruction, 0);
8639 vixl32::Register second_reg = InputRegisterAt(instruction, 1);
8640 vixl32::Register out_reg = OutputRegister(instruction);
8641 if (instruction->IsAnd()) {
8642 __ And(out_reg, first_reg, second_reg);
8643 } else if (instruction->IsOr()) {
8644 __ Orr(out_reg, first_reg, second_reg);
8645 } else {
8646 DCHECK(instruction->IsXor());
8647 __ Eor(out_reg, first_reg, second_reg);
8648 }
8649 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008650 DCHECK_EQ(instruction->GetResultType(), DataType::Type::kInt64);
Artem Serov02109dd2016-09-23 17:17:54 +01008651 vixl32::Register first_low = LowRegisterFrom(first);
8652 vixl32::Register first_high = HighRegisterFrom(first);
8653 vixl32::Register second_low = LowRegisterFrom(second);
8654 vixl32::Register second_high = HighRegisterFrom(second);
8655 vixl32::Register out_low = LowRegisterFrom(out);
8656 vixl32::Register out_high = HighRegisterFrom(out);
8657 if (instruction->IsAnd()) {
8658 __ And(out_low, first_low, second_low);
8659 __ And(out_high, first_high, second_high);
8660 } else if (instruction->IsOr()) {
8661 __ Orr(out_low, first_low, second_low);
8662 __ Orr(out_high, first_high, second_high);
8663 } else {
8664 DCHECK(instruction->IsXor());
8665 __ Eor(out_low, first_low, second_low);
8666 __ Eor(out_high, first_high, second_high);
8667 }
8668 }
8669}
8670
Artem Serovcfbe9132016-10-14 15:58:56 +01008671void InstructionCodeGeneratorARMVIXL::GenerateReferenceLoadOneRegister(
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008672 HInstruction* instruction,
Artem Serovcfbe9132016-10-14 15:58:56 +01008673 Location out,
8674 uint32_t offset,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008675 Location maybe_temp,
8676 ReadBarrierOption read_barrier_option) {
Artem Serovcfbe9132016-10-14 15:58:56 +01008677 vixl32::Register out_reg = RegisterFrom(out);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008678 if (read_barrier_option == kWithReadBarrier) {
8679 CHECK(kEmitCompilerReadBarrier);
8680 DCHECK(maybe_temp.IsRegister()) << maybe_temp;
8681 if (kUseBakerReadBarrier) {
8682 // Load with fast path based Baker's read barrier.
8683 // /* HeapReference<Object> */ out = *(out + offset)
8684 codegen_->GenerateFieldLoadWithBakerReadBarrier(
Andreas Gampe3db70682018-12-26 15:12:03 -08008685 instruction, out, out_reg, offset, maybe_temp, /* needs_null_check= */ false);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008686 } else {
8687 // Load with slow path based read barrier.
8688 // Save the value of `out` into `maybe_temp` before overwriting it
8689 // in the following move operation, as we will need it for the
8690 // read barrier below.
8691 __ Mov(RegisterFrom(maybe_temp), out_reg);
8692 // /* HeapReference<Object> */ out = *(out + offset)
8693 GetAssembler()->LoadFromOffset(kLoadWord, out_reg, out_reg, offset);
8694 codegen_->GenerateReadBarrierSlow(instruction, out, out, maybe_temp, offset);
8695 }
Artem Serovcfbe9132016-10-14 15:58:56 +01008696 } else {
8697 // Plain load with no read barrier.
8698 // /* HeapReference<Object> */ out = *(out + offset)
8699 GetAssembler()->LoadFromOffset(kLoadWord, out_reg, out_reg, offset);
8700 GetAssembler()->MaybeUnpoisonHeapReference(out_reg);
8701 }
8702}
8703
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008704void InstructionCodeGeneratorARMVIXL::GenerateReferenceLoadTwoRegisters(
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008705 HInstruction* instruction,
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008706 Location out,
8707 Location obj,
8708 uint32_t offset,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008709 Location maybe_temp,
8710 ReadBarrierOption read_barrier_option) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008711 vixl32::Register out_reg = RegisterFrom(out);
8712 vixl32::Register obj_reg = RegisterFrom(obj);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008713 if (read_barrier_option == kWithReadBarrier) {
8714 CHECK(kEmitCompilerReadBarrier);
8715 if (kUseBakerReadBarrier) {
8716 DCHECK(maybe_temp.IsRegister()) << maybe_temp;
8717 // Load with fast path based Baker's read barrier.
8718 // /* HeapReference<Object> */ out = *(obj + offset)
8719 codegen_->GenerateFieldLoadWithBakerReadBarrier(
Andreas Gampe3db70682018-12-26 15:12:03 -08008720 instruction, out, obj_reg, offset, maybe_temp, /* needs_null_check= */ false);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008721 } else {
8722 // Load with slow path based read barrier.
8723 // /* HeapReference<Object> */ out = *(obj + offset)
8724 GetAssembler()->LoadFromOffset(kLoadWord, out_reg, obj_reg, offset);
8725 codegen_->GenerateReadBarrierSlow(instruction, out, out, obj, offset);
8726 }
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008727 } else {
8728 // Plain load with no read barrier.
8729 // /* HeapReference<Object> */ out = *(obj + offset)
8730 GetAssembler()->LoadFromOffset(kLoadWord, out_reg, obj_reg, offset);
8731 GetAssembler()->MaybeUnpoisonHeapReference(out_reg);
8732 }
8733}
8734
Vladimir Markoca1e0382018-04-11 09:58:41 +00008735void CodeGeneratorARMVIXL::GenerateGcRootFieldLoad(
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008736 HInstruction* instruction,
Scott Wakelinga7812ae2016-10-17 10:03:36 +01008737 Location root,
8738 vixl32::Register obj,
8739 uint32_t offset,
Artem Serovd4cc5b22016-11-04 11:19:09 +00008740 ReadBarrierOption read_barrier_option) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01008741 vixl32::Register root_reg = RegisterFrom(root);
Artem Serovd4cc5b22016-11-04 11:19:09 +00008742 if (read_barrier_option == kWithReadBarrier) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008743 DCHECK(kEmitCompilerReadBarrier);
8744 if (kUseBakerReadBarrier) {
8745 // Fast path implementation of art::ReadBarrier::BarrierForRoot when
Roland Levillainba650a42017-03-06 13:52:32 +00008746 // Baker's read barrier are used.
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008747
Vladimir Marko008e09f32018-08-06 15:42:43 +01008748 // Query `art::Thread::Current()->GetIsGcMarking()` (stored in
8749 // the Marking Register) to decide whether we need to enter
8750 // the slow path to mark the GC root.
8751 //
8752 // We use shared thunks for the slow path; shared within the method
8753 // for JIT, across methods for AOT. That thunk checks the reference
8754 // and jumps to the entrypoint if needed.
8755 //
8756 // lr = &return_address;
8757 // GcRoot<mirror::Object> root = *(obj+offset); // Original reference load.
8758 // if (mr) { // Thread::Current()->GetIsGcMarking()
8759 // goto gc_root_thunk<root_reg>(lr)
8760 // }
8761 // return_address:
Roland Levillainba650a42017-03-06 13:52:32 +00008762
Vladimir Marko008e09f32018-08-06 15:42:43 +01008763 UseScratchRegisterScope temps(GetVIXLAssembler());
8764 temps.Exclude(ip);
8765 bool narrow = CanEmitNarrowLdr(root_reg, obj, offset);
8766 uint32_t custom_data = EncodeBakerReadBarrierGcRootData(root_reg.GetCode(), narrow);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008767
Vladimir Markod887ed82018-08-14 13:52:12 +00008768 size_t narrow_instructions = /* CMP */ (mr.IsLow() ? 1u : 0u) + /* LDR */ (narrow ? 1u : 0u);
8769 size_t wide_instructions = /* ADR+CMP+LDR+BNE */ 4u - narrow_instructions;
8770 size_t exact_size = wide_instructions * vixl32::k32BitT32InstructionSizeInBytes +
8771 narrow_instructions * vixl32::k16BitT32InstructionSizeInBytes;
8772 ExactAssemblyScope guard(GetVIXLAssembler(), exact_size);
Vladimir Marko008e09f32018-08-06 15:42:43 +01008773 vixl32::Label return_address;
8774 EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
8775 __ cmp(mr, Operand(0));
8776 // Currently the offset is always within range. If that changes,
8777 // we shall have to split the load the same way as for fields.
8778 DCHECK_LT(offset, kReferenceLoadMinFarOffset);
8779 ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
8780 __ ldr(EncodingSize(narrow ? Narrow : Wide), root_reg, MemOperand(obj, offset));
8781 EmitBakerReadBarrierBne(custom_data);
Vladimir Markod887ed82018-08-14 13:52:12 +00008782 __ bind(&return_address);
Vladimir Marko008e09f32018-08-06 15:42:43 +01008783 DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
8784 narrow ? BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_OFFSET
8785 : BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_OFFSET);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008786 } else {
8787 // GC root loaded through a slow path for read barriers other
8788 // than Baker's.
8789 // /* GcRoot<mirror::Object>* */ root = obj + offset
8790 __ Add(root_reg, obj, offset);
8791 // /* mirror::Object* */ root = root->Read()
Vladimir Markoca1e0382018-04-11 09:58:41 +00008792 GenerateReadBarrierForRootSlow(instruction, root, root);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008793 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01008794 } else {
8795 // Plain GC root load with no read barrier.
8796 // /* GcRoot<mirror::Object> */ root = *(obj + offset)
8797 GetAssembler()->LoadFromOffset(kLoadWord, root_reg, obj, offset);
8798 // Note that GC roots are not affected by heap poisoning, thus we
8799 // do not have to unpoison `root_reg` here.
8800 }
Andra Danciua0130e82020-07-23 12:34:56 +00008801 MaybeGenerateMarkingRegisterCheck(/* code= */ 20);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01008802}
8803
Vladimir Markod887ed82018-08-14 13:52:12 +00008804void CodeGeneratorARMVIXL::GenerateUnsafeCasOldValueAddWithBakerReadBarrier(
8805 vixl::aarch32::Register old_value,
8806 vixl::aarch32::Register adjusted_old_value,
8807 vixl::aarch32::Register expected) {
8808 DCHECK(kEmitCompilerReadBarrier);
8809 DCHECK(kUseBakerReadBarrier);
8810
8811 // Similar to the Baker RB path in GenerateGcRootFieldLoad(), with an ADD instead of LDR.
8812 uint32_t custom_data = EncodeBakerReadBarrierUnsafeCasData(old_value.GetCode());
8813
8814 size_t narrow_instructions = /* CMP */ (mr.IsLow() ? 1u : 0u);
8815 size_t wide_instructions = /* ADR+CMP+ADD+BNE */ 4u - narrow_instructions;
8816 size_t exact_size = wide_instructions * vixl32::k32BitT32InstructionSizeInBytes +
8817 narrow_instructions * vixl32::k16BitT32InstructionSizeInBytes;
8818 ExactAssemblyScope guard(GetVIXLAssembler(), exact_size);
8819 vixl32::Label return_address;
8820 EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
8821 __ cmp(mr, Operand(0));
8822 ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
8823 __ add(EncodingSize(Wide), old_value, adjusted_old_value, Operand(expected)); // Preserves flags.
8824 EmitBakerReadBarrierBne(custom_data);
8825 __ bind(&return_address);
8826 DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
8827 BAKER_MARK_INTROSPECTION_UNSAFE_CAS_ADD_OFFSET);
8828}
8829
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008830void CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
8831 Location ref,
8832 vixl32::Register obj,
Vladimir Marko248141f2018-08-10 10:40:07 +01008833 const vixl32::MemOperand& src,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008834 bool needs_null_check) {
8835 DCHECK(kEmitCompilerReadBarrier);
8836 DCHECK(kUseBakerReadBarrier);
8837
Vladimir Marko008e09f32018-08-06 15:42:43 +01008838 // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
8839 // Marking Register) to decide whether we need to enter the slow
8840 // path to mark the reference. Then, in the slow path, check the
8841 // gray bit in the lock word of the reference's holder (`obj`) to
8842 // decide whether to mark `ref` or not.
8843 //
8844 // We use shared thunks for the slow path; shared within the method
8845 // for JIT, across methods for AOT. That thunk checks the holder
8846 // and jumps to the entrypoint if needed. If the holder is not gray,
8847 // it creates a fake dependency and returns to the LDR instruction.
8848 //
8849 // lr = &gray_return_address;
8850 // if (mr) { // Thread::Current()->GetIsGcMarking()
8851 // goto field_thunk<holder_reg, base_reg>(lr)
8852 // }
8853 // not_gray_return_address:
8854 // // Original reference load. If the offset is too large to fit
8855 // // into LDR, we use an adjusted base register here.
8856 // HeapReference<mirror::Object> reference = *(obj+offset);
8857 // gray_return_address:
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008858
Vladimir Marko248141f2018-08-10 10:40:07 +01008859 DCHECK(src.GetAddrMode() == vixl32::Offset);
8860 DCHECK_ALIGNED(src.GetOffsetImmediate(), sizeof(mirror::HeapReference<mirror::Object>));
Vladimir Marko008e09f32018-08-06 15:42:43 +01008861 vixl32::Register ref_reg = RegisterFrom(ref, DataType::Type::kReference);
Vladimir Marko248141f2018-08-10 10:40:07 +01008862 bool narrow = CanEmitNarrowLdr(ref_reg, src.GetBaseRegister(), src.GetOffsetImmediate());
8863
Vladimir Marko008e09f32018-08-06 15:42:43 +01008864 UseScratchRegisterScope temps(GetVIXLAssembler());
8865 temps.Exclude(ip);
Vladimir Marko248141f2018-08-10 10:40:07 +01008866 uint32_t custom_data =
8867 EncodeBakerReadBarrierFieldData(src.GetBaseRegister().GetCode(), obj.GetCode(), narrow);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008868
Vladimir Marko008e09f32018-08-06 15:42:43 +01008869 {
Vladimir Markod887ed82018-08-14 13:52:12 +00008870 size_t narrow_instructions =
8871 /* CMP */ (mr.IsLow() ? 1u : 0u) +
8872 /* LDR+unpoison? */ (narrow ? (kPoisonHeapReferences ? 2u : 1u) : 0u);
8873 size_t wide_instructions =
8874 /* ADR+CMP+LDR+BNE+unpoison? */ (kPoisonHeapReferences ? 5u : 4u) - narrow_instructions;
8875 size_t exact_size = wide_instructions * vixl32::k32BitT32InstructionSizeInBytes +
8876 narrow_instructions * vixl32::k16BitT32InstructionSizeInBytes;
8877 ExactAssemblyScope guard(GetVIXLAssembler(), exact_size);
Vladimir Marko008e09f32018-08-06 15:42:43 +01008878 vixl32::Label return_address;
8879 EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
8880 __ cmp(mr, Operand(0));
8881 EmitBakerReadBarrierBne(custom_data);
8882 ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
Vladimir Marko248141f2018-08-10 10:40:07 +01008883 __ ldr(EncodingSize(narrow ? Narrow : Wide), ref_reg, src);
Vladimir Marko008e09f32018-08-06 15:42:43 +01008884 if (needs_null_check) {
8885 MaybeRecordImplicitNullCheck(instruction);
8886 }
8887 // Note: We need a specific width for the unpoisoning NEG.
8888 if (kPoisonHeapReferences) {
8889 if (narrow) {
8890 // The only 16-bit encoding is T1 which sets flags outside IT block (i.e. RSBS, not RSB).
8891 __ rsbs(EncodingSize(Narrow), ref_reg, ref_reg, Operand(0));
8892 } else {
8893 __ rsb(EncodingSize(Wide), ref_reg, ref_reg, Operand(0));
8894 }
8895 }
Vladimir Markod887ed82018-08-14 13:52:12 +00008896 __ bind(&return_address);
Vladimir Marko008e09f32018-08-06 15:42:43 +01008897 DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
8898 narrow ? BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET
8899 : BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET);
8900 }
Andra Danciua0130e82020-07-23 12:34:56 +00008901 MaybeGenerateMarkingRegisterCheck(/* code= */ 21, /* temp_loc= */ LocationFrom(ip));
Roland Levillain6070e882016-11-03 17:51:58 +00008902}
8903
Vladimir Marko248141f2018-08-10 10:40:07 +01008904void CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
8905 Location ref,
8906 vixl32::Register obj,
8907 uint32_t offset,
Vladimir Marko01b65522020-10-28 15:43:54 +00008908 Location maybe_temp,
Vladimir Marko248141f2018-08-10 10:40:07 +01008909 bool needs_null_check) {
8910 DCHECK_ALIGNED(offset, sizeof(mirror::HeapReference<mirror::Object>));
8911 vixl32::Register base = obj;
8912 if (offset >= kReferenceLoadMinFarOffset) {
Vladimir Marko01b65522020-10-28 15:43:54 +00008913 base = RegisterFrom(maybe_temp);
Vladimir Marko248141f2018-08-10 10:40:07 +01008914 static_assert(IsPowerOfTwo(kReferenceLoadMinFarOffset), "Expecting a power of 2.");
8915 __ Add(base, obj, Operand(offset & ~(kReferenceLoadMinFarOffset - 1u)));
8916 offset &= (kReferenceLoadMinFarOffset - 1u);
8917 }
8918 GenerateFieldLoadWithBakerReadBarrier(
8919 instruction, ref, obj, MemOperand(base, offset), needs_null_check);
8920}
8921
Vladimir Marko008e09f32018-08-06 15:42:43 +01008922void CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier(Location ref,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008923 vixl32::Register obj,
8924 uint32_t data_offset,
8925 Location index,
8926 Location temp,
8927 bool needs_null_check) {
8928 DCHECK(kEmitCompilerReadBarrier);
8929 DCHECK(kUseBakerReadBarrier);
8930
8931 static_assert(
8932 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
8933 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008934 ScaleFactor scale_factor = TIMES_4;
8935
Vladimir Marko008e09f32018-08-06 15:42:43 +01008936 // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
8937 // Marking Register) to decide whether we need to enter the slow
8938 // path to mark the reference. Then, in the slow path, check the
8939 // gray bit in the lock word of the reference's holder (`obj`) to
8940 // decide whether to mark `ref` or not.
8941 //
8942 // We use shared thunks for the slow path; shared within the method
8943 // for JIT, across methods for AOT. That thunk checks the holder
8944 // and jumps to the entrypoint if needed. If the holder is not gray,
8945 // it creates a fake dependency and returns to the LDR instruction.
8946 //
8947 // lr = &gray_return_address;
8948 // if (mr) { // Thread::Current()->GetIsGcMarking()
8949 // goto array_thunk<base_reg>(lr)
8950 // }
8951 // not_gray_return_address:
8952 // // Original reference load. If the offset is too large to fit
8953 // // into LDR, we use an adjusted base register here.
8954 // HeapReference<mirror::Object> reference = data[index];
8955 // gray_return_address:
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008956
Vladimir Marko008e09f32018-08-06 15:42:43 +01008957 DCHECK(index.IsValid());
8958 vixl32::Register index_reg = RegisterFrom(index, DataType::Type::kInt32);
8959 vixl32::Register ref_reg = RegisterFrom(ref, DataType::Type::kReference);
8960 vixl32::Register data_reg = RegisterFrom(temp, DataType::Type::kInt32); // Raw pointer.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008961
Vladimir Marko008e09f32018-08-06 15:42:43 +01008962 UseScratchRegisterScope temps(GetVIXLAssembler());
8963 temps.Exclude(ip);
8964 uint32_t custom_data = EncodeBakerReadBarrierArrayData(data_reg.GetCode());
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008965
Vladimir Marko008e09f32018-08-06 15:42:43 +01008966 __ Add(data_reg, obj, Operand(data_offset));
8967 {
Vladimir Markod887ed82018-08-14 13:52:12 +00008968 size_t narrow_instructions = /* CMP */ (mr.IsLow() ? 1u : 0u);
8969 size_t wide_instructions =
8970 /* ADR+CMP+BNE+LDR+unpoison? */ (kPoisonHeapReferences ? 5u : 4u) - narrow_instructions;
8971 size_t exact_size = wide_instructions * vixl32::k32BitT32InstructionSizeInBytes +
8972 narrow_instructions * vixl32::k16BitT32InstructionSizeInBytes;
8973 ExactAssemblyScope guard(GetVIXLAssembler(), exact_size);
Vladimir Marko008e09f32018-08-06 15:42:43 +01008974 vixl32::Label return_address;
8975 EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
8976 __ cmp(mr, Operand(0));
8977 EmitBakerReadBarrierBne(custom_data);
8978 ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
8979 __ ldr(ref_reg, MemOperand(data_reg, index_reg, vixl32::LSL, scale_factor));
8980 DCHECK(!needs_null_check); // The thunk cannot handle the null check.
8981 // Note: We need a Wide NEG for the unpoisoning.
8982 if (kPoisonHeapReferences) {
8983 __ rsb(EncodingSize(Wide), ref_reg, ref_reg, Operand(0));
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008984 }
Vladimir Markod887ed82018-08-14 13:52:12 +00008985 __ bind(&return_address);
Vladimir Marko008e09f32018-08-06 15:42:43 +01008986 DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
8987 BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008988 }
Andra Danciua0130e82020-07-23 12:34:56 +00008989 MaybeGenerateMarkingRegisterCheck(/* code= */ 22, /* temp_loc= */ LocationFrom(ip));
Roland Levillain6070e882016-11-03 17:51:58 +00008990}
8991
Roland Levillain5daa4952017-07-03 17:23:56 +01008992void CodeGeneratorARMVIXL::MaybeGenerateMarkingRegisterCheck(int code, Location temp_loc) {
8993 // The following condition is a compile-time one, so it does not have a run-time cost.
8994 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier && kIsDebugBuild) {
8995 // The following condition is a run-time one; it is executed after the
8996 // previous compile-time test, to avoid penalizing non-debug builds.
8997 if (GetCompilerOptions().EmitRunTimeChecksInDebugMode()) {
8998 UseScratchRegisterScope temps(GetVIXLAssembler());
8999 vixl32::Register temp = temp_loc.IsValid() ? RegisterFrom(temp_loc) : temps.Acquire();
9000 GetAssembler()->GenerateMarkingRegisterCheck(temp,
9001 kMarkingRegisterCheckBreakCodeBaseCode + code);
9002 }
9003 }
9004}
9005
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009006void CodeGeneratorARMVIXL::GenerateReadBarrierSlow(HInstruction* instruction,
9007 Location out,
9008 Location ref,
9009 Location obj,
9010 uint32_t offset,
9011 Location index) {
9012 DCHECK(kEmitCompilerReadBarrier);
9013
9014 // Insert a slow path based read barrier *after* the reference load.
9015 //
9016 // If heap poisoning is enabled, the unpoisoning of the loaded
9017 // reference will be carried out by the runtime within the slow
9018 // path.
9019 //
9020 // Note that `ref` currently does not get unpoisoned (when heap
9021 // poisoning is enabled), which is alright as the `ref` argument is
9022 // not used by the artReadBarrierSlow entry point.
9023 //
9024 // TODO: Unpoison `ref` when it is used by artReadBarrierSlow.
Vladimir Marko174b2e22017-10-12 13:34:49 +01009025 SlowPathCodeARMVIXL* slow_path = new (GetScopedAllocator())
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009026 ReadBarrierForHeapReferenceSlowPathARMVIXL(instruction, out, ref, obj, offset, index);
9027 AddSlowPath(slow_path);
9028
9029 __ B(slow_path->GetEntryLabel());
9030 __ Bind(slow_path->GetExitLabel());
9031}
9032
9033void CodeGeneratorARMVIXL::MaybeGenerateReadBarrierSlow(HInstruction* instruction,
Artem Serov02d37832016-10-25 15:25:33 +01009034 Location out,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009035 Location ref,
9036 Location obj,
9037 uint32_t offset,
9038 Location index) {
Artem Serov02d37832016-10-25 15:25:33 +01009039 if (kEmitCompilerReadBarrier) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009040 // Baker's read barriers shall be handled by the fast path
Roland Levillain9983e302017-07-14 14:34:22 +01009041 // (CodeGeneratorARMVIXL::GenerateReferenceLoadWithBakerReadBarrier).
Artem Serov02d37832016-10-25 15:25:33 +01009042 DCHECK(!kUseBakerReadBarrier);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009043 // If heap poisoning is enabled, unpoisoning will be taken care of
9044 // by the runtime within the slow path.
9045 GenerateReadBarrierSlow(instruction, out, ref, obj, offset, index);
Artem Serov02d37832016-10-25 15:25:33 +01009046 } else if (kPoisonHeapReferences) {
9047 GetAssembler()->UnpoisonHeapReference(RegisterFrom(out));
9048 }
9049}
9050
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009051void CodeGeneratorARMVIXL::GenerateReadBarrierForRootSlow(HInstruction* instruction,
9052 Location out,
9053 Location root) {
9054 DCHECK(kEmitCompilerReadBarrier);
9055
9056 // Insert a slow path based read barrier *after* the GC root load.
9057 //
9058 // Note that GC roots are not affected by heap poisoning, so we do
9059 // not need to do anything special for this here.
9060 SlowPathCodeARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01009061 new (GetScopedAllocator()) ReadBarrierForRootSlowPathARMVIXL(instruction, out, root);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009062 AddSlowPath(slow_path);
9063
9064 __ B(slow_path->GetEntryLabel());
9065 __ Bind(slow_path->GetExitLabel());
9066}
9067
Artem Serov02d37832016-10-25 15:25:33 +01009068// Check if the desired_dispatch_info is supported. If it is, return it,
9069// otherwise return a fall-back info that should be used instead.
9070HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARMVIXL::GetSupportedInvokeStaticOrDirectDispatch(
Artem Serovd4cc5b22016-11-04 11:19:09 +00009071 const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
Vladimir Marko86c87522020-05-11 16:55:55 +01009072 ArtMethod* method) {
Vladimir Markod3e9c622020-08-05 12:20:28 +01009073 if (method->IsIntrinsic() &&
Nicolas Geoffray6d69b522020-09-23 14:47:28 +01009074 desired_dispatch_info.code_ptr_location == CodePtrLocation::kCallCriticalNative) {
Vladimir Markod3e9c622020-08-05 12:20:28 +01009075 // As a work-around for soft-float native ABI interfering with type checks, we are
9076 // inserting fake calls to Float.floatToRawIntBits() or Double.doubleToRawLongBits()
9077 // when a float or double argument is passed in core registers but we cannot do that
9078 // for actual intrinsic implementations that expect them in FP registers. Therefore
9079 // we do not use `kCallCriticalNative` for intrinsics with FP arguments; if they are
9080 // properly intrinsified, the dispatch type does not matter anyway.
Vladimir Marko86c87522020-05-11 16:55:55 +01009081 ScopedObjectAccess soa(Thread::Current());
9082 uint32_t shorty_len;
9083 const char* shorty = method->GetShorty(&shorty_len);
Vladimir Marko86c87522020-05-11 16:55:55 +01009084 for (uint32_t i = 1; i != shorty_len; ++i) {
Vladimir Marko86c87522020-05-11 16:55:55 +01009085 if (shorty[i] == 'D' || shorty[i] == 'F') {
9086 HInvokeStaticOrDirect::DispatchInfo dispatch_info = desired_dispatch_info;
Nicolas Geoffray6d69b522020-09-23 14:47:28 +01009087 dispatch_info.code_ptr_location = CodePtrLocation::kCallArtMethod;
Vladimir Marko86c87522020-05-11 16:55:55 +01009088 return dispatch_info;
9089 }
Vladimir Marko86c87522020-05-11 16:55:55 +01009090 }
9091 }
Nicolas Geoffraye807ff72017-01-23 09:03:12 +00009092 return desired_dispatch_info;
Artem Serov02d37832016-10-25 15:25:33 +01009093}
9094
Nicolas Geoffray8d34a182020-09-16 09:46:58 +01009095
9096void CodeGeneratorARMVIXL::LoadMethod(MethodLoadKind load_kind, Location temp, HInvoke* invoke) {
9097 switch (load_kind) {
Nicolas Geoffray6d69b522020-09-23 14:47:28 +01009098 case MethodLoadKind::kBootImageLinkTimePcRelative: {
Vladimir Marko44ca0752019-07-29 10:18:25 +01009099 DCHECK(GetCompilerOptions().IsBootImage() || GetCompilerOptions().IsBootImageExtension());
Nicolas Geoffraye6c0f2a2020-09-07 08:30:52 +01009100 PcRelativePatchInfo* labels = NewBootImageMethodPatch(invoke->GetResolvedMethodReference());
Vladimir Marko65979462017-05-19 17:25:12 +01009101 vixl32::Register temp_reg = RegisterFrom(temp);
9102 EmitMovwMovtPlaceholder(labels, temp_reg);
9103 break;
9104 }
Nicolas Geoffray6d69b522020-09-23 14:47:28 +01009105 case MethodLoadKind::kBootImageRelRo: {
Vladimir Markoe47f60c2018-02-21 13:43:28 +00009106 uint32_t boot_image_offset = GetBootImageOffset(invoke);
Vladimir Markob066d432018-01-03 13:14:37 +00009107 PcRelativePatchInfo* labels = NewBootImageRelRoPatch(boot_image_offset);
9108 vixl32::Register temp_reg = RegisterFrom(temp);
9109 EmitMovwMovtPlaceholder(labels, temp_reg);
9110 GetAssembler()->LoadFromOffset(kLoadWord, temp_reg, temp_reg, /* offset*/ 0);
9111 break;
9112 }
Nicolas Geoffray6d69b522020-09-23 14:47:28 +01009113 case MethodLoadKind::kBssEntry: {
Nicolas Geoffraye6c0f2a2020-09-07 08:30:52 +01009114 PcRelativePatchInfo* labels = NewMethodBssEntryPatch(invoke->GetMethodReference());
Vladimir Marko0eb882b2017-05-15 13:39:18 +01009115 vixl32::Register temp_reg = RegisterFrom(temp);
9116 EmitMovwMovtPlaceholder(labels, temp_reg);
Vladimir Markod5fd5c32019-07-02 14:46:32 +01009117 // All aligned loads are implicitly atomic consume operations on ARM.
Vladimir Marko0eb882b2017-05-15 13:39:18 +01009118 GetAssembler()->LoadFromOffset(kLoadWord, temp_reg, temp_reg, /* offset*/ 0);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01009119 break;
9120 }
Nicolas Geoffray8d34a182020-09-16 09:46:58 +01009121 case MethodLoadKind::kJitDirectAddress: {
9122 __ Mov(RegisterFrom(temp), Operand::From(invoke->GetResolvedMethod()));
Vladimir Marko8e524ad2018-07-13 10:27:43 +01009123 break;
Nicolas Geoffray8d34a182020-09-16 09:46:58 +01009124 }
9125 case MethodLoadKind::kRuntimeCall: {
9126 // Test situation, don't do anything.
9127 break;
9128 }
9129 default: {
9130 LOG(FATAL) << "Load kind should have already been handled " << load_kind;
9131 UNREACHABLE();
9132 }
9133 }
9134}
9135
9136void CodeGeneratorARMVIXL::GenerateStaticOrDirectCall(
9137 HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path) {
9138 Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp.
9139 switch (invoke->GetMethodLoadKind()) {
9140 case MethodLoadKind::kStringInit: {
9141 uint32_t offset =
9142 GetThreadOffset<kArmPointerSize>(invoke->GetStringInitEntryPoint()).Int32Value();
9143 // temp = thread->string_init_entrypoint
9144 GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(temp), tr, offset);
9145 break;
9146 }
9147 case MethodLoadKind::kRecursive: {
9148 callee_method = invoke->GetLocations()->InAt(invoke->GetCurrentMethodIndex());
9149 break;
9150 }
Nicolas Geoffray6d69b522020-09-23 14:47:28 +01009151 case MethodLoadKind::kRuntimeCall: {
Vladimir Markoe7197bf2017-06-02 17:00:23 +01009152 GenerateInvokeStaticOrDirectRuntimeCall(invoke, temp, slow_path);
9153 return; // No code pointer retrieval; the runtime performs the call directly.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01009154 }
Vladimir Markoeb9eb002020-10-02 13:54:19 +01009155 case MethodLoadKind::kBootImageLinkTimePcRelative:
9156 // Note: Unlike arm64, x86 and x86-64, we do not avoid the materialization of method
9157 // pointer for kCallCriticalNative because it would not save us an instruction from
9158 // the current sequence MOVW+MOVT+ADD(pc)+LDR+BL. The ADD(pc) separates the patched
9159 // offset instructions MOVW+MOVT from the entrypoint load, so they cannot be fused.
9160 FALLTHROUGH_INTENDED;
Nicolas Geoffray8d34a182020-09-16 09:46:58 +01009161 default: {
9162 LoadMethod(invoke->GetMethodLoadKind(), temp, invoke);
9163 break;
9164 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01009165 }
9166
Vladimir Marko86c87522020-05-11 16:55:55 +01009167 auto call_code_pointer_member = [&](MemberOffset offset) {
9168 // LR = callee_method->member;
9169 GetAssembler()->LoadFromOffset(kLoadWord, lr, RegisterFrom(callee_method), offset.Int32Value());
9170 {
9171 // Use a scope to help guarantee that `RecordPcInfo()` records the correct pc.
9172 // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
9173 ExactAssemblyScope aas(GetVIXLAssembler(),
9174 vixl32::k16BitT32InstructionSizeInBytes,
9175 CodeBufferCheckScope::kExactSize);
9176 // LR()
9177 __ blx(lr);
9178 RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
9179 }
9180 };
Artem Serovd4cc5b22016-11-04 11:19:09 +00009181 switch (invoke->GetCodePtrLocation()) {
Nicolas Geoffray6d69b522020-09-23 14:47:28 +01009182 case CodePtrLocation::kCallSelf:
Vladimir Markoe7197bf2017-06-02 17:00:23 +01009183 {
9184 // Use a scope to help guarantee that `RecordPcInfo()` records the correct pc.
9185 ExactAssemblyScope aas(GetVIXLAssembler(),
9186 vixl32::k32BitT32InstructionSizeInBytes,
9187 CodeBufferCheckScope::kMaximumSize);
9188 __ bl(GetFrameEntryLabel());
9189 RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
9190 }
Artem Serovd4cc5b22016-11-04 11:19:09 +00009191 break;
Nicolas Geoffray6d69b522020-09-23 14:47:28 +01009192 case CodePtrLocation::kCallCriticalNative: {
Vladimir Marko86c87522020-05-11 16:55:55 +01009193 size_t out_frame_size =
9194 PrepareCriticalNativeCall<CriticalNativeCallingConventionVisitorARMVIXL,
9195 kAapcsStackAlignment,
Vladimir Markodec78172020-06-19 15:31:23 +01009196 GetCriticalNativeDirectCallFrameSize>(invoke);
Vladimir Marko86c87522020-05-11 16:55:55 +01009197 call_code_pointer_member(ArtMethod::EntryPointFromJniOffset(kArmPointerSize));
9198 // Move the result when needed due to native and managed ABI mismatch.
9199 switch (invoke->GetType()) {
9200 case DataType::Type::kFloat32:
9201 __ Vmov(s0, r0);
9202 break;
9203 case DataType::Type::kFloat64:
9204 __ Vmov(d0, r0, r1);
9205 break;
9206 case DataType::Type::kBool:
9207 case DataType::Type::kInt8:
9208 case DataType::Type::kUint16:
9209 case DataType::Type::kInt16:
9210 case DataType::Type::kInt32:
9211 case DataType::Type::kInt64:
9212 case DataType::Type::kVoid:
9213 break;
9214 default:
9215 DCHECK(false) << invoke->GetType();
9216 break;
9217 }
9218 if (out_frame_size != 0u) {
Vladimir Markodec78172020-06-19 15:31:23 +01009219 DecreaseFrame(out_frame_size);
Vladimir Marko86c87522020-05-11 16:55:55 +01009220 }
9221 break;
9222 }
Nicolas Geoffray6d69b522020-09-23 14:47:28 +01009223 case CodePtrLocation::kCallArtMethod:
Vladimir Marko86c87522020-05-11 16:55:55 +01009224 call_code_pointer_member(ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize));
Artem Serovd4cc5b22016-11-04 11:19:09 +00009225 break;
Scott Wakelinga7812ae2016-10-17 10:03:36 +01009226 }
9227
Scott Wakelinga7812ae2016-10-17 10:03:36 +01009228 DCHECK(!IsLeafMethod());
9229}
9230
Vladimir Markoe7197bf2017-06-02 17:00:23 +01009231void CodeGeneratorARMVIXL::GenerateVirtualCall(
9232 HInvokeVirtual* invoke, Location temp_location, SlowPathCode* slow_path) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01009233 vixl32::Register temp = RegisterFrom(temp_location);
9234 uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
9235 invoke->GetVTableIndex(), kArmPointerSize).Uint32Value();
9236
9237 // Use the calling convention instead of the location of the receiver, as
9238 // intrinsics may have put the receiver in a different register. In the intrinsics
9239 // slow path, the arguments have been moved to the right place, so here we are
9240 // guaranteed that the receiver is the first register of the calling convention.
9241 InvokeDexCallingConventionARMVIXL calling_convention;
9242 vixl32::Register receiver = calling_convention.GetRegisterAt(0);
9243 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
Alexandre Rames374ddf32016-11-04 10:40:49 +00009244 {
9245 // Make sure the pc is recorded immediately after the `ldr` instruction.
Artem Serov0fb37192016-12-06 18:13:40 +00009246 ExactAssemblyScope aas(GetVIXLAssembler(),
9247 vixl32::kMaxInstructionSizeInBytes,
9248 CodeBufferCheckScope::kMaximumSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00009249 // /* HeapReference<Class> */ temp = receiver->klass_
9250 __ ldr(temp, MemOperand(receiver, class_offset));
9251 MaybeRecordImplicitNullCheck(invoke);
9252 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01009253 // Instead of simply (possibly) unpoisoning `temp` here, we should
9254 // emit a read barrier for the previous class reference load.
9255 // However this is not required in practice, as this is an
9256 // intermediate/temporary reference and because the current
9257 // concurrent copying collector keeps the from-space memory
9258 // intact/accessible until the end of the marking phase (the
9259 // concurrent copying collector may not in the future).
9260 GetAssembler()->MaybeUnpoisonHeapReference(temp);
9261
Nicolas Geoffraye2a3aa92019-11-25 17:52:58 +00009262 // If we're compiling baseline, update the inline cache.
9263 MaybeGenerateInlineCacheCheck(invoke, temp);
9264
Scott Wakelinga7812ae2016-10-17 10:03:36 +01009265 // temp = temp->GetMethodAt(method_offset);
9266 uint32_t entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(
9267 kArmPointerSize).Int32Value();
9268 GetAssembler()->LoadFromOffset(kLoadWord, temp, temp, method_offset);
9269 // LR = temp->GetEntryPoint();
9270 GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, entry_point);
Vladimir Markoe7197bf2017-06-02 17:00:23 +01009271 {
9272 // Use a scope to help guarantee that `RecordPcInfo()` records the correct pc.
9273 // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
9274 ExactAssemblyScope aas(GetVIXLAssembler(),
9275 vixl32::k16BitT32InstructionSizeInBytes,
9276 CodeBufferCheckScope::kExactSize);
9277 // LR();
9278 __ blx(lr);
9279 RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
9280 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01009281}
9282
Vladimir Marko6fd16062018-06-26 11:02:04 +01009283CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewBootImageIntrinsicPatch(
9284 uint32_t intrinsic_data) {
Vladimir Marko2d06e022019-07-08 15:45:19 +01009285 return NewPcRelativePatch(/* dex_file= */ nullptr, intrinsic_data, &boot_image_other_patches_);
Vladimir Marko6fd16062018-06-26 11:02:04 +01009286}
9287
Vladimir Markob066d432018-01-03 13:14:37 +00009288CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewBootImageRelRoPatch(
9289 uint32_t boot_image_offset) {
Andreas Gampe3db70682018-12-26 15:12:03 -08009290 return NewPcRelativePatch(/* dex_file= */ nullptr,
Vladimir Markob066d432018-01-03 13:14:37 +00009291 boot_image_offset,
Vladimir Marko2d06e022019-07-08 15:45:19 +01009292 &boot_image_other_patches_);
Vladimir Markob066d432018-01-03 13:14:37 +00009293}
9294
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009295CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewBootImageMethodPatch(
Vladimir Marko65979462017-05-19 17:25:12 +01009296 MethodReference target_method) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009297 return NewPcRelativePatch(
9298 target_method.dex_file, target_method.index, &boot_image_method_patches_);
Artem Serovd4cc5b22016-11-04 11:19:09 +00009299}
9300
Vladimir Marko0eb882b2017-05-15 13:39:18 +01009301CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewMethodBssEntryPatch(
9302 MethodReference target_method) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009303 return NewPcRelativePatch(
9304 target_method.dex_file, target_method.index, &method_bss_entry_patches_);
Vladimir Marko0eb882b2017-05-15 13:39:18 +01009305}
9306
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009307CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewBootImageTypePatch(
Artem Serovd4cc5b22016-11-04 11:19:09 +00009308 const DexFile& dex_file, dex::TypeIndex type_index) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009309 return NewPcRelativePatch(&dex_file, type_index.index_, &boot_image_type_patches_);
Artem Serovd4cc5b22016-11-04 11:19:09 +00009310}
9311
Vladimir Marko1998cd02017-01-13 13:02:58 +00009312CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewTypeBssEntryPatch(
Vladimir Marko8f63f102020-09-28 12:10:28 +01009313 HLoadClass* load_class) {
9314 const DexFile& dex_file = load_class->GetDexFile();
9315 dex::TypeIndex type_index = load_class->GetTypeIndex();
9316 ArenaDeque<PcRelativePatchInfo>* patches = nullptr;
9317 switch (load_class->GetLoadKind()) {
9318 case HLoadClass::LoadKind::kBssEntry:
9319 patches = &type_bss_entry_patches_;
9320 break;
9321 case HLoadClass::LoadKind::kBssEntryPublic:
9322 patches = &public_type_bss_entry_patches_;
9323 break;
9324 case HLoadClass::LoadKind::kBssEntryPackage:
9325 patches = &package_type_bss_entry_patches_;
9326 break;
9327 default:
9328 LOG(FATAL) << "Unexpected load kind: " << load_class->GetLoadKind();
9329 UNREACHABLE();
9330 }
Vladimir Markobaade402020-09-30 14:45:39 +00009331 return NewPcRelativePatch(&dex_file, type_index.index_, patches);
Vladimir Marko1998cd02017-01-13 13:02:58 +00009332}
9333
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009334CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewBootImageStringPatch(
Vladimir Marko65979462017-05-19 17:25:12 +01009335 const DexFile& dex_file, dex::StringIndex string_index) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009336 return NewPcRelativePatch(&dex_file, string_index.index_, &boot_image_string_patches_);
Vladimir Marko65979462017-05-19 17:25:12 +01009337}
9338
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01009339CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewStringBssEntryPatch(
9340 const DexFile& dex_file, dex::StringIndex string_index) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009341 return NewPcRelativePatch(&dex_file, string_index.index_, &string_bss_entry_patches_);
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01009342}
9343
Artem Serovd4cc5b22016-11-04 11:19:09 +00009344CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewPcRelativePatch(
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009345 const DexFile* dex_file, uint32_t offset_or_index, ArenaDeque<PcRelativePatchInfo>* patches) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00009346 patches->emplace_back(dex_file, offset_or_index);
9347 return &patches->back();
9348}
9349
Vladimir Markof6675082019-05-17 12:05:28 +01009350void CodeGeneratorARMVIXL::EmitEntrypointThunkCall(ThreadOffset32 entrypoint_offset) {
9351 DCHECK(!__ AllowMacroInstructions()); // In ExactAssemblyScope.
Vladimir Marko695348f2020-05-19 14:42:02 +01009352 DCHECK(!GetCompilerOptions().IsJitCompiler());
Vladimir Markof6675082019-05-17 12:05:28 +01009353 call_entrypoint_patches_.emplace_back(/*dex_file*/ nullptr, entrypoint_offset.Uint32Value());
9354 vixl::aarch32::Label* bl_label = &call_entrypoint_patches_.back().label;
9355 __ bind(bl_label);
9356 vixl32::Label placeholder_label;
9357 __ bl(&placeholder_label); // Placeholder, patched at link-time.
9358 __ bind(&placeholder_label);
9359}
9360
Vladimir Marko966b46f2018-08-03 10:20:19 +00009361void CodeGeneratorARMVIXL::EmitBakerReadBarrierBne(uint32_t custom_data) {
Vladimir Markod887ed82018-08-14 13:52:12 +00009362 DCHECK(!__ AllowMacroInstructions()); // In ExactAssemblyScope.
Vladimir Marko695348f2020-05-19 14:42:02 +01009363 if (GetCompilerOptions().IsJitCompiler()) {
Vladimir Marko966b46f2018-08-03 10:20:19 +00009364 auto it = jit_baker_read_barrier_slow_paths_.FindOrAdd(custom_data);
9365 vixl::aarch32::Label* slow_path_entry = &it->second.label;
9366 __ b(ne, EncodingSize(Wide), slow_path_entry);
9367 } else {
9368 baker_read_barrier_patches_.emplace_back(custom_data);
9369 vixl::aarch32::Label* patch_label = &baker_read_barrier_patches_.back().label;
9370 __ bind(patch_label);
9371 vixl32::Label placeholder_label;
9372 __ b(ne, EncodingSize(Wide), &placeholder_label); // Placeholder, patched at link-time.
9373 __ bind(&placeholder_label);
9374 }
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009375}
9376
Artem Serovc5fcb442016-12-02 19:19:58 +00009377VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateBootImageAddressLiteral(uint32_t address) {
Vladimir Marko8e524ad2018-07-13 10:27:43 +01009378 return DeduplicateUint32Literal(address, &uint32_literals_);
Artem Serovc5fcb442016-12-02 19:19:58 +00009379}
9380
Nicolas Geoffrayf0acfe72017-01-09 20:54:52 +00009381VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateJitStringLiteral(
9382 const DexFile& dex_file,
9383 dex::StringIndex string_index,
9384 Handle<mirror::String> handle) {
Vladimir Marko174b2e22017-10-12 13:34:49 +01009385 ReserveJitStringRoot(StringReference(&dex_file, string_index), handle);
Artem Serovc5fcb442016-12-02 19:19:58 +00009386 return jit_string_patches_.GetOrCreate(
9387 StringReference(&dex_file, string_index),
9388 [this]() {
Andreas Gampe3db70682018-12-26 15:12:03 -08009389 return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* value= */ 0u);
Artem Serovc5fcb442016-12-02 19:19:58 +00009390 });
9391}
9392
9393VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateJitClassLiteral(const DexFile& dex_file,
9394 dex::TypeIndex type_index,
Nicolas Geoffray5247c082017-01-13 14:17:29 +00009395 Handle<mirror::Class> handle) {
Vladimir Marko174b2e22017-10-12 13:34:49 +01009396 ReserveJitClassRoot(TypeReference(&dex_file, type_index), handle);
Artem Serovc5fcb442016-12-02 19:19:58 +00009397 return jit_class_patches_.GetOrCreate(
9398 TypeReference(&dex_file, type_index),
9399 [this]() {
Andreas Gampe3db70682018-12-26 15:12:03 -08009400 return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* value= */ 0u);
Artem Serovc5fcb442016-12-02 19:19:58 +00009401 });
9402}
9403
Vladimir Marko6fd16062018-06-26 11:02:04 +01009404void CodeGeneratorARMVIXL::LoadBootImageAddress(vixl32::Register reg,
9405 uint32_t boot_image_reference) {
9406 if (GetCompilerOptions().IsBootImage()) {
9407 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
9408 NewBootImageIntrinsicPatch(boot_image_reference);
9409 EmitMovwMovtPlaceholder(labels, reg);
Vladimir Markoa2da9b92018-10-10 14:21:55 +01009410 } else if (GetCompilerOptions().GetCompilePic()) {
Vladimir Marko6fd16062018-06-26 11:02:04 +01009411 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
9412 NewBootImageRelRoPatch(boot_image_reference);
Vladimir Markoeebb8212018-06-05 14:57:24 +01009413 EmitMovwMovtPlaceholder(labels, reg);
Andreas Gampe3db70682018-12-26 15:12:03 -08009414 __ Ldr(reg, MemOperand(reg, /* offset= */ 0));
Vladimir Markoeebb8212018-06-05 14:57:24 +01009415 } else {
Vladimir Marko695348f2020-05-19 14:42:02 +01009416 DCHECK(GetCompilerOptions().IsJitCompiler());
Vladimir Markoeebb8212018-06-05 14:57:24 +01009417 gc::Heap* heap = Runtime::Current()->GetHeap();
9418 DCHECK(!heap->GetBootImageSpaces().empty());
9419 uintptr_t address =
Vladimir Marko6fd16062018-06-26 11:02:04 +01009420 reinterpret_cast<uintptr_t>(heap->GetBootImageSpaces()[0]->Begin() + boot_image_reference);
Vladimir Markoeebb8212018-06-05 14:57:24 +01009421 __ Ldr(reg, DeduplicateBootImageAddressLiteral(dchecked_integral_cast<uint32_t>(address)));
9422 }
9423}
9424
Vladimir Markode91ca92020-10-27 13:41:40 +00009425void CodeGeneratorARMVIXL::LoadIntrinsicDeclaringClass(vixl32::Register reg, HInvoke* invoke) {
9426 DCHECK_NE(invoke->GetIntrinsic(), Intrinsics::kNone);
Vladimir Marko6fd16062018-06-26 11:02:04 +01009427 if (GetCompilerOptions().IsBootImage()) {
Vladimir Marko6fd16062018-06-26 11:02:04 +01009428 // Load the class the same way as for HLoadClass::LoadKind::kBootImageLinkTimePcRelative.
Nicolas Geoffraye6c0f2a2020-09-07 08:30:52 +01009429 MethodReference target_method = invoke->GetResolvedMethodReference();
Vladimir Marko6fd16062018-06-26 11:02:04 +01009430 dex::TypeIndex type_idx = target_method.dex_file->GetMethodId(target_method.index).class_idx_;
9431 PcRelativePatchInfo* labels = NewBootImageTypePatch(*target_method.dex_file, type_idx);
Vladimir Markode91ca92020-10-27 13:41:40 +00009432 EmitMovwMovtPlaceholder(labels, reg);
Vladimir Marko6fd16062018-06-26 11:02:04 +01009433 } else {
Vladimir Markode91ca92020-10-27 13:41:40 +00009434 uint32_t boot_image_offset = GetBootImageOffsetOfIntrinsicDeclaringClass(invoke);
9435 LoadBootImageAddress(reg, boot_image_offset);
Vladimir Marko6fd16062018-06-26 11:02:04 +01009436 }
Vladimir Marko6fd16062018-06-26 11:02:04 +01009437}
9438
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009439template <linker::LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)>
Artem Serovd4cc5b22016-11-04 11:19:09 +00009440inline void CodeGeneratorARMVIXL::EmitPcRelativeLinkerPatches(
9441 const ArenaDeque<PcRelativePatchInfo>& infos,
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009442 ArenaVector<linker::LinkerPatch>* linker_patches) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00009443 for (const PcRelativePatchInfo& info : infos) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009444 const DexFile* dex_file = info.target_dex_file;
Artem Serovd4cc5b22016-11-04 11:19:09 +00009445 size_t offset_or_index = info.offset_or_index;
9446 DCHECK(info.add_pc_label.IsBound());
9447 uint32_t add_pc_offset = dchecked_integral_cast<uint32_t>(info.add_pc_label.GetLocation());
9448 // Add MOVW patch.
9449 DCHECK(info.movw_label.IsBound());
9450 uint32_t movw_offset = dchecked_integral_cast<uint32_t>(info.movw_label.GetLocation());
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009451 linker_patches->push_back(Factory(movw_offset, dex_file, add_pc_offset, offset_or_index));
Artem Serovd4cc5b22016-11-04 11:19:09 +00009452 // Add MOVT patch.
9453 DCHECK(info.movt_label.IsBound());
9454 uint32_t movt_offset = dchecked_integral_cast<uint32_t>(info.movt_label.GetLocation());
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009455 linker_patches->push_back(Factory(movt_offset, dex_file, add_pc_offset, offset_or_index));
Artem Serovd4cc5b22016-11-04 11:19:09 +00009456 }
9457}
9458
Vladimir Marko6fd16062018-06-26 11:02:04 +01009459template <linker::LinkerPatch (*Factory)(size_t, uint32_t, uint32_t)>
9460linker::LinkerPatch NoDexFileAdapter(size_t literal_offset,
9461 const DexFile* target_dex_file,
9462 uint32_t pc_insn_offset,
9463 uint32_t boot_image_offset) {
9464 DCHECK(target_dex_file == nullptr); // Unused for these patches, should be null.
9465 return Factory(literal_offset, pc_insn_offset, boot_image_offset);
Vladimir Markob066d432018-01-03 13:14:37 +00009466}
9467
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009468void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* linker_patches) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00009469 DCHECK(linker_patches->empty());
9470 size_t size =
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009471 /* MOVW+MOVT for each entry */ 2u * boot_image_method_patches_.size() +
Vladimir Marko0eb882b2017-05-15 13:39:18 +01009472 /* MOVW+MOVT for each entry */ 2u * method_bss_entry_patches_.size() +
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009473 /* MOVW+MOVT for each entry */ 2u * boot_image_type_patches_.size() +
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009474 /* MOVW+MOVT for each entry */ 2u * type_bss_entry_patches_.size() +
Vladimir Marko8f63f102020-09-28 12:10:28 +01009475 /* MOVW+MOVT for each entry */ 2u * public_type_bss_entry_patches_.size() +
9476 /* MOVW+MOVT for each entry */ 2u * package_type_bss_entry_patches_.size() +
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009477 /* MOVW+MOVT for each entry */ 2u * boot_image_string_patches_.size() +
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01009478 /* MOVW+MOVT for each entry */ 2u * string_bss_entry_patches_.size() +
Vladimir Marko2d06e022019-07-08 15:45:19 +01009479 /* MOVW+MOVT for each entry */ 2u * boot_image_other_patches_.size() +
Vladimir Markof6675082019-05-17 12:05:28 +01009480 call_entrypoint_patches_.size() +
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009481 baker_read_barrier_patches_.size();
Artem Serovd4cc5b22016-11-04 11:19:09 +00009482 linker_patches->reserve(size);
Vladimir Marko44ca0752019-07-29 10:18:25 +01009483 if (GetCompilerOptions().IsBootImage() || GetCompilerOptions().IsBootImageExtension()) {
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009484 EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeMethodPatch>(
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009485 boot_image_method_patches_, linker_patches);
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009486 EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeTypePatch>(
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009487 boot_image_type_patches_, linker_patches);
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009488 EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeStringPatch>(
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009489 boot_image_string_patches_, linker_patches);
Vladimir Marko65979462017-05-19 17:25:12 +01009490 } else {
Vladimir Marko2d06e022019-07-08 15:45:19 +01009491 DCHECK(boot_image_method_patches_.empty());
Vladimir Markoe47f60c2018-02-21 13:43:28 +00009492 DCHECK(boot_image_type_patches_.empty());
9493 DCHECK(boot_image_string_patches_.empty());
Vladimir Marko2d06e022019-07-08 15:45:19 +01009494 }
9495 if (GetCompilerOptions().IsBootImage()) {
9496 EmitPcRelativeLinkerPatches<NoDexFileAdapter<linker::LinkerPatch::IntrinsicReferencePatch>>(
9497 boot_image_other_patches_, linker_patches);
9498 } else {
9499 EmitPcRelativeLinkerPatches<NoDexFileAdapter<linker::LinkerPatch::DataBimgRelRoPatch>>(
9500 boot_image_other_patches_, linker_patches);
Artem Serovd4cc5b22016-11-04 11:19:09 +00009501 }
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009502 EmitPcRelativeLinkerPatches<linker::LinkerPatch::MethodBssEntryPatch>(
9503 method_bss_entry_patches_, linker_patches);
9504 EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeBssEntryPatch>(
9505 type_bss_entry_patches_, linker_patches);
Vladimir Marko8f63f102020-09-28 12:10:28 +01009506 EmitPcRelativeLinkerPatches<linker::LinkerPatch::PublicTypeBssEntryPatch>(
9507 public_type_bss_entry_patches_, linker_patches);
9508 EmitPcRelativeLinkerPatches<linker::LinkerPatch::PackageTypeBssEntryPatch>(
9509 package_type_bss_entry_patches_, linker_patches);
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009510 EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringBssEntryPatch>(
9511 string_bss_entry_patches_, linker_patches);
Vladimir Markof6675082019-05-17 12:05:28 +01009512 for (const PatchInfo<vixl32::Label>& info : call_entrypoint_patches_) {
9513 DCHECK(info.target_dex_file == nullptr);
9514 linker_patches->push_back(linker::LinkerPatch::CallEntrypointPatch(
9515 info.label.GetLocation(), info.offset_or_index));
9516 }
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009517 for (const BakerReadBarrierPatchInfo& info : baker_read_barrier_patches_) {
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009518 linker_patches->push_back(linker::LinkerPatch::BakerReadBarrierBranchPatch(
9519 info.label.GetLocation(), info.custom_data));
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009520 }
Vladimir Marko1998cd02017-01-13 13:02:58 +00009521 DCHECK_EQ(size, linker_patches->size());
Artem Serovc5fcb442016-12-02 19:19:58 +00009522}
9523
Vladimir Markoca1e0382018-04-11 09:58:41 +00009524bool CodeGeneratorARMVIXL::NeedsThunkCode(const linker::LinkerPatch& patch) const {
Vladimir Markof6675082019-05-17 12:05:28 +01009525 return patch.GetType() == linker::LinkerPatch::Type::kCallEntrypoint ||
9526 patch.GetType() == linker::LinkerPatch::Type::kBakerReadBarrierBranch ||
Vladimir Markoca1e0382018-04-11 09:58:41 +00009527 patch.GetType() == linker::LinkerPatch::Type::kCallRelative;
9528}
9529
9530void CodeGeneratorARMVIXL::EmitThunkCode(const linker::LinkerPatch& patch,
9531 /*out*/ ArenaVector<uint8_t>* code,
9532 /*out*/ std::string* debug_name) {
9533 arm::ArmVIXLAssembler assembler(GetGraph()->GetAllocator());
9534 switch (patch.GetType()) {
Vladimir Markof6675082019-05-17 12:05:28 +01009535 case linker::LinkerPatch::Type::kCallRelative: {
Vladimir Markoca1e0382018-04-11 09:58:41 +00009536 // The thunk just uses the entry point in the ArtMethod. This works even for calls
9537 // to the generic JNI and interpreter trampolines.
Vladimir Markof6675082019-05-17 12:05:28 +01009538 MemberOffset offset = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize);
9539 assembler.LoadFromOffset(arm::kLoadWord, vixl32::pc, vixl32::r0, offset.Int32Value());
Vladimir Markoca1e0382018-04-11 09:58:41 +00009540 assembler.GetVIXLAssembler()->Bkpt(0);
9541 if (GetCompilerOptions().GenerateAnyDebugInfo()) {
9542 *debug_name = "MethodCallThunk";
9543 }
9544 break;
Vladimir Markof6675082019-05-17 12:05:28 +01009545 }
9546 case linker::LinkerPatch::Type::kCallEntrypoint: {
9547 assembler.LoadFromOffset(arm::kLoadWord, vixl32::pc, tr, patch.EntrypointOffset());
9548 assembler.GetVIXLAssembler()->Bkpt(0);
9549 if (GetCompilerOptions().GenerateAnyDebugInfo()) {
9550 *debug_name = "EntrypointCallThunk_" + std::to_string(patch.EntrypointOffset());
9551 }
9552 break;
9553 }
9554 case linker::LinkerPatch::Type::kBakerReadBarrierBranch: {
Vladimir Markoca1e0382018-04-11 09:58:41 +00009555 DCHECK_EQ(patch.GetBakerCustomValue2(), 0u);
9556 CompileBakerReadBarrierThunk(assembler, patch.GetBakerCustomValue1(), debug_name);
9557 break;
Vladimir Markof6675082019-05-17 12:05:28 +01009558 }
Vladimir Markoca1e0382018-04-11 09:58:41 +00009559 default:
9560 LOG(FATAL) << "Unexpected patch type " << patch.GetType();
9561 UNREACHABLE();
9562 }
9563
9564 // Ensure we emit the literal pool if any.
9565 assembler.FinalizeCode();
9566 code->resize(assembler.CodeSize());
9567 MemoryRegion code_region(code->data(), code->size());
9568 assembler.FinalizeInstructions(code_region);
9569}
9570
Artem Serovc5fcb442016-12-02 19:19:58 +00009571VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateUint32Literal(
9572 uint32_t value,
9573 Uint32ToLiteralMap* map) {
9574 return map->GetOrCreate(
9575 value,
9576 [this, value]() {
Andreas Gampe3db70682018-12-26 15:12:03 -08009577 return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* value= */ value);
Artem Serovc5fcb442016-12-02 19:19:58 +00009578 });
9579}
9580
Artem Serov2bbc9532016-10-21 11:51:50 +01009581void LocationsBuilderARMVIXL::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
9582 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01009583 new (GetGraph()->GetAllocator()) LocationSummary(instr, LocationSummary::kNoCall);
Artem Serov2bbc9532016-10-21 11:51:50 +01009584 locations->SetInAt(HMultiplyAccumulate::kInputAccumulatorIndex,
9585 Location::RequiresRegister());
9586 locations->SetInAt(HMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister());
9587 locations->SetInAt(HMultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister());
9588 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
9589}
9590
9591void InstructionCodeGeneratorARMVIXL::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
9592 vixl32::Register res = OutputRegister(instr);
9593 vixl32::Register accumulator =
9594 InputRegisterAt(instr, HMultiplyAccumulate::kInputAccumulatorIndex);
9595 vixl32::Register mul_left =
9596 InputRegisterAt(instr, HMultiplyAccumulate::kInputMulLeftIndex);
9597 vixl32::Register mul_right =
9598 InputRegisterAt(instr, HMultiplyAccumulate::kInputMulRightIndex);
9599
9600 if (instr->GetOpKind() == HInstruction::kAdd) {
9601 __ Mla(res, mul_left, mul_right, accumulator);
9602 } else {
9603 __ Mls(res, mul_left, mul_right, accumulator);
9604 }
9605}
9606
Artem Serov551b28f2016-10-18 19:11:30 +01009607void LocationsBuilderARMVIXL::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
9608 // Nothing to do, this should be removed during prepare for register allocator.
9609 LOG(FATAL) << "Unreachable";
9610}
9611
9612void InstructionCodeGeneratorARMVIXL::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
9613 // Nothing to do, this should be removed during prepare for register allocator.
9614 LOG(FATAL) << "Unreachable";
9615}
9616
9617// Simple implementation of packed switch - generate cascaded compare/jumps.
9618void LocationsBuilderARMVIXL::VisitPackedSwitch(HPackedSwitch* switch_instr) {
9619 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01009620 new (GetGraph()->GetAllocator()) LocationSummary(switch_instr, LocationSummary::kNoCall);
Artem Serov551b28f2016-10-18 19:11:30 +01009621 locations->SetInAt(0, Location::RequiresRegister());
9622 if (switch_instr->GetNumEntries() > kPackedSwitchCompareJumpThreshold &&
9623 codegen_->GetAssembler()->GetVIXLAssembler()->IsUsingT32()) {
9624 locations->AddTemp(Location::RequiresRegister()); // We need a temp for the table base.
9625 if (switch_instr->GetStartValue() != 0) {
9626 locations->AddTemp(Location::RequiresRegister()); // We need a temp for the bias.
9627 }
9628 }
9629}
9630
9631// TODO(VIXL): Investigate and reach the parity with old arm codegen.
9632void InstructionCodeGeneratorARMVIXL::VisitPackedSwitch(HPackedSwitch* switch_instr) {
9633 int32_t lower_bound = switch_instr->GetStartValue();
9634 uint32_t num_entries = switch_instr->GetNumEntries();
9635 LocationSummary* locations = switch_instr->GetLocations();
9636 vixl32::Register value_reg = InputRegisterAt(switch_instr, 0);
9637 HBasicBlock* default_block = switch_instr->GetDefaultBlock();
9638
9639 if (num_entries <= kPackedSwitchCompareJumpThreshold ||
9640 !codegen_->GetAssembler()->GetVIXLAssembler()->IsUsingT32()) {
9641 // Create a series of compare/jumps.
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009642 UseScratchRegisterScope temps(GetVIXLAssembler());
Artem Serov551b28f2016-10-18 19:11:30 +01009643 vixl32::Register temp_reg = temps.Acquire();
9644 // Note: It is fine for the below AddConstantSetFlags() using IP register to temporarily store
9645 // the immediate, because IP is used as the destination register. For the other
9646 // AddConstantSetFlags() and GenerateCompareWithImmediate(), the immediate values are constant,
9647 // and they can be encoded in the instruction without making use of IP register.
9648 __ Adds(temp_reg, value_reg, -lower_bound);
9649
9650 const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors();
9651 // Jump to successors[0] if value == lower_bound.
9652 __ B(eq, codegen_->GetLabelOf(successors[0]));
9653 int32_t last_index = 0;
9654 for (; num_entries - last_index > 2; last_index += 2) {
9655 __ Adds(temp_reg, temp_reg, -2);
9656 // Jump to successors[last_index + 1] if value < case_value[last_index + 2].
9657 __ B(lo, codegen_->GetLabelOf(successors[last_index + 1]));
9658 // Jump to successors[last_index + 2] if value == case_value[last_index + 2].
9659 __ B(eq, codegen_->GetLabelOf(successors[last_index + 2]));
9660 }
9661 if (num_entries - last_index == 2) {
9662 // The last missing case_value.
9663 __ Cmp(temp_reg, 1);
9664 __ B(eq, codegen_->GetLabelOf(successors[last_index + 1]));
9665 }
9666
9667 // And the default for any other value.
9668 if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) {
9669 __ B(codegen_->GetLabelOf(default_block));
9670 }
9671 } else {
9672 // Create a table lookup.
9673 vixl32::Register table_base = RegisterFrom(locations->GetTemp(0));
9674
9675 JumpTableARMVIXL* jump_table = codegen_->CreateJumpTable(switch_instr);
9676
9677 // Remove the bias.
9678 vixl32::Register key_reg;
9679 if (lower_bound != 0) {
9680 key_reg = RegisterFrom(locations->GetTemp(1));
9681 __ Sub(key_reg, value_reg, lower_bound);
9682 } else {
9683 key_reg = value_reg;
9684 }
9685
9686 // Check whether the value is in the table, jump to default block if not.
9687 __ Cmp(key_reg, num_entries - 1);
9688 __ B(hi, codegen_->GetLabelOf(default_block));
9689
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009690 UseScratchRegisterScope temps(GetVIXLAssembler());
Artem Serov551b28f2016-10-18 19:11:30 +01009691 vixl32::Register jump_offset = temps.Acquire();
9692
9693 // Load jump offset from the table.
Scott Wakeling86e9d262017-01-18 15:59:24 +00009694 {
9695 const size_t jump_size = switch_instr->GetNumEntries() * sizeof(int32_t);
9696 ExactAssemblyScope aas(GetVIXLAssembler(),
9697 (vixl32::kMaxInstructionSizeInBytes * 4) + jump_size,
9698 CodeBufferCheckScope::kMaximumSize);
9699 __ adr(table_base, jump_table->GetTableStartLabel());
9700 __ ldr(jump_offset, MemOperand(table_base, key_reg, vixl32::LSL, 2));
Artem Serov551b28f2016-10-18 19:11:30 +01009701
Scott Wakeling86e9d262017-01-18 15:59:24 +00009702 // Jump to target block by branching to table_base(pc related) + offset.
9703 vixl32::Register target_address = table_base;
9704 __ add(target_address, table_base, jump_offset);
9705 __ bx(target_address);
Artem Serov09a940d2016-11-11 16:15:11 +00009706
Scott Wakeling86e9d262017-01-18 15:59:24 +00009707 jump_table->EmitTable(codegen_);
9708 }
Artem Serov551b28f2016-10-18 19:11:30 +01009709 }
9710}
9711
Artem Serov02d37832016-10-25 15:25:33 +01009712// Copy the result of a call into the given target.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01009713void CodeGeneratorARMVIXL::MoveFromReturnRegister(Location trg, DataType::Type type) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009714 if (!trg.IsValid()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01009715 DCHECK_EQ(type, DataType::Type::kVoid);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009716 return;
9717 }
9718
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01009719 DCHECK_NE(type, DataType::Type::kVoid);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009720
Artem Serovd4cc5b22016-11-04 11:19:09 +00009721 Location return_loc = InvokeDexCallingConventionVisitorARMVIXL().GetReturnLocation(type);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009722 if (return_loc.Equals(trg)) {
9723 return;
9724 }
9725
Vladimir Markoac3fcff2020-11-17 12:17:58 +00009726 // Let the parallel move resolver take care of all of this.
9727 HParallelMove parallel_move(GetGraph()->GetAllocator());
9728 parallel_move.AddMove(return_loc, trg, type, nullptr);
9729 GetMoveResolver()->EmitNativeCode(&parallel_move);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01009730}
Scott Wakelingfe885462016-09-22 10:24:38 +01009731
xueliang.zhong8d2c4592016-11-23 17:05:25 +00009732void LocationsBuilderARMVIXL::VisitClassTableGet(HClassTableGet* instruction) {
9733 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01009734 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
xueliang.zhong8d2c4592016-11-23 17:05:25 +00009735 locations->SetInAt(0, Location::RequiresRegister());
9736 locations->SetOut(Location::RequiresRegister());
Artem Serov551b28f2016-10-18 19:11:30 +01009737}
9738
xueliang.zhong8d2c4592016-11-23 17:05:25 +00009739void InstructionCodeGeneratorARMVIXL::VisitClassTableGet(HClassTableGet* instruction) {
9740 if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) {
9741 uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
9742 instruction->GetIndex(), kArmPointerSize).SizeValue();
9743 GetAssembler()->LoadFromOffset(kLoadWord,
9744 OutputRegister(instruction),
9745 InputRegisterAt(instruction, 0),
9746 method_offset);
9747 } else {
9748 uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement(
9749 instruction->GetIndex(), kArmPointerSize));
9750 GetAssembler()->LoadFromOffset(kLoadWord,
9751 OutputRegister(instruction),
9752 InputRegisterAt(instruction, 0),
9753 mirror::Class::ImtPtrOffset(kArmPointerSize).Uint32Value());
9754 GetAssembler()->LoadFromOffset(kLoadWord,
9755 OutputRegister(instruction),
9756 OutputRegister(instruction),
9757 method_offset);
9758 }
Artem Serov551b28f2016-10-18 19:11:30 +01009759}
9760
Artem Serovc5fcb442016-12-02 19:19:58 +00009761static void PatchJitRootUse(uint8_t* code,
9762 const uint8_t* roots_data,
9763 VIXLUInt32Literal* literal,
9764 uint64_t index_in_table) {
9765 DCHECK(literal->IsBound());
9766 uint32_t literal_offset = literal->GetLocation();
9767 uintptr_t address =
9768 reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>);
9769 uint8_t* data = code + literal_offset;
9770 reinterpret_cast<uint32_t*>(data)[0] = dchecked_integral_cast<uint32_t>(address);
9771}
9772
9773void CodeGeneratorARMVIXL::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) {
9774 for (const auto& entry : jit_string_patches_) {
Vladimir Marko7d157fc2017-05-10 16:29:23 +01009775 const StringReference& string_reference = entry.first;
9776 VIXLUInt32Literal* table_entry_literal = entry.second;
Vladimir Marko174b2e22017-10-12 13:34:49 +01009777 uint64_t index_in_table = GetJitStringRootIndex(string_reference);
Vladimir Marko7d157fc2017-05-10 16:29:23 +01009778 PatchJitRootUse(code, roots_data, table_entry_literal, index_in_table);
Artem Serovc5fcb442016-12-02 19:19:58 +00009779 }
9780 for (const auto& entry : jit_class_patches_) {
Vladimir Marko7d157fc2017-05-10 16:29:23 +01009781 const TypeReference& type_reference = entry.first;
9782 VIXLUInt32Literal* table_entry_literal = entry.second;
Vladimir Marko174b2e22017-10-12 13:34:49 +01009783 uint64_t index_in_table = GetJitClassRootIndex(type_reference);
Vladimir Marko7d157fc2017-05-10 16:29:23 +01009784 PatchJitRootUse(code, roots_data, table_entry_literal, index_in_table);
Artem Serovc5fcb442016-12-02 19:19:58 +00009785 }
9786}
9787
Artem Serovd4cc5b22016-11-04 11:19:09 +00009788void CodeGeneratorARMVIXL::EmitMovwMovtPlaceholder(
9789 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels,
9790 vixl32::Register out) {
Artem Serov0fb37192016-12-06 18:13:40 +00009791 ExactAssemblyScope aas(GetVIXLAssembler(),
9792 3 * vixl32::kMaxInstructionSizeInBytes,
9793 CodeBufferCheckScope::kMaximumSize);
Artem Serovd4cc5b22016-11-04 11:19:09 +00009794 // TODO(VIXL): Think about using mov instead of movw.
9795 __ bind(&labels->movw_label);
Andreas Gampe3db70682018-12-26 15:12:03 -08009796 __ movw(out, /* operand= */ 0u);
Artem Serovd4cc5b22016-11-04 11:19:09 +00009797 __ bind(&labels->movt_label);
Andreas Gampe3db70682018-12-26 15:12:03 -08009798 __ movt(out, /* operand= */ 0u);
Artem Serovd4cc5b22016-11-04 11:19:09 +00009799 __ bind(&labels->add_pc_label);
9800 __ add(out, out, pc);
9801}
9802
Scott Wakelingfe885462016-09-22 10:24:38 +01009803#undef __
9804#undef QUICK_ENTRY_POINT
9805#undef TODO_VIXL32
9806
Vladimir Markoca1e0382018-04-11 09:58:41 +00009807#define __ assembler.GetVIXLAssembler()->
9808
9809static void EmitGrayCheckAndFastPath(ArmVIXLAssembler& assembler,
9810 vixl32::Register base_reg,
9811 vixl32::MemOperand& lock_word,
9812 vixl32::Label* slow_path,
Vladimir Marko7a695052018-04-12 10:26:50 +01009813 int32_t raw_ldr_offset,
9814 vixl32::Label* throw_npe = nullptr) {
Vladimir Markoca1e0382018-04-11 09:58:41 +00009815 // Load the lock word containing the rb_state.
9816 __ Ldr(ip, lock_word);
9817 // Given the numeric representation, it's enough to check the low bit of the rb_state.
Roland Levillain14e5a292018-06-28 12:00:56 +01009818 static_assert(ReadBarrier::NonGrayState() == 0, "Expecting non-gray to have value 0");
Vladimir Markoca1e0382018-04-11 09:58:41 +00009819 static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
9820 __ Tst(ip, Operand(LockWord::kReadBarrierStateMaskShifted));
Andreas Gampe3db70682018-12-26 15:12:03 -08009821 __ B(ne, slow_path, /* is_far_target= */ false);
Vladimir Marko7a695052018-04-12 10:26:50 +01009822 // To throw NPE, we return to the fast path; the artificial dependence below does not matter.
9823 if (throw_npe != nullptr) {
9824 __ Bind(throw_npe);
9825 }
Vladimir Markoca1e0382018-04-11 09:58:41 +00009826 __ Add(lr, lr, raw_ldr_offset);
9827 // Introduce a dependency on the lock_word including rb_state,
9828 // to prevent load-load reordering, and without using
9829 // a memory barrier (which would be more expensive).
9830 __ Add(base_reg, base_reg, Operand(ip, LSR, 32));
9831 __ Bx(lr); // And return back to the function.
9832 // Note: The fake dependency is unnecessary for the slow path.
9833}
9834
9835// Load the read barrier introspection entrypoint in register `entrypoint`
Vladimir Markodcd117e2018-04-19 11:54:00 +01009836static vixl32::Register LoadReadBarrierMarkIntrospectionEntrypoint(ArmVIXLAssembler& assembler) {
Vladimir Markoca1e0382018-04-11 09:58:41 +00009837 // The register where the read barrier introspection entrypoint is loaded
Vladimir Markodcd117e2018-04-19 11:54:00 +01009838 // is the marking register. We clobber it here and the entrypoint restores it to 1.
9839 vixl32::Register entrypoint = mr;
Vladimir Markoca1e0382018-04-11 09:58:41 +00009840 // entrypoint = Thread::Current()->pReadBarrierMarkReg12, i.e. pReadBarrierMarkIntrospection.
9841 DCHECK_EQ(ip.GetCode(), 12u);
9842 const int32_t entry_point_offset =
9843 Thread::ReadBarrierMarkEntryPointsOffset<kArmPointerSize>(ip.GetCode());
9844 __ Ldr(entrypoint, MemOperand(tr, entry_point_offset));
Vladimir Markodcd117e2018-04-19 11:54:00 +01009845 return entrypoint;
Vladimir Markoca1e0382018-04-11 09:58:41 +00009846}
9847
9848void CodeGeneratorARMVIXL::CompileBakerReadBarrierThunk(ArmVIXLAssembler& assembler,
9849 uint32_t encoded_data,
9850 /*out*/ std::string* debug_name) {
9851 BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data);
9852 switch (kind) {
9853 case BakerReadBarrierKind::kField: {
Vladimir Markoca1e0382018-04-11 09:58:41 +00009854 vixl32::Register base_reg(BakerReadBarrierFirstRegField::Decode(encoded_data));
9855 CheckValidReg(base_reg.GetCode());
9856 vixl32::Register holder_reg(BakerReadBarrierSecondRegField::Decode(encoded_data));
9857 CheckValidReg(holder_reg.GetCode());
9858 BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data);
9859 UseScratchRegisterScope temps(assembler.GetVIXLAssembler());
9860 temps.Exclude(ip);
Roland Levillain988c3912019-09-25 19:33:35 +01009861 // In the case of a field load, if `base_reg` differs from
9862 // `holder_reg`, the offset was too large and we must have emitted (during the construction
9863 // of the HIR graph, see `art::HInstructionBuilder::BuildInstanceFieldAccess`) and preserved
9864 // (see `art::PrepareForRegisterAllocation::VisitNullCheck`) an explicit null check before
9865 // the load. Otherwise, for implicit null checks, we need to null-check the holder as we do
9866 // not necessarily do that check before going to the thunk.
Vladimir Marko7a695052018-04-12 10:26:50 +01009867 vixl32::Label throw_npe_label;
9868 vixl32::Label* throw_npe = nullptr;
9869 if (GetCompilerOptions().GetImplicitNullChecks() && holder_reg.Is(base_reg)) {
9870 throw_npe = &throw_npe_label;
Andreas Gampe3db70682018-12-26 15:12:03 -08009871 __ CompareAndBranchIfZero(holder_reg, throw_npe, /* is_far_target= */ false);
Vladimir Markoca1e0382018-04-11 09:58:41 +00009872 }
Vladimir Marko7a695052018-04-12 10:26:50 +01009873 // Check if the holder is gray and, if not, add fake dependency to the base register
9874 // and return to the LDR instruction to load the reference. Otherwise, use introspection
9875 // to load the reference and call the entrypoint that performs further checks on the
9876 // reference and marks it if needed.
Vladimir Markoca1e0382018-04-11 09:58:41 +00009877 vixl32::Label slow_path;
9878 MemOperand lock_word(holder_reg, mirror::Object::MonitorOffset().Int32Value());
9879 const int32_t raw_ldr_offset = (width == BakerReadBarrierWidth::kWide)
9880 ? BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET
9881 : BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET;
Vladimir Marko7a695052018-04-12 10:26:50 +01009882 EmitGrayCheckAndFastPath(
9883 assembler, base_reg, lock_word, &slow_path, raw_ldr_offset, throw_npe);
Vladimir Markoca1e0382018-04-11 09:58:41 +00009884 __ Bind(&slow_path);
9885 const int32_t ldr_offset = /* Thumb state adjustment (LR contains Thumb state). */ -1 +
9886 raw_ldr_offset;
Vladimir Markodcd117e2018-04-19 11:54:00 +01009887 vixl32::Register ep_reg = LoadReadBarrierMarkIntrospectionEntrypoint(assembler);
Vladimir Markoca1e0382018-04-11 09:58:41 +00009888 if (width == BakerReadBarrierWidth::kWide) {
9889 MemOperand ldr_half_address(lr, ldr_offset + 2);
9890 __ Ldrh(ip, ldr_half_address); // Load the LDR immediate half-word with "Rt | imm12".
9891 __ Ubfx(ip, ip, 0, 12); // Extract the offset imm12.
9892 __ Ldr(ip, MemOperand(base_reg, ip)); // Load the reference.
9893 } else {
9894 MemOperand ldr_address(lr, ldr_offset);
9895 __ Ldrh(ip, ldr_address); // Load the LDR immediate, encoding T1.
9896 __ Add(ep_reg, // Adjust the entrypoint address to the entrypoint
9897 ep_reg, // for narrow LDR.
9898 Operand(BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_ENTRYPOINT_OFFSET));
9899 __ Ubfx(ip, ip, 6, 5); // Extract the imm5, i.e. offset / 4.
9900 __ Ldr(ip, MemOperand(base_reg, ip, LSL, 2)); // Load the reference.
9901 }
9902 // Do not unpoison. With heap poisoning enabled, the entrypoint expects a poisoned reference.
9903 __ Bx(ep_reg); // Jump to the entrypoint.
Vladimir Markoca1e0382018-04-11 09:58:41 +00009904 break;
9905 }
9906 case BakerReadBarrierKind::kArray: {
9907 vixl32::Register base_reg(BakerReadBarrierFirstRegField::Decode(encoded_data));
9908 CheckValidReg(base_reg.GetCode());
9909 DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg,
9910 BakerReadBarrierSecondRegField::Decode(encoded_data));
9911 DCHECK(BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide);
9912 UseScratchRegisterScope temps(assembler.GetVIXLAssembler());
9913 temps.Exclude(ip);
9914 vixl32::Label slow_path;
9915 int32_t data_offset =
9916 mirror::Array::DataOffset(Primitive::ComponentSize(Primitive::kPrimNot)).Int32Value();
9917 MemOperand lock_word(base_reg, mirror::Object::MonitorOffset().Int32Value() - data_offset);
9918 DCHECK_LT(lock_word.GetOffsetImmediate(), 0);
9919 const int32_t raw_ldr_offset = BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET;
9920 EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path, raw_ldr_offset);
9921 __ Bind(&slow_path);
9922 const int32_t ldr_offset = /* Thumb state adjustment (LR contains Thumb state). */ -1 +
9923 raw_ldr_offset;
9924 MemOperand ldr_address(lr, ldr_offset + 2);
9925 __ Ldrb(ip, ldr_address); // Load the LDR (register) byte with "00 | imm2 | Rm",
9926 // i.e. Rm+32 because the scale in imm2 is 2.
Vladimir Markodcd117e2018-04-19 11:54:00 +01009927 vixl32::Register ep_reg = LoadReadBarrierMarkIntrospectionEntrypoint(assembler);
Vladimir Markoca1e0382018-04-11 09:58:41 +00009928 __ Bfi(ep_reg, ip, 3, 6); // Insert ip to the entrypoint address to create
9929 // a switch case target based on the index register.
9930 __ Mov(ip, base_reg); // Move the base register to ip0.
9931 __ Bx(ep_reg); // Jump to the entrypoint's array switch case.
9932 break;
9933 }
Vladimir Markod887ed82018-08-14 13:52:12 +00009934 case BakerReadBarrierKind::kGcRoot:
9935 case BakerReadBarrierKind::kUnsafeCas: {
Vladimir Markoca1e0382018-04-11 09:58:41 +00009936 // Check if the reference needs to be marked and if so (i.e. not null, not marked yet
9937 // and it does not have a forwarding address), call the correct introspection entrypoint;
9938 // otherwise return the reference (or the extracted forwarding address).
9939 // There is no gray bit check for GC roots.
9940 vixl32::Register root_reg(BakerReadBarrierFirstRegField::Decode(encoded_data));
9941 CheckValidReg(root_reg.GetCode());
9942 DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg,
9943 BakerReadBarrierSecondRegField::Decode(encoded_data));
9944 BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data);
9945 UseScratchRegisterScope temps(assembler.GetVIXLAssembler());
9946 temps.Exclude(ip);
9947 vixl32::Label return_label, not_marked, forwarding_address;
Andreas Gampe3db70682018-12-26 15:12:03 -08009948 __ CompareAndBranchIfZero(root_reg, &return_label, /* is_far_target= */ false);
Vladimir Markoca1e0382018-04-11 09:58:41 +00009949 MemOperand lock_word(root_reg, mirror::Object::MonitorOffset().Int32Value());
9950 __ Ldr(ip, lock_word);
9951 __ Tst(ip, LockWord::kMarkBitStateMaskShifted);
9952 __ B(eq, &not_marked);
9953 __ Bind(&return_label);
9954 __ Bx(lr);
9955 __ Bind(&not_marked);
9956 static_assert(LockWord::kStateShift == 30 && LockWord::kStateForwardingAddress == 3,
9957 "To use 'CMP ip, #modified-immediate; BHS', we need the lock word state in "
9958 " the highest bits and the 'forwarding address' state to have all bits set");
9959 __ Cmp(ip, Operand(0xc0000000));
9960 __ B(hs, &forwarding_address);
Vladimir Markodcd117e2018-04-19 11:54:00 +01009961 vixl32::Register ep_reg = LoadReadBarrierMarkIntrospectionEntrypoint(assembler);
Vladimir Markoca1e0382018-04-11 09:58:41 +00009962 // Adjust the art_quick_read_barrier_mark_introspection address in kBakerCcEntrypointRegister
Vladimir Markod887ed82018-08-14 13:52:12 +00009963 // to one of art_quick_read_barrier_mark_introspection_{gc_roots_{wide,narrow},unsafe_cas}.
9964 DCHECK(kind != BakerReadBarrierKind::kUnsafeCas || width == BakerReadBarrierWidth::kWide);
9965 int32_t entrypoint_offset =
9966 (kind == BakerReadBarrierKind::kGcRoot)
9967 ? (width == BakerReadBarrierWidth::kWide)
9968 ? BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_ENTRYPOINT_OFFSET
9969 : BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_ENTRYPOINT_OFFSET
9970 : BAKER_MARK_INTROSPECTION_UNSAFE_CAS_ENTRYPOINT_OFFSET;
Vladimir Markoca1e0382018-04-11 09:58:41 +00009971 __ Add(ep_reg, ep_reg, Operand(entrypoint_offset));
9972 __ Mov(ip, root_reg);
9973 __ Bx(ep_reg);
9974 __ Bind(&forwarding_address);
9975 __ Lsl(root_reg, ip, LockWord::kForwardingAddressShift);
9976 __ Bx(lr);
9977 break;
9978 }
9979 default:
9980 LOG(FATAL) << "Unexpected kind: " << static_cast<uint32_t>(kind);
9981 UNREACHABLE();
9982 }
9983
Vladimir Marko966b46f2018-08-03 10:20:19 +00009984 // For JIT, the slow path is considered part of the compiled method,
Vladimir Markof91fc122020-05-13 09:21:00 +01009985 // so JIT should pass null as `debug_name`.
Vladimir Marko695348f2020-05-19 14:42:02 +01009986 DCHECK(!GetCompilerOptions().IsJitCompiler() || debug_name == nullptr);
Vladimir Marko966b46f2018-08-03 10:20:19 +00009987 if (debug_name != nullptr && GetCompilerOptions().GenerateAnyDebugInfo()) {
Vladimir Markoca1e0382018-04-11 09:58:41 +00009988 std::ostringstream oss;
9989 oss << "BakerReadBarrierThunk";
9990 switch (kind) {
9991 case BakerReadBarrierKind::kField:
9992 oss << "Field";
9993 if (BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide) {
9994 oss << "Wide";
9995 }
9996 oss << "_r" << BakerReadBarrierFirstRegField::Decode(encoded_data)
9997 << "_r" << BakerReadBarrierSecondRegField::Decode(encoded_data);
9998 break;
9999 case BakerReadBarrierKind::kArray:
10000 oss << "Array_r" << BakerReadBarrierFirstRegField::Decode(encoded_data);
10001 DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg,
10002 BakerReadBarrierSecondRegField::Decode(encoded_data));
10003 DCHECK(BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide);
10004 break;
10005 case BakerReadBarrierKind::kGcRoot:
10006 oss << "GcRoot";
10007 if (BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide) {
10008 oss << "Wide";
10009 }
10010 oss << "_r" << BakerReadBarrierFirstRegField::Decode(encoded_data);
10011 DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg,
10012 BakerReadBarrierSecondRegField::Decode(encoded_data));
10013 break;
Vladimir Markod887ed82018-08-14 13:52:12 +000010014 case BakerReadBarrierKind::kUnsafeCas:
10015 oss << "UnsafeCas_r" << BakerReadBarrierFirstRegField::Decode(encoded_data);
10016 DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg,
10017 BakerReadBarrierSecondRegField::Decode(encoded_data));
10018 DCHECK(BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide);
10019 break;
Vladimir Markoca1e0382018-04-11 09:58:41 +000010020 }
10021 *debug_name = oss.str();
10022 }
10023}
10024
10025#undef __
10026
Scott Wakelingfe885462016-09-22 10:24:38 +010010027} // namespace arm
10028} // namespace art